Потоки
Поток — это упорядоченный надёжный журнал пакетов, идентифицируемый по base62-идентификатору и ограниченный окном хранения. Эта страница раскрывает понятие целиком: идентичность, курсор, хранение, режимы и гарантии, на которые можно и нельзя опираться.
Идентичность
Каждый поток именуется 22-символьным base62-идентификатором, например
cBczepiZSW8GJaCae0xIj7. Никакого префикса и никакого другого формата id нет. Вы
можете позволить серверу сгенерировать id при создании, а можете сгенерировать base62-id на
стороне клиента и создать поток с ним; передача собственного id делает само создание потока
идемпотентным.
Человекочитаемое name — это метаданные для вашего удобства. По умолчанию оно не
уникально в рамках проекта и никогда не используется для адресации потока.
Курсор
cursor потока — это номер новейшего принятого им пакета. На пустом потоке он
начинается с 0 и движется только вперёд. Каждый подписчик отслеживает свою
собственную позицию, тоже называемую курсором, — это просто номер последнего обработанного им
пакета. «Возобновить с курсора N» означает «дай мне всё после номера N».
{"cursor":41823,"updated_at":"2026-08-04T11:02:13.418Z"}
Хранение
Поток хранит пакеты, пока они не выйдут за пределы любой из двух границ, смотря какая достигнута первой:
retention_seconds— возраст. Пакет старше этого значения подлежит вытеснению. Диапазон от 60 до 2 592 000 секунд (от одной минуты до тридцати дней); значения вне диапазона отклоняются сretention_out_of_range.max_bytes— суммарный хранимый размер. Когда сумма размеров пакетов превысила бы это значение, старейшие пакеты вытесняются, пока не поместится. По умолчанию 256 МиБ.
Вытеснение идёт с хвоста (старейшие первыми) и является единственным способом, которым пакеты
покидают поток, помимо удаления самого потока. Вытеснение сдвигает эффективную нижнюю границу, но
никогда — cursor: номера уцелевших пакетов не меняются. Подписчик, возобновляющий с
курсора старше нижней границы, следующим получает старейший уцелевший пакет, а разрыв проявляется
как скачок в номерах — это ваш сигнал, что он отстал за пределы окна.
Выбирайте хранение исходя из худшего реалистичного простоя подписчика, а не среднего. Если потребитель может быть недоступен два часа во время деплоя, час хранения потеряет данные; дайте комфортный запас. Подробный расчёт есть в блоге: Как рассчитать кольцевые буферы.
Режимы json и raw
Поток создаётся в одном из двух режимов, зафиксированном на всю его жизнь:
| Режим | Тело публикации | Доставляется как | Применять для |
|---|---|---|---|
json (по умолчанию) | {"events":[...]} | JSON-пакет с массивом events | Структурированные доменные события |
raw | байты application/octet-stream | Base64 в поле data SSE или дословные байты при повторе | protobuf, msgpack, Avro, собственный формат кадров |
В режиме raw Runnev никогда не разбирает вашу полезную нагрузку. Оба пути см. в
разделе Публикация.
Жизненный цикл: создание, осмотр, удаление
Создание
curl https://runnev.dev/v1/streams \
-H "Authorization: Bearer $RUNNEV_API_KEY" \
-d '{"name":"orders-eu","retention_seconds":86400,"mode":"json"}'
Обязательно только name. Дублирующееся name допускается; если вам нужна
уникальность имени, обеспечьте её на своей стороне или генерируйте id сами. Повторное использование
уже созданного id возвращает 409 с stream_name_taken.
Осмотр
Обычный GET потока (без Accept: text/event-stream) возвращает его
метаданные:
{
"id": "cBczepiZSW8GJaCae0xIj7",
"name": "orders-eu",
"created_at": "2026-06-11T09:22:31Z",
"retention_seconds": 86400,
"max_bytes": 268435456,
"cursor": 41823,
"bytes_stored": 118293011,
"mode": "json"
}
Список всех потоков проекта — через GET /v1/streams, который пагинируется с
?limit= и ?cursor= (токен пагинации, не связанный с курсором номеров
потока).
Удаление
curl -X DELETE https://runnev.dev/v1/streams/$STREAM \
-H "Authorization: Bearer $RUNNEV_API_KEY"
# 204 No Content
Удаление немедленно и безвозвратно. Открытые подписчики получают завершающее
event: end, и соединение закрывается.
Гарантии
На что можно полагаться:
- Порядок. В пределах потока пакеты доставляются в порядке номеров.
- Надёжность в пределах хранения. Принятый пакет доступен для чтения любому подписчику, пока не будет вытеснен правилами хранения.
- Доставка не менее одного раза. Подписчик, корректно возобновляющий с своего
курсора, видит каждый уцелевший пакет как минимум один раз. Переподключение вокруг сетевого
сбоя может повторно доставить последний пакет; дедуплицируйте по
seq. - Идемпотентные записи. Публикация одного и того же
(stream, seq)дважды сохраняет его один раз.
Что явно не гарантируется:
- Нет exactly-once. См. «не менее одного раза» выше.
- Нет подтверждений или повторной доставки на потребителя. На стороне сервера нет понятия «потребитель обработал пакет».
- Нет упорядочивания между потоками. У двух потоков нет определённого взаимного порядка. Если нужен глобальный порядок — используйте один поток.
- Нет обработки dead-letter. Пакет, который вы не можете обработать, — ваша забота; Runnev не помещает его в карантин.