Merge pull request #389 from metosin/QuickRouterCreation

Quick router creation
This commit is contained in:
Tommi Reiman 2020-04-27 15:11:46 +03:00 committed by GitHub
commit e5c4ae533f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 86 additions and 28 deletions

View file

@ -301,7 +301,7 @@
([compiled-routes]
(quarantine-router compiled-routes {}))
([compiled-routes opts]
(let [conflicting-paths (-> compiled-routes (impl/path-conflicting-routes opts) impl/conflicting-paths)
(let [conflicting-paths (impl/conflicting-paths (or (::path-conflicting opts) (impl/path-conflicting-routes compiled-routes opts)))
conflicting? #(contains? conflicting-paths (first %))
{conflicting true, non-conflicting false} (group-by conflicting? compiled-routes)
linear-router (linear-router conflicting opts)
@ -364,10 +364,10 @@
([raw-routes]
(router raw-routes {}))
([raw-routes opts]
(let [{:keys [router] :as opts} (merge (default-router-options) opts)]
(let [{:keys [router conflicts] :as opts} (merge (default-router-options) opts)]
(try
(let [routes (impl/resolve-routes raw-routes opts)
path-conflicting (impl/path-conflicting-routes routes opts)
path-conflicting (if-not (and router (not conflicts)) (impl/path-conflicting-routes routes opts))
name-conflicting (impl/name-conflicting-routes routes)
compiled-routes (impl/compile-routes routes opts)
wilds? (boolean (some (impl/->wild-route? opts) compiled-routes))
@ -380,10 +380,8 @@
all-wilds? trie-router
:else mixed-router)]
(when-let [conflicts (:conflicts opts)]
(when-let [conflict-report (impl/unresolved-conflicts
path-conflicting)]
(conflicts conflict-report)))
(when-let [conflict-report (and conflicts (impl/unresolved-conflicts path-conflicting))]
(conflicts conflict-report))
(when name-conflicting
(exception/fail! :name-conflicts name-conflicting))
@ -391,7 +389,7 @@
(when-let [validate (:validate opts)]
(validate compiled-routes opts))
(router compiled-routes opts))
(router compiled-routes (assoc opts ::path-conflicting path-conflicting)))
(catch #?(:clj Exception, :cljs js/Error) e
(throw ((get opts :exception identity) e)))))))

View file

@ -71,17 +71,18 @@
(defn resolve-routes [raw-routes {:keys [coerce] :as opts}]
(cond->> (->> (walk raw-routes opts) (map-data merge-data))
coerce (into [] (keep #(coerce % opts)))))
coerce (into [] (keep #(coerce % opts)))))
(defn path-conflicting-routes [routes opts]
(-> (into {}
(comp (map-indexed (fn [index route]
[route (into #{}
(filter #(trie/conflicting-paths? (first route) (first %) opts))
(subvec routes (inc index)))]))
(filter (comp seq second)))
routes)
(not-empty)))
(let [parts-and-routes (mapv (fn [[s :as r]] [(trie/split-path s opts) r]) routes)]
(-> (into {} (comp (map-indexed (fn [index [p r]]
[r (reduce
(fn [acc [p' r']]
(if (trie/conflicting-parts? p p')
(conj acc r') acc))
#{} (subvec parts-and-routes (inc index)))]))
(filter (comp seq second))) parts-and-routes)
(not-empty))))
(defn unresolved-conflicts [path-conflicting]
(-> (into {}
@ -179,7 +180,7 @@
(URLDecoder/decode
(if (.contains ^String s "+")
(.replace ^String s "+" "%2B")
s)
^String s)
"UTF-8"))
:cljs (js/decodeURIComponent s))))

View file

@ -132,17 +132,18 @@
(concat [(subs x i)] xs)
xs)))
(defn conflicting-parts? [parts1 parts2]
(let [[[s1 & ss1] [s2 & ss2]] (-slice-start parts1 parts2)]
(cond
(= s1 s2 nil) true
(or (nil? s1) (nil? s2)) false
(or (catch-all? s1) (catch-all? s2)) true
(or (wild? s1) (wild? s2)) (recur (-slice-end s1 ss1) (-slice-end s2 ss2))
(not= s1 s2) false
:else (recur ss1 ss2))))
(defn conflicting-paths? [path1 path2 opts]
(loop [parts1 (split-path path1 opts)
parts2 (split-path path2 opts)]
(let [[[s1 & ss1] [s2 & ss2]] (-slice-start parts1 parts2)]
(cond
(= s1 s2 nil) true
(or (nil? s1) (nil? s2)) false
(or (catch-all? s1) (catch-all? s2)) true
(or (wild? s1) (wild? s2)) (recur (-slice-end s1 ss1) (-slice-end s2 ss2))
(not= s1 s2) false
:else (recur ss1 ss2)))))
(conflicting-parts? (split-path path1 opts) (split-path path2 opts)))
;;
;; Creating Tries

View file

@ -0,0 +1,58 @@
(ns reitit.router-creation-perf-test
(:require [reitit.perf-utils :refer [bench! suite]]
[reitit.core :as r]
[clojure.string :as str])
(:import (java.util Random)))
;;
;; start repl with `lein perf repl`
;; perf measured with the following setup:
;;
;; Model Name: MacBook Pro
;; Model Identifier: MacBookPro113
;; Processor Name: Intel Core i7
;; Processor Speed: 2,5 GHz
;; Number of Processors: 1
;; Total Number of Cores: 4
;; L2 Cache (per Core): 256 KB
;; L3 Cache: 6 MB
;; Memory: 16 GB
;;
(defn random [^long seed]
(Random. seed))
(defn rand-str [^Random rnd len]
(apply str (take len (repeatedly #(char (+ (.nextInt rnd 26) 97))))))
(defn route [rnd]
(str/join "/" (repeatedly (+ 2 (.nextInt rnd 8)) (fn [] (rand-str rnd (.nextInt rnd 10))))))
(def hundred-routes
(let [rnd (random 1)]
(mapv (fn [n] [(route rnd) (keyword (str "route" n))]) (range 100))))
(conj hundred-routes (last hundred-routes))
(defn bench-routers []
(suite "non-conflicting")
;; 104ms
;; 11ms (reuse parts in conflict resolution)
(bench! "default" (r/router hundred-routes))
;; 7ms
(bench! "linear" (r/router hundred-routes {:router r/linear-router, :conflicts nil}))
(suite "conflicting")
(let [routes (conj hundred-routes [(first (last hundred-routes)) ::route])]
;; 205ms
;; 105ms (cache path-conflicts)
;; 13ms (reuse parts in conflict resolution)
(bench! "default" (r/router routes {:conflicts nil}))))
(comment
(bench-routers))