Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Reifying the Call Stack
Search
Norbert Wójtowicz
January 16, 2018
Programming
0
83
Reifying the Call Stack
Norbert Wójtowicz
January 16, 2018
Tweet
Share
More Decks by Norbert Wójtowicz
See All by Norbert Wójtowicz
Datalog: Data All Truth · Append Log · Obtain Graph
pithyless
2
800
Datalog: Biting the Silver Bullet
pithyless
0
270
Data-Oriented Architecture (LambdaDays 2016)
pithyless
3
710
ClojureScript + React.js: How I learned to stop worrying and love the browser.
pithyless
1
500
Other Decks in Programming
See All in Programming
Click-free releases & the making of a CLI app
oheyadam
2
110
CSC509 Lecture 12
javiergs
PRO
0
160
Arm移行タイムアタック
qnighy
0
320
聞き手から登壇者へ: RubyKaigi2024 LTでの初挑戦が 教えてくれた、可能性の星
mikik0
1
130
【Kaigi on Rails 2024】YOUTRUST スポンサーLT
krpk1900
1
330
受け取る人から提供する人になるということ
little_rubyist
0
230
GitHub Actionsのキャッシュと手を挙げることの大切さとそれに必要なこと
satoshi256kbyte
5
430
役立つログに取り組もう
irof
28
9.6k
Webの技術スタックで マルチプラットフォームアプリ開発を可能にするElixirDesktopの紹介
thehaigo
2
1k
NSOutlineView何もわからん:( 前編 / I Don't Understand About NSOutlineView :( Pt. 1
usagimaru
0
330
Duckdb-Wasmでローカルダッシュボードを作ってみた
nkforwork
0
120
AWS IaCの注目アップデート 2024年10月版
konokenj
3
3.3k
Featured
See All Featured
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
31
2.7k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
232
17k
Optimizing for Happiness
mojombo
376
70k
Designing Experiences People Love
moore
138
23k
How GitHub (no longer) Works
holman
310
140k
Building a Scalable Design System with Sketch
lauravandoore
459
33k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
506
140k
Speed Design
sergeychernyshev
24
610
Music & Morning Musume
bryan
46
6.2k
GitHub's CSS Performance
jonrohan
1030
460k
The Invisible Side of Design
smashingmag
298
50k
Bash Introduction
62gerente
608
210k
Transcript
Interceptors: Reifying the Call Stack Norbert Wójtowicz · @pithyless
Ruby · Clojure
Rack class HelloWorld def call(env) [200, {"Content-Type" => “text/plain”}, ["Hello
world!"]] end end
Rack Middleware - Enter class AddGUID def call(env) env["tx_id"] =
SecureRandom.uuid status, headers, body = @app.call(env) [status, headers, body] end end
Rack Middleware - Leave class NoCache def call(env) status, headers,
body = @app.call(env) headers["Cache-Control"] = "no-cache" [status, headers, body] end end
Rack Middleware - Around class ProfileRequest def call(env) before =
Time.now.to_f status, headers, body = @app.call(env) after = Time.now.to_f diff = after - before log_result(diff) [status, headers, body] end end
Rack Middleware - Error class CatchErrors def call(env) @app.call(env) rescue
StandardError => e log_error(e) [500, {...}, ["<html>Oh noes!</html>"]] end end
Rack - Application use CatchErrors use ProfileRequest use NoCache use
AddGUID run HelloWorld
Call Stack enter leave leave enter leave env resp env
resp CatchErrors ProfileRequest NoCache AddGUID HelloWorld
Call Stack - Stack Overflow def fib(num) num < 3
? 1 : fib(num - 2) + fib(num - 1) end fib(10) #=> 55 fib(100_000) #=> Stack Level Too Deep
Call Stack - Asynchronous class AsyncHelloWorld def call(env) Thread.new sleep(5)
response = [200, {...}, ["Hello world!"]] env['async.callback'].call(response) end throw :async end end
Call Stack - Asynchronous Middleware def call(env) response = @app.call
env throw :async if @throw_on.include? response.first response end def call(env) response = @async_response catch(:async) { response = @app.call env } response end
Call Stack - Streaming def call(env) stream = env['rack.stream'] stream.after_open
do stream.chunk "Hello" stream.chunk "World" stream.close end [200, {'Content-Type' => 'text/plain'}, []] end
Call Stack - Global Ordering @use << proc { |app|
middleware.new(app, *args, &block) } ... app = @use.reverse.inject(app) { |a,e| e[a] }
Call Stack enter leave leave enter leave env resp env
resp CatchErrors ProfileRequest NoCache AddGUID HelloWorld
Ring (defn hello-world [request] {:status 200 :headers {"Content-Type" "text/plain"} :body
"Hello World!”})
Ring - Streaming (defn hello-world [request] {:status 200 :headers {"Content-Type"
"text/plain"} :body "Hello World!”}) ;; Body: ;; - String ;; - ISeq ;; - File ;; - InputStream
Ring - Asynchronous (defn hello-world ;; Synchronous ([request] {:status 200
:headers {"Content-Type" "text/plain"} :body "Hello World!"}) ;; Asynchronous ([request respond raise] (respond (hello-world request))))
Ring - Application (def app (-> handler (add-guid) (no-cache) (profile-request)
(catch-errors)))
Reify verb: make (something abstract) more concrete or real
Pedestal - Interceptors 1. enter · leave · error 2.
reified call stack
Interceptor - Handler (defn hello-world [request] {:status 200 :headers {"Content-Type"
"text/plain"} :body "Hello World!”}) ;; Body: ;; - String ;; - ISeq ;; - File ;; - InputStream
Interceptor - Enter {:name ::attach-guid :enter (fn [context] (assoc context
::guid (java.util.UUID/randomUUID)))}
Interceptor - Leave {:name ::disable-cache :leave (fn [context] (assoc-in context
[:response :headers "Cache-Control"] "no-cache"))}
Interceptor - Around {:name ::profile-request :enter (fn [context] (let [before
(System/currentTimeMillis)] (assoc context ::start-time before))) :leave (fn [context] (let [before (get context ::start-time) after (System/currentTimeMillis) diff (- after before)] (log diff) context))}
Interceptor - Errors {:name ::handle-error :enter (fn [context] ...) :leave
(fn [context] ...) :error (fn [context ex] ;; 1. Handle error context ;; 2. Cannot handle error (assoc context ::interceptor/error ex) ;; 3. Should have handled error, but... (throw (ex-info "Oh noes!" {:extra :data})))}
Interceptors - Application enter leave leave enter leave ctx ctx
ctx CatchErrors ProfileRequest NoCache AddGUID HelloWorld ctx
Interceptor - Deferred Calls {:name ::third-party-auth :enter (fn [context] (go
(assoc context :account (call-auth-system context))))}
Interceptor - Parameterized (defn auth-system [env] (if (dev? env) {:name
::fake-auth :enter (fn [context] (assoc context :account (fake-account context)))} {:name ::third-party-auth :enter (fn [context] (go (assoc context :account (call-auth-system context))))}))
Interceptor - Stateful (defn attach-database [uri] (let [db (db-pool uri)]
{:name ::attach-database :enter (fn [context] (assoc context ::db db))}))
Interceptor - Custom Ordering (defroutes routes ["/api" [(requires rate-limiter :account)]
["/slack" [(provides slack-auth :account)] ...] ["/hipchat" [(provides hipchat-auth :account)] ...]])
Interceptor - Dynamic Ordering (keys context) [... :io.pedestal.interceptor.chain/queue :io.pedestal.interceptor.chain/stack ...]
Interceptor - Dynamic Routing GET /docs auth → params →
read-db → handler → to-json GET /docs/secret.pdf auth → File (streaming) POST /docs auth → params → upload → process file → write-db → SSE
Interceptor - Performance
Interceptor - Performance
Reify all the things! @pithyless