-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathbootstrap.dot
More file actions
162 lines (143 loc) · 11.9 KB
/
Copy pathbootstrap.dot
File metadata and controls
162 lines (143 loc) · 11.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// walshadow — BASE_BACKUP fan-out (component view)
// Differentiated from timeline_bootstrap.dot: that one walks the
// 5 phases top→bottom in time; this one freezes phases 1-4 and
// exposes the MultiplexSink fan-out structure — BackupSource trait
// + two impls, per-file routing, parallel ShadowDataDir + PageWalkSink
// rails, drain into the shared insert tail (same unit as streaming).
//
// regeneration spec:
// sources of truth: plans/bootstrap.md · src/backfill/{backup_source_direct,backup_source_object_store,backup_page_walk,backup_source}.rs · src/emit/pipeline/{bootstrap,tail}.rs
// subsumes: plans/bootstrap.md § "MultiplexSink" / fan-out
// differentiates: timeline_bootstrap.dot walks the 5-phase TIMELINE; this freezes phases 1-4 and exposes the FAN-OUT structure (BackupSource impls, MultiplexSink, simultaneous shadow-dir + CH writes)
// quality bar:
// - "both" fan-out from classify visible (two edges, not one merged)
// - CatalogMap lookup edge clearly secondary (dashed, constraint=false)
// - bootstrap CH path reads as the SAME shared insert tail streaming uses (annotated "no DdlApplicator")
// shared style: palette.md
digraph bootstrap {
rankdir=TB;
compound=true;
newrank=true;
graph [fontname="Helvetica", labelloc="t", label="walshadow greenfield bootstrap — BASE_BACKUP fan-out (MultiplexSink, parallel rails)", fontsize=14, splines=spline, nodesep=0.45, ranksep=0.6, bgcolor="#272623", fontcolor="#ECE1D7"];
node [fontname="Helvetica", fontsize=10, shape=box, style="rounded,filled", color="#6E6963", fontcolor="#ECE1D7"];
edge [fontname="Helvetica", fontsize=9, arrowsize=0.8, color="#c1a78e", fontcolor="#ECE1D7"];
// ════════ External actors (top) ════════
src [label="source PG\nreplication slot", fillcolor="#3D3D54", shape=cylinder];
s3 [label="object store\nwal-g layout\n(DynStorage)", fillcolor="#3D3D54", shape=cylinder];
{ rank=same; src; s3; }
// ════════ Catalog seed sidecar (off-pump) ════════
subgraph cluster_seed {
label="catalog seed — REPEATABLE READ sidecar, runs once before BASE_BACKUP";
style="rounded,filled"; color="#4c4641"; fillcolor="#34302c"; fontcolor="#ECE1D7";
seedq [label="seed_in_snapshot\nSELECT pg_class / pg_attribute /\npg_type / pg_index WHERE oid≥16384", fillcolor="#4D4D28"];
catmap [label="CatalogMap\n(db_node, rel_node) → RelDescriptor\nsnapshot, no replay gate", fillcolor="#4D4D28", shape=parallelogram];
seedq -> catmap;
}
// ════════ BackupSource trait + two impls ════════
subgraph cluster_source {
label="BackupSource — async fn run(data_dir, Arc<Mutex<dyn BackupSink>>) → (StartInfo, EndInfo)";
style="rounded,filled"; color="#4c4641"; fillcolor="#34302c"; fontcolor="#ECE1D7";
direct [label="BackupSourceDirect\nwal-rus run_base_backup\ntokio_tar over ChannelReader", fillcolor="#4D3A28"];
obj [label="BackupSourceObjectStore\nfetch_sentinel + list_tar_parts\nbuffer_unordered, pg_control barrier", fillcolor="#4D3A28"];
ev [label="per-file events into sink\nstart → begin → chunk* → end → finish\nFileMeta {path, size, mode, kind}", fillcolor="#4D3A28", shape=parallelogram];
{ rank=same; direct; obj; }
direct -> ev;
obj -> ev;
}
src -> direct [label="BASE_BACKUP\n+ pg_export_snapshot()", color="#A1A9CC", dir=both, arrowtail=open, penwidth=2];
s3 -> obj [label="GET tar parts", color="#A1A9CC", penwidth=2];
src -> seedq [label="libpq sidecar", color="#CBA85E", dir=both, arrowtail=open];
// ════════ Orchestrator + MultiplexSink dispatcher ════════
orch [label="backfill_bootstrap orchestrator\nspawn_greenfield_bootstrap\nholds Arc<Mutex<MultiplexSink<PageWalkSink>>>", fillcolor="#4D3A28"];
mux [label="MultiplexSink (impl BackupSink, async)\nbegin(meta) → DiskLanderSink.classify():\n Keep → lander only\n SkipDenylist → lander Skip (dir entry kept as empty dir)\n SkipUserHeap → tap.begin (Tap | Skip)\nchunk / end route to chosen sink; finish observed by both",
fillcolor="#4D3340", width=4.6];
ev -> orch [label="drives via run(sink)", color="#A1A9CC"];
orch -> mux [label="install sink"];
// ════════ Left rail — shadow data dir landing ════════
subgraph cluster_land {
label="rail A — catalog + system files (Keep)";
style="rounded,filled"; color="#4c4641"; fillcolor="#34302c"; fontcolor="#ECE1D7";
lander [label="DiskLanderSink\nKeep: global/, pg_xact/, pg_multixact/,\n pg_filenode.map, tablespace_map,\n pg_control, backup_label,\n pg_tblspc/<oid> symlinks,\n base/<db>/<fn<16384> + CatalogFilenodes\nSkipUserHeap: fn ≥ 16384 (rerouted by mux)", fillcolor="#4D3340"];
write [label="write_kept\nFile / Dir / Symlink\nsync_data on close\npg_control lands last (barrier)", fillcolor="#4D3340"];
ddir [label="shadow data_dir\n+ postgresql.auto.conf\n+ standby.signal\n+ restore_command", fillcolor="#4D3850", shape=note];
lander -> write [color="#6E6963"];
write -> ddir [color="#6E6963", style=dashed, label="fsync"];
}
// ════════ Right rail — page-walk → CH ════════
subgraph cluster_walk {
label="rail B — user heap (Tap, 2A page-walk decoder)";
style="rounded,filled"; color="#4c4641"; fillcolor="#34302c"; fontcolor="#ECE1D7";
pwsink [label="PageWalkSink\nparse_base_path → rfn ≥ 16384\naccumulate 8 KiB at a time", fillcolor="#4D4128"];
walker [label="PageWalker::walk_page\npd_lower/pd_upper bounds-check\nfor each LP_NORMAL ItemIdData slot\n decode_on_page_tuple\nreshape HeapTupleHeaderData →\n xl_heap_header + bitmap + cols", fillcolor="#4D4128"];
dec [label="heap_decoder::decode_block_data\nshared with WAL hot path\nmain tuple → BackfillTuple\npg_toast tuple → columns + on-page TID\n(no FPI replay on backup pages)", fillcolor="#4D4128"];
bft [label="BackfillTuple\n{rfn, xid, source_lsn = start_lsn,\n columns}", fillcolor="#4D4128", shape=parallelogram];
queue [label="bounded mpsc (cap 256)\nchunk() awaits a free slot —\nslow drain backpressures the pump", fillcolor="#4D4128", shape=parallelogram];
drain [label="pipeline::bootstrap::drain\ntoast tuple → batch ToastRow\nmain ExternalToast → Deferred\nflush mirror rows, then resolve deferred\nmap + route main rows to tail\none ack seq per rfn flip", fillcolor="#4D4128"];
pwsink -> walker -> dec -> bft -> queue -> drain;
}
// MultiplexSink → rails (lander left, walker right) — same rank pulls them side-by-side
mux -> lander [label="Keep /\nSkipDenylist"];
mux -> pwsink [label="SkipUserHeap →\ntap.begin (Tap)"];
{ rank=same; lander; pwsink; }
// ════════ Shared insert tail (same unit as streaming) ════════
subgraph cluster_emit {
label="shared insert tail (pipeline/tail.rs) — same batcher + inserter pool + ack collector streaming uses; no DdlApplicator (descriptors frozen at snapshot)";
style="rounded,filled"; color="#4c4641"; fillcolor="#34302c"; fontcolor="#ECE1D7";
batch [label="InsertBatcher\nTableEncoder per table\nNative columns + _lsn=start_lsn /\n_xid / _commit_ts / _is_deleted=0\nbudget + deadline seal", fillcolor="#5D4628"];
ins [label="inserter pool ×N\none complete INSERT per sealed batch\nsend_with_retry + reconnect", fillcolor="#5D4628"];
ackc [label="ack collector\nRegister(seq, start_lsn) / Placed / Acked\nwait_through(K) = all durable", fillcolor="#4D3A28", shape=note];
batch -> ins;
ins -> ackc [style=dotted, color="#b380b0", label="Acked"];
}
drain -> batch [color="#BF8C5F", penwidth=2, style=dashed, label="BatcherMsg::Row"];
drain -> ackc [style=dotted, color="#b380b0", constraint=false, label="Register / Placed\n(per rfn)"];
catmap -> drain [color="#CBA85E", style=dashed, constraint=false, label="get(db_node, rel_node)"];
ch [label="ClickHouse\nReplacingMergeTree(_lsn)", fillcolor="#4D4128", shape=cylinder];
toastch [label="ClickHouse TOAST mirrors\npg_toast_<relid>\nTID-keyed births + tombstones", fillcolor="#4D4128", shape=cylinder];
ins -> ch [color="#BF8C5F", style=dashed, penwidth=2, label="Native\n(bootstrap)"];
drain -> toastch [color="#BF8C5F", style=dashed, penwidth=2, label="ToastResolver::put\nbefore deferred fetch"];
// ════════ Handoff at end_lsn ════════
subgraph cluster_handoff {
label="handoff — finish initial load, start streaming";
style="rounded,filled"; color="#4c4641"; fillcolor="#34302c"; fontcolor="#ECE1D7";
out [label="BootstrapOutcome\n{start_lsn, end_lsn,\n DiskLanderStats, PageWalkStats}\ntail.finish: FlushAll →\nwait_through(next_seq) → drain cascade", fillcolor="#4D3A28"];
seed [label="use backup end as\nnew restart point", fillcolor="#4D3A28"];
manifest [label="manifest.toml\nsaved restart state", fillcolor="#4D3850", shape=note];
feed [label="SourceFeed open\nSTART_REPLICATION\nPHYSICAL <end_lsn>", fillcolor="#4D3A28"];
shd [label="shadow PG\npostmaster + walreceiver\nbegin replay at end_lsn", fillcolor="#3D4128", shape=cylinder];
out -> seed;
seed -> feed;
feed -> manifest [color="#b380b0", style=dotted, label="save on first\nstatus update"];
feed -> shd [color="#BD8183", style=dashed, label="walsender wire\n(steady-state)"];
}
orch -> out [label="finish()\nend_lsn", color="#A1A9CC", constraint=false];
ddir -> shd [color="#6E6963", style=dashed, label="shadow boots\nfrom data_dir"];
// ════════ Legend — anchored off ch, rank=sink so it floats to the right ════════
legend [shape=plaintext, label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
<TR><TD COLSPAN="2" BGCOLOR="#34302c"><B>node fill — role</B></TD></TR>
<TR><TD BGCOLOR="#3D3D54" WIDTH="28"></TD><TD>source PG / object store ingress</TD></TR>
<TR><TD BGCOLOR="#4D3A28"></TD><TD>orchestrator / BackupSource impls / ack collector</TD></TR>
<TR><TD BGCOLOR="#4D3340"></TD><TD>MultiplexSink + DiskLanderSink (rail A)</TD></TR>
<TR><TD BGCOLOR="#4D4128"></TD><TD>PageWalkSink + heap decoder (rail B)</TD></TR>
<TR><TD BGCOLOR="#4D4D28"></TD><TD>CatalogMap seed</TD></TR>
<TR><TD BGCOLOR="#5D4628"></TD><TD>shared insert tail (batcher + inserters)</TD></TR>
<TR><TD BGCOLOR="#4D3850"></TD><TD>on-disk artifact</TD></TR>
<TR><TD BGCOLOR="#3D4128"></TD><TD>shadow Postgres (post-handoff)</TD></TR>
<TR><TD COLSPAN="2" BGCOLOR="#34302c"><B>edge colour</B></TD></TR>
<TR><TD><FONT COLOR="#A1A9CC"><B>━━</B></FONT></TD><TD>replication protocol (BASE_BACKUP, START_REPLICATION)</TD></TR>
<TR><TD><FONT COLOR="#BD8183"><B>┄┄</B></FONT></TD><TD>walsender wire (post-handoff)</TD></TR>
<TR><TD><FONT COLOR="#CBA85E"><B>━━</B></FONT></TD><TD>libpq catalog query / CatalogMap lookup</TD></TR>
<TR><TD><FONT COLOR="#BF8C5F"><B>┄┄</B></FONT></TD><TD>CH Native (bootstrap, via shared tail)</TD></TR>
<TR><TD><FONT COLOR="#b380b0"><B>···</B></FONT></TD><TD>manifest durability / ack events</TD></TR>
<TR><TD><FONT COLOR="#6E6963"><B>┄┄</B></FONT></TD><TD>filesystem (data_dir land, shadow boot)</TD></TR>
<TR><TD COLSPAN="2" BGCOLOR="#34302c"><B>file routing (MultiplexSink)</B></TD></TR>
<TR><TD>catalog filenode<BR/>(fn < 16384 ∪ whitelist)</TD><TD>rail A only</TD></TR>
<TR><TD>global/, pg_xact/, pg_control,<BR/>tablespace_map, conf files</TD><TD>rail A only</TD></TR>
<TR><TD>pg_replslot/, pg_stat_tmp/,<BR/>pg_logical/, pgsql_tmp/, temp_*</TD><TD>Skip (denylist;<BR/>dir entry kept as empty dir)</TD></TR>
<TR><TD>user heap<BR/>base/<db>/<fn ≥ 16384></TD><TD>rail B only<BR/>(never lands on shadow)</TD></TR>
<TR><TD>pg_toast_<relid></TD><TD>rail B decodes on-page TIDs;<BR/>mirror put precedes deferred resolution</TD></TR>
</TABLE>
>];
toastch -> legend [style=invis];
}