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
64
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
730
Datalog: Biting the Silver Bullet
pithyless
0
240
Data-Oriented Architecture (LambdaDays 2016)
pithyless
3
620
ClojureScript + React.js: How I learned to stop worrying and love the browser.
pithyless
1
490
Other Decks in Programming
See All in Programming
Build Apps for iOS, Android & Desktop in 100% Kotlin With Compose Multiplatform (mDevCamp 2024)
zsmb
0
340
Rethinking UI building strategies @ SFI 2024
letelete
0
270
Git Rebase
bkuhlmann
11
1.6k
大規模Reactアプリのリアーキテクチャ~8万行のTanStack Query移行の軌跡~
kj455
4
960
VS Code をプロダクトにどう取り込むか
onomax
1
360
try!Swift Tokyo 2024 参加報告 LT
akidon0000
1
220
R言語の環境構築と基礎 Tokyo.R 112
bob3bob3
0
270
PHP8.3の機能を振り返る / Review of PHP 8.3 features
seike460
PRO
1
110
AWS Application Composerで始める、 サーバーレスなデータ基盤構築 / 20240406-jawsug-hokuriku-shinkansen
kasacchiful
1
260
#phpcon_odawara オープン・クローズドなテストフィクスチャを求めて / open closed test fixtures
77web
3
230
PHPはいつから死んでいるかの調査
chiroruxx
1
400
1BRC--Nerd Sniping the Java Community
gunnarmorling
0
340
Featured
See All Featured
Designing for humans not robots
tammielis
248
25k
Gamification - CAS2011
davidbonilla
76
4.6k
What's in a price? How to price your products and services
michaelherold
237
11k
Teambox: Starting and Learning
jrom
128
8.4k
From Idea to $5000 a Month in 5 Months
shpigford
377
45k
How to train your dragon (web standard)
notwaldorf
73
5.2k
Building a Scalable Design System with Sketch
lauravandoore
456
32k
GraphQLとの向き合い方2022年版
quramy
32
12k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
659
120k
Faster Mobile Websites
deanohume
299
30k
ParisWeb 2013: Learning to Love: Crash Course in Emotional UX Design
dotmariusz
104
6.6k
BBQ
matthewcrist
80
8.8k
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