sql

All things SQL and JDBC...
jmckitrick 2021-04-09T13:10:32.110100Z

I just asked a question in #clojure that probably should have been here, but there's too much content to paste this time.

jmckitrick 2021-04-09T13:10:44.110500Z

It's an issue I'm having with next-jdbc and streaming.

jmckitrick 2021-04-09T13:11:11.110900Z

I'm dipping my toes in the 'next-jdbc' waters, and ran into a small issue. It's probably a gap in my knowledge of streaming, but here goes.... [9:01 AM] I have a query using 'plan' that then can be reduced 'into []' with no issue.. it's a huge result set, which is what I expect. [9:02 AM] But when I try to reduce that into something streamable, I'm getting an empty result.. [9:02 AM] So...

[9:03 AM] (ring-io/piped-input-stream
                                (fn [ostream]
                                  (let [^java.io.Writer w (<http://clojure.java.io/make-writer|clojure.java.io/make-writer> ostream {})]
                                    (get-big-data-streaming w))
                                  (.close ostream)))
[9:03 AM] This is in the handler ☝️:skin-tone-2: [9:03 AM] And the function of interest:
[9:04 AM] (reduce (fn [^java.io.Writer w record]
              (let [m (datafiable-row record *db* {})]
                (.write w m)
                ))
            writer
            (jdbc/plan *db* sv))
[9:05 AM] We can safely assume that `sv` is a sqlvec that works and generates a large dataset outside of this streaming code [9:05 AM] So the 'w' passed in is the Writer and locally is named `writer` [9:07 AM] Hitting the endpoint takes about 4 seconds, the same as the non-streaming version, but returns no data.

jmckitrick 2021-04-09T13:11:20.111200Z

Well, the copy/paste was easier than I thought it would be.

aratare 2021-04-09T13:16:33.112400Z

@jmckitrick the function f supplied to reduce has to return a value to be passed on, but I have a feeling this bit here

(fn [^java.io.Writer w record]
              (let [m (datafiable-row record *db* {})]
                (.write w m)
                ))
doesn’t actually return anything after the first call?

jmckitrick 2021-04-09T13:37:59.112900Z

You're right, I missed that. But once I fixed it, I'm still getting the empty result....

jmckitrick 2021-04-09T13:38:15.113200Z

(fn [^java.io.Writer w record]
              (let [m (datafiable-row record *db* {})]
                (.write w m))
              w)

aratare 2021-04-09T13:55:52.115100Z

I think you already know this, but if reduce is working fine in the non-stream version, then if you manually write something to the stream, does it work as intended?

jmckitrick 2021-04-09T14:13:46.115900Z

Well, when I output a debugging line after the .write it is apparently only reached once.

dominicm 2021-04-09T17:26:44.116Z

@jmckitrick your original call to .close is redundant fwiw, that's done automatically.

1
dominicm 2021-04-09T17:28:14.116200Z

@jmckitrick What does datafiable-row return? The fn name makes it sound like a map or something, but it should be a string/char/int

jmckitrick 2021-04-09T17:29:09.116900Z

I think that's my issue. I wrapped it in 'str' in the call to '.write' and that seems to have done the trick....

dominicm 2021-04-09T17:30:33.117Z

@jmckitrick consider setting up https://stuartsierra.com/2015/05/27/clojure-uncaught-exceptions as it will help reveal these errors

jmckitrick 2021-04-09T17:31:17.117300Z

Ah, great! Thanks@

dominicm 2021-04-09T17:32:32.117400Z

Actually, in this case, I'm lying. ring-io uses future for it's threads, that means they will hold onto their exception until deref'd. A little annoying.

dominicm 2021-04-09T17:34:37.117500Z

https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html#get() docs this behaviour. You'll have to do your own try/catch to make sure you have visibility into these fns, or rewrite the piped-input-stream to just use a threadpool instead.