Postgres упирался в 2900 записей в секунду, не нагружая при этом ничего
Команда DBOS разобрала, откуда у LISTEN/NOTIFY репутация нерабочего при нагрузке механизма, и переписала свой стриминг до 60 тысяч записей в секунду на одном сервере.
Исходная схема обычная: каждый кусок потока, например токен ответа модели, становится строкой в таблице, а триггер на вставку шлёт NOTIFY, чтобы читатели просыпались вместо опроса. Дальше 2900 записей в секунду эта конструкция не шла, причём ни процессор, ни диск, ни память базы заняты не были.
Причина в том, что коммит транзакции с NOTIFY берёт глобальный эксклюзивный лок и держит его до конца коммита вместе с fsync. Postgres обещает доставлять уведомления в порядке коммитов, а сам порядок известен только когда коммит завершён: лок решает это противоречие ценой полной сериализации. Групповой коммит при этом отключается, и записи идут строго по одной.
🔘 патч, который ждут в Postgres 19, глобальный лок не убирает. Он оптимизирует более узкий случай, когда каналов много и каждый слушатель ждёт свой;
🔘 обход: копить уведомления в памяти и сбрасывать пачкой одной транзакцией, тогда лок берётся раз на пачку, а не на каждую запись;
🔘 плата за это — уведомления, потерянные при падении процесса, поэтому читатели вдобавок редко опрашивают таблицу как запасной путь;
🔘 после переделки упор идёт уже в процессор базы, а не в блокировку.
Про задержку авторы пишут 15–100 мс, но по их же графику это диапазон для нагрузки до 40 тысяч записей в секунду. Ближе к 60 тысячам медиана уходит примерно к 0,5 с, а 99-й перцентиль к секунде. Код бенчмарка выложен, конкретный инстанс базы в статье не назван.
Полная статья: https://www.dbos.dev/blog/postgres-listen-notify-scalability
Сохранять тем, кто держит очередь или пуш-уведомления прямо в Postgres и однажды упрётся в потолок, которого не видно ни в одном мониторинге.
@prog_stuff
Post #2893
729

- 👍 1