Fork me on GitHub
#pedestal
<
2022-10-26
>
plins20:10:05

hello everyone, I have a potentially large lazy dataset returned by jdbc-next, and I want to stream the response as a csv file whats the best way of doing this? returning an input steram? a function? I took a look at the docs but its not clear to me how to connect the dots

souenzzo20:10:46

Something like this

(defn get-huge-data 
  [req]
  (let [c (async/chan)]
    (async/thread 
      (doseq [v (jdbc/get-values)]
        (async/>!! c v)))
    {:body c
     :status 200}))

plins20:10:11

that makes sense, thanks! I wonder how to factor in the writer for the csv

plins20:10:25

maybe instead of returning a channel I could return an input stream, and write to that input using a thread instead of core.async?

plins20:10:14

I wonder if Pedestal will wait for the thread to finish to finish the response

souenzzo20:10:50

searching an old complete/correct example here.

plins20:10:10

thank you very much mister ❤️

souenzzo20:10:17

note that the key is "return a channel" How you will pipe the data into this channel may be tricky

souenzzo20:10:26

(pipe jdbc, that is a sync library, into an async chan, may be tricky)

plins20:10:54

given it returns a lazy list as a result I was under the impression that I could doseq without having to load the entire collection in memory

souenzzo20:10:30

yeah but it will require a dedicated thread

souenzzo20:10:37

In this specific case, of a lazy seq from jdbc into the response, may be easier to write as you read, like this

(require '[io.pedestal.http :as http]
  '[clojure.core.async :as async]
  '[io.pedestal.test :refer [response-for]])
(let [index (fn [req]
              (let [lazy-jdbc-result (#_jdbc/execute.. do
                                       [1 2 3])]
                {:body   (fn [out]
                           (with-open [w (io/writer out)]
                             (doseq [v lazy-jdbc-result]
                               (json/write v w)
                               (.write w (int \newline)))))
                 :status 200}))
      service-fn (-> {::http/routes `#{["/" :get ~index
                                        :route-name ::index]}}
                   http/default-interceptors
                   http/create-servlet
                   ::http/service-fn)]
  (response-for service-fn :get "/"))

plins20:10:34

yep looks like this will do the trick thanks a lot ❤️ !!