metaclass: (Default)
metaclass ([personal profile] metaclass) wrote2013-03-26 09:25 pm
Entry tags:

Clojure, ленивость и отложенные отчеты

Очередной раз стукнулся об ленивость.

Делаю фоновый расчет отчетов - клиент просит у сервиса "посчитай мне отчет", тот ему возвращает идентификатор-ссылку на future со считалкой отчета и далее клиент опрашивает сервис на предмет "а не готов ли наш отчет" и тот ему отвечает либо "не готов" либо "готов, вот тебе данные".
Ну, склепал очередь отчетов - атом, в нем мап, в мапе идентификаторы запрошенных отчетов и future к ним. Запускаю - а оно мгновенно говорит, "да, вот отчет готов, забирай" и давай его считать и отдавать. В потоке запроса, не в рабочем потоке future.
Ленивость. Оно возвращает башку последовательности данных отчета сразу, "на тэбэ, дорогой, форси меня". И запрос готовых данных (в том числе, даже просто попытка в целях отладки посмотреть на мап с future) - вызывает форсинг последовательности.

PS: И еще обнаружил очередную фичу кложури - thread-local bindings протаскиваются во все вызовы, которые перекладывают работу в другие потоки. Т.е. конкретно тут - я в запросе часть общих параметров (типа имени пользователя) складываю в thread-local binding чтобы не протаскивать их руками через 100500 функций, думал, что при вычислении внутри future придется их доставать и перекладывать во второй поток - а оно уже сделано.
Авторы кложури не перестают удивлять своей крайней адекватностью и практичностью языка.

[identity profile] plumqqz.livejournal.com 2013-03-26 07:13 pm (UTC)(link)
Вообще, хм, нормальное поведение для бд - если вам отдали курсор, это вовсе не значит, что его удастся бодро выфетчить.
Правда, у бд есть преимущество - курсоры, в отличие от ленивых вычислений, будут более-менее согласованны; и согласованность эту можно задавать.
Впрочем, тут на это, как я понимаю, в общем, наплевать.

[identity profile] metaclass.livejournal.com 2013-03-26 07:27 pm (UTC)(link)
Хм, с тем, чтобы курсор не фетчился, я сталкивался только при ошибках. А как он может не фетчится в обычных условиях работы?

С согласованностью у кложури свои заморочки, у нее есть встроенная транзакционная память и тому подобные чудеса, но я ее не использую, т.к. все интересующее меня состояние живет в БД и транзакции с уровнем изоляции snapshot вроде нужный уровень согласованности гарантируют.

[identity profile] plumqqz.livejournal.com 2013-03-26 07:48 pm (UTC)(link)
Хм, с тем, чтобы курсор не фетчился, я сталкивался только при ошибках. А как он может не фетчится в обычных условиях работы?
Ну почему не фетчится. Фетчится, только медленно.

[identity profile] levgem.livejournal.com 2013-03-26 07:18 pm (UTC)(link)
может оно и к лучшему. Отдаст ленивый ответ, посчитает первую страницу, а дальше всё равно никому не интересно.

[identity profile] berezovsky.livejournal.com 2013-03-26 07:23 pm (UTC)(link)
Ага, листаешь жежешечку, а там эта хуетень по центру крутится. Пока ждёшь, десять раз читать передумаешь.

[identity profile] metaclass.livejournal.com 2013-03-26 07:29 pm (UTC)(link)
Ну, это другая печаль, нормальный пейджинг делать я не хочу, т.к. мне мерещится что на каждую страницу придется повторно sql запрос выполнять :)

[identity profile] berezovsky.livejournal.com 2013-03-26 07:38 pm (UTC)(link)
Кстати да. Как нормальный пейджинг делают?
Два селект топа каждый раз, или тянут всё на клиент и перекладывают на грид, или что-то промежуточное, вроде "посчитали конечную табличку, оставили на сервере, по мере листания на клиенте получаем из неё куски"?

[identity profile] metaclass.livejournal.com 2013-03-26 07:41 pm (UTC)(link)
Не знаю. Наверно выполняют запрос с select first/top/limit
Меня огорчает мысль о том, что этот запрос выполняется повторно на каждую страницу, поэтому теоретически, надо бы выполнить его один раз и закэшировать.

[identity profile] swizard.livejournal.com 2013-03-26 10:15 pm (UTC)(link)
> Меня огорчает мысль о том, что этот запрос выполняется повторно на каждую страницу, поэтому теоретически, надо бы выполнить его один раз и закэшировать.

Кстати, интернет-поисковики как раз перевыполняют запросы каждый раз при листании серпа, кэшировать им получается сложнее. При этом, что интересно, чем дальше листать, тем им технически печальней -- яндекс, например, принципиально дальше сотой страницы не пускает, даже если десять миллионов результатов найдено.

