Expand description
Server-Sent Events: a response that sends events as they happen (Rails’ ActionController::Live with SSE).
A handler returns stream()(state, step): Ocre calls step for each
event, and the Worker streams every event to the client as soon as it is
produced, until step returns None. The response is axum’s Sse
(Content-Type: text/event-stream, Cache-Control: no-cache), which
browsers read with new EventSource(url) or htmx’s SSE extension.
Pace events with sleep.
Streams fit work that one request follows from start to end: the
progress of an import, tokens from an AI model, a countdown. To push
changes to many open pages (a new comment), use realtime channels,
which do not keep a request open per page.
§Free plan
- CPU: only the work of each
stepcounts toward the 10 ms per request; waiting insleepor on I/O does not. - Duration: a response may stream for as long as the client stays connected; when the client goes away, the stream stops at the next event.
- One invocation: the whole stream is one request. Its binding calls
count toward the per-request limits (50 subrequests on the free plan,
D1 queries included), so a stream cannot poll D1 every second for
minutes; end it and let the client reconnect, or use
realtime. - Reconnects:
EventSourcereconnects about 3 s after a stream ends, and each reconnect is a new request (100,000 a day on the free plan). Send a last event (e.g.event: done) on which the page callssource.close(), or setEvent::retryto reconnect less often.
§Examples
use std::time::Duration;
use axum::{Router, routing::get};
use ocre::{Ctx, sse::{self, Event}};
pub fn routes() -> Router<Ctx> {
Router::new().route("/countdown", get(countdown))
}
/// `data: 3`, `data: 2`, `data: 1` one second apart, then `event: done`.
async fn countdown() -> impl axum::response::IntoResponse {
sse::stream(Some(3), |left: Option<u32>| async move {
let left = left?;
ocre::sleep(Duration::from_secs(1)).await;
Some(match left {
0 => (Event::default().event("done").data(""), None),
n => (Event::default().data(n.to_string()), Some(n - 1)),
})
})
}In the page: const source = new EventSource("/countdown"); source.addEventListener("done", () => source.close());.
Structs§
Functions§
- stream
- An event stream:
step(state)gives the next event and the next state, orNoneto end the stream.