Да, именно так и есть: изначальная идея "описываем ИИ ситуацию, запрашиваем данные/решение - вся логика и креатив на ИИ" оказалась нереализуемой и постепенно сместилась до "использование ИИ должно быть минимизировано на столько, на сколько это вообще возможно". Не скрою, это обстоятельство меня сильно разочаровало. Но зато после осознания, как говориться, дело пошло.
Если будет возможность - опишу этот процесс подробно отдельной статьей.
Да, согласен, ситуация в этом плане постепенно меняется. Поэтому "большинство", а не "все". Т.е. если нужен качественный русский язык, сейчас это не непреодолимое препятствие, а лишь существенный ограничитель выбора.
Что касается Qwen3 - тестировали вариант 8B 100+ дневной давности - были некоторые проблемы. Ваш вариант обязательно погоняем. Среди прочих опробованных (в разных вариантах квантизации): llama3 8B, gemma2 9B, llama3.1 в разных вариантах, ministral 8B, qwen2.5, deepseek-r1 (разные дистилляции), t-lite-it-1, yandexGPT5 8b, saiga, phi 3.5, gemma3.
Отчего ж? Аж двойная.
1. Спец. флаг в Redis (защищает при первом генерировании в основном)
2. Контрольная проверка наличия кеш-значения с t < TTL1 в начале процедуры перегенерации. Это если очередь долго разгребалась.
Совершенно верно. И придя к тем же выводам, я убрал «фоновую актуализацию» и внедрил «2-х диапазонные кеши», которые обновляются только в том случае, если востребованы.
Да, видимо оно самое. Было бы странно, если бы такую, в общем-то несложную, вещь я бы придумал первым. Но тем не менее, информации на русском — найти не удалось, на английском — очень сложно (и то только благодаря комментариям к этой статье).
Не совсем. Подобную технологию я использовал ранее. Ее цель — убрать случаи пиковой нагрузки.
Например, если на странице нужны 10 кеш-значений, с TTL=100, то по истечении этих 100 секунд все 10 блоков будут перегенерироваться в рамках одного запроса. Чтобы этого избежать, у меня автоматом добавлялась вариативность времен в пределах +-20%. Что гарантировало «постепенное» обновление всего набора.
Получается, что идеи хоть и очень схожи, но все-таки разные. Моя основная цель — «ленивое» обновление, что как часть включает в себя и «защиту сервера».
Идея как раз в том, чтобы по-возможности вообще никогда не заставлять ждать (самое первое генерирование не в счет). А большая «навороченность» обусловлена далеко не столько идеей 2-диапазонного кеширования, сколько размером системы — у много-серверных продуктов свои особенности и требования.
О, с редисом все нормально — ставим довольно короткий TTL для записи самого флага, так что дедлоков не случается. Да и новый консюмер поднимается в течение 10 секунд максимум, если что.
А вот на счет мемкеш — спасибо, учтем.
Да, концепция не дает. Но указанную проблему мы решили. Дело в том, что задачи на обновление у нас идут через Rabbit. Соответственно, поток, занимающийся пересчетом, получая задачу, просто проверяет наличие в кеше соответствующего значения с t < TTL1. Если так и есть, значит задача — дубль, пропускаем. Если t > TTL1 — работаем. И есть отдельный поток — он занимается принудительным пересчетом (иногда надо обновить срочно и вне общей очереди).
PS На самом деле немного обманул — дополнительно еще ставится/проверяется/удаляется спец. флаг, означающий «задача есть в очереди». Для этого мы используем Redis.
По второму замечанию — тут сложнее определять, что значит «порядочно» в каждом конкретном случае. Фиксация в секундах или процентах наверняка где-то будет препядствием. Да и в плане разработки — посложнее. При появлении нового набора данных для кеширования, надо внести правки и в общую процедуру «дай кеш-значение».
Согласен, тоже хорошее решение. Я пробовал фоновый поток, обходящий кеши. Но очень быстро вышло, что слишком много надо было пересчитывать. Когда пришлось ввести 2 фоновых потока, постоянно без пауз что-то считающих, я пошел «в другую сторону».
Ну я вроде и не говорил, что очередь прямо в воркере сделана. Согласен, что подход с «отдельной очередью» более привлекателен, чем «очередь в самом воркере». Поэтому я использую RabbitMQ.
Вообще, я хотел написать целый цикл статей, т.к. за время развития проекта накопилась масса интересных вещей. А тут почти каждый из коментариев что-то да затрагивает из этого.
«Бизнес-логика в монолите». О, такие задачи уже были. И применено два типа решений — конвеер и обратное обращение. Но подробно я опишу это отдельно.
Все верно. В чем суть вашего вопроса? Почему просто монолит на новой ноде не подняли?
Отвечаю: чтобы поднять полноценную ноду с монолитом, надо дополнительно установить и настроить десяток утилит; при запуске монолит устанавливает кучу коннектов и производит много действий по инициализации — надо существенно его переработать, чтобы делал это не при каждом обращении, а один раз при старте; дополнительные работы по настройке обновлений; сохранение всех зависимостей монолита; многократно выше требования к железу. Это — большой пласт работ по подготовке к «run as node» + дополнительные сложности в сопровождении. Зачем? Когда можно сделать маленький сервис без лишних подключений, монструозного кода и прочих проблем.
C «копированием всего себя» хорошо работают системы, которые изначально ориентированы на подобный тип масштабирования. Моя — нет.
Поэтому вместо «возить Белазом по одному кирпичу» я сконструировал быструю «тележку с выделенной полосой движения».
Полностью согласен с аспектом критичности данных. Поэтому все важные вещи пишутся, как раньше — сразу в БД без всяких очередей. А вот менее важные — через очередь, хотя сама по себе она обеспечивает очень хороший уровень безопасности, сохраняя данные «для сервиса» у себя локально на диск до тех пор, пока сервис не подтвердит, что он полностью все обработал.
Да, использовать посредника для записи в БД субъективно страшнова-то. Но объективно, риск в этом случае всего лишь удваивается, по сравнению с риском в варианте «пишем сразу». Тут много надо говорить, так что, наверное, напишу об этом отдельную статью потом.
Предмет разговора — сайт на PHP. Я не нашел эффективного способа организовать очередь запросов в монолите. В приложении на C++/Delphi — запросто. А тут — нет. Как ее сделать? Эмулировать через ту же БД, записывая в нее сами запросы? Или Redis задействовать? А как выполнять? И вот продумывая варианты, я пришел к выводу, что самый простой способ и с точки зрения реализации и с точки зрения логики — просто вынести функционал записи «во вне», переложив всю организацию очереди на специализированный софт.
По производительности, вот с чем я боролся:
1. Запускается один процесс обработки данных. Он делает «тяжелые» запросы на выборку, а потом много «легких» на запись.
2. Запускается второй процесс, которому наоборот надо много выбрать и редко, но «сильно» записать.
3. Еще один процесс делает частые-частые маленькие запросы на запись. Логирует.
Что в итоге: создается значительная нагрузка на HDD сервера с СУБД. И он полностью начинает тормозить. Просто потому, что HDD с такой интенсивностью не справляется. А это сказывается на всей системе в целом.
Варианты: вложиться в железо или ограничить нагрузку. Я пошел по второму пути.
В идеале — должны храниться отдельно и заправшиваться через «обращение к внешнему сервису». На практике — поскольку с железом не богато и для всех БД все-равно один сервер используется, смысла отделять полностью не было. Хранятся в той же. Поэтому, с одной стороны, с точки зрения чистоты подхода — это не совсем внешний сервис, но с точки зрения «работает вне ядра, отдельно» — все-таки да.
В идеале — должны храниться отдельно и заправшиваться через «обращение к внешнему сервису». На практике — поскольку с железом не богато и для всех БД все-равно один сервер используется, смысла отделять полностью не было. Хранятся в той же. Поэтому, с одной стороны, с точки зрения чистоты подхода — это не совсем внешний сервис, но с точки зрения «работает вне ядра, отдельно» — все-таки да.
VolCh ответил совершенно точно, но хочу дополнительно пояснить на примере: допустим надо писать что-то в БД, при этом данные не критичны — пересчет каких-то кешей по истории действий пользователя. Чуть раньше они пересчитаются или чуть позже — не важно. И таких процессов пересчета несколько и организовать их последовательное выполнение одним cron-ом затруднительно. По факту имеем периодически «проседающую» под нагрузкой БД. Решение в таком случае — отдельный сервис, который занимается пересчетом. А по cron-у только постановка задач этому сервису. Результат: все пересчеты идут строго последовательного по одному, БД не «напрягается».
В моей системе реализован сервис чуть более низкого уровня — «Отложенная запись в БД». Я в него отправляю непосредственно запросы. Очень маленькая по размеру, но важная по смыслу штука получилась. И эффект дает огромный.
Да, смысл заголовка не совсем «склеился» с текстом. Но ничего точнее не придумалось. Зачем вообще взялся: много материалов про монолиты, много — про микросервисы. Каждый агитирует за свое. И разработчики часто (все, кого я опрашивал) начинают противопоставлять подходы. И никто не пытается взять из обоих лучшее. Хотелось обратить внимание на то, что однобокий подход, хоть и дает иногда очень хорошие результаты, но именно лишь иногда. А на практике очень часто лучший результат достигается «сбалансированной смесью» подходов и технологий.
Да, именно так и есть: изначальная идея "описываем ИИ ситуацию, запрашиваем данные/решение - вся логика и креатив на ИИ" оказалась нереализуемой и постепенно сместилась до "использование ИИ должно быть минимизировано на столько, на сколько это вообще возможно". Не скрою, это обстоятельство меня сильно разочаровало. Но зато после осознания, как говориться, дело пошло.
Если будет возможность - опишу этот процесс подробно отдельной статьей.
Спасибо! Учту
Да, согласен, ситуация в этом плане постепенно меняется. Поэтому "большинство", а не "все". Т.е. если нужен качественный русский язык, сейчас это не непреодолимое препятствие, а лишь существенный ограничитель выбора.
Что касается Qwen3 - тестировали вариант 8B 100+ дневной давности - были некоторые проблемы. Ваш вариант обязательно погоняем. Среди прочих опробованных (в разных вариантах квантизации): llama3 8B, gemma2 9B, llama3.1 в разных вариантах, ministral 8B, qwen2.5, deepseek-r1 (разные дистилляции), t-lite-it-1, yandexGPT5 8b, saiga, phi 3.5, gemma3.
1. Спец. флаг в Redis (защищает при первом генерировании в основном)
2. Контрольная проверка наличия кеш-значения с t < TTL1 в начале процедуры перегенерации. Это если очередь долго разгребалась.
Например, если на странице нужны 10 кеш-значений, с TTL=100, то по истечении этих 100 секунд все 10 блоков будут перегенерироваться в рамках одного запроса. Чтобы этого избежать, у меня автоматом добавлялась вариативность времен в пределах +-20%. Что гарантировало «постепенное» обновление всего набора.
Получается, что идеи хоть и очень схожи, но все-таки разные. Моя основная цель — «ленивое» обновление, что как часть включает в себя и «защиту сервера».
А вот на счет мемкеш — спасибо, учтем.
PS На самом деле немного обманул — дополнительно еще ставится/проверяется/удаляется спец. флаг, означающий «задача есть в очереди». Для этого мы используем Redis.
По второму замечанию — тут сложнее определять, что значит «порядочно» в каждом конкретном случае. Фиксация в секундах или процентах наверняка где-то будет препядствием. Да и в плане разработки — посложнее. При появлении нового набора данных для кеширования, надо внести правки и в общую процедуру «дай кеш-значение».
Вообще, я хотел написать целый цикл статей, т.к. за время развития проекта накопилась масса интересных вещей. А тут почти каждый из коментариев что-то да затрагивает из этого.
«Бизнес-логика в монолите». О, такие задачи уже были. И применено два типа решений — конвеер и обратное обращение. Но подробно я опишу это отдельно.
Отвечаю: чтобы поднять полноценную ноду с монолитом, надо дополнительно установить и настроить десяток утилит; при запуске монолит устанавливает кучу коннектов и производит много действий по инициализации — надо существенно его переработать, чтобы делал это не при каждом обращении, а один раз при старте; дополнительные работы по настройке обновлений; сохранение всех зависимостей монолита; многократно выше требования к железу. Это — большой пласт работ по подготовке к «run as node» + дополнительные сложности в сопровождении. Зачем? Когда можно сделать маленький сервис без лишних подключений, монструозного кода и прочих проблем.
C «копированием всего себя» хорошо работают системы, которые изначально ориентированы на подобный тип масштабирования. Моя — нет.
Поэтому вместо «возить Белазом по одному кирпичу» я сконструировал быструю «тележку с выделенной полосой движения».
Да, использовать посредника для записи в БД субъективно страшнова-то. Но объективно, риск в этом случае всего лишь удваивается, по сравнению с риском в варианте «пишем сразу». Тут много надо говорить, так что, наверное, напишу об этом отдельную статью потом.
По производительности, вот с чем я боролся:
1. Запускается один процесс обработки данных. Он делает «тяжелые» запросы на выборку, а потом много «легких» на запись.
2. Запускается второй процесс, которому наоборот надо много выбрать и редко, но «сильно» записать.
3. Еще один процесс делает частые-частые маленькие запросы на запись. Логирует.
Что в итоге: создается значительная нагрузка на HDD сервера с СУБД. И он полностью начинает тормозить. Просто потому, что HDD с такой интенсивностью не справляется. А это сказывается на всей системе в целом.
Варианты: вложиться в железо или ограничить нагрузку. Я пошел по второму пути.
В моей системе реализован сервис чуть более низкого уровня — «Отложенная запись в БД». Я в него отправляю непосредственно запросы. Очень маленькая по размеру, но важная по смыслу штука получилась. И эффект дает огромный.