Как адаптировать IReduceInit из next.jdbc для потоковой передачи JSON с помощью Cheshire в ответ HTTP с помощью кольца - PullRequest
1 голос
/ 23 сентября 2019

tl; dr, как превратить IReduceInit в lazy-seq преобразованных значений

У меня есть запрос к базе данных, который дает достаточно большой набор данных для динамического поворота на клиенте (миллион или две строки, 25 атрибутов- нет проблем для современного ноутбука).

Мой (упрощенный) стек должен был вызывать clojure.jdbc, чтобы получить (что я считал ленивым) последовательность строк результата.Я мог бы просто сериализовать это, передав его как тело через связующее ПО ring-json.Была проблема с тем, что ring-json создавал строку ответа в куче, но у него была опция с 0.5.0 для потоковой передачи ответа.

Я обнаружил путем профилирования нескольких случаев сбоев, которые фактически замыкаются.jdbc реализует весь набор результатов в памяти, прежде чем передать его обратно.Нет проблем!Вместо того, чтобы работать с reducible-query в этой библиотеке, я решил перейти к новому next.jdbc.

Ключевой операцией в next.jdbc является plan, которая возвращает IReduceInit, который я могу использовать для запусказапрос и получить набор результатов ...

(into [] (map :cc_id) (jdbc/plan ds ["select cc_id from organisation where cc_id = '675192'"]))
["675192"]

Однако это реализует весь набор результатов, и в приведенном выше случае я получу все идентификаторы заранее и в памяти.Не проблема для одного, но у меня их обычно много.

План IReduceInit - это то, что я могу уменьшить, если я дам начальное значение, чтобы я мог сделать вывод в функции сокращения ... (thx @amalloy)

(reduce #(println (:cc_id %2)) [] (jdbc/plan ds ["select cc_id from organisation where cc_id = '675192'"]))
675192
nil

... но в идеале я бы хотел превратить этот IReduceInit в ленивую последовательность значений после применения к ним функции преобразования, чтобы я мог использовать их с ring-json и cheshire,Я не вижу очевидного способа сделать это.

Ответы [ 2 ]

2 голосов
/ 23 сентября 2019

reduce отлично работает с IReduceInit.IReduceInit требует начального значения, которое вы указали при вызове .reduce, но не при использовании функции Reduce;это объясняет, почему вы видели один работающий, а не другой.

Однако это не приведет вас к ленивой последовательности.Часть контракта reduce заключается в том, что он охотно потребляет весь ввод (мы будем игнорировать reduced, который ничего не меняет).Ваш вопрос является частным случаем более общей проблемы динамической области видимости: последовательность, создаваемая JDBC, «действительна» только в некотором контексте, и вам необходимо выполнить всю вашу обработку в этом контексте, поэтому она не может быть ленивой.Вместо этого вы, как правило, выворачиваете свою программу наизнанку: не используйте возвращаемое значение в качестве последовательности, а передайте обработчику запросов функцию и скажите: «Пожалуйста, вызовите эту функцию со своими результатами».Затем механизм гарантирует, что данные являются действительными, пока он вызывает эту функцию, и, как только функция возвращает, очищает данные.Я не знаю о jdbc.next, но с более старым jdbc вы бы использовали что-то вроде db-query-with-resultset для этого.Вы передадите ему некоторую функцию, которая может добавить байты к ожидающему HTTP-ответу, и она будет вызывать эту функцию много раз.

Это все немного расплывчато, потому что я не знаю, какой вы обработчик HTTPиспользование или то, что его средства для обработки потоковых ответов не лениво, но это общая идея, с которой вам придется столкнуться, если вы хотите обрабатывать динамически ограниченный ресурс: лень просто не вариант.

1 голос
/ 24 сентября 2019

Разочарование.

Почему вы не можете сделать это с JDBC?без каких-либо слоев Clojure?

(let [resultset (.executeQuery connection "select ...")]
  (loop 
   (when (.next resultset)
     (let [row [(.getString resultset 1)
                (.getString resultset 2)
                ...]])
     (json/send row)
     (recur)))
  (json/end))

Конечно, с ResultSetMetaData вы можете автоматизировать генерацию строки в функцию, которая может справиться с чем-либо возвращенным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...