Read and write a vertex — the data plane (L4 graph)¶
Two of the three data calls (CONTEXT.md §read / write / await), on a
STORED_VALUE vertex. The example writes twice and reads three times, which is enough to
pin down the storage contract: one store per vertex, last-writer-wins, and a read serves
the latest stored value — never behind a notification, legitimately newer
(reference 02 §Subtree subscriptions, branch writes, and
write-creates).
What to notice¶
A never-written vertex has no last-known-value. It answers
NOT_FOUND, the same status an address that does not resolve answers. The collapse is deliberate and documented (reference 02 §Retirement notification lists the three meaningstr::path::not_foundcarries); a consumer that must tell them apart carries its own expectation of what should exist.readreturns a reference, not a copy.value_ref_tnames the published value; the rope is not cloned link-by-link on the way out (RFC-0022 is a different concern — the reference-vs-copy rule and its measurement live onvertex_t::value_ref_tincore/include/libtracer/vertex.hpp). Holding one keeps that value alive, which the example checks: the first read still reads21.5Cafter the second write replaced the store.The payload is opaque. L4 never interprets a
VALUE’s bytes — the example writes ASCII because it is legible, not because the graph knows what a temperature is.A write is not a primitive. It is
assign(state) followed bypropagate(edges), RFC-0008. Nothing subscribes here, so only theassignhalf is observable.
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 — the data plane: `write` then `read`, and the one store per vertex.
9 *
10 * A `STORED_VALUE` vertex is last-writer-wins: a write replaces the stored value, a read
11 * serves the latest one, and a vertex that was never written has no last-known-value at all
12 * (`NOT_FOUND`, distinct from an address that does not resolve — both spell `NOT_FOUND`
13 * here, `docs/reference/02-graph-model.md` §Vertex lifecycle). `read` hands back a
14 * `value_ref_t` — a REFERENCE to the published value, not a copy of it.
15 *
16 * Runs under ctest as `example_graph_read_write`; returns non-zero on any failed check.
17 */
18
19#include <cstdio>
20#include <cstring>
21#include <span>
22#include <string_view>
23
24#include "libtracer/tracer.hpp"
25
26namespace {
27
28using tr::graph::path_t;
29using tr::graph::role_t;
30using tr::graph::status_t;
31
32/** @brief An owned one-segment view over @p text — a VALUE's bytes are opaque to L4. */
33tr::view::view_t value_of(std::string_view text) {
34 return *tr::view::over_bytes(std::as_bytes(std::span<const char>(text.data(), text.size())));
35}
36
37/** @brief True iff @p v's bytes equal @p text. */
38bool holds(const tr::view::view_t& v, std::string_view text) {
39 return v.bytes().size() == text.size() &&
40 std::memcmp(v.bytes().data(), text.data(), text.size()) == 0;
41}
42
43/** @brief Report expectation @p what and record a failure on @p ok. */
44void check(bool& ok, bool cond, const char* what) {
45 std::printf(" [%s] %s\n", cond ? "ok" : "FAIL", what);
46 ok = ok && cond;
47}
48
49} // namespace
50
51int main() {
52 tr::graph::graph_t g;
53 bool ok = true;
54
55 const auto temp = g.register_vertex(path_t("/sensor/temp"), role_t::STORED_VALUE);
56 const auto unwritten = g.read(temp);
57 check(ok, !unwritten && unwritten.error() == status_t::NOT_FOUND,
58 "a never-written vertex has no last-known-value");
59
60 (void)g.write(temp, value_of("21.5C"));
61 const auto first = g.read(temp);
62 check(ok, first && holds((*first)->only(), "21.5C"), "read serves what was written");
63
64 // Last-writer-wins: the second write REPLACES the store; there is one store per vertex.
65 (void)g.write(temp, value_of("22.0C"));
66 const auto second = g.read(temp);
67 check(ok, second && holds((*second)->only(), "22.0C"),
68 "a second write replaces the stored value");
69
70 // The first read still holds its own reference — the value it names stays alive.
71 check(ok, holds((*first)->only(), "21.5C"), "an outstanding value_ref_t keeps its value alive");
72 std::printf("/sensor/temp now reads %.*s\n", static_cast<int>((*second)->only().bytes().size()),
73 reinterpret_cast<const char*>((*second)->only().bytes().data()));
74 return ok ? 0 : 1;
75}
See also: graph module · views · graph model reference · communication flows.