A STREAM vertex and its bounded history ring (L4 graph)¶
The STORED_VALUE contract is “the latest”: k writes leave one value. A STREAM vertex
additionally appends each write to a bounded ring, and its contract is “observe every
buffered entry” — which is why its propagation is a drain of the entries appended since
the previous flush, not a coalesced last-writer-wins flush
(reference 02 §Stream drain semantics).
What to notice¶
The depth has no wire surface at all. It is a retention intent only the application can supply, so it is declared owner-side with
set_history_depth— not a:settingsknob. The withdrawnhistory_keep_lastanswersSCHEMA_NOT_FOUNDon read and on write, caller-independently (RFC-0022 §3.B / §3.C).Nothing is inherited. A declaration reaches exactly the vertex it names — no ancestor walk, no subtree push, no propagation question when a parent is reconfigured after its children exist (§3.F).
The ring is drain-only, and indexing never reaches it.
[n]counts children of the stored value, never entries of the history ring (CONTEXT.md §Element addressing). The example reads the ring throughhistory()and separately checks that a plainreadstill serves the latest value.Overflow is not silent, in general. Shedding under pressure carries the flow-gap signal and is accounted for (RFC-0025 §4.5). What this example shows is the ordinary trim to the declared depth on a local producer, which is the retention intent doing its job — not a receiver-visible gap.
Source¶
1/*
2 * SPDX-License-Identifier: Apache-2.0
3 * SPDX-FileCopyrightText: Copyright 2026 avatarsd LLC
4 */
5
6/**
7 * @file
8 * @brief ONE CONCEPT — a STREAM vertex keeps a bounded history ring, depth declared owner-side.
9 *
10 * A `STORED_VALUE` vertex is last-writer-wins: *k* writes leave one value. A `STREAM` vertex
11 * additionally appends each write to a bounded ring, and its contract is "observe every
12 * buffered entry" rather than "the latest" (`docs/reference/02-graph-model.md` §Stream drain
13 * semantics). The depth is a RETENTION INTENT only the application can supply, so it is an
14 * owner-side call with **no wire surface at all** — `set_history_depth`, not a `:settings`
15 * knob (RFC-0022 §3.C; the withdrawn `history_keep_last` reads `SCHEMA_NOT_FOUND`).
16 *
17 * A plain `read` is unchanged: it still serves the latest value, never the ring.
18 *
19 * Runs under ctest as `example_graph_stream`; returns non-zero on any failed check.
20 */
21
22#include <cstdio>
23#include <cstring>
24#include <span>
25#include <string_view>
26
27#include "libtracer/tracer.hpp"
28
29namespace {
30
31using tr::graph::path_t;
32using tr::graph::role_t;
33
34/** @brief An owned one-segment view over @p text. */
35tr::view::view_t value_of(std::string_view text) {
36 return *tr::view::over_bytes(std::as_bytes(std::span<const char>(text.data(), text.size())));
37}
38
39/** @brief True iff @p r's single view holds @p text. */
40bool holds(const tr::view::rope_t& r, std::string_view text) {
41 return r.only().bytes().size() == text.size() &&
42 std::memcmp(r.only().bytes().data(), text.data(), text.size()) == 0;
43}
44
45/** @brief Report expectation @p what and record a failure on @p ok. */
46void check(bool& ok, bool cond, const char* what) {
47 std::printf(" [%s] %s\n", cond ? "ok" : "FAIL", what);
48 ok = ok && cond;
49}
50
51} // namespace
52
53int main() {
54 tr::graph::graph_t g;
55 bool ok = true;
56
57 const auto events = g.register_vertex(path_t("/dev/can0/rx"), role_t::STREAM);
58 g.set_history_depth(events, 3); // owner-side retention intent; no peer can read or write it
59
60 for (const char* frame : {"f1", "f2", "f3", "f4"}) (void)g.write(events, value_of(frame));
61
62 const auto ring = g.history(events);
63 check(ok, ring.has_value(), "a STREAM vertex serves its history ring");
64 std::printf("ring holds %zu of the 4 writes\n", ring ? ring->size() : 0u);
65 check(ok, ring && ring->size() == 3,
66 "the ring is bounded at the declared depth (oldest trimmed)");
67 check(ok, ring && holds(ring->front(), "f2"), "history is oldest-first, and f1 was trimmed");
68 check(ok, ring && holds(ring->back(), "f4"), "history is newest-last");
69
70 const auto latest = g.read(events);
71 check(ok, latest && holds(**latest, "f4"),
72 "a plain read still serves the latest value, not the ring");
73 return ok ? 0 : 1;
74}
See also: graph module · vertex roles and aggregation · user data packing.