ermouth: (Default)
[personal profile] ermouth

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

Производительность достигается за счёт того, что страницы двинаньюса собраны из блоков и кэшируются не страницы целиком, а отдельные блоки. То-есть подавляющее большинство страниц при сборке даже если и требуют обращения к БД, то буквально единственного. Остальные блоки берутся из кэша.

Кэш – в оперативной памяти, полностью синхронный. Используется просто очень большой javascript-объект, hash table. Главные ключи формируются из параметра вызова шаблонизатора блока.

Получается, что каждый экземпляр приложения (при многопоточной реализации) имеет свой полный экземпляр кэша. Это неэкономно, на первый взгляд, с точки зрения использования RAM. Тем не менее, при моих прикидках у меня получалось, что мне никогда не нужно будет больше гигабайта памяти на ядро процессора – и Амазон EC2 прекрасно подошел.

Главная фича – как блоки инвалидировать, как кэшу узнать, что блок пора пересчитать при следующем запросе.

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

В какие именно таблицы указателей помещать, решает конкретный шаблонизатор – на основании параметров, с которыми он был вызван. Например, если мы запросили вывод списка заголовков по теме Культура, только с картинками – результат будет положен и в таблицу тега “Культура”, и в таблицу признака “С картинками”, и в таблицу блоков, отрендерённых шаблонизатором “Список заголовков”. Это называется “полностью ассоциативный кэш”.

Теперь надо узнать, когда эти таблицы инвалидировать.

CouchDB имеет интерфейс, уведомляющий подключеного клиента о появлении или обновлении документа в той или иной БД. По такому уведомлению новый док загружается и кэшируется в RAM, а все блоки, на которые он мог бы повлиять, отмечаются как устаревшие. Это очень быстрая операция, потому что итератор просто обходит все объекты по нужным хэш-таблицам и сбрасывает признак готовности.

Физически объекты из памяти не удаляются – потому что они скорее всего будут вскоре заново сгенерированы и поверх переписаны, это раз. И потому, что удаление, скажем, 50К объектов (например, мы обновили какой-то часто используемый шаблонизатор) вызовет stall на время сборки мусора.

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

В однопроцессорной системе можно удалять по ttl, причём это вполне можно делать асинхронно. Но так как у нас система многопоточная (несколько одинаковых экземпляров js-процесса, по которым раскидываются запросы), гораздо проще по минимуму нагрузки эти потоки по очереди рестартовать – так и сделано.

С помощью этой же механики происходит рестарт потока при падении – оно очень живучее.

Со старта обслужено примерно 1.5М запросов к серверу, даже не чихнуло ничего.

Profile

ermouth: (Default)
ermouth

November 2021

S M T W T F S
 123456
78910111213
14151617181920
21 222324252627
282930    

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Feb. 1st, 2026 07:54 pm
Powered by Dreamwidth Studios