[identity profile] jakobz.livejournal.com 2013-03-26 09:17 pm (UTC)(link)
Делают select top каждый раз. Прикол в том, что никто списки дальше чем страницы на 3 не листает. Т.е. пейджинг - он только в теории нужен, а на практике надо нормальный UI со списками заголовков, фильтрацией, и т.п.

[identity profile] swizard.livejournal.com 2013-03-26 10:24 pm (UTC)(link)
Вот, кстати, совершенно согласен про ненужность пейджинга. Имхо он реально необходим только для двух вещей:
  • Чисто психологически, чтобы у пользователя не терялось ощущение, что вот они все данные, перед моими глазами, ничего никуда не пропало.
  • Когда надо найти "то, не знаю что" / "увижу глазами -- узнаю". Например, у меня так бывает изредка с платежами в истории инет-банка, когда не помнишь ни даты, ни одного из реквизитов.

[identity profile] sergiej.livejournal.com 2013-03-27 08:58 am (UTC)(link)
Если делать каждый раз топ на динамичных данных с критериями поиска то получается бред - можно на следующих "страницах" получать то что уже видел и "терять" на прошлых то чего не видел вообще.

[identity profile] metaclass.livejournal.com 2013-03-27 09:00 am (UTC)(link)
У меня обычно отчеты по неизменяемым данным, там проще.
А для динамических нужны какие-то более другие методы работы, в т.ч. и кэширование результата запроса.

[identity profile] sergiej.livejournal.com 2013-03-27 09:04 am (UTC)(link)
Не надо кешировать. Первым запросом берутся только айдишки да побольше, скажем страниц на 10, если больше говорим "заузь критерии, всё не влазит". Потом очередной набор айдишек кверится для каждой страницы с деталями, вот и всё. Обеспечено что пациент пролистав увидит все записи. Можно не листать а дописывать внизу вновь прочитанные - так намного удобнее, но не во всех гридах это возможно без извращений.
Пейджинг собственно только для того и нужен, чтобы особо недоверчивые были уверены что они "проверили всё глазками".

(no subject)

[identity profile] metaclass.livejournal.com - 2013-03-27 09:10 (UTC) - Expand

(no subject)

[identity profile] sergiej.livejournal.com - 2013-03-27 09:13 (UTC) - Expand

(no subject)

[identity profile] metaclass.livejournal.com - 2013-03-27 09:20 (UTC) - Expand

(no subject)

[identity profile] sergiej.livejournal.com - 2013-03-27 09:29 (UTC) - Expand

[identity profile] jakobz.livejournal.com 2013-03-27 09:03 am (UTC)(link)
Вот на это кладут вообще всегда и все.

(no subject)

[identity profile] sergiej.livejournal.com - 2013-03-27 09:11 (UTC) - Expand

(no subject)

[identity profile] jakobz.livejournal.com - 2013-03-27 09:24 (UTC) - Expand

(no subject)

[identity profile] sergiej.livejournal.com - 2013-03-27 09:32 (UTC) - Expand

(no subject)

[identity profile] jakobz.livejournal.com - 2013-03-27 09:43 (UTC) - Expand

(no subject)

[identity profile] sergiej.livejournal.com - 2013-03-27 09:51 (UTC) - Expand

(no subject)

[identity profile] jakobz.livejournal.com - 2013-03-27 09:57 (UTC) - Expand

(no subject)

[identity profile] sergiej.livejournal.com - 2013-03-27 10:03 (UTC) - Expand

(no subject)

[identity profile] jakobz.livejournal.com - 2013-03-27 11:19 (UTC) - Expand

(no subject)

[identity profile] sergiej.livejournal.com - 2013-03-27 11:22 (UTC) - Expand

(no subject)

[identity profile] jakobz.livejournal.com - 2013-03-27 11:29 (UTC) - Expand

(no subject)

[identity profile] sergiej.livejournal.com - 2013-03-27 11:54 (UTC) - Expand

(no subject)

[identity profile] jakobz.livejournal.com - 2013-03-27 17:05 (UTC) - Expand

(no subject)

[identity profile] sergiej.livejournal.com - 2013-03-27 17:30 (UTC) - Expand

(no subject)

[identity profile] jakobz.livejournal.com - 2013-03-27 17:33 (UTC) - Expand

(no subject)

[identity profile] berezovsky.livejournal.com - 2013-03-27 17:35 (UTC) - Expand

(no subject)

[identity profile] metaclass.livejournal.com - 2013-03-27 17:45 (UTC) - Expand

(no subject)

[identity profile] jakobz.livejournal.com - 2013-03-27 17:50 (UTC) - Expand

(no subject)

[identity profile] berezovsky.livejournal.com - 2013-03-27 18:01 (UTC) - Expand

(no subject)

[identity profile] sergiej.livejournal.com - 2013-03-27 17:35 (UTC) - Expand

(no subject)

[identity profile] jakobz.livejournal.com - 2013-03-27 17:37 (UTC) - Expand

(no subject)

[identity profile] sergiej.livejournal.com - 2013-03-27 17:57 (UTC) - Expand

(no subject)

[identity profile] jakobz.livejournal.com - 2013-03-27 18:32 (UTC) - Expand

(no subject)

[identity profile] sergiej.livejournal.com - 2013-03-27 18:57 (UTC) - Expand

(no subject)

[identity profile] berezovsky.livejournal.com - 2013-03-27 19:33 (UTC) - Expand

(no subject)

[identity profile] metaclass.livejournal.com - 2013-03-27 19:40 (UTC) - Expand

(no subject)

[identity profile] sergiej.livejournal.com - 2013-03-27 19:59 (UTC) - Expand

(no subject)

[identity profile] jakobz.livejournal.com - 2013-03-27 10:08 (UTC) - Expand

(no subject)

[identity profile] sergiej.livejournal.com - 2013-03-27 10:24 (UTC) - Expand

(no subject)

[identity profile] volodymir-k.livejournal.com - 2013-03-27 10:53 (UTC) - Expand

(no subject)

[identity profile] jakobz.livejournal.com - 2013-03-27 11:33 (UTC) - Expand

(no subject)

[identity profile] jakobz.livejournal.com - 2013-03-27 11:37 (UTC) - Expand

[identity profile] vit-r.livejournal.com 2013-03-27 01:22 am (UTC)(link)
Думаю, всё-таки зависит от сценариев использования. Если пользователь открывает страницу и пять минут её разглядывает, а открывает следующую, то повторный запрос можно пережить. Если он открывает и сразу печатает весь документ, тут уже нужен другой подход.

[identity profile] theiced.livejournal.com 2013-03-26 08:38 pm (UTC)(link)
ну - всё как надо.

кложа - для нормальных людей. скалы, жсы и прочие груви - для ортурегов и ждевелопов.

[identity profile] jakobz.livejournal.com 2013-03-26 09:18 pm (UTC)(link)
А это не то ли IO вперемешку с ленью, с которым борятся монадой в хаскеле? Или у тебя тупо чистые вычисления там, и просто не зафорсилось где надо?

[identity profile] metaclass.livejournal.com 2013-03-26 09:20 pm (UTC)(link)
Оно самое, побочные эффекты+лень.

[identity profile] jakobz.livejournal.com 2013-03-26 10:19 pm (UTC)(link)
У меня похожая прикольная тема была. OpenGL-окошко на хацкеле, стейт в MVar, обновляется в idle на нужное количество тиков по таймеру. Сворачиваешь окошко, разворачиваешь - stack overflow.

Т.е. в общем виде проблема в том, что мы считаем стейт на основе предыдущего много раз, и хз когда он нам и в каком виде потребуется. Если у нас нет лени - мы просто жарим CPU, и если он успевает - все ок. А с ленью всплывают интересные философские вопросы.

Как такое чинить в общем виде в хаскеле - не ясно. Мне хватило зафорсить один санк, это почему-то вылечило stack overflow, но стейт до текущего времени при разворачивании окна оно все равно догоняет, для меня это уже не критично было.

[identity profile] jakobz.livejournal.com 2013-03-26 10:21 pm (UTC)(link)
Вот была бы, скажем, возможность форсить санки в каком-нибудь фоновом idle-процессе. Вероятно была бы интересная модель параллельных вычислений.

[identity profile] gds.livejournal.com 2013-03-26 10:35 pm (UTC)(link)
> Если у нас нет лени - мы просто жарим CPU, и если он успевает - все ок. А с ленью всплывают интересные философские вопросы.

да какие там вопросы -- сплошные ответы. С ленью в этом случае сначала жарим память, откладывая личинки вычислений, а только потом жарим совместно память, цпу и бедного мусорщика. Причём цпу обычно жарим на большее количество тактов -- надо же эту лентяйку обслуживать как-то: смотреть, посчитали личинку или ещё нет, например.
Выгода тут есть только тогда, когда многие вычисления не нужны. Но при гуе навряд ли так выйдет.

[identity profile] tonsky.livejournal.com 2013-03-27 06:38 am (UTC)(link)
Насколько я помню по реализации, только если запускаешь вычисления через Future или Agent. Мы как-то стартовали треды сами и выковыривали соответствующий код из clojure.core чтобы оно так же работало. Появилось, кстати, недавно, в 1.3 или 1.4

[identity profile] metaclass.livejournal.com 2013-03-27 06:44 am (UTC)(link)
Да, в 1.3 и при запуске стандартными средствами.
А разве 1.3 это недавно? Я вроде 1.4 год назад начал использовать, 1.5 недавно вышла.

[identity profile] prepor.livejournal.com 2013-03-30 02:52 pm (UTC)(link)
эм, простите, вы разве не про это?

(def ^:dynamic *foo*)
(binding [*foo* "bar"]
	       (-> (Thread. (bound-fn [] (prn *foo*)))
		   (.start)))

=> "bar"
Edited 2013-03-30 14:52 (UTC)