SlideShare ist ein Scribd-Unternehmen logo
1 von 76
Downloaden Sie, um offline zu lesen
Reitit
The Ancient Art of Data-Driven
tommi@metosin.ïŹ
Clojure/North
Me(tosin)
Sinclair Spectrum in 1983
...
Co-founded Metosin 2012
20 developers, Tampere & Helsinki / Finland
Projects, Startups and Design
FP, mostly Clojure/Script
https://github.com/metosin
ClojuTRE 2019 (8th)
September 26-27th 2019, Helsinki
FP & Clojure, 300++ on last two years
Basic ïŹ‚ights & hotels for speakers
Free Student & Diversity tickets
Call For Speakers Open
https://clojutre.org
This talk
Routing and dispatching
Reitit, the library
Reitit, the framework
Performance
Takeaways
Routing in Clojure/Script
Lot's of options: Ataraxy, Bide, Bidi, Compojure,
Keechma, Pedestal, Silk, Secretary, ...
Something we need in mostly all the (web)apps
Mapping from path -> match
Mapping from name -> match (reverse routing)
HTTP (both server & browser)
Messaging
Others
Dispatching in Clojure/Script
Here: composing end executing functionality in
the request-response pipeline
Common patterns
Middleware (Ring, nREPL, ..)
Interceptors (Pedestal, Re-Frame, ..)
Controllers (Keechma)
Promises
Sync & Async
New Routing Library?
More data-driven
Leveraging clojure.spec
Built for performance
Friendly & explicit API
Reach: browser, JVM, Node, Others
Both routing and dispatching
The Yellow (Hiccup) Castle of Routing?
Reitit, the Library
reitit-core
(c) https://www.artstation.com/kinixuki
Basics
reitit.core/router to create a router
Matching by path or by name (reverse routing)
Functions to browse the route trees
(defprotocol Router
(router-name [this])
(routes [this])
(compiled-routes [this])
(options [this])
(route-names [this])
(match-by-path [this path])
(match-by-name [this name] [this name path-params]))
(require '[reitit.core :as r])
(def router
(r/router
[["/ping" ::ping]
["/users/:id" ::user]]))
(r/match-by-path router "/ping")
;#Match{:template "/ping"
; :data {:name :user/ping}
; :result nil
; :path-params {}
; :path "/ping"}
(r/match-by-name router ::ping)
;#Match{:template "/ping"
; :data {:name :user/ping}
; :result nil
; :path-params {}
; :path "/ping"}
Route Syntax (~hiccup)
Paths are concatenated, route data meta-merged
(r/router
["/api" {:interceptors [::api]}
["/ping" ::ping]
["/admin" {:roles #{:admin}}
["/users" ::users]
["/db" {:interceptors [::db]
:roles ^:replace #{:db-admin}}]]]))
(r/router
[["/api/ping" {:interceptors [::api]
:name ::ping}]
["/api/admin/users" {:interceptors [::api]
:roles #{:admin}
:name ::users}]
["/api/admin/db" {:interceptors [::api ::db]
:roles #{:db-admin}}]])
Programming routes
nil s and nested sequences are ïŹ‚attened
(defn router [dev?]
(r/router
[["/command/"
(for [mutation [:olipa :kerran :avaruus]]
[(name mutation) mutation])]
(if dev? ["/dev-tools" :dev-tools])]))
(-> (router true) (r/routes))
;[["/command/olipa" {:name :olipa}]
; ["/command/kerran" {:name :kerran}]
; ["/command/avaruus" {:name :avaruus}]
; ["/dev-tools" {:name :dev-tools}]]
Composing routes
It's just data, merge trees
Single route tree allows to optimize the whole
Merging & nesting routers
Empty route fragments, e.g. :<> in Reagent
(-> (r/router
[["" {:interceptors [::cors]}
["/api" ::api]
["/ipa" ::ipa]]
["/ping" ::ping]])
(r/routes))
;[["/api" {:interceptors [::cors], :name ::api}]
; ["/ipa" {:interceptors [::cors], :name ::ipa}]
; ["/ping" {:name ::ping}]]
Route ConïŹ‚ict Resolution
Multiple sources (code/EDN/DB) for routes?
Are all routes still reachable?
Reitit fails-fast by default on path & name conïŹ‚icts
(require '[reitit.core :as r])
(require '[reitit.dev.pretty :as pretty])
(r/router
[["/ping"]
["/:user-id/orders"]
["/bulk/:bulk-id"]
["/public/*path"]
["/:version/status"]]
{:exception pretty/exception})
Route ConïŹ‚ict Error
Community-driven Clojure
error formatter?
(c) https://www.artstation.com/kinixuki
What is in the route data?
Can be anything, the Router doesn't care.
Returned on successful Match
Can be queried from a Router
Build your own interpreters for the data
A Route First Architecture: Match -> Data -> React
(r/match-by-path router "/api/admin/db")
;#Match{:template "/api/admin/db",
; :data {:interceptors [::api ::db]
; :roles #{:db-admin}},
; :result nil,
; :path-params {},
; :path "/api/admin/db"}
Example use cases
Authorization via :roles
Frontend components via :view
Dispatching via :middleware and :interceptors
Stateful dispatching with :controllers
Coercion via :parameters and :coercion
Run requests in a server io-pool with :server/io
Deploy endpoints separately to :server/target
Frontend Example
https://router.vuejs.org/guide/essentials/nested-routes.html
(require '[reitit.frontend :as rf])
(require '[reitit.frontend.easy :as rfe])
(defn frontpage-view [match] ...)
(defn topics-view [match] ... (rf/nested-view match)
(defn topic-view [match] ...)
(def router
(rf/router
[["/" {:name :frontpage
:views [frontpage-view]}]
["/topics"
{:controllers [load-topics]
:views [topics-view]}
["" {:name :topics}]
["/:id"
{:name :topic
:parameters {:path {:id int?}}
:controllers [load-topic]
:views [topic-view]}]]]))
(rfe/start! router ...)
Route Data Validation
Route Data can be anything -> data spaghetti?
Leverage clojure.spec at router creation
DeïŹne and enforce route data specs
Fail-fast, missing, extra or misspelled keys
With help of spec-tools and spell-spec
Closed specs coming to Spec2 (TBD)
Invalid Route Data Example
(require '[reitit.spec :as spec])
(require '[clojure.spec.alpha :as s])
(s/def ::role #{:admin :user})
(s/def ::roles (s/coll-of ::role :into #{}))
(r/router
["/api/admin" {::roles #{:adminz}}]
{:validate spec/validate
:exception pretty/exception})
Route Data Error
Closed Functional Specs
Failing Fast
During static analysis (e.g. linter)
At compile-time (macros, defs)
At development-time (schema/spec annos)
At creation-time
At runtime
At runtime special condition
(c) https://www.artstation.com/kinixuki
Coercion (DĂ©jĂ -vu)
Coercion is a process of transforming values between
domain (e.g. JSON->EDN, String->EDN)
Route data keys :parameters & :coercion
Utilities to apply coercion in reitit.coercion
Implementation for Schema & clojure.spec <-- !!!
(defprotocol Coercion
"Pluggable coercion protocol"
(-get-name [this])
(-get-options [this])
(-get-apidocs [this specification data])
(-compile-model [this model name])
(-open-model [this model])
(-encode-error [this error])
(-request-coercer [this type model])
(-response-coercer [this model]))
Reitit, the Framework
(c) https://www.artstation.com/kinixuki
Framework
You call the library, but the framework calls you
Reitit ships with multiple Routing Frameworks
reitit-ring Middleware-dispatch for Ring
reitit-http Async Interceptor-dispatch for http
reitit-pedestal Reitit Pedestal
reitit-frontend (Keechma-style) Controllers,
History & Fragment Router, helpers
metosin/reitit-ring
(c) https://www.artstation.com/kinixuki
Ring Routing
A separate lightweight module
Routing based on path and :request-method
Adds support for :middleware dispatch
chain is executed after a match
ring-handler to create a ring-compatible handler
Supports Both sync & async
Supports Both JVM & Node
No magic, no default middleware
Ring Application
(require '[reitit.ring :as ring])
(def app
(ring/ring-handler
(ring/router
["/api" {:middleware [wrap-api wrap-roles]}
["/ping" {:get ping-handler]}
["/users" {:middleware [db-middleware]
:roles #{:admin} ;; who reads this?
:get get-users
:post add-user}]])
(ring/create-default-handler)))
(app {:uri "/api/ping" :request-method :get})
; {:status 200, :body "pong"}
Accessing route data
Match and Router are injected into the request
Components can read these at request time and
do what ever they want, "Ad-hoc extensions"
Pattern used in Kekkonen and in Yada
(defn wrap-roles [handler]
;; roles injected via session-middleware
(fn [{:keys [roles] :as request}]
;; read the route-data at request-time
(let [required (-> request (ring/get-match) :data :roles)]
(if (and (seq required)
(not (set/subset? required roles)))
{:status 403, :body "forbidden"}
(handler request)))))
Middleware as data
Ring middleware are opaque functions
Reitit adds a ïŹrst class values, Middleware records
Recursive IntoMiddleware protocol to expand to
Attach documentation, specs, requirements, ...
Can be used in place of middleware functions
Zero runtime penalty
(defn roles-middleware []
{:name ::roles-middleware
:description "Middleware to enforce roles"
:requires #{::session-middleware}
:spec (s/keys :opt-un [::roles])
:wrap wrap-roles})
Compiling middleware
Each middleware knows the endpoint it's mounted to
We can pass the route data in at router creation time
Big win for optimizing chains
(def roles-middleware
{:name ::roles-middleware
:description "Middleware to enforce roles"
:requires #{::session-middleware}
:spec (s/keys :opt-un [::roles])
:compile (fn [{required :roles} _]
;; unmount if there are no roles required
(if (seq required)
(fn [handler]
(fn [{:keys [roles] :as request}]
(if (not (set/subset? required roles))
{:status 403, :body "forbidden"}
(handler request))))))})
Partial Specs Example
"All routes under /account should require a role"
;; look ma, not part of request processing!
(def roles-defined
{:name ::roles-defined
:description "requires a ::role for the routes"
:spec (s/keys :req-un [::roles])})
["/api" {:middleware [roles-middleware]} ;; behavior
["/ping"] ;; unmounted
["/account" {:middleware [roles-defined]} ;; :roles mandatory
["/admin" {:roles #{:admin}}] ;; ok
["/user" {:roles #{:user}}] ;; ok
["/manager"]]] ;; fail!
Middleware chain as data
Each endpoint has it's own vector of middleware
Documents of what is in the chain
Chain can be manipulated at router creation time
Reordering
Completing
Interleaving
Interleaving a request diff console printer:
reitit.ring.middleware.dev/print-request-diffs
Data deïŹnitions (as data)
The core coercion for all (OpenAPI) paramerer &
response types ( :query , :body , header , :path etc)
Separerate Middleware to apply request & response
coercion and format coercion errors
Separate modules, spec coercion via spec-tools
["/plus/:y"
{:get {:parameters {:query {:x int?},
:path {:y int?}}
:responses {200 {:body {:total pos-int?}}}
:handler (fn [{:keys [parameters]}]
;; parameters are coerced
(let [x (-> parameters :query :x)
y (-> parameters :path :y)]
{:status 200
:body {:total (+ x y)}}))}}]
Everything as data.
(c) https://www.artstation.com/kinixuki
Interceptors
metosin/reitit-http
e.g. going async
(c) https://www.artstation.com/kinixuki
Going Async
Interceptors are much better ïŹt for async
reitit-http uses the Interceptor model
Requires an Interceptor Executor
reitit-pedestal or reitit-sieppari
Sieppari supports core.async , Manifold and Promesa
Pedestal is more proven, supports core.async
Target Both JVM & Node (via Sieppari)
Http Application
(require '[reitit.http :as http])
(require '[reitit.interceptor.sieppari :as sieppari]
(def app
(http/ring-handler
(http/router
["/api" {:interceptors [api-interceptor
roles-interceptor]}
["/ping" ping-handler]
["/users" {:interceptors [db-interceptor]
:roles #{:admin}
:get get-users
:post add-user}]])
(ring/create-default-handler)
{:executor sieppari/executor}))
(app {:uri "/api/ping" :request-method :get})
; {:status 200, :body "pong"}
Example Interceptor
(defn coerce-request-interceptor
"Interceptor for pluggable request coercion.
Expects a :coercion of type `reitit.coercion/Coercion`
and :parameters from route data, otherwise does not mount."
[]
{:name ::coerce-request
:spec ::rs/parameters
:compile (fn [{:keys [coercion parameters]} opts]
(cond
;; no coercion, skip
(not coercion) nil
;; just coercion, don't mount
(not parameters) {}
;; mount
:else
(let [coercers (coercion/request-coercers coercion parameters opts)]
{:enter (fn [ctx]
(let [request (:request ctx)
coerced (coercion/coerce-request coercers request)
request (impl/fast-assoc request :parameters coerced)]
(assoc ctx :request request)))})))})
DEMO
(c) https://www.artstation.com/kinixuki
Route-driven frameworks
Routing and dispatching is separated, middleware (or
interceptors) are applied only after a match
Each endpoint has a unique dispatch chain
Each component can be compiled and optimized against
the endpoint at creation time
Components can deïŹne partial route data specs that
only effect the routes they are mounted to.
We get both Performance & Correctness
... this is Kinda Awesome.
(c) https://www.artstation.com/kinixuki
Performance
(c) https://www.artstation.com/kinixuki
Performance
how can we make compojure-api faster?
we moved from Clojure to GO because of perf
How fast are the current Clojure libraries?
How fast can we go with Java/Clojure?
Measuring Performance
Always measure
Both micro & macro benchmarks
In the end, the order of magnitude matters
Lot's of good tools, some favourites:
clojure.core/time
criterium
com.clojure-goes-fast/clj-async-profiler
com.clojure-goes-fast/clj-java-decompiler
https://github.com/wg/wrk
(def defaults {:keywords? true})
(time
(dotimes [_ 10000]
(merge defaults {})))
; "Elapsed time: 4.413803 msecs"
(require '[criterium.core :as cc])
(cc/quick-bench
(merge defaults {}))
; Evaluation count : 2691372 in 6 samples of 448562 calls.
; Execution time mean : 230.346208 ns
; Execution time std-deviation : 10.355077 ns
; Execution time lower quantile : 221.101397 ns ( 2.5%)
; Execution time upper quantile : 245.331388 ns (97.5%)
; Overhead used : 1.881561 ns
(require '[clj-async-profiler.core :as prof])
(prof/serve-files 8080) ;; serve the svgs here
(prof/profile
(dotimes [_ 40000000] ;; ~10sec period
(merge defaults {})))
Reitit performance
Designed group up to be performant
Perf suite to see how performance evolves
Measured against Clojure/JavaScript/GO Routers
Performance toolbox:
Optimized routing algorithms
Separation of creation & request time
The Usual Suspects
Routing algorithms
When a router is created, route tree is inspected
and a best possible routing algorith is chosen
No regexps, use linear-router as a last effort
lookup-router , single-static-path-router
trie-router , linear-router
mixed-router , quarantine-router
trie-router
For non-conïŹ‚icting trees with wildcards
First insert data into Trie AST, then compile it into
fast lookup functions using a TrieCompiler
On JVM, backed by a fast Java-based Radix-trie
(require '[reitit.trie :as trie])
(-> [["/v2/whoami" 1]
["/v2/users/:user-id/datasets" 2]
["/v2/public/projects/:project-id/datasets" 3]
["/v1/public/topics/:topic" 4]
["/v1/users/:user-id/orgs/:org-id" 5]
["/v1/search/topics/:term" 6]
["/v1/users/:user-id/invitations" 7]
["/v1/users/:user-id/topics" 9]
["/v1/users/:user-id/bookmarks/followers" 10]
["/v2/datasets/:dataset-id" 11]
["/v1/orgs/:org-id/usage-stats" 12]
["/v1/orgs/:org-id/devices/:client-id" 13]
["/v1/messages/user/:user-id" 14]
["/v1/users/:user-id/devices" 15]
["/v1/public/users/:user-id" 16]
["/v1/orgs/:org-id/errors" 17]
["/v1/public/orgs/:org-id" 18]
["/v1/orgs/:org-id/invitations" 19]
["/v1/users/:user-id/device-errors" 22]]
(trie/insert)
(trie/compile)
(trie/pretty))
["/v"
[["1/"
[["users/" [:user-id ["/" [["device" [["-errors" 22]
["s" 15]]]
["orgs/" [:org-id 5]]
["bookmarks/followers" 10]
["invitations" 7]
["topics" 9]]]]]
["orgs/" [:org-id ["/" [["devices/" [:client-id 13]]
["usage-stats" 12]
["invitations" 19]
["errors" 17]]]]]
["public/" [["topics/" [:topic 4]]
["users/" [:user-id 16]]
["orgs/" [:org-id 18]]]]
["search/topics/" [:term 6]]
["messages/user/" [:user-id 14]]]]
["2/"
[["public/projects/" [:project-id ["/datasets" 3]]]
["users/" [:user-id ["/datasets" 2]]]
["datasets/" [:dataset-id 11]]
["whoami" 1]]]]]
Optimization Log
160ns (httprouter/GO)
3000ns (clojure, interpreted)
... (clueless poking)
990ns (clojure-trie)
830ns (faster decode params)
560ns (java-segment-router)
490ns (java-segment-router, no injects)
440ns (java-segment-router, no injects, single-wild-optimization)
305ns (trie-router, no injects)
281ns (trie-router, no injects, optimized)
277ns (trie-router, no injects, switch-case)
273ns (trie-router, no injects, direct-data)
256ns (trie-router, pre-defined parameters)
237ns (trie-router, single-sweep wild-params)
191ns (trie-router, record parameters)
Initial Segment Trie
(defn- segment
([] (segment {} #{} nil nil))
([children wilds catch-all match]
(let [children' (impl/fast-map children)
wilds? (seq wilds)]
^{:type ::segment}
(reify
Segment
(-insert [_ [p & ps] d]
(if-not p
(segment children wilds catch-all d)
(let [[w c] ((juxt impl/wild-param impl/catch-all-param) p)
wilds (if w (conj wilds w) wilds)
catch-all (or c catch-all)
children (update children (or w c p) #(-insert (or % (segment)) ps d))]
(segment children wilds catch-all match))))
(-lookup [_ [p & ps] path-params]
(if (nil? p)
(when match (assoc match :path-params path-params))
(or (-lookup (impl/fast-get children' p) ps path-params)
(if (and wilds? (not (str/blank? p))) (some #(-lookup (impl/fast-get children' %) ps
(if catch-all (-catch-all children' catch-all path-params p ps)))))))))
Flamegraph it away
Optimizing the Java Trie
Java Trie
Set of matchers deïŹned by the TrieCompiler
Order of magnitude faster than the original impl
@Override
public Match match(int i, int max, char[] path) {
boolean hasPercent = false;
boolean hasPlus = false;
if (i < max && path[i] != end) {
int stop = max;
for (int j = i; j < max; j++) {
final char c = path[j];
hasPercent = hasPercent || c == '%';
hasPlus = hasPlus || c == '+';
if (c == end) {
stop = j;
break;
}
}
final Match m = child.match(stop, max, path);
if (m != null) {
m.params = m.params.assoc(key, decode(new String(path, i, stop - i), hasPercent, hasPlus));
}
return m;
}
return null;
}
The Usual Suspects
Persistent Data Structures -> Records, Reify
Multimethods -> Protocols
Map Destructuring -> Manually
Unroll recursive functions ( assoc-in , ...)
Too generic functions ( walk , zip , ...)
Dynamic Binding
Manual inlining
Regexps
The Numbers
(c) https://www.artstation.com/kinixuki
RESTful api test
50+ routes, mostly wildcards
Reitit is orders of magnitude faster
220ns vs 22000ns, actually matters for busy sites
Looking out of the box
https://github.com/julienschmidt/httprouter
One of the fastest router in GO
(and source of many of the optimizations in reitit)
In Github api test, Reitit is ~40% slower
That's good! Still work to do.
Web Server Performance in
Clojure?
(c) https://www.artstation.com/kinixuki
Current Status
Most Clojure libraries don't even try to be fast
And that's totally ok for most apps
Compojure+ring-defaults vs reitit, with same response
headers, simple json echo
;; 10198tps
;; http :3000/api/ping
;; wrk -d ${DURATION:="30s"} http://127.0.0.1:3000/api/ping
(http/start-server defaults-app {:port 3000})
;; 48084tps
;; http :3002/api/ping
;; wrk -d ${DURATION:="30s"} http://127.0.0.1:3002/api/ping
(http/start-server reitit-app {:port 3002})
=> that's 5x more requests per sec.
TechEmpower Benchmark
TechEmpower Benchmark
TechEmpower Benchmark
Web Stacks
We know Clojure is a great tool for building web stuff
We can build a REALLY fast server stack for Clojure
aleph or immutant-nio as web server (nio, zero-copy)
reitit -based routing
Fast formatters like jsonista for JSON
next.jdbc (or porsas ) for database access
Good tools for async values & streams
lot's of other important components
Simple tools on top, the (coastal) castles?
Clojure Web & Data Next?
We have Ring, Pedestal, Yada & friends
We have nice templates & examples
Ring2 Spec? Spec for interceptors?
New performant reference architecture?
Bigger building blocks for rapid prototypes?
Making noice that Clojure is kinda awesome?
Community built Error Formatter?
Data-driven tools & inventories?
clojure.spec as data?
(c) https://www.artstation.com/kinixuki
Reitit bubbin' under
Support for OpenAPI3
JSON Schema validation
Spec2 support (when it's out)
Developer UI with remote debugger
More Batteries & Guides (frontend)
(Help make next.jdbc java-fast)
Sieppari.next
(c) https://www.artstation.com/kinixuki
Wrap-up
Reitit is a new routing library & framework
Embrace data-driven design, all the way down
Clojure is a Dynamic Language -> fail fast
clojure.spec to validate & transform data
Understand performance, test on your libraries
We can build beautiful & fast things with Clojure
Try out https://github.com/metosin/reitit
Chatting on #reitit in Slack
(c) https://www.artstation.com/kinixuki
 
 
 
 
Thanks.
@ikitommi
 
 
background artwork (c) Anna Dvorackova
https://www.artstation.com/kinixuki
(c) https://www.artstation.com/kinixuki

Weitere Àhnliche Inhalte

Was ist angesagt?

Getting started with MariaDB with Docker
Getting started with MariaDB with DockerGetting started with MariaDB with Docker
Getting started with MariaDB with DockerMariaDB plc
 
雑ăȘMySQLăƒ‘ăƒ•ă‚©ăƒŒăƒžăƒłă‚čăƒăƒ„ăƒŒăƒ‹ăƒłă‚°
雑ăȘMySQLăƒ‘ăƒ•ă‚©ăƒŒăƒžăƒłă‚čăƒăƒ„ăƒŒăƒ‹ăƒłă‚°é›‘ăȘMySQLăƒ‘ăƒ•ă‚©ăƒŒăƒžăƒłă‚čăƒăƒ„ăƒŒăƒ‹ăƒłă‚°
雑ăȘMySQLăƒ‘ăƒ•ă‚©ăƒŒăƒžăƒłă‚čăƒăƒ„ăƒŒăƒ‹ăƒłă‚°yoku0825
 
Tanel Poder - Troubleshooting Complex Oracle Performance Issues - Part 1
Tanel Poder - Troubleshooting Complex Oracle Performance Issues - Part 1Tanel Poder - Troubleshooting Complex Oracle Performance Issues - Part 1
Tanel Poder - Troubleshooting Complex Oracle Performance Issues - Part 1Tanel Poder
 
9/14にăƒȘăƒȘăƒŒă‚čă•ă‚ŒăŸă°ă‹ă‚Šăźæ–°LTS版Java 17、ここ3ćčŽé–“たJavaăźć€‰ćŒ–ă‚’çŸ„ă‚ă†ïŒïŒˆOpen Source Conference 2021 O...
9/14にăƒȘăƒȘăƒŒă‚čă•ă‚ŒăŸă°ă‹ă‚Šăźæ–°LTS版Java 17、ここ3ćčŽé–“たJavaăźć€‰ćŒ–ă‚’çŸ„ă‚ă†ïŒïŒˆOpen Source Conference 2021 O...9/14にăƒȘăƒȘăƒŒă‚čă•ă‚ŒăŸă°ă‹ă‚Šăźæ–°LTS版Java 17、ここ3ćčŽé–“たJavaăźć€‰ćŒ–ă‚’çŸ„ă‚ă†ïŒïŒˆOpen Source Conference 2021 O...
9/14にăƒȘăƒȘăƒŒă‚čă•ă‚ŒăŸă°ă‹ă‚Šăźæ–°LTS版Java 17、ここ3ćčŽé–“たJavaăźć€‰ćŒ–ă‚’çŸ„ă‚ă†ïŒïŒˆOpen Source Conference 2021 O...NTT DATA Technology & Innovation
 
Kotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is comingKotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is comingKirill Rozov
 
MySQL InnoDB Cluster - New Features in 8.0 Releases - Best Practices
MySQL InnoDB Cluster - New Features in 8.0 Releases - Best PracticesMySQL InnoDB Cluster - New Features in 8.0 Releases - Best Practices
MySQL InnoDB Cluster - New Features in 8.0 Releases - Best PracticesKenny Gryp
 
MySQL 5.7ăźçœ ăŒă‚ăȘたを狙っどいる
MySQL 5.7ăźçœ ăŒă‚ăȘたを狙っどいるMySQL 5.7ăźçœ ăŒă‚ăȘたを狙っどいる
MySQL 5.7ăźçœ ăŒă‚ăȘたを狙っどいるyoku0825
 
ăƒ€ăƒ•ăƒŒç€Ÿć†…ă§ă‚„ăŁăŠă‚‹MySQLăƒăƒ„ăƒŒăƒ‹ăƒłă‚°ă‚»ăƒŸăƒŠăƒŒć€§ć…Źé–‹
ăƒ€ăƒ•ăƒŒç€Ÿć†…ă§ă‚„ăŁăŠă‚‹MySQLăƒăƒ„ăƒŒăƒ‹ăƒłă‚°ă‚»ăƒŸăƒŠăƒŒć€§ć…Źé–‹ăƒ€ăƒ•ăƒŒç€Ÿć†…ă§ă‚„ăŁăŠă‚‹MySQLăƒăƒ„ăƒŒăƒ‹ăƒłă‚°ă‚»ăƒŸăƒŠăƒŒć€§ć…Źé–‹
ăƒ€ăƒ•ăƒŒç€Ÿć†…ă§ă‚„ăŁăŠă‚‹MySQLăƒăƒ„ăƒŒăƒ‹ăƒłă‚°ă‚»ăƒŸăƒŠăƒŒć€§ć…Źé–‹Yahoo!ăƒ‡ăƒ™ăƒ­ăƒƒăƒ‘ăƒŒăƒăƒƒăƒˆăƒŻăƒŒă‚Ż
 
How We Reduced Performance Tuning Time by Orders of Magnitude with Database O...
How We Reduced Performance Tuning Time by Orders of Magnitude with Database O...How We Reduced Performance Tuning Time by Orders of Magnitude with Database O...
How We Reduced Performance Tuning Time by Orders of Magnitude with Database O...ScyllaDB
 
MySQLă‚čăƒ†ăƒŒă‚żă‚čヱニタăƒȘング
MySQLă‚čăƒ†ăƒŒă‚żă‚čヱニタăƒȘングMySQLă‚čăƒ†ăƒŒă‚żă‚čヱニタăƒȘング
MySQLă‚čăƒ†ăƒŒă‚żă‚čヱニタăƒȘングyoku0825
 
RFC 〜 ăƒăƒƒăƒˆăƒŻăƒŒă‚Żć‹‰ćŒ·äŒš
RFC 〜 ăƒăƒƒăƒˆăƒŻăƒŒă‚Żć‹‰ćŒ·äŒšRFC 〜 ăƒăƒƒăƒˆăƒŻăƒŒă‚Żć‹‰ćŒ·äŒš
RFC 〜 ăƒăƒƒăƒˆăƒŻăƒŒă‚Żć‹‰ćŒ·äŒšKen SASAKI
 
Innodb Deep Talk #2 ă§ăŠè©±ă—ăŸă‚čăƒ©ă‚€ăƒ‰
Innodb Deep Talk #2 ă§ăŠè©±ă—ăŸă‚čăƒ©ă‚€ăƒ‰Innodb Deep Talk #2 ă§ăŠè©±ă—ăŸă‚čăƒ©ă‚€ăƒ‰
Innodb Deep Talk #2 ă§ăŠè©±ă—ăŸă‚čăƒ©ă‚€ăƒ‰Yasufumi Kinoshita
 
Composer ćŸžć…„é–€ćˆ°ćŻŠæˆ°
Composer ćŸžć…„é–€ćˆ°ćŻŠæˆ°Composer ćŸžć…„é–€ćˆ°ćŻŠæˆ°
Composer ćŸžć…„é–€ćˆ°ćŻŠæˆ°Shengyou Fan
 
Under the Hood of a Shard-per-Core Database Architecture
Under the Hood of a Shard-per-Core Database ArchitectureUnder the Hood of a Shard-per-Core Database Architecture
Under the Hood of a Shard-per-Core Database ArchitectureScyllaDB
 
Tanel Poder - Troubleshooting Complex Oracle Performance Issues - Part 2
Tanel Poder - Troubleshooting Complex Oracle Performance Issues - Part 2Tanel Poder - Troubleshooting Complex Oracle Performance Issues - Part 2
Tanel Poder - Troubleshooting Complex Oracle Performance Issues - Part 2Tanel Poder
 
JJUG CCC 2018 Spring - I-7 (äżșが)はじめどぼ Netty
JJUG CCC 2018 Spring - I-7 (äżșが)はじめどぼ NettyJJUG CCC 2018 Spring - I-7 (äżșが)はじめどぼ Netty
JJUG CCC 2018 Spring - I-7 (äżșが)はじめどぼ NettyShinya Mochida
 
Fluentdたお拧めシă‚čăƒ†ăƒ æ§‹æˆăƒ‘ă‚żăƒŒăƒł
Fluentdたお拧めシă‚čăƒ†ăƒ æ§‹æˆăƒ‘ă‚żăƒŒăƒłFluentdたお拧めシă‚čăƒ†ăƒ æ§‹æˆăƒ‘ă‚żăƒŒăƒł
Fluentdたお拧めシă‚čăƒ†ăƒ æ§‹æˆăƒ‘ă‚żăƒŒăƒłKentaro Yoshida
 
Utilizing kotlin flows in an android application
Utilizing kotlin flows in an android applicationUtilizing kotlin flows in an android application
Utilizing kotlin flows in an android applicationSeven Peaks Speaks
 

Was ist angesagt? (20)

Getting started with MariaDB with Docker
Getting started with MariaDB with DockerGetting started with MariaDB with Docker
Getting started with MariaDB with Docker
 
雑ăȘMySQLăƒ‘ăƒ•ă‚©ăƒŒăƒžăƒłă‚čăƒăƒ„ăƒŒăƒ‹ăƒłă‚°
雑ăȘMySQLăƒ‘ăƒ•ă‚©ăƒŒăƒžăƒłă‚čăƒăƒ„ăƒŒăƒ‹ăƒłă‚°é›‘ăȘMySQLăƒ‘ăƒ•ă‚©ăƒŒăƒžăƒłă‚čăƒăƒ„ăƒŒăƒ‹ăƒłă‚°
雑ăȘMySQLăƒ‘ăƒ•ă‚©ăƒŒăƒžăƒłă‚čăƒăƒ„ăƒŒăƒ‹ăƒłă‚°
 
Tanel Poder - Troubleshooting Complex Oracle Performance Issues - Part 1
Tanel Poder - Troubleshooting Complex Oracle Performance Issues - Part 1Tanel Poder - Troubleshooting Complex Oracle Performance Issues - Part 1
Tanel Poder - Troubleshooting Complex Oracle Performance Issues - Part 1
 
9/14にăƒȘăƒȘăƒŒă‚čă•ă‚ŒăŸă°ă‹ă‚Šăźæ–°LTS版Java 17、ここ3ćčŽé–“たJavaăźć€‰ćŒ–ă‚’çŸ„ă‚ă†ïŒïŒˆOpen Source Conference 2021 O...
9/14にăƒȘăƒȘăƒŒă‚čă•ă‚ŒăŸă°ă‹ă‚Šăźæ–°LTS版Java 17、ここ3ćčŽé–“たJavaăźć€‰ćŒ–ă‚’çŸ„ă‚ă†ïŒïŒˆOpen Source Conference 2021 O...9/14にăƒȘăƒȘăƒŒă‚čă•ă‚ŒăŸă°ă‹ă‚Šăźæ–°LTS版Java 17、ここ3ćčŽé–“たJavaăźć€‰ćŒ–ă‚’çŸ„ă‚ă†ïŒïŒˆOpen Source Conference 2021 O...
9/14にăƒȘăƒȘăƒŒă‚čă•ă‚ŒăŸă°ă‹ă‚Šăźæ–°LTS版Java 17、ここ3ćčŽé–“たJavaăźć€‰ćŒ–ă‚’çŸ„ă‚ă†ïŒïŒˆOpen Source Conference 2021 O...
 
Kotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is comingKotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is coming
 
MySQL InnoDB Cluster - New Features in 8.0 Releases - Best Practices
MySQL InnoDB Cluster - New Features in 8.0 Releases - Best PracticesMySQL InnoDB Cluster - New Features in 8.0 Releases - Best Practices
MySQL InnoDB Cluster - New Features in 8.0 Releases - Best Practices
 
MySQL 5.7ăźçœ ăŒă‚ăȘたを狙っどいる
MySQL 5.7ăźçœ ăŒă‚ăȘたを狙っどいるMySQL 5.7ăźçœ ăŒă‚ăȘたを狙っどいる
MySQL 5.7ăźçœ ăŒă‚ăȘたを狙っどいる
 
ăƒ€ăƒ•ăƒŒç€Ÿć†…ă§ă‚„ăŁăŠă‚‹MySQLăƒăƒ„ăƒŒăƒ‹ăƒłă‚°ă‚»ăƒŸăƒŠăƒŒć€§ć…Źé–‹
ăƒ€ăƒ•ăƒŒç€Ÿć†…ă§ă‚„ăŁăŠă‚‹MySQLăƒăƒ„ăƒŒăƒ‹ăƒłă‚°ă‚»ăƒŸăƒŠăƒŒć€§ć…Źé–‹ăƒ€ăƒ•ăƒŒç€Ÿć†…ă§ă‚„ăŁăŠă‚‹MySQLăƒăƒ„ăƒŒăƒ‹ăƒłă‚°ă‚»ăƒŸăƒŠăƒŒć€§ć…Źé–‹
ăƒ€ăƒ•ăƒŒç€Ÿć†…ă§ă‚„ăŁăŠă‚‹MySQLăƒăƒ„ăƒŒăƒ‹ăƒłă‚°ă‚»ăƒŸăƒŠăƒŒć€§ć…Źé–‹
 
How We Reduced Performance Tuning Time by Orders of Magnitude with Database O...
How We Reduced Performance Tuning Time by Orders of Magnitude with Database O...How We Reduced Performance Tuning Time by Orders of Magnitude with Database O...
How We Reduced Performance Tuning Time by Orders of Magnitude with Database O...
 
MySQLă‚čăƒ†ăƒŒă‚żă‚čヱニタăƒȘング
MySQLă‚čăƒ†ăƒŒă‚żă‚čヱニタăƒȘングMySQLă‚čăƒ†ăƒŒă‚żă‚čヱニタăƒȘング
MySQLă‚čăƒ†ăƒŒă‚żă‚čヱニタăƒȘング
 
RFC 〜 ăƒăƒƒăƒˆăƒŻăƒŒă‚Żć‹‰ćŒ·äŒš
RFC 〜 ăƒăƒƒăƒˆăƒŻăƒŒă‚Żć‹‰ćŒ·äŒšRFC 〜 ăƒăƒƒăƒˆăƒŻăƒŒă‚Żć‹‰ćŒ·äŒš
RFC 〜 ăƒăƒƒăƒˆăƒŻăƒŒă‚Żć‹‰ćŒ·äŒš
 
Innodb Deep Talk #2 ă§ăŠè©±ă—ăŸă‚čăƒ©ă‚€ăƒ‰
Innodb Deep Talk #2 ă§ăŠè©±ă—ăŸă‚čăƒ©ă‚€ăƒ‰Innodb Deep Talk #2 ă§ăŠè©±ă—ăŸă‚čăƒ©ă‚€ăƒ‰
Innodb Deep Talk #2 ă§ăŠè©±ă—ăŸă‚čăƒ©ă‚€ăƒ‰
 
The basics of fluentd
The basics of fluentdThe basics of fluentd
The basics of fluentd
 
Composer ćŸžć…„é–€ćˆ°ćŻŠæˆ°
Composer ćŸžć…„é–€ćˆ°ćŻŠæˆ°Composer ćŸžć…„é–€ćˆ°ćŻŠæˆ°
Composer ćŸžć…„é–€ćˆ°ćŻŠæˆ°
 
Under the Hood of a Shard-per-Core Database Architecture
Under the Hood of a Shard-per-Core Database ArchitectureUnder the Hood of a Shard-per-Core Database Architecture
Under the Hood of a Shard-per-Core Database Architecture
 
Tanel Poder - Troubleshooting Complex Oracle Performance Issues - Part 2
Tanel Poder - Troubleshooting Complex Oracle Performance Issues - Part 2Tanel Poder - Troubleshooting Complex Oracle Performance Issues - Part 2
Tanel Poder - Troubleshooting Complex Oracle Performance Issues - Part 2
 
JJUG CCC 2018 Spring - I-7 (äżșが)はじめどぼ Netty
JJUG CCC 2018 Spring - I-7 (äżșが)はじめどぼ NettyJJUG CCC 2018 Spring - I-7 (äżșが)はじめどぼ Netty
JJUG CCC 2018 Spring - I-7 (äżșが)はじめどぼ Netty
 
Fluentdたお拧めシă‚čăƒ†ăƒ æ§‹æˆăƒ‘ă‚żăƒŒăƒł
Fluentdたお拧めシă‚čăƒ†ăƒ æ§‹æˆăƒ‘ă‚żăƒŒăƒłFluentdたお拧めシă‚čăƒ†ăƒ æ§‹æˆăƒ‘ă‚żăƒŒăƒł
Fluentdたお拧めシă‚čăƒ†ăƒ æ§‹æˆăƒ‘ă‚żăƒŒăƒł
 
Utilizing kotlin flows in an android application
Utilizing kotlin flows in an android applicationUtilizing kotlin flows in an android application
Utilizing kotlin flows in an android application
 
Apache ZooKeeper
Apache ZooKeeperApache ZooKeeper
Apache ZooKeeper
 

Ähnlich wie Reitit - Clojure/North 2019

Fun with errors? - Clojure Finland Meetup 26.3.2019 Tampere
Fun with errors? - Clojure Finland Meetup 26.3.2019 TampereFun with errors? - Clojure Finland Meetup 26.3.2019 Tampere
Fun with errors? - Clojure Finland Meetup 26.3.2019 TampereMetosin Oy
 
Presto anatomy
Presto anatomyPresto anatomy
Presto anatomyDongmin Yu
 
The Road To Reactive with RxJava JEEConf 2016
The Road To Reactive with RxJava JEEConf 2016The Road To Reactive with RxJava JEEConf 2016
The Road To Reactive with RxJava JEEConf 2016Frank Lyaruu
 
Monitoring with Prometheus
Monitoring with PrometheusMonitoring with Prometheus
Monitoring with PrometheusShiao-An Yuan
 
[245] presto á„‚á…ąá„‡á…źá„€á…źá„Œá…© 퍼ᄒᅊᄎᅔᄀᅔ
[245] presto á„‚á…ąá„‡á…źá„€á…źá„Œá…© 퍼ᄒᅊᄎᅔᄀᅔ[245] presto á„‚á…ąá„‡á…źá„€á…źá„Œá…© 퍼ᄒᅊᄎᅔᄀᅔ
[245] presto á„‚á…ąá„‡á…źá„€á…źá„Œá…© 퍼ᄒᅊᄎᅔᄀᅔNAVER D2
 
How to make the fastest Router in Python
How to make the fastest Router in PythonHow to make the fastest Router in Python
How to make the fastest Router in Pythonkwatch
 
How and why i roll my own node.js framework
How and why i roll my own node.js frameworkHow and why i roll my own node.js framework
How and why i roll my own node.js frameworkBen Lin
 
PuppetDB: Sneaking Clojure into Operations
PuppetDB: Sneaking Clojure into OperationsPuppetDB: Sneaking Clojure into Operations
PuppetDB: Sneaking Clojure into Operationsgrim_radical
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
Using Sinatra to Build REST APIs in Ruby
Using Sinatra to Build REST APIs in RubyUsing Sinatra to Build REST APIs in Ruby
Using Sinatra to Build REST APIs in RubyLaunchAny
 
CONFidence 2015: DTrace + OSX = Fun - Andrzej Dyjak
CONFidence 2015: DTrace + OSX = Fun - Andrzej Dyjak   CONFidence 2015: DTrace + OSX = Fun - Andrzej Dyjak
CONFidence 2015: DTrace + OSX = Fun - Andrzej Dyjak PROIDEA
 
Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applicationsTom Croucher
 
Server side JavaScript: going all the way
Server side JavaScript: going all the wayServer side JavaScript: going all the way
Server side JavaScript: going all the wayOleg Podsechin
 
How Many Ohs? (An Integration Guide to Apex & Triple-o)
How Many Ohs? (An Integration Guide to Apex & Triple-o)How Many Ohs? (An Integration Guide to Apex & Triple-o)
How Many Ohs? (An Integration Guide to Apex & Triple-o)OPNFV
 
Exploring Async PHP (SF Live Berlin 2019)
Exploring Async PHP (SF Live Berlin 2019)Exploring Async PHP (SF Live Berlin 2019)
Exploring Async PHP (SF Live Berlin 2019)dantleech
 
Clojure and the Web
Clojure and the WebClojure and the Web
Clojure and the Webnickmbailey
 
JRuby with Java Code in Data Processing World
JRuby with Java Code in Data Processing WorldJRuby with Java Code in Data Processing World
JRuby with Java Code in Data Processing WorldSATOSHI TAGOMORI
 
Pune Clojure Course Outline
Pune Clojure Course OutlinePune Clojure Course Outline
Pune Clojure Course OutlineBaishampayan Ghose
 

Ähnlich wie Reitit - Clojure/North 2019 (20)

Fun with errors? - Clojure Finland Meetup 26.3.2019 Tampere
Fun with errors? - Clojure Finland Meetup 26.3.2019 TampereFun with errors? - Clojure Finland Meetup 26.3.2019 Tampere
Fun with errors? - Clojure Finland Meetup 26.3.2019 Tampere
 
Presto anatomy
Presto anatomyPresto anatomy
Presto anatomy
 
The Road To Reactive with RxJava JEEConf 2016
The Road To Reactive with RxJava JEEConf 2016The Road To Reactive with RxJava JEEConf 2016
The Road To Reactive with RxJava JEEConf 2016
 
Monitoring with Prometheus
Monitoring with PrometheusMonitoring with Prometheus
Monitoring with Prometheus
 
Camel as a_glue
Camel as a_glueCamel as a_glue
Camel as a_glue
 
[245] presto á„‚á…ąá„‡á…źá„€á…źá„Œá…© 퍼ᄒᅊᄎᅔᄀᅔ
[245] presto á„‚á…ąá„‡á…źá„€á…źá„Œá…© 퍼ᄒᅊᄎᅔᄀᅔ[245] presto á„‚á…ąá„‡á…źá„€á…źá„Œá…© 퍼ᄒᅊᄎᅔᄀᅔ
[245] presto á„‚á…ąá„‡á…źá„€á…źá„Œá…© 퍼ᄒᅊᄎᅔᄀᅔ
 
How to make the fastest Router in Python
How to make the fastest Router in PythonHow to make the fastest Router in Python
How to make the fastest Router in Python
 
How and why i roll my own node.js framework
How and why i roll my own node.js frameworkHow and why i roll my own node.js framework
How and why i roll my own node.js framework
 
PuppetDB: Sneaking Clojure into Operations
PuppetDB: Sneaking Clojure into OperationsPuppetDB: Sneaking Clojure into Operations
PuppetDB: Sneaking Clojure into Operations
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
Using Sinatra to Build REST APIs in Ruby
Using Sinatra to Build REST APIs in RubyUsing Sinatra to Build REST APIs in Ruby
Using Sinatra to Build REST APIs in Ruby
 
Aimaf
AimafAimaf
Aimaf
 
CONFidence 2015: DTrace + OSX = Fun - Andrzej Dyjak
CONFidence 2015: DTrace + OSX = Fun - Andrzej Dyjak   CONFidence 2015: DTrace + OSX = Fun - Andrzej Dyjak
CONFidence 2015: DTrace + OSX = Fun - Andrzej Dyjak
 
Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applications
 
Server side JavaScript: going all the way
Server side JavaScript: going all the wayServer side JavaScript: going all the way
Server side JavaScript: going all the way
 
How Many Ohs? (An Integration Guide to Apex & Triple-o)
How Many Ohs? (An Integration Guide to Apex & Triple-o)How Many Ohs? (An Integration Guide to Apex & Triple-o)
How Many Ohs? (An Integration Guide to Apex & Triple-o)
 
Exploring Async PHP (SF Live Berlin 2019)
Exploring Async PHP (SF Live Berlin 2019)Exploring Async PHP (SF Live Berlin 2019)
Exploring Async PHP (SF Live Berlin 2019)
 
Clojure and the Web
Clojure and the WebClojure and the Web
Clojure and the Web
 
JRuby with Java Code in Data Processing World
JRuby with Java Code in Data Processing WorldJRuby with Java Code in Data Processing World
JRuby with Java Code in Data Processing World
 
Pune Clojure Course Outline
Pune Clojure Course OutlinePune Clojure Course Outline
Pune Clojure Course Outline
 

Mehr von Metosin Oy

Navigating container technology for enhanced security by Niklas Saari
Navigating container technology for enhanced security by Niklas SaariNavigating container technology for enhanced security by Niklas Saari
Navigating container technology for enhanced security by Niklas SaariMetosin Oy
 
Where is Technical Debt?
Where is Technical Debt?Where is Technical Debt?
Where is Technical Debt?Metosin Oy
 
Creating an experimental GraphQL formatter using Clojure, Instaparse, and Gra...
Creating an experimental GraphQL formatter using Clojure, Instaparse, and Gra...Creating an experimental GraphQL formatter using Clojure, Instaparse, and Gra...
Creating an experimental GraphQL formatter using Clojure, Instaparse, and Gra...Metosin Oy
 
Serverless Clojure and ML prototyping: an experience report
Serverless Clojure and ML prototyping: an experience reportServerless Clojure and ML prototyping: an experience report
Serverless Clojure and ML prototyping: an experience reportMetosin Oy
 
Designing with malli
Designing with malliDesigning with malli
Designing with malliMetosin Oy
 
Malli: inside data-driven schemas
Malli: inside data-driven schemasMalli: inside data-driven schemas
Malli: inside data-driven schemasMetosin Oy
 
Naked Performance With Clojure
Naked Performance With ClojureNaked Performance With Clojure
Naked Performance With ClojureMetosin Oy
 
Clojutre Real Life (2012 ClojuTRE Retro Edition)
Clojutre Real Life (2012 ClojuTRE Retro Edition)Clojutre Real Life (2012 ClojuTRE Retro Edition)
Clojutre Real Life (2012 ClojuTRE Retro Edition)Metosin Oy
 
The Ancient Art of Data-Driven - reitit, the library -
The Ancient Art of Data-Driven - reitit, the library - The Ancient Art of Data-Driven - reitit, the library -
The Ancient Art of Data-Driven - reitit, the library - Metosin Oy
 
Craft Beer & Clojure
Craft Beer & ClojureCraft Beer & Clojure
Craft Beer & ClojureMetosin Oy
 
Performance and Abstractions
Performance and AbstractionsPerformance and Abstractions
Performance and AbstractionsMetosin Oy
 
ClojuTRE2016 Opening slides
ClojuTRE2016 Opening slidesClojuTRE2016 Opening slides
ClojuTRE2016 Opening slidesMetosin Oy
 
Schema tools-and-trics-and-quick-intro-to-clojure-spec-22.6.2016
Schema tools-and-trics-and-quick-intro-to-clojure-spec-22.6.2016Schema tools-and-trics-and-quick-intro-to-clojure-spec-22.6.2016
Schema tools-and-trics-and-quick-intro-to-clojure-spec-22.6.2016Metosin Oy
 
ClojuTRE - a (very) brief history
ClojuTRE - a (very) brief historyClojuTRE - a (very) brief history
ClojuTRE - a (very) brief historyMetosin Oy
 
Wieldy remote apis with Kekkonen - ClojureD 2016
Wieldy remote apis with Kekkonen - ClojureD 2016Wieldy remote apis with Kekkonen - ClojureD 2016
Wieldy remote apis with Kekkonen - ClojureD 2016Metosin Oy
 
ClojuTRE2015: Kekkonen - making your Clojure web APIs more awesome
ClojuTRE2015: Kekkonen - making your Clojure web APIs more awesomeClojuTRE2015: Kekkonen - making your Clojure web APIs more awesome
ClojuTRE2015: Kekkonen - making your Clojure web APIs more awesomeMetosin Oy
 
Clojure in real life 17.10.2014
Clojure in real life 17.10.2014Clojure in real life 17.10.2014
Clojure in real life 17.10.2014Metosin Oy
 
Euroclojure2014: Schema & Swagger - making your Clojure web APIs more awesome
Euroclojure2014: Schema & Swagger - making your Clojure web APIs more awesomeEuroclojure2014: Schema & Swagger - making your Clojure web APIs more awesome
Euroclojure2014: Schema & Swagger - making your Clojure web APIs more awesomeMetosin Oy
 
Swaggered web apis in Clojure
Swaggered web apis in ClojureSwaggered web apis in Clojure
Swaggered web apis in ClojureMetosin Oy
 

Mehr von Metosin Oy (19)

Navigating container technology for enhanced security by Niklas Saari
Navigating container technology for enhanced security by Niklas SaariNavigating container technology for enhanced security by Niklas Saari
Navigating container technology for enhanced security by Niklas Saari
 
Where is Technical Debt?
Where is Technical Debt?Where is Technical Debt?
Where is Technical Debt?
 
Creating an experimental GraphQL formatter using Clojure, Instaparse, and Gra...
Creating an experimental GraphQL formatter using Clojure, Instaparse, and Gra...Creating an experimental GraphQL formatter using Clojure, Instaparse, and Gra...
Creating an experimental GraphQL formatter using Clojure, Instaparse, and Gra...
 
Serverless Clojure and ML prototyping: an experience report
Serverless Clojure and ML prototyping: an experience reportServerless Clojure and ML prototyping: an experience report
Serverless Clojure and ML prototyping: an experience report
 
Designing with malli
Designing with malliDesigning with malli
Designing with malli
 
Malli: inside data-driven schemas
Malli: inside data-driven schemasMalli: inside data-driven schemas
Malli: inside data-driven schemas
 
Naked Performance With Clojure
Naked Performance With ClojureNaked Performance With Clojure
Naked Performance With Clojure
 
Clojutre Real Life (2012 ClojuTRE Retro Edition)
Clojutre Real Life (2012 ClojuTRE Retro Edition)Clojutre Real Life (2012 ClojuTRE Retro Edition)
Clojutre Real Life (2012 ClojuTRE Retro Edition)
 
The Ancient Art of Data-Driven - reitit, the library -
The Ancient Art of Data-Driven - reitit, the library - The Ancient Art of Data-Driven - reitit, the library -
The Ancient Art of Data-Driven - reitit, the library -
 
Craft Beer & Clojure
Craft Beer & ClojureCraft Beer & Clojure
Craft Beer & Clojure
 
Performance and Abstractions
Performance and AbstractionsPerformance and Abstractions
Performance and Abstractions
 
ClojuTRE2016 Opening slides
ClojuTRE2016 Opening slidesClojuTRE2016 Opening slides
ClojuTRE2016 Opening slides
 
Schema tools-and-trics-and-quick-intro-to-clojure-spec-22.6.2016
Schema tools-and-trics-and-quick-intro-to-clojure-spec-22.6.2016Schema tools-and-trics-and-quick-intro-to-clojure-spec-22.6.2016
Schema tools-and-trics-and-quick-intro-to-clojure-spec-22.6.2016
 
ClojuTRE - a (very) brief history
ClojuTRE - a (very) brief historyClojuTRE - a (very) brief history
ClojuTRE - a (very) brief history
 
Wieldy remote apis with Kekkonen - ClojureD 2016
Wieldy remote apis with Kekkonen - ClojureD 2016Wieldy remote apis with Kekkonen - ClojureD 2016
Wieldy remote apis with Kekkonen - ClojureD 2016
 
ClojuTRE2015: Kekkonen - making your Clojure web APIs more awesome
ClojuTRE2015: Kekkonen - making your Clojure web APIs more awesomeClojuTRE2015: Kekkonen - making your Clojure web APIs more awesome
ClojuTRE2015: Kekkonen - making your Clojure web APIs more awesome
 
Clojure in real life 17.10.2014
Clojure in real life 17.10.2014Clojure in real life 17.10.2014
Clojure in real life 17.10.2014
 
Euroclojure2014: Schema & Swagger - making your Clojure web APIs more awesome
Euroclojure2014: Schema & Swagger - making your Clojure web APIs more awesomeEuroclojure2014: Schema & Swagger - making your Clojure web APIs more awesome
Euroclojure2014: Schema & Swagger - making your Clojure web APIs more awesome
 
Swaggered web apis in Clojure
Swaggered web apis in ClojureSwaggered web apis in Clojure
Swaggered web apis in Clojure
 

KĂŒrzlich hochgeladen

Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)wesley chun
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...Martijn de Jong
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?Igalia
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxKatpro Technologies
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUK Journal
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEarley Information Science
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
 

KĂŒrzlich hochgeladen (20)

Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 

Reitit - Clojure/North 2019

  • 1. Reitit The Ancient Art of Data-Driven tommi@metosin.ïŹ Clojure/North
  • 2. Me(tosin) Sinclair Spectrum in 1983 ... Co-founded Metosin 2012 20 developers, Tampere & Helsinki / Finland Projects, Startups and Design FP, mostly Clojure/Script https://github.com/metosin
  • 3. ClojuTRE 2019 (8th) September 26-27th 2019, Helsinki FP & Clojure, 300++ on last two years Basic ïŹ‚ights & hotels for speakers Free Student & Diversity tickets Call For Speakers Open https://clojutre.org
  • 4. This talk Routing and dispatching Reitit, the library Reitit, the framework Performance Takeaways
  • 5. Routing in Clojure/Script Lot's of options: Ataraxy, Bide, Bidi, Compojure, Keechma, Pedestal, Silk, Secretary, ... Something we need in mostly all the (web)apps Mapping from path -> match Mapping from name -> match (reverse routing) HTTP (both server & browser) Messaging Others
  • 6. Dispatching in Clojure/Script Here: composing end executing functionality in the request-response pipeline Common patterns Middleware (Ring, nREPL, ..) Interceptors (Pedestal, Re-Frame, ..) Controllers (Keechma) Promises Sync & Async
  • 7. New Routing Library? More data-driven Leveraging clojure.spec Built for performance Friendly & explicit API Reach: browser, JVM, Node, Others Both routing and dispatching The Yellow (Hiccup) Castle of Routing?
  • 8.
  • 9. Reitit, the Library reitit-core (c) https://www.artstation.com/kinixuki
  • 10. Basics reitit.core/router to create a router Matching by path or by name (reverse routing) Functions to browse the route trees (defprotocol Router (router-name [this]) (routes [this]) (compiled-routes [this]) (options [this]) (route-names [this]) (match-by-path [this path]) (match-by-name [this name] [this name path-params]))
  • 11. (require '[reitit.core :as r]) (def router (r/router [["/ping" ::ping] ["/users/:id" ::user]])) (r/match-by-path router "/ping") ;#Match{:template "/ping" ; :data {:name :user/ping} ; :result nil ; :path-params {} ; :path "/ping"} (r/match-by-name router ::ping) ;#Match{:template "/ping" ; :data {:name :user/ping} ; :result nil ; :path-params {} ; :path "/ping"}
  • 12. Route Syntax (~hiccup) Paths are concatenated, route data meta-merged (r/router ["/api" {:interceptors [::api]} ["/ping" ::ping] ["/admin" {:roles #{:admin}} ["/users" ::users] ["/db" {:interceptors [::db] :roles ^:replace #{:db-admin}}]]])) (r/router [["/api/ping" {:interceptors [::api] :name ::ping}] ["/api/admin/users" {:interceptors [::api] :roles #{:admin} :name ::users}] ["/api/admin/db" {:interceptors [::api ::db] :roles #{:db-admin}}]])
  • 13. Programming routes nil s and nested sequences are ïŹ‚attened (defn router [dev?] (r/router [["/command/" (for [mutation [:olipa :kerran :avaruus]] [(name mutation) mutation])] (if dev? ["/dev-tools" :dev-tools])])) (-> (router true) (r/routes)) ;[["/command/olipa" {:name :olipa}] ; ["/command/kerran" {:name :kerran}] ; ["/command/avaruus" {:name :avaruus}] ; ["/dev-tools" {:name :dev-tools}]]
  • 14. Composing routes It's just data, merge trees Single route tree allows to optimize the whole Merging & nesting routers Empty route fragments, e.g. :<> in Reagent (-> (r/router [["" {:interceptors [::cors]} ["/api" ::api] ["/ipa" ::ipa]] ["/ping" ::ping]]) (r/routes)) ;[["/api" {:interceptors [::cors], :name ::api}] ; ["/ipa" {:interceptors [::cors], :name ::ipa}] ; ["/ping" {:name ::ping}]]
  • 15. Route ConïŹ‚ict Resolution Multiple sources (code/EDN/DB) for routes? Are all routes still reachable? Reitit fails-fast by default on path & name conïŹ‚icts (require '[reitit.core :as r]) (require '[reitit.dev.pretty :as pretty]) (r/router [["/ping"] ["/:user-id/orders"] ["/bulk/:bulk-id"] ["/public/*path"] ["/:version/status"]] {:exception pretty/exception})
  • 17. Community-driven Clojure error formatter? (c) https://www.artstation.com/kinixuki
  • 18. What is in the route data? Can be anything, the Router doesn't care. Returned on successful Match Can be queried from a Router Build your own interpreters for the data A Route First Architecture: Match -> Data -> React (r/match-by-path router "/api/admin/db") ;#Match{:template "/api/admin/db", ; :data {:interceptors [::api ::db] ; :roles #{:db-admin}}, ; :result nil, ; :path-params {}, ; :path "/api/admin/db"}
  • 19. Example use cases Authorization via :roles Frontend components via :view Dispatching via :middleware and :interceptors Stateful dispatching with :controllers Coercion via :parameters and :coercion Run requests in a server io-pool with :server/io Deploy endpoints separately to :server/target
  • 20. Frontend Example https://router.vuejs.org/guide/essentials/nested-routes.html (require '[reitit.frontend :as rf]) (require '[reitit.frontend.easy :as rfe]) (defn frontpage-view [match] ...) (defn topics-view [match] ... (rf/nested-view match) (defn topic-view [match] ...) (def router (rf/router [["/" {:name :frontpage :views [frontpage-view]}] ["/topics" {:controllers [load-topics] :views [topics-view]} ["" {:name :topics}] ["/:id" {:name :topic :parameters {:path {:id int?}} :controllers [load-topic] :views [topic-view]}]]])) (rfe/start! router ...)
  • 21. Route Data Validation Route Data can be anything -> data spaghetti? Leverage clojure.spec at router creation DeïŹne and enforce route data specs Fail-fast, missing, extra or misspelled keys With help of spec-tools and spell-spec Closed specs coming to Spec2 (TBD)
  • 22. Invalid Route Data Example (require '[reitit.spec :as spec]) (require '[clojure.spec.alpha :as s]) (s/def ::role #{:admin :user}) (s/def ::roles (s/coll-of ::role :into #{})) (r/router ["/api/admin" {::roles #{:adminz}}] {:validate spec/validate :exception pretty/exception})
  • 25. Failing Fast During static analysis (e.g. linter) At compile-time (macros, defs) At development-time (schema/spec annos) At creation-time At runtime At runtime special condition (c) https://www.artstation.com/kinixuki
  • 26. Coercion (DĂ©jĂ -vu) Coercion is a process of transforming values between domain (e.g. JSON->EDN, String->EDN) Route data keys :parameters & :coercion Utilities to apply coercion in reitit.coercion Implementation for Schema & clojure.spec <-- !!! (defprotocol Coercion "Pluggable coercion protocol" (-get-name [this]) (-get-options [this]) (-get-apidocs [this specification data]) (-compile-model [this model name]) (-open-model [this model]) (-encode-error [this error]) (-request-coercer [this type model]) (-response-coercer [this model]))
  • 27. Reitit, the Framework (c) https://www.artstation.com/kinixuki
  • 28. Framework You call the library, but the framework calls you Reitit ships with multiple Routing Frameworks reitit-ring Middleware-dispatch for Ring reitit-http Async Interceptor-dispatch for http reitit-pedestal Reitit Pedestal reitit-frontend (Keechma-style) Controllers, History & Fragment Router, helpers
  • 30. Ring Routing A separate lightweight module Routing based on path and :request-method Adds support for :middleware dispatch chain is executed after a match ring-handler to create a ring-compatible handler Supports Both sync & async Supports Both JVM & Node No magic, no default middleware
  • 31. Ring Application (require '[reitit.ring :as ring]) (def app (ring/ring-handler (ring/router ["/api" {:middleware [wrap-api wrap-roles]} ["/ping" {:get ping-handler]} ["/users" {:middleware [db-middleware] :roles #{:admin} ;; who reads this? :get get-users :post add-user}]]) (ring/create-default-handler))) (app {:uri "/api/ping" :request-method :get}) ; {:status 200, :body "pong"}
  • 32. Accessing route data Match and Router are injected into the request Components can read these at request time and do what ever they want, "Ad-hoc extensions" Pattern used in Kekkonen and in Yada (defn wrap-roles [handler] ;; roles injected via session-middleware (fn [{:keys [roles] :as request}] ;; read the route-data at request-time (let [required (-> request (ring/get-match) :data :roles)] (if (and (seq required) (not (set/subset? required roles))) {:status 403, :body "forbidden"} (handler request)))))
  • 33. Middleware as data Ring middleware are opaque functions Reitit adds a ïŹrst class values, Middleware records Recursive IntoMiddleware protocol to expand to Attach documentation, specs, requirements, ... Can be used in place of middleware functions Zero runtime penalty (defn roles-middleware [] {:name ::roles-middleware :description "Middleware to enforce roles" :requires #{::session-middleware} :spec (s/keys :opt-un [::roles]) :wrap wrap-roles})
  • 34. Compiling middleware Each middleware knows the endpoint it's mounted to We can pass the route data in at router creation time Big win for optimizing chains (def roles-middleware {:name ::roles-middleware :description "Middleware to enforce roles" :requires #{::session-middleware} :spec (s/keys :opt-un [::roles]) :compile (fn [{required :roles} _] ;; unmount if there are no roles required (if (seq required) (fn [handler] (fn [{:keys [roles] :as request}] (if (not (set/subset? required roles)) {:status 403, :body "forbidden"} (handler request))))))})
  • 35. Partial Specs Example "All routes under /account should require a role" ;; look ma, not part of request processing! (def roles-defined {:name ::roles-defined :description "requires a ::role for the routes" :spec (s/keys :req-un [::roles])}) ["/api" {:middleware [roles-middleware]} ;; behavior ["/ping"] ;; unmounted ["/account" {:middleware [roles-defined]} ;; :roles mandatory ["/admin" {:roles #{:admin}}] ;; ok ["/user" {:roles #{:user}}] ;; ok ["/manager"]]] ;; fail!
  • 36. Middleware chain as data Each endpoint has it's own vector of middleware Documents of what is in the chain Chain can be manipulated at router creation time Reordering Completing Interleaving Interleaving a request diff console printer: reitit.ring.middleware.dev/print-request-diffs
  • 37.
  • 38. Data deïŹnitions (as data) The core coercion for all (OpenAPI) paramerer & response types ( :query , :body , header , :path etc) Separerate Middleware to apply request & response coercion and format coercion errors Separate modules, spec coercion via spec-tools ["/plus/:y" {:get {:parameters {:query {:x int?}, :path {:y int?}} :responses {200 {:body {:total pos-int?}}} :handler (fn [{:keys [parameters]}] ;; parameters are coerced (let [x (-> parameters :query :x) y (-> parameters :path :y)] {:status 200 :body {:total (+ x y)}}))}}]
  • 39. Everything as data. (c) https://www.artstation.com/kinixuki
  • 40. Interceptors metosin/reitit-http e.g. going async (c) https://www.artstation.com/kinixuki
  • 41. Going Async Interceptors are much better ïŹt for async reitit-http uses the Interceptor model Requires an Interceptor Executor reitit-pedestal or reitit-sieppari Sieppari supports core.async , Manifold and Promesa Pedestal is more proven, supports core.async Target Both JVM & Node (via Sieppari)
  • 42. Http Application (require '[reitit.http :as http]) (require '[reitit.interceptor.sieppari :as sieppari] (def app (http/ring-handler (http/router ["/api" {:interceptors [api-interceptor roles-interceptor]} ["/ping" ping-handler] ["/users" {:interceptors [db-interceptor] :roles #{:admin} :get get-users :post add-user}]]) (ring/create-default-handler) {:executor sieppari/executor})) (app {:uri "/api/ping" :request-method :get}) ; {:status 200, :body "pong"}
  • 43. Example Interceptor (defn coerce-request-interceptor "Interceptor for pluggable request coercion. Expects a :coercion of type `reitit.coercion/Coercion` and :parameters from route data, otherwise does not mount." [] {:name ::coerce-request :spec ::rs/parameters :compile (fn [{:keys [coercion parameters]} opts] (cond ;; no coercion, skip (not coercion) nil ;; just coercion, don't mount (not parameters) {} ;; mount :else (let [coercers (coercion/request-coercers coercion parameters opts)] {:enter (fn [ctx] (let [request (:request ctx) coerced (coercion/coerce-request coercers request) request (impl/fast-assoc request :parameters coerced)] (assoc ctx :request request)))})))})
  • 45. Route-driven frameworks Routing and dispatching is separated, middleware (or interceptors) are applied only after a match Each endpoint has a unique dispatch chain Each component can be compiled and optimized against the endpoint at creation time Components can deïŹne partial route data specs that only effect the routes they are mounted to. We get both Performance & Correctness ... this is Kinda Awesome. (c) https://www.artstation.com/kinixuki
  • 47. Performance how can we make compojure-api faster? we moved from Clojure to GO because of perf How fast are the current Clojure libraries? How fast can we go with Java/Clojure?
  • 48. Measuring Performance Always measure Both micro & macro benchmarks In the end, the order of magnitude matters Lot's of good tools, some favourites: clojure.core/time criterium com.clojure-goes-fast/clj-async-profiler com.clojure-goes-fast/clj-java-decompiler https://github.com/wg/wrk
  • 49. (def defaults {:keywords? true}) (time (dotimes [_ 10000] (merge defaults {}))) ; "Elapsed time: 4.413803 msecs" (require '[criterium.core :as cc]) (cc/quick-bench (merge defaults {})) ; Evaluation count : 2691372 in 6 samples of 448562 calls. ; Execution time mean : 230.346208 ns ; Execution time std-deviation : 10.355077 ns ; Execution time lower quantile : 221.101397 ns ( 2.5%) ; Execution time upper quantile : 245.331388 ns (97.5%) ; Overhead used : 1.881561 ns
  • 50. (require '[clj-async-profiler.core :as prof]) (prof/serve-files 8080) ;; serve the svgs here (prof/profile (dotimes [_ 40000000] ;; ~10sec period (merge defaults {})))
  • 51. Reitit performance Designed group up to be performant Perf suite to see how performance evolves Measured against Clojure/JavaScript/GO Routers Performance toolbox: Optimized routing algorithms Separation of creation & request time The Usual Suspects
  • 52. Routing algorithms When a router is created, route tree is inspected and a best possible routing algorith is chosen No regexps, use linear-router as a last effort lookup-router , single-static-path-router trie-router , linear-router mixed-router , quarantine-router
  • 53. trie-router For non-conïŹ‚icting trees with wildcards First insert data into Trie AST, then compile it into fast lookup functions using a TrieCompiler On JVM, backed by a fast Java-based Radix-trie
  • 54. (require '[reitit.trie :as trie]) (-> [["/v2/whoami" 1] ["/v2/users/:user-id/datasets" 2] ["/v2/public/projects/:project-id/datasets" 3] ["/v1/public/topics/:topic" 4] ["/v1/users/:user-id/orgs/:org-id" 5] ["/v1/search/topics/:term" 6] ["/v1/users/:user-id/invitations" 7] ["/v1/users/:user-id/topics" 9] ["/v1/users/:user-id/bookmarks/followers" 10] ["/v2/datasets/:dataset-id" 11] ["/v1/orgs/:org-id/usage-stats" 12] ["/v1/orgs/:org-id/devices/:client-id" 13] ["/v1/messages/user/:user-id" 14] ["/v1/users/:user-id/devices" 15] ["/v1/public/users/:user-id" 16] ["/v1/orgs/:org-id/errors" 17] ["/v1/public/orgs/:org-id" 18] ["/v1/orgs/:org-id/invitations" 19] ["/v1/users/:user-id/device-errors" 22]] (trie/insert) (trie/compile) (trie/pretty))
  • 55. ["/v" [["1/" [["users/" [:user-id ["/" [["device" [["-errors" 22] ["s" 15]]] ["orgs/" [:org-id 5]] ["bookmarks/followers" 10] ["invitations" 7] ["topics" 9]]]]] ["orgs/" [:org-id ["/" [["devices/" [:client-id 13]] ["usage-stats" 12] ["invitations" 19] ["errors" 17]]]]] ["public/" [["topics/" [:topic 4]] ["users/" [:user-id 16]] ["orgs/" [:org-id 18]]]] ["search/topics/" [:term 6]] ["messages/user/" [:user-id 14]]]] ["2/" [["public/projects/" [:project-id ["/datasets" 3]]] ["users/" [:user-id ["/datasets" 2]]] ["datasets/" [:dataset-id 11]] ["whoami" 1]]]]]
  • 56. Optimization Log 160ns (httprouter/GO) 3000ns (clojure, interpreted) ... (clueless poking) 990ns (clojure-trie) 830ns (faster decode params) 560ns (java-segment-router) 490ns (java-segment-router, no injects) 440ns (java-segment-router, no injects, single-wild-optimization) 305ns (trie-router, no injects) 281ns (trie-router, no injects, optimized) 277ns (trie-router, no injects, switch-case) 273ns (trie-router, no injects, direct-data) 256ns (trie-router, pre-defined parameters) 237ns (trie-router, single-sweep wild-params) 191ns (trie-router, record parameters)
  • 57. Initial Segment Trie (defn- segment ([] (segment {} #{} nil nil)) ([children wilds catch-all match] (let [children' (impl/fast-map children) wilds? (seq wilds)] ^{:type ::segment} (reify Segment (-insert [_ [p & ps] d] (if-not p (segment children wilds catch-all d) (let [[w c] ((juxt impl/wild-param impl/catch-all-param) p) wilds (if w (conj wilds w) wilds) catch-all (or c catch-all) children (update children (or w c p) #(-insert (or % (segment)) ps d))] (segment children wilds catch-all match)))) (-lookup [_ [p & ps] path-params] (if (nil? p) (when match (assoc match :path-params path-params)) (or (-lookup (impl/fast-get children' p) ps path-params) (if (and wilds? (not (str/blank? p))) (some #(-lookup (impl/fast-get children' %) ps (if catch-all (-catch-all children' catch-all path-params p ps)))))))))
  • 60. Java Trie Set of matchers deïŹned by the TrieCompiler Order of magnitude faster than the original impl @Override public Match match(int i, int max, char[] path) { boolean hasPercent = false; boolean hasPlus = false; if (i < max && path[i] != end) { int stop = max; for (int j = i; j < max; j++) { final char c = path[j]; hasPercent = hasPercent || c == '%'; hasPlus = hasPlus || c == '+'; if (c == end) { stop = j; break; } } final Match m = child.match(stop, max, path); if (m != null) { m.params = m.params.assoc(key, decode(new String(path, i, stop - i), hasPercent, hasPlus)); } return m; } return null; }
  • 61. The Usual Suspects Persistent Data Structures -> Records, Reify Multimethods -> Protocols Map Destructuring -> Manually Unroll recursive functions ( assoc-in , ...) Too generic functions ( walk , zip , ...) Dynamic Binding Manual inlining Regexps
  • 63. RESTful api test 50+ routes, mostly wildcards Reitit is orders of magnitude faster 220ns vs 22000ns, actually matters for busy sites
  • 64.
  • 65. Looking out of the box https://github.com/julienschmidt/httprouter One of the fastest router in GO (and source of many of the optimizations in reitit) In Github api test, Reitit is ~40% slower That's good! Still work to do.
  • 66. Web Server Performance in Clojure? (c) https://www.artstation.com/kinixuki
  • 67. Current Status Most Clojure libraries don't even try to be fast And that's totally ok for most apps Compojure+ring-defaults vs reitit, with same response headers, simple json echo ;; 10198tps ;; http :3000/api/ping ;; wrk -d ${DURATION:="30s"} http://127.0.0.1:3000/api/ping (http/start-server defaults-app {:port 3000}) ;; 48084tps ;; http :3002/api/ping ;; wrk -d ${DURATION:="30s"} http://127.0.0.1:3002/api/ping (http/start-server reitit-app {:port 3002}) => that's 5x more requests per sec.
  • 71. Web Stacks We know Clojure is a great tool for building web stuff We can build a REALLY fast server stack for Clojure aleph or immutant-nio as web server (nio, zero-copy) reitit -based routing Fast formatters like jsonista for JSON next.jdbc (or porsas ) for database access Good tools for async values & streams lot's of other important components Simple tools on top, the (coastal) castles?
  • 72. Clojure Web & Data Next? We have Ring, Pedestal, Yada & friends We have nice templates & examples Ring2 Spec? Spec for interceptors? New performant reference architecture? Bigger building blocks for rapid prototypes? Making noice that Clojure is kinda awesome? Community built Error Formatter? Data-driven tools & inventories? clojure.spec as data?
  • 74. Reitit bubbin' under Support for OpenAPI3 JSON Schema validation Spec2 support (when it's out) Developer UI with remote debugger More Batteries & Guides (frontend) (Help make next.jdbc java-fast) Sieppari.next (c) https://www.artstation.com/kinixuki
  • 75. Wrap-up Reitit is a new routing library & framework Embrace data-driven design, all the way down Clojure is a Dynamic Language -> fail fast clojure.spec to validate & transform data Understand performance, test on your libraries We can build beautiful & fast things with Clojure Try out https://github.com/metosin/reitit Chatting on #reitit in Slack (c) https://www.artstation.com/kinixuki
  • 76.         Thanks. @ikitommi     background artwork (c) Anna Dvorackova https://www.artstation.com/kinixuki (c) https://www.artstation.com/kinixuki