This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-09-10
Channels
- # announcements (9)
- # babashka (19)
- # beginners (39)
- # calva (7)
- # cherry (1)
- # cider (2)
- # clojure (31)
- # clojure-europe (8)
- # clojure-norway (9)
- # datalevin (10)
- # events (2)
- # fulcro (10)
- # hyperfiddle (9)
- # joker (1)
- # lsp (50)
- # membrane (34)
- # minecraft (2)
- # missionary (21)
- # off-topic (17)
- # pedestal (1)
- # polylith (8)
- # reitit (3)
- # sql (4)
- # squint (16)
- # xtdb (14)
What could cause error not to propagate up a nested missionary workflow ? I'm reducing a flow and when something throw inside the fail callback doesn't get called.
can you post code?
I'm modeling "scraping" an api that require a lot of request some in paralles some seq wih retry ect.. so the code is quite spread. I'm trying to reproduce with a simple example but it's draining, repl can hang, need to restart often so it's just slow to get there.
(defn batch-request-operations!
"Take a list of operations, request each with pagination when required.
tap> a report on each step when fetching paginated data
Return a map of name->response"
([transaction cookie]
(batch-request-operations! transaction cookie {}))
([operations cookie {:keys [connection-manager http-client step]
:or {step 1000}}]
(->> (m/ap
(let [transaction (operations->transaction (map :request (apply concat operations)))
res (m/? (post-transaction! transaction
cookie
connection-manager
http-client))
d (m/?> (m/seed (map vector (apply concat operations) (:body res))))
[op original-res] d]
(if (more-rows? original-res)
(->> (m/ap
(loop [op' op
meta original-res]
(let [end-row (get meta "endRow")
start-row (get meta "startRow")
step (or step
(- end-row
start-row))
[start-row' end-row'] (page-range end-row step)
operations' (paginate-operation op' start-row' end-row')
res (m/? (post-transaction!
(operations->transaction operations')
cookie connection-manager http-client))]
(fetched-rows-report res)
(m/amb (:body res)
(when (more-rows? (-> res :body first))
(recur operations' (-> res :body first)))))))
(m/eduction
(mapcat (fn [r] (->> r
(mapcat #(get % "data"))
(reduce conj [])))))
(m/reduce conj [])
m/?)
(get original-res "data"))))
(m/reduce conj [])
m/?
(map vector (map :name (apply concat operations)))
(chunk-split (map count operations))
(map #(into {} %))
m/sp)))
I was catching -> printing and re throwing. I think I refactored my out of the issue for now. Would you have an example of that ? https://clojurians.slack.com/archives/CL85MBPEF/p1662809302216159?thread_ts=1662808593.677959&cid=CL85MBPEF
(defn do-async-stuff [cookie]
(let [http-manager (build-http-client ...)]
(->> (m/ap ... (do-something! http-manager) ...)
(m/eduction (map ...)))))
;; consumer part
((m/reduce conj [] (->> (do-async-stuff "pecan-cookie")
(m/eduction (map ...))))
(fn s [s] (prn :succes s))
(fn f [f] (prn :fail s)))
I need to close the http-manager when the ap in do-async-stuff is done, I'm not sure how to do that here without exposing the http-manager to the consumer part of the code.
So basically something like a .finally on a js/Promise.
Edit: it's not possible.
https://clojurians.slack.com/archives/CL85MBPEF/p1647595996869289?thread_ts=1647593869.885839&cid=CL85MBPEFwhy not m/observe?
Here is a continuous time (m/cp) object lifecycle - https://nextjournal.com/dustingetz/missionary-object-lifecycle - i think should work in m/ap as well
you can construct a service in the m/observe constructor and the m/observe destructor will be called per the supervision tree
Do you specifically not want to reuse the http client? Typically these are expensive to create and get reused wherever possible. Sometimes the consumer part can be extracted to a separate fn too, in which case I'd rather create the client there and send it in as an arg, which would make cleanup trivial in an m/sp block
Thanks for the response, in my "example" do-async-stuff is reusing http client for many request, I was looking for a way to do that without leaking the complexity to the consumer.
My point was the http client might get reused across several do-async-stuff
calls, or a do-another-async-stuff
etc, in which case its lifecycle won't be bound to your flow at all.
If that's not your case then either try what Dustin suggested
(m/ap (let [http-manager (m/?> (m/observe ...))] ...))
or what Leo suggested
(defn do-async-stuff [cookie]
(let [http-manager (build-http-client ...)]
(m/sp (try (m/reduce conj [] (m/ap ...)) (finally (.close http-manager))))))
Interesting idea to reuse the client, you could even keep it in a dependencies management tool like a db connection i guess.
The fetching I'm doing with this project is seconds long on each call, so time wise it's actually negligible. But maybe in term of CPU cycle / warming the data-center it might be helpful.
it's not just about time but also about clean code. If you find the correct lifetime of an object you'll know where to deal with it in the most efficient way, in LOC, failure handling etc