Retirement — leaving the graph empties a vertex, it does not erase it (L4 graph)¶
retire marks a vertex and its whole subtree logically absent
(RFC-0009
§A.1 / §B): the path then reads NOT_FOUND, identical to never-existed. There is no
distinct retired status — no such code is allocated, and the collapse is an accepted cost
rather than an oversight (reference 02 §Retirement
notification).
What to notice¶
The object is not freed. The vertex map is pinned and insert-only (ADR-0057), so an outstanding
vertex_handle_tstays dereferenceable after the retirement — the example keeps using it. What tells a holder its cached resolution went stale isretire_generation, bumped by the retirement (ADR-0062). A generation match says the vertex is the same one; it never says the caller may still act on it, so an authorization decision must not be cached this way.Retirement takes the subtree.
/zone/air/humiditygoes with/zone/air.It is idempotent and silent. Retiring an already-retired vertex succeeds and does nothing; the retirement delivers along no edge and wakes no
await(§B.5), which is why disappearance is observable only by polling.Revival inherits nothing. A later local write-creates revives the address, and the revived vertex takes its live ancestor’s ACL policy, never the retired owner’s (§B.6) — a stale grant cannot outlive the retirement.
collect()is the embedder’s, and is not needed here. A retired vertex parks its detached value seam iff a handler was installed at registration; these vertices carry none, so nothing is parked. A bus node with peer churn must callgraph_t::collect(), andgraph_t::parked_seam_count()makes an uncollected park observable (reference 02 §Vertex lifecycle).
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 — retirement: leaving the graph is emptying a vertex, not erasing it.
9 *
10 * `retire` marks a vertex and its whole subtree logically absent (RFC-0009 §A.1/§B): the
11 * path then reads `NOT_FOUND`, identical to never-existed — there is no distinct `retired`
12 * status. The vertex OBJECT is not freed, so an outstanding `vertex_handle_t` stays
13 * dereferenceable (ADR-0057, insert-only); what tells a holder its cached resolution went
14 * stale is `retire_generation`, which is bumped by the retirement (ADR-0062).
15 *
16 * Retirement delivers nothing and wakes no `await` (§B.5): a composite subscriber is never
17 * told a child went away, so disappearance is observable only by re-reading
18 * (`docs/reference/02-graph-model.md` §Retirement notification).
19 *
20 * Runs under ctest as `example_graph_retire`; returns non-zero on any failed check.
21 */
22
23#include <cstdint>
24#include <cstdio>
25#include <span>
26#include <string_view>
27
28#include "libtracer/tracer.hpp"
29
30namespace {
31
32using tr::graph::path_t;
33using tr::graph::role_t;
34using tr::graph::status_t;
35
36/** @brief An owned one-segment view over @p text. */
37tr::view::view_t value_of(std::string_view text) {
38 return *tr::view::over_bytes(std::as_bytes(std::span<const char>(text.data(), text.size())));
39}
40
41/** @brief Report expectation @p what and record a failure on @p ok. */
42void check(bool& ok, bool cond, const char* what) {
43 std::printf(" [%s] %s\n", cond ? "ok" : "FAIL", what);
44 ok = ok && cond;
45}
46
47} // namespace
48
49int main() {
50 tr::graph::graph_t g;
51 bool ok = true;
52
53 const auto air = g.register_vertex(path_t("/zone/air"), role_t::STORED_VALUE);
54 (void)g.register_vertex(path_t("/zone/air/humidity"), role_t::STORED_VALUE);
55 (void)g.write(air, value_of("ok"));
56 const std::uint32_t gen_before = g.retire_generation(air);
57
58 check(ok, g.retire(air).has_value(), "retire succeeds");
59 check(ok, !g.find(path_t("/zone/air").key()), "the retired path no longer resolves");
60 check(ok, !g.find(path_t("/zone/air/humidity").key()), "retirement takes the whole subtree");
61
62 const auto r = g.read(path_t("/zone/air"));
63 check(ok, !r && r.error() == status_t::NOT_FOUND,
64 "a retired path reads NOT_FOUND, exactly like never-existed");
65 check(ok, g.retire_generation(air) != gen_before,
66 "the handle stays usable, and its generation moved");
67 check(ok, g.retire(air).has_value(),
68 "retiring an already-retired vertex is a no-op, not an error");
69
70 // A later LOCAL write revives the address; the revived vertex inherits nothing (§B.6).
71 check(ok, g.write(path_t("/zone/air"), value_of("fresh")).has_value(),
72 "write-creates revives a retired address");
73 std::printf("generation %u -> %u across one retirement\n", gen_before,
74 g.retire_generation(air));
75 return ok ? 0 : 1;
76}
See also: graph module · write-creates (the other half of the lifecycle) · graph model reference.