Небольшая ремарка о монге.
Некоторое время назад меня поразило поведение массовой записи в MongoDB (это когда ты берешь несколько тысяч документов и кидаешь их на запись одной командой).
Если появляются дублирующиеся ключи - кидается исключение. Логично было бы ожидать, что запись всей пачки будет отменена. Но нет: все документы, которые могут провалятся в базу.
А если эксплуатировать эту особенность для очистки данных от дублей - при некотором критическом пороге монга просто падает.
Возможно, есть обходные пути, но поведение "из коробки" - такое.
#кодинг
#mongodb
Некоторое время назад меня поразило поведение массовой записи в MongoDB (это когда ты берешь несколько тысяч документов и кидаешь их на запись одной командой).
Если появляются дублирующиеся ключи - кидается исключение. Логично было бы ожидать, что запись всей пачки будет отменена. Но нет: все документы, которые могут провалятся в базу.
А если эксплуатировать эту особенность для очистки данных от дублей - при некотором критическом пороге монга просто падает.
Возможно, есть обходные пути, но поведение "из коробки" - такое.
#кодинг
#mongodb
Наткнулся на подборку небольших околонаучных бложиков от уважаемого @trueresearch
Читал еще не все каналы, но подборка прям огонь:
Читал еще не все каналы, но подборка прям огонь:
Forwarded from Русский research
Наступает пора конференций и отпусков, когда мой канал традиционно снижает активность. Тем не менее, я остаюсь на связи в комментариях и через @RResearcherBot, а посты продолжат иногда выходить — просто не слишком регулярно.
Тем временем мир научно-образовательного телеграма продолжает расцветать, и я хочу познакомить подписчиков с небольшими (пока) каналами, которые порекомендовали неравнодушные читатели и авторы в комментариях под недавним постом. Я убрал из подборки агрегаторы, официальные каналы организаций и просто крупные каналы, отдав предпочтение неформальному авторскому жизнеописанию и лёгкому научпопу. Привожу в произвольном порядке, иногда вместе с личным впечатлением по последним постам.
Авторское о жизни в науке и образовании:
https://www.tgoop.com/seryogaBombit - задорно-ироничный блог новоиспечённого кандидата технических наук
https://www.tgoop.com/consordino - блог почвоведа, личные мысли автора, рассказы о работе и немного научпопа
https://www.tgoop.com/VetusDepartment - замолкавшая и восставшая недавно Старая кафедра
https://www.tgoop.com/kavunnaya - блог квантово-механического теоретика
https://www.tgoop.com/sciencylife - живой канал биолога, немного в стиле Твиттера
https://www.tgoop.com/mikhail_ignatov31 - канал преподавателя философии из Белгорода
https://www.tgoop.com/tisnu_channel - о книгах и науке
https://www.tgoop.com/ragootootoo - блог преподавателя, с честными историями из жизни родной кафедры
https://www.tgoop.com/kantius - канал Анны Кулешовой об этике публикаций и многом другом
https://www.tgoop.com/Halls_of_Pilipenko - канал сурового проректора (наиболее живой из виденных мной аналогичных каналов)
https://www.tgoop.com/TiberiusRu - канал химика-материаловеда из Швейцарии
С уклоном в научпоп:
https://www.tgoop.com/newbioethics – блог о биотехнологиях
https://www.tgoop.com/sciscent - научный канал о духах (которые во флаконе, а не на спиритическом сеансе)
https://www.tgoop.com/ananikovlab - канал научной школы академика Ананикова
https://www.tgoop.com/memory_studies - про исследования памяти и истории, с юмором
https://www.tgoop.com/tozhe_nauka - прекрасный микроскопический канал о математических методах в литературоведении
https://www.tgoop.com/courageous_geologist - фоточки и мемы от геологов
https://www.tgoop.com/macevagram - исследования еврейских кладбищ
https://www.tgoop.com/georg_chronicles - что-то на инженерном
https://www.tgoop.com/radio_73 - ламповейший канал радиолюбителя, если не сказать радиофила
Тем временем мир научно-образовательного телеграма продолжает расцветать, и я хочу познакомить подписчиков с небольшими (пока) каналами, которые порекомендовали неравнодушные читатели и авторы в комментариях под недавним постом. Я убрал из подборки агрегаторы, официальные каналы организаций и просто крупные каналы, отдав предпочтение неформальному авторскому жизнеописанию и лёгкому научпопу. Привожу в произвольном порядке, иногда вместе с личным впечатлением по последним постам.
Авторское о жизни в науке и образовании:
https://www.tgoop.com/seryogaBombit - задорно-ироничный блог новоиспечённого кандидата технических наук
https://www.tgoop.com/consordino - блог почвоведа, личные мысли автора, рассказы о работе и немного научпопа
https://www.tgoop.com/VetusDepartment - замолкавшая и восставшая недавно Старая кафедра
https://www.tgoop.com/kavunnaya - блог квантово-механического теоретика
https://www.tgoop.com/sciencylife - живой канал биолога, немного в стиле Твиттера
https://www.tgoop.com/mikhail_ignatov31 - канал преподавателя философии из Белгорода
https://www.tgoop.com/tisnu_channel - о книгах и науке
https://www.tgoop.com/ragootootoo - блог преподавателя, с честными историями из жизни родной кафедры
https://www.tgoop.com/kantius - канал Анны Кулешовой об этике публикаций и многом другом
https://www.tgoop.com/Halls_of_Pilipenko - канал сурового проректора (наиболее живой из виденных мной аналогичных каналов)
https://www.tgoop.com/TiberiusRu - канал химика-материаловеда из Швейцарии
С уклоном в научпоп:
https://www.tgoop.com/newbioethics – блог о биотехнологиях
https://www.tgoop.com/sciscent - научный канал о духах (которые во флаконе, а не на спиритическом сеансе)
https://www.tgoop.com/ananikovlab - канал научной школы академика Ананикова
https://www.tgoop.com/memory_studies - про исследования памяти и истории, с юмором
https://www.tgoop.com/tozhe_nauka - прекрасный микроскопический канал о математических методах в литературоведении
https://www.tgoop.com/courageous_geologist - фоточки и мемы от геологов
https://www.tgoop.com/macevagram - исследования еврейских кладбищ
https://www.tgoop.com/georg_chronicles - что-то на инженерном
https://www.tgoop.com/radio_73 - ламповейший канал радиолюбителя, если не сказать радиофила
Спустя всего 1.5 года работы с докером через Visual Studio я обнаружил, что можно прямо из неё без боли залезть в файловую систему запущенного контейнера и посмотреть, а что там собственно происходит (например - посмотреть логи или конфиги).
Всё это время я шёл true linux-way: цеплялся к контейнеру и прыгал по файловой системе через консоль, для просмотра текста используя vi.
Всё это время я шёл true linux-way: цеплялся к контейнеру и прыгал по файловой системе через консоль, для просмотра текста используя vi.
🔥9
Больше года я ходил вокруг стека ELK, думая, что вот надо, надо. Но никак не складывалось. И вот стукнуло: пора.
ELK - стандартное средство для сбора, хранения и визуализации логов. Расшифровывается как Elasticsearch (поиск и хранение) + Logstash (собор из разнородных источников) + Kibana (Визуализация).
Попытался завести в докере вместе со всем остальным решением образ sebp/elk. Не взлетело: не хватило оперативки (у меня было 16 Гб). Ну чтож, добавил её, стало 64.
Взлетело! Процесс vmmem, который по сути является внутренним линуксом винды, стал есть 36 Гб (!).
Относительно быстро разобрался, как писать логи стандартным инструментом Serilog из c# напрямую в эластик, соответственно Logstash стал мне не нужен, отключил его запуск в контейнере с elk.
В целом, впечатление достаточно приятное, даже при том, что я использую его только на 1%, как средство просмотра логов. Со временем подключу туда данные из Prometheus и можно будет запилить красивеньких дашбордов для мониторинга проекта.
#кодинг
ELK - стандартное средство для сбора, хранения и визуализации логов. Расшифровывается как Elasticsearch (поиск и хранение) + Logstash (собор из разнородных источников) + Kibana (Визуализация).
Попытался завести в докере вместе со всем остальным решением образ sebp/elk. Не взлетело: не хватило оперативки (у меня было 16 Гб). Ну чтож, добавил её, стало 64.
Взлетело! Процесс vmmem, который по сути является внутренним линуксом винды, стал есть 36 Гб (!).
Относительно быстро разобрался, как писать логи стандартным инструментом Serilog из c# напрямую в эластик, соответственно Logstash стал мне не нужен, отключил его запуск в контейнере с elk.
В целом, впечатление достаточно приятное, даже при том, что я использую его только на 1%, как средство просмотра логов. Со временем подключу туда данные из Prometheus и можно будет запилить красивеньких дашбордов для мониторинга проекта.
#кодинг
Elastic
Elastic Stack: (ELK) Elasticsearch, Kibana & Logstash
Reliably and securely take data from any source, in any format, then search, analyze, and visualize it in real time.
Как нормальные люди пишут тесты? Выявляют кейсы, которые надо протестировать, пилят в той или иной форме генератор тестовых данных, покрывают кейсы тестами, а потом гоняют их и радуются.
Если структура данных сложная, а вникать в суть происходящего лень - велик соблазн выгрузить пул данных из базы, сохранить в каком-то виде (например - штук 500 json файликов в специальной папочке в проекте с тестами) и на этой сохраненке и гонять тесты.
Я попробовал, больше не буду идти на поводу у низменных желаний. Тесты написались мгновенно, закрыли существенную часть бизнес логики, всё просто отлично. Я продолжил работу над проектом и каждый раз при провале теста мне приходилось разбирать происшествие с двух сторон: что я сломал? Хм, вроде ничего. А теперь посмотрим юзеркейс и добьёмся воспроизводимости.
Времени на отработку битых данных было потрачено столько, что намного быстрее было бы просто написать нормальные тесты и двигаться дальше.
#кодинг
Если структура данных сложная, а вникать в суть происходящего лень - велик соблазн выгрузить пул данных из базы, сохранить в каком-то виде (например - штук 500 json файликов в специальной папочке в проекте с тестами) и на этой сохраненке и гонять тесты.
Я попробовал, больше не буду идти на поводу у низменных желаний. Тесты написались мгновенно, закрыли существенную часть бизнес логики, всё просто отлично. Я продолжил работу над проектом и каждый раз при провале теста мне приходилось разбирать происшествие с двух сторон: что я сломал? Хм, вроде ничего. А теперь посмотрим юзеркейс и добьёмся воспроизводимости.
Времени на отработку битых данных было потрачено столько, что намного быстрее было бы просто написать нормальные тесты и двигаться дальше.
#кодинг
👍1
Палантир. Часть 25. Рефлексия год спустя.
#палантир@eshu_coding
Описание получившейся системы можно посмотреть по ссылке. Одной из некритичных ошибок была попытка сделать шину данных из костылей и велосипедов на gRPC вместо использования стандартного решения типа RabbitMQ.
Возможно, я сколько-то выиграл в производительности, но ценой этого был отказ от персистентности пропускаемых через шину данных и использование страшненьких костылей для подключения дополнительных потребителей данных.
Если мне надо раздвоить поток данных, уведя копию на другой сервис - в RabbitMQ требуется просто подключиться к нему, указав источник данных и новую очередь.
Мне пришлось вкрячивать внутрь приемника данных от сборщиков сервис подписок, чтобы данные сразу по получении рассылались всем кто подключен. Персистентность? Гарантированная доставка? Не, это не наш метод.
В принципе, ошибка крайне неэстетичная, но на функциональность особого влияния не оказывающая.
#палантир@eshu_coding
Описание получившейся системы можно посмотреть по ссылке. Одной из некритичных ошибок была попытка сделать шину данных из костылей и велосипедов на gRPC вместо использования стандартного решения типа RabbitMQ.
Возможно, я сколько-то выиграл в производительности, но ценой этого был отказ от персистентности пропускаемых через шину данных и использование страшненьких костылей для подключения дополнительных потребителей данных.
Если мне надо раздвоить поток данных, уведя копию на другой сервис - в RabbitMQ требуется просто подключиться к нему, указав источник данных и новую очередь.
Мне пришлось вкрячивать внутрь приемника данных от сборщиков сервис подписок, чтобы данные сразу по получении рассылались всем кто подключен. Персистентность? Гарантированная доставка? Не, это не наш метод.
В принципе, ошибка крайне неэстетичная, но на функциональность особого влияния не оказывающая.
👍4🔥1
Палантир. Часть 26. Рефлексия год спустя.
#палантир@eshu_coding
Описание получившейся системы можно посмотреть по ссылке. Намного менее приятной ошибкой было скатывание к монолиту. Центральный сервис (БД на postgresql + надстройка на шарпе) у меня выполнял четыре основные функции:
1. Прием и укладка данных в БД
2. Анализ БД и выдача заданий сборщикам
3. Поисковые кэш и запросы к нему.
4. Анализ потока входящей информации на наличие ключевых слов и выдача оповещений при обнаружении.
Данные попадали в надстройку над БД, проталкивались в базу огромными транзакциями, заодно обновляя служебные таблицы для выдачи заданий сборщикам. Индекс для полнотекстового поиска был построен прямо в основной таблице, поисковые запросы соответственно летели к ней. Триггер, срабатывающий на вставление новых данных, запускал цепочку других триггеров, в которых осуществлялся анализ добавляемого текста. В случае соответствия забитым в базу поисковым паттернам - база отправляла во внешний мир оповещение с помощью функции pg_notify.
В итоге такой подход вылился в жуткую боль как по администрированию, так и по поддержке. В конечном итоге, я вытащил функционал (4) в отдельный сервис, но боль от этого не сильно уменьшилась.
Как на мой нынешний взгляд надо было сделать:
1. Использовать RabbitMQ вместо самопала на gRPC (см прошлый пост)
2. Отработать запись данных в БД. Накрыть тестами функционал.
3. Запустить отдельным сервисом постановку задач сборщикам. Накрыть тестами функционал. Скорее всего, после окончательной отладки этот сервис сольётся с п. 2. Я начал делать их вместе, в итоге так и не смог до конца искоренить дублирование выгружаемых сообщений.
4. Завести отдельную пару сервис + бд для поискового кэша. Сделать как обновление в реальном времени, отведя с помощью RabbitMQ поток данных в сторону + предусмотреть функционал "перелива" данных из основной базы. Теперь я не привязан в PostgreSQL rum индексу на базе дефолтного словаря! И можно экспериментировать с поисковыми движками как душе угодно, не нарушая функциональность основного сервера. Хоть эластик попробовать, хоть сделать прослойку на питоне для умной обработки текста готовыми инструментами.
5. Отвести поток данных для анализа в реальном времени (для выдачи оповещений пользователям) и экспериментировать сколько душе угодно.
P.S. "Отвести поток данных" в случае RabbitMQ значит добавить несколько символов в месте подключения клиентской библиотеки.
P.P.S. Никто не мешает после отладки функционала по отдельности вернуться к монолиту, например для повышения быстродействия. Если закладывать такую возможность на старте - это дело 1-2 дней.
#палантир@eshu_coding
Описание получившейся системы можно посмотреть по ссылке. Намного менее приятной ошибкой было скатывание к монолиту. Центральный сервис (БД на postgresql + надстройка на шарпе) у меня выполнял четыре основные функции:
1. Прием и укладка данных в БД
2. Анализ БД и выдача заданий сборщикам
3. Поисковые кэш и запросы к нему.
4. Анализ потока входящей информации на наличие ключевых слов и выдача оповещений при обнаружении.
Данные попадали в надстройку над БД, проталкивались в базу огромными транзакциями, заодно обновляя служебные таблицы для выдачи заданий сборщикам. Индекс для полнотекстового поиска был построен прямо в основной таблице, поисковые запросы соответственно летели к ней. Триггер, срабатывающий на вставление новых данных, запускал цепочку других триггеров, в которых осуществлялся анализ добавляемого текста. В случае соответствия забитым в базу поисковым паттернам - база отправляла во внешний мир оповещение с помощью функции pg_notify.
В итоге такой подход вылился в жуткую боль как по администрированию, так и по поддержке. В конечном итоге, я вытащил функционал (4) в отдельный сервис, но боль от этого не сильно уменьшилась.
Как на мой нынешний взгляд надо было сделать:
1. Использовать RabbitMQ вместо самопала на gRPC (см прошлый пост)
2. Отработать запись данных в БД. Накрыть тестами функционал.
3. Запустить отдельным сервисом постановку задач сборщикам. Накрыть тестами функционал. Скорее всего, после окончательной отладки этот сервис сольётся с п. 2. Я начал делать их вместе, в итоге так и не смог до конца искоренить дублирование выгружаемых сообщений.
4. Завести отдельную пару сервис + бд для поискового кэша. Сделать как обновление в реальном времени, отведя с помощью RabbitMQ поток данных в сторону + предусмотреть функционал "перелива" данных из основной базы. Теперь я не привязан в PostgreSQL rum индексу на базе дефолтного словаря! И можно экспериментировать с поисковыми движками как душе угодно, не нарушая функциональность основного сервера. Хоть эластик попробовать, хоть сделать прослойку на питоне для умной обработки текста готовыми инструментами.
5. Отвести поток данных для анализа в реальном времени (для выдачи оповещений пользователям) и экспериментировать сколько душе угодно.
P.S. "Отвести поток данных" в случае RabbitMQ значит добавить несколько символов в месте подключения клиентской библиотеки.
P.P.S. Никто не мешает после отладки функционала по отдельности вернуться к монолиту, например для повышения быстродействия. Если закладывать такую возможность на старте - это дело 1-2 дней.
Telegram
Эшу быдлокодит
Палантир. Часть 24. Итоги, общая архитектура проекта.
#палантир@eshu_coding
По завершении ботов-агрегаторов проект я считаю завершенным, время описать что же получилось и подвести итоги.
На картинке:
DataFair - мастер сервер
DataLoader - сборщик
Пунтиром…
#палантир@eshu_coding
По завершении ботов-агрегаторов проект я считаю завершенным, время описать что же получилось и подвести итоги.
На картинке:
DataFair - мастер сервер
DataLoader - сборщик
Пунтиром…
👍3💩1
Эшу быдлокодит
Палантир. Часть 26. Рефлексия год спустя. #палантир@eshu_coding Описание получившейся системы можно посмотреть по ссылке. Намного менее приятной ошибкой было скатывание к монолиту. Центральный сервис (БД на postgresql + надстройка на шарпе) у меня выполнял…
В общем, если бы я строил Палантир сейчас, у меня вышло бы что-то подобное. Несколько проще, чем на схеме по ссылке, да и технологически беднее.
#палантир
#палантир
👍3
Выложил на гитхаб небольшой проектик - переделку бота-модератора телеграммных чатов. При написании его использовал несколько новых для себя подходов: TDD - test driven development, отказ от хранения состояния в приложении, использование тарантула в качестве персистентной очереди команд.
Бот не хранит никакой информации о собственном состоянии, зная лишь свой id, строку подключения к тарантулу и токен бота. Из телеграма прилетают оповещения об обновлениях, анализируются, после чего ставятся в очередь задач, хранящуюся в Тарантуле. За своим текущим состоянием, ответами на команды и т.д. бот тоже каждый раз ходит в тарантул. Отдельный поток слушает очередь задач в тарантуле, при появлении новых - исполняет.
Тарантул полностью живет в оперативной памяти, для связи по дефолту использует достаточно компактный и шустрый протокол - MessagePack. Это располагает к оптимизациям и работе с отдельными байтиками. Так, задача в очереди - это просто массив байт. Первый байт указывает на тип задачи, в соответствии с которым она и парсится и исполняется. Обычно задачи содержат, кроме первого байта, еще 2-3 int64 айдишника, то есть объем задачи получается во основном 17 или 25 байт. Такие объемы информации летают очень шустро.
К построению хранилища из нескольких инстансов на тарантуле есть два подхода: олдскульный, со сложными манипуляциями с конфигами и более современный, с использованием внешней утилиты - tarantool cartridge и модуля crud, предоставляющего синтаксический сахар над обычными операциями с данными и функционал для шардирования тарантула.
В репозитории по сути выложен шарповый стартер для экспериментов с конфигурацией кластера не выходя из Visual Studio: при запуске из докерфайла билдится контейнер, содержащих заданную конфигурацию кластера (по дефолту - мастер + реплика + балансировщик) и уже готовый к работе.
#tarantool
Бот не хранит никакой информации о собственном состоянии, зная лишь свой id, строку подключения к тарантулу и токен бота. Из телеграма прилетают оповещения об обновлениях, анализируются, после чего ставятся в очередь задач, хранящуюся в Тарантуле. За своим текущим состоянием, ответами на команды и т.д. бот тоже каждый раз ходит в тарантул. Отдельный поток слушает очередь задач в тарантуле, при появлении новых - исполняет.
Тарантул полностью живет в оперативной памяти, для связи по дефолту использует достаточно компактный и шустрый протокол - MessagePack. Это располагает к оптимизациям и работе с отдельными байтиками. Так, задача в очереди - это просто массив байт. Первый байт указывает на тип задачи, в соответствии с которым она и парсится и исполняется. Обычно задачи содержат, кроме первого байта, еще 2-3 int64 айдишника, то есть объем задачи получается во основном 17 или 25 байт. Такие объемы информации летают очень шустро.
К построению хранилища из нескольких инстансов на тарантуле есть два подхода: олдскульный, со сложными манипуляциями с конфигами и более современный, с использованием внешней утилиты - tarantool cartridge и модуля crud, предоставляющего синтаксический сахар над обычными операциями с данными и функционал для шардирования тарантула.
В репозитории по сути выложен шарповый стартер для экспериментов с конфигурацией кластера не выходя из Visual Studio: при запуске из докерфайла билдится контейнер, содержащих заданную конфигурацию кластера (по дефолту - мастер + реплика + балансировщик) и уже готовый к работе.
#tarantool
👍6🔥1
Эшу быдлокодит
Как нормальные люди пишут тесты? Выявляют кейсы, которые надо протестировать, пилят в той или иной форме генератор тестовых данных, покрывают кейсы тестами, а потом гоняют их и радуются. Если структура данных сложная, а вникать в суть происходящего лень …
И сегодня, на эти же грабли, в этом же самом месте, два раза, хренак, хренак!
Да, я так и не написал генератор данных, поленился.
Да, я так и не написал генератор данных, поленился.
🔥2🤣2🤯1
Получил лвлап и кучу экспириенса по работе с тарантулом, что характерно - на ровном месте.
Рассмотрим кейс: есть некая запись, относящаяся к человеку (user_id, unsigned(UInt64)). У нее есть время создания (creation_time, integer (Int64)) и время закрытия (cancelled_at, integer (Int64)).
Нужно дёрнуть все записи, относящиеся к человеку, актуальные на определенный момент времени. То есть запрос вида:
И все было прекрасно, пока я не столкнулся с реальностью: на "жирном" пользователе, с 20к+ записей, запрос выполняется неадекватные 20+ секунд.
В тарантуле есть составные индексы, туда можно передавать запросы из нескольких частей. Но вот беда: запрос, объединяющий сравнения == <= и >= он выполнить не может.
Решил зайти с необычной стороны: сделать условием >= и построить следующий индекс:
Сравнение происходит не по частям индекса. В сравнении участвует весь массив целиком как единая сущность. Говорят, что логика сравнения где-то была описана, но с ходу страница документации не ищется и где она лежит никто не помнит.
В тарантуле есть специальный инструментарий для работы с пространственными данными. Казалось бы, при чём тут они?
Поднимемся по лестнице абстракций на пару ступеней. Представим себе запись о пользователе прямоугольником в системе координат (id пользователя/время). Одна точка прямоугольника - создание записи, имеет координаты (user_id, creation_time). Вторая - (user_id, cancelled_at).
Соответственно, проверка "существовала ли запись о пользователе в определенный момент времени"? Сводится к задаче о принадлежности точки прямоугольнику. Готовое решение для запроса по индексу - в наличии. При желании, можно добавить ещё измерений (до 20), но что-то пока не хочется.
Проблема решена? Фииигушки. Индекс не ест мои значения в Int64 и UInt64, ему подавай Float64. При ковертации "в лоб" хвостики id теряются, из-за ограничений точности Float64. Добавляем очередной велосипед.
Барабанная дробь... Стандартный инструмент для запросов к кластеру не поддерживает (и скорее всего не будет) запросы по этому типу индексов. Опускаемся на пару уровней абстракций ниже, берем апи маршрутизации и делаем небольшую велосипедную хранимку, позволяющую вызвать хранимку на инстансах с данными через инстанс-маршрутизатор.
А вот теперь - всё. Ну или мне так кажется:)
P.S. В монге решил эту же задачу за 25 (!) минут, запрос + индексы, всё работает с первого раза.
#tarantool
Рассмотрим кейс: есть некая запись, относящаяся к человеку (user_id, unsigned(UInt64)). У нее есть время создания (creation_time, integer (Int64)) и время закрытия (cancelled_at, integer (Int64)).
Нужно дёрнуть все записи, относящиеся к человеку, актуальные на определенный момент времени. То есть запрос вида:
Входящие параметры: id, time.
user_id == id && creation_time <= time && (cancelled_at == nil || cancelled_at >= time)
Во всех мануалах предлагается простое решение, сделать select по самому лучшему условию, а дальше с помощью специального синтаксиса перебрать результат и отфильтровать нужное. Использовал этот подход, решил задачу и пошел дальше.И все было прекрасно, пока я не столкнулся с реальностью: на "жирном" пользователе, с 20к+ записей, запрос выполняется неадекватные 20+ секунд.
В тарантуле есть составные индексы, туда можно передавать запросы из нескольких частей. Но вот беда: запрос, объединяющий сравнения == <= и >= он выполнить не может.
Решил зайти с необычной стороны: сделать условием >= и построить следующий индекс:
creation_time, user_id, - user_id, - cancellation_time
То есть добавить в индекс инвертированный id пользователя и инвертировать время закрытия. Тогда в теории все должно выцепляться одним условием ">=". Что могло пойти не так? Всё. Как оказалось, для составных условий используется логика обработки частей запроса, отличающаяся от той, что заложена в схожей ситуации в MongoDB или PostgreSQL.Сравнение происходит не по частям индекса. В сравнении участвует весь массив целиком как единая сущность. Говорят, что логика сравнения где-то была описана, но с ходу страница документации не ищется и где она лежит никто не помнит.
В тарантуле есть специальный инструментарий для работы с пространственными данными. Казалось бы, при чём тут они?
Поднимемся по лестнице абстракций на пару ступеней. Представим себе запись о пользователе прямоугольником в системе координат (id пользователя/время). Одна точка прямоугольника - создание записи, имеет координаты (user_id, creation_time). Вторая - (user_id, cancelled_at).
Соответственно, проверка "существовала ли запись о пользователе в определенный момент времени"? Сводится к задаче о принадлежности точки прямоугольнику. Готовое решение для запроса по индексу - в наличии. При желании, можно добавить ещё измерений (до 20), но что-то пока не хочется.
Барабанная дробь... Стандартный инструмент для запросов к кластеру не поддерживает (и скорее всего не будет) запросы по этому типу индексов. Опускаемся на пару уровней абстракций ниже, берем апи маршрутизации и делаем небольшую велосипедную хранимку, позволяющую вызвать хранимку на инстансах с данными через инстанс-маршрутизатор.
А вот теперь - всё. Ну или мне так кажется:)
#tarantool
🔥4🤡2
Эшу быдлокодит
Получил лвлап и кучу экспириенса по работе с тарантулом, что характерно - на ровном месте. Рассмотрим кейс: есть некая запись, относящаяся к человеку (user_id, unsigned(UInt64)). У нее есть время создания (creation_time, integer (Int64)) и время закрытия…
Решил добавить в индекс еще пару измерений. Теперь главное не пытаться представить, как выглядит запись в базе данных в качестве 4х мерного прямоугольника-параллелепипеда (термин из тарантуловской документации), где измерения - id пользователя, время, ранг записи в древовидной структуре и её тип.
Forwarded from Вехденская правда
CV_StolbovV_short.pdf
90.2 KB
А разнесите резюму по своим каналам, что ли.
👍1💩1
Эшу быдлокодит
И сегодня, на эти же грабли, в этом же самом месте, два раза, хренак, хренак! Да, я так и не написал генератор данных, поленился.
И в четвертый раз, на те же грабли, в том же месте!
Да, я пока ленюсь написать нормальный генератор данных.
Да, я пока ленюсь написать нормальный генератор данных.
🤡4🤣2
Эшу быдлокодит
И в четвертый раз, на те же грабли, в том же месте! Да, я пока ленюсь написать нормальный генератор данных.
И в пятый, надеюсь - в последний.
😁6🤡3
Пока монга выполняет роль хранилища-помойки, вида: положил кучку - забрал по id или паре простых запросов - она прекрасна и крайне дружелюбна.
Но стоит сделать шаг втемноту сторону каких-то статистических запросов - все перестает быть столь уютным.
Для человека, два года пользующегося монгой и казалось бы привычного к ней, первое составление запроса вида (дико извиняюсь за псевдо-sql):
Сначала, как обычно, пытаешься слепить запрос прямо в c#, используя синтаксический сахар, предоставляемый библиотекой-конектором.
После полутора дней мучений в с# я отправился в дефолтную графическую оболочку над базой, где часа за 2 в конструкторе составил нужный мне запрос.
Теперь я имею json в 30 строк, который при выполнении в монге выдает нужный мне результат. Осталось корректно перенести его в с#.
#mongodb
Но стоит сделать шаг в
Для человека, два года пользующегося монгой и казалось бы привычного к ней, первое составление запроса вида (дико извиняюсь за псевдо-sql):
select distinct field1, field2 from table where (пара условий) order by field2 limit 100 skip 100
становится увлекательным квестом на пару дней. То есть мне нужно взять данные без дублирования по одному полю из таблицы по условию, отсортировать и выдать из базы с пагинацией (постранично).Сначала, как обычно, пытаешься слепить запрос прямо в c#, используя синтаксический сахар, предоставляемый библиотекой-конектором.
После полутора дней мучений в с# я отправился в дефолтную графическую оболочку над базой, где часа за 2 в конструкторе составил нужный мне запрос.
Теперь я имею json в 30 строк, который при выполнении в монге выдает нужный мне результат. Осталось корректно перенести его в с#.
#mongodb
💩2
Эшу быдлокодит
Пока монга выполняет роль хранилища-помойки, вида: положил кучку - забрал по id или паре простых запросов - она прекрасна и крайне дружелюбна. Но стоит сделать шаг в темноту сторону каких-то статистических запросов - все перестает быть столь уютным. Для…
Вот примерный json, генерируемый запросом. Сравните по читаемости с псевдо-sql версией в прошлом посте.
Монга умеет в distinct запросы, но туда не завезли пагинации (постраничной отдачи), потому идём окольным путём 🤡
#mongodb
[{
$match: {
Field3: "qwerty123",
},
},
{
$group: {
_id: "$Field1",
id: {
$first: "$Field1",
},
Field2: {
$first: "$Field2",
},
},
},
{
$sort: {
Field2: -1,
},
},
{
$skip: 0,
},
{
$limit: 2,
},
{
$project: {
_id: 1,
},
},
]
group в данном случае используется для получения эффекта distinct из sql запроса.Монга умеет в distinct запросы, но туда не завезли пагинации (постраничной отдачи), потому идём окольным путём 🤡
#mongodb