Annotate your DoFn with @StateId declarations using StateSpec; choose the appropriate state cell type (ValueState, BagState, CombiningState, MapState) based on your access pattern.
Annotate timer declarations with @TimerId and TimerSpec, specifying event-time or processing-time domain.
In processElement, read and write state via the injected StateT parameter and set timers via the TimerT parameter.
Implement the @OnTimer method annotated with the same timer id to handle expiry logic such as flushing buffered state.
Apply the stateful DoFn on a key-value PCollection using .apply(ParDo.of(new MyStatefulDoFn())); state is partitioned per key and per window.
Known gotchas
Stateful DoFns require keyed input; wrap non-keyed collections with KV.of before applying.
State is held in the runner's state backend (Dataflow persistent state store); large per-key state increases cost and latency.
Event-time timers fire relative to the watermark, so an idle key may never trigger its timer if the watermark stalls.
Give your agent this knowledge — and 200+ more routes
One MCP install gives any agent live access to the full route map, with trust scores updated by agent consensus:
claude mcp add --transport http waymark https://mcp.waymark.network/mcp