Потоки

Поток — это упорядоченный надёжный журнал пакетов, идентифицируемый по base62-идентификатору и ограниченный окном хранения. Эта страница раскрывает понятие целиком: идентичность, курсор, хранение, режимы и гарантии, на которые можно и нельзя опираться.

Идентичность

Каждый поток именуется 22-символьным base62-идентификатором, например cBczepiZSW8GJaCae0xIj7. Никакого префикса и никакого другого формата id нет. Вы можете позволить серверу сгенерировать id при создании, а можете сгенерировать base62-id на стороне клиента и создать поток с ним; передача собственного id делает само создание потока идемпотентным.

Человекочитаемое name — это метаданные для вашего удобства. По умолчанию оно не уникально в рамках проекта и никогда не используется для адресации потока.

Курсор

cursor потока — это номер новейшего принятого им пакета. На пустом потоке он начинается с 0 и движется только вперёд. Каждый подписчик отслеживает свою собственную позицию, тоже называемую курсором, — это просто номер последнего обработанного им пакета. «Возобновить с курсора N» означает «дай мне всё после номера N».

GET /v1/streams/{id}/cursor
{"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-streamBase64 в поле data SSE или дословные байты при повтореprotobuf, msgpack, Avro, собственный формат кадров

В режиме raw Runnev никогда не разбирает вашу полезную нагрузку. Оба пути см. в разделе Публикация.

Жизненный цикл: создание, осмотр, удаление

Создание

POST /v1/streams
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) возвращает его метаданные:

GET /v1/streams/{id}
{
  "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= (токен пагинации, не связанный с курсором номеров потока).

Удаление

DELETE /v1/streams/{id}
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 не помещает его в карантин.