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
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Norbert Wójtowicz
January 16, 2018
Programming
0
98
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
880
Datalog: Biting the Silver Bullet
pithyless
0
280
Data-Oriented Architecture (LambdaDays 2016)
pithyless
3
910
ClojureScript + React.js: How I learned to stop worrying and love the browser.
pithyless
1
530
Other Decks in Programming
See All in Programming
AIプロダクト時代のQAエンジニアに求められること
imtnd
1
500
new(1.26) ← これすき / kamakura.go #8
utgwkk
0
380
クライアントワークでSREをするということ。あるいは事業会社におけるSREと同じこと・違うこと
nnaka2992
0
170
24時間止められないシステムを守る-医療ITにおけるランサムウェア対策の実際
koukimiura
2
180
Railsの気持ちを考えながらコントローラとビューを整頓する/tidying-rails-controllers-and-views-as-rails-think
moro
4
350
AIエージェントのキホンから学ぶ「エージェンティックコーディング」実践入門
masahiro_nishimi
7
1.2k
JPUG勉強会 OSSデータベースの内部構造を理解しよう
oga5
2
220
AIに仕事を丸投げしたら、本当に楽になれるのか
dip_tech
PRO
0
170
nilとは何か 〜interfaceの構造とnil!=nilから理解する〜 / Understanding nil in Go Interface Representation and Why nil != nil
kuro_kurorrr
3
1.3k
CSC307 Lecture 11
javiergs
PRO
0
580
今、アーキテクトとして 品質保証にどう関わるか
nealle
0
190
Lambda のコードストレージ容量に気をつけましょう
tattwan718
0
200
Featured
See All Featured
Embracing the Ebb and Flow
colly
88
5k
Code Reviewing Like a Champion
maltzj
527
40k
Designing for Timeless Needs
cassininazir
0
140
How to Get Subject Matter Experts Bought In and Actively Contributing to SEO & PR Initiatives.
livdayseo
0
75
For a Future-Friendly Web
brad_frost
183
10k
SEO in 2025: How to Prepare for the Future of Search
ipullrank
3
3.3k
Collaborative Software Design: How to facilitate domain modelling decisions
baasie
0
150
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1k
The Cost Of JavaScript in 2023
addyosmani
55
9.7k
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
59
50k
Data-driven link building: lessons from a $708K investment (BrightonSEO talk)
szymonslowik
1
940
Leading Effective Engineering Teams in the AI Era
addyosmani
9
1.7k
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