Перейти до вмісту

Проєкти05 / 07 · сервіс рендеру · 2022–дотепер

ImageMaker 05

Відповідайте «цитата» на будь-яке повідомлення й отримуйте готове зображення приблизно за секунду — і той самий сервіс тихо рендерить для другого продукту.

Техлід · архітектор · devops
Одразу до інженерної частини
Простими словами

Люди цитують одне одного скриншотами. Це замінюється одним словом.

У групових чатах люди цитують одне одного скриншотами — некрасиво, нечитабельно, і поза застосунком таким не поділишся. ImageMaker замінює це одним словом: відповідаєте «цитата» на будь-яке повідомлення, і бот повертає верстане зображення з текстом, аватаром автора, його ім’ям і датою на підібраному тлі.

Рішення, яке подвоїло цінність. Замість того щоб лишити це окремою іграшкою, я зробив із нього спільний рендер-рушій для другого продукту компанії. Той самий сервіс тепер обслуговує власну аудиторію і три оточення іншого бота на іншому фізичному сервері — одна кодова база, одна команда, дві продуктові лінійки.

У продакшні з грудня 2022-го, 118 тисяч нових користувачів лише за останній рік, і весь рахунок за хостинг — дванадцять доларів на місяць. Ця цифра не про хвастощі економністю; вона про те, чому існує половина інженерії нижче.

Для інженерів

Типографіка через вимірювання і 586 МБ рендеру на сервері з 1,9 ГБ

Дворежимний рантайм, підбір кегля бінарним пошуком, каскадний LLM-фолбек, кожен memory-кіл замкнений у власному cgroup.

Дворежимний рантайм

Одна кодова база відповідає на Telegram-вебхуки і працює як RPC-консюмер — request-reply за correlation-id через RabbitMQ, зображення передаються як zlib-стиснений base64. Той самий шлях рендеру обслуговує власних користувачів і три оточення іншого продукту, тож виправлений тут баг виправлений двічі.

Асиметрична федерація черг між двома серверами

Рендер-хост забирає задачі з app-хоста й пише відповіді локально; app-хост забирає їх назад. Жодних відкритих HTTP-портів між машинами, жодної прямої зв’язності — і типовий режим відмови задокументований для того, хто на чергуванні. Мертвий хост деградує фічу, а не валить каскадом другий продукт.

Типографіка через вимірювання, а не евристику

Три незалежні бінарні пошуки підбирають розмір цитати, імені й дати — цитату по реальних вимірах рендеру Pango, ім’я й дату по caption-рендереру ImageMagick, — обмежені попередньо налаштованою таблицею максимумів зі швидким шляхом, плюс четвертий, уже на Pango, щоб найдовше слово не виїхало за межі. Жодних магічних констант кегля й жодного «зазвичай вміщається».

586 МБ пікового рендеру на 1,9 ГБ

Багатошарові ліміти ресурсів ImageMagick, щоб патологічний вхід не поклав хост; тюнінг арен glibc malloc (MALLOC_ARENA_MAX=2 у systemd drop-in), бо RMagick тримає пам’ять поза GC Ruby; дисципліноване знищення зображень у кожному блоці ensure; рендер винесено з веб-процесу в Sidekiq, після чого веб-процес перестало вбивати зовсім — а кожен кіл поза ним лишається у власному cgroup.

Каскадний LLM-фолбек

Бот сам підбирає тло: AI читає цитату й вибирає тему. Чотири моделі пробуються по черзі, кожна у власному rescue, вихід валідується проти білого списку — тож жодна відмова моделі не ламає рендер, і користувач ніколи не бачить помилки. AI тут — приємний бонус у пасках безпеки, а не залежність.

Форматування виживає · особа автора

Сутності (entities) Telegram перетворюються на Pango-розмітку: фільтруються перетином із цитованим фрагментом, застосовуються у зворотному порядку, щоб вставка тегів не зсувала наступні індекси, далі — екранування в три проходи. Форварди, приховані відправники, пости каналів і супергруп, анонімні адміни й сервісні боти визначаються по-різному — з ланцюжком фолбеків аж до одного з 17 плейсхолдер-аватарів і круглим кропом через маску.
+118 тис.
користувачів за рік
0
OOM-кілів у веб-процесі, відколи рендер переїхав у Sidekiq
18 тис.
найбільший чат
179
написаних spec-блоків · RSpec і RuboCop у CI
Варто сказати прямо: цікава інженерія тут не в масштабі — а в тому, щоб робити рендер зображень, виклики LLM і міжсерверний RPC у межах дванадцяти доларів на місяць так, щоб користувач ніколи не побачив збою.

Хто над цим працював · 5 учасників

Даниїл Шкоропад

техлід · архітектор · devops · 2022–2026
  • Дворежимний рантайм і рішення зробити з нього спільний рендер-рушій для другого продукту.
  • Звів піковий рендер до 586 МБ на 1,9 ГБ, і веб-процес перестало вбивати зовсім.

Владислав Фоменко

фулстек · 2024–2025
  • Переніс рендер цитат у цей сервіс і зробив API-ендпоінт, який викликає другий продукт.
  • Частина вимірів кегля й форматування тексту, на яких тримається зображення.

Олександр Шемберко

бекенд · 2025–2026
  • Команда /qx — готові цитати авторів, тобто та половина сервісу, яку викликає другий продукт.
  • Налаштування цитат на користувача, фони, які підбирає AI, і адмінська команда /stat.

Михаїл Юн

співзасновник · продукт · 2022–2026
  • Розмова, що перетворила окрему іграшку на спільний рендер-рушій для другого продукту.
  • Позиціювання й вимоги до того, що має бути на картці цитати.

Claude

рев’ю pull request’ів · 2026
  • Рев’ює шлях рендеру, де витік пам’яті проявляється як OOM через два дні.
  • Перевіряє, що кожен об’єкт зображення знищено в блоці ensure.

Дочитали до кінця й хочете обговорити?

rubyco.in