# The ONE bootstrap topology file, shared by EVERY pod (m11p5 §4). # # Before m11p5, k8s cluster mode needed a per-pod ConfigMap variant: a node bound # its own `grpc_addr` LITERALLY and a pod cannot bind a Service ClusterIP, so each # pod's self-entry had to be a bindable address while peers' entries stayed the # routable ones — defeating the "every process parses the SAME file" contract. # # The m11p5 bind/advertise split kills that hack: # - `grpc_addr` is the ADVERTISED address siblings DIAL (a per-pod DNS name); # with a DNS name tonic re-resolves on every reconnect, so a rescheduled pod # on a new IP is reachable again with NO peer restart. # - `grpc_bind` is the LOCAL socket — `0.0.0.0:9601` on every pod. The local # socket never tries to bind the DNS name. # So one file names all three initial regions by their stable headless-Service # DNS names, and every pod mounts THIS file unmodified. # # Region declaration order == RegionId (topology.rs:24-28). The three regions are # named after the StatefulSet's stable pod identities tidaldb-{0,1,2}. The pod's # `--region` comes from POD_NAME (statefulset.yaml fieldRef), so pod `tidaldb-0` # is region `tidaldb-0`, etc. — the names line up by construction. # # SCALING PAST 3 DOES NOT EDIT THIS FILE: pod N>=3 boots with `--seed` and learns # its roster/id/term from a seed (membership is data, on the replicated log). It # still mounts THIS file for the behavioral knob blocks only — `--seed` boots # REQUIRE a local config for `replication`/`wal`/`election`/`timeouts`/`grpc_tls` # (m11p5 §3.5); the `regions:` list below is IGNORED for the roster of a seed # joiner (the join response is authoritative), it supplies only those knobs. # # DNS form: ...svc.cluster.local # tidaldb-0.tidaldb-peers.tidaldb-cluster.svc.cluster.local apiVersion: v1 kind: ConfigMap metadata: name: tidaldb-cluster-topology namespace: tidaldb-cluster labels: app.kubernetes.io/name: tidaldb app.kubernetes.io/part-of: tidaldb data: cluster-topology.yaml: | # Three initial regions, named by their per-pod headless-Service DNS. # grpc_addr = ADVERTISED gRPC (DNS, port 9601) — what peers dial. # grpc_bind = LOCAL gRPC bind (0.0.0.0:9601) — the pod can't bind the DNS. # http_addr = ADVERTISED HTTP (DNS, port 9500) — peers forward writes/status. # metrics_addr = per-region Prometheus listener (0.0.0.0:9091). regions: - name: tidaldb-0 grpc_addr: tidaldb-0.tidaldb-peers.tidaldb-cluster.svc.cluster.local:9601 grpc_bind: 0.0.0.0:9601 http_addr: tidaldb-0.tidaldb-peers.tidaldb-cluster.svc.cluster.local:9500 metrics_addr: 0.0.0.0:9091 - name: tidaldb-1 grpc_addr: tidaldb-1.tidaldb-peers.tidaldb-cluster.svc.cluster.local:9601 grpc_bind: 0.0.0.0:9601 http_addr: tidaldb-1.tidaldb-peers.tidaldb-cluster.svc.cluster.local:9500 metrics_addr: 0.0.0.0:9091 - name: tidaldb-2 grpc_addr: tidaldb-2.tidaldb-peers.tidaldb-cluster.svc.cluster.local:9601 grpc_bind: 0.0.0.0:9601 http_addr: tidaldb-2.tidaldb-peers.tidaldb-cluster.svc.cluster.local:9500 metrics_addr: 0.0.0.0:9091 # Term-0 bootstrap leader only — post-election this field is dead config # (durable election_state governs; a restart always boots a follower). leader: tidaldb-0 replication: # ack=quorum: a write succeeds once a MAJORITY of the replica set durably # holds it (m11p3). Callers can still override per-request with x-tidal-ack. ack: quorum # reseed_self_restart=true (m11p5 §2.4): a node that latches the durable # `reseed_required` marker drains and exits(0) so the StatefulSet restarts # it and the boot-time install re-runs. The exit is REFUSED (loudly, in # status + gauge) when the remaining voters can't sustain quorum without # this node — exiting during a 2-voter window would be a total write outage. reseed_self_restart: true election: # Defaults (300ms heartbeats, 1500-3000ms election timeout, 900ms lease, # auto-election on) are right for an in-cluster (low-RTT) deployment. The # C2 safety invariant lease+heartbeat < election_timeout_min holds for the # defaults; spell the block out so an operator widening WAN budgets edits # here, not the compiled-in defaults. heartbeat_interval_ms: 300 election_timeout_min_ms: 1500 election_timeout_max_ms: 3000 leader_lease_ms: 900 auto_election: true