Commit Graph
6932 Commits
Author SHA1 Message Date
Grant Limberg 4f5dab30e8 index fixups for the prod DB 2026-08-03 10:28:30 -07:00
Grant Limberg 6bf7bb67b3 controller: collapse duplicate OTel spans on the request path
Reduce spans emitted per controller request without touching the head
sample rate. Removes 20 StartSpan sites (97 -> 77) that traced the same
logical op at multiple layers:

- DB.cpp: drop all db:: read-helper/leaf spans; the db_mirror_set::
  wrapper survives as the single span per read op (also removes the
  latent member_id-from-networkId attribute bug).
- CentralDB.cpp: drop inner write-op spans (save/+children, nodeIsOnline,
  eraseNetwork, eraseMember); keep getSSOAuthInfo (errors) and all
  background-thread spans (commitThread, onlineNotificationThread, init).
- EmbeddedNetworkController.cpp: drop per-rule parseRule span and the
  no-op _startThreads span; keep request/_request roots.

Sampler, trace-context propagation, commit/Bigtable-write spans, and all
business logic are unchanged.
2026-07-23 11:35:15 +02:00
Grant Limberg f5ea9422a6 controller: observability and write-path hygiene for change propagation
- Log network/member ids on every inbound PubSub config ('has old/new config'
  lines were untraceable without them), on the frontend-gate drops (previously
  fully silent), on the invalid-id save skips (the one silent path where a
  delivered-and-acked change could vanish), and on the 100-attempt commit drop
- commitThread member branch: missing network is a permanent condition (nodes
  join-looping a deleted network) -- skip with one log line instead of the
  COUNT+GROUP BY+one_row() form that threw on zero rows and turned each item
  into a 100-attempt retry storm; NULL frontend now reads as ''
- cleanMember also strips change_source: request-path saves inherited the
  frontend's provenance from the cached record, mis-attributing the write and
  silently skipping the controller->Central echo (stable CV1/controller
  divergence); the authorized-path save now tags change_source after cleaning
- Deny and SSO-pending paths persist the member only on first sighting or a
  just-learned identity: the old per-request full-snapshot save could race
  with and overwrite a concurrent authorize arriving via PubSub
2026-07-22 17:02:33 +02:00
Grant Limberg 3dcbde5185 controller: start member revisions at 1 to unblock echo sync for manual members
A member auto-created by the controller started at revision 0; its first save
bumped to 1 and the echo to Central carried 1. A manually created CV1 member
row also defaults to revision 1, and Central's UpsertMemberFromController only
applies echoes with a strictly greater revision -- 1 < 1 never passed, so the
echo carrying the learned identity was skipped forever and manually created
members stayed identity=""/revision=1 in CV1. Starting at 1 (matching
Central's DB default) makes the first echo carry 2.
2026-07-22 16:42:52 +02:00
Grant Limberg 66479cd2da controller: review fixes for commit-pipeline liveness change
From adversarial review of the previous commit:

- CtlUtil: guard <pthread.h> for Windows (client build compiles the controller
  sources) and define _GNU_SOURCE on Linux for the two-arg pthread_setname_np
  under the strict -std=c++17 make build
- Zero _commitThreadLastActive before the heartbeat thread and PubSub listeners
  start; C++17 atomics are indeterminate until stored (C++20 masked this in
  local checks) and a garbage stamp could spuriously abort at startup
- Watchdog aborts only when every started commit thread is stalled past the
  threshold (the incident signature), or one thread exceeds 3x -- a single slow
  item (e.g. the deauth fan-out of a huge network deletion) can no longer kill
  a healthy controller
- BlockingQueue: lock size() (raced with push/pop; now read cross-thread by
  the health gate, watchdog, and depth gauge) and notify gc in the timed get()
  to match the untimed variant
- Drop statement_timeout from the hardened conninfo: it also capped the
  startup bulk COPY loads, where a timeout exits the process -> crash loop on
  a slow/degraded DB; dead-peer hangs are covered by keepalives and
  tcp_user_timeout, and it could clobber an operator-supplied options=
- Rate-limit the listener nack logs (every redelivery attempt passes through
  the health gate during a stall)
2026-07-22 16:28:27 +02:00
Grant Limberg 2e6fe413b6 controller: prevent commit-pipeline freezes and message loss
The Jul 21-22 incident: a commit thread wedged inside the change-notification
fan-out while holding _changeListeners_l/_dbs_l, every other commit thread piled
up behind it, and the PubSub listeners kept acking member/network changes into a
queue nothing drained -- silently losing them (CV1 authorize never applied).

- Lock hygiene: snapshot-then-call in DB::_memberChanged/_networkChanged,
  DBMirrorSet fan-out, and ENC onNetworkUpdate/onNetworkMemberUpdate/
  onNetworkMemberDeauthorize; no listener callback runs under notification locks
- Liveness watchdog in heartbeat(): warn when a commit thread makes no progress
  for 60s, abort the process at 5min so the orchestrator restarts a zombie
- Ack gating: PubSub listeners nack (redeliver) instead of acking when the
  commit pipeline is stalled or the queue is over depth
- libpq connect_timeout/keepalives/tcp_user_timeout/statement_timeout on pooled
  connections so a dead DB peer throws instead of blocking a thread forever
- Name controller threads (wchan/stack dumps become readable); commit queue
  depth and enqueue-to-commit latency gauges
2026-07-22 16:06:09 +02:00
Grant Limberg ca3d17ddb0 controller: log how long each BigTable batch write took
Timed in the worker around the BulkApply call; reported on the per-batch
completion line and the exception line.  Gives a direct client-observed
latency signal to compare against Bigtable's server-side metrics.
2026-07-21 16:54:09 +02:00
Grant Limberg 2e7e4a58a8 controller: write BigTable status batches from sync worker threads; re-queue failures
Replace AsyncBulkApply with synchronous BulkApply calls fanned out to a
small worker pool (8 threads, 500-row batches).  The async client path
makes progress only on the connection's shared background event threads,
which stall for tens of seconds at a time in production (observed
2026-07-21: 26k-row flushes taking 3.5-8 minutes against a near-idle
cluster with server-side MutateRows p99 of 50-80ms, completions arriving
in window-sized bursts).  A sync call is driven entirely by its own
thread and has months of healthy history in this environment.

Batches are built and their results processed on the calling thread, so
_lastNodeInfo and the re-queue list stay lock-free; workers only perform
the RPC and record the outcome.

Also stop dropping failed mutations: re-queue them for the next cycle
(idempotent replays via deterministic cell timestamps, backlog capped by
requeuePendingStatus) unless the status is INVALID_ARGUMENT.  This
covers transient per-mutation failures the client does not retry
internally, e.g. ACCESS_TOKEN_EXPIRED and UNAVAILABLE.
2026-07-21 16:29:10 +02:00
Grant Limberg b2bd72953a smaller parallel batches of writes 2026-07-21 10:48:19 +02:00
Grant Limberg 2f0cae4447 bootstrap-deps: force WITH_API_ONLY=OFF in the full OTel build
A stale .deps-build/otel CMake cache from an API-only run kept
WITH_API_ONLY=ON even under ZT_CONTROLLER_DEPS=1, silently installing an
API-only opentelemetry-cpp; the controller configure then fails with
"Component sdk is not a built component". Pass the flag explicitly so
the full build always overrides a cached value.
2026-07-21 02:15:57 +02:00
Grant Limberg 121ce0e8b9 controller: chunk BigTable status writes under the 100k mutation limit
Bigtable's MutateRows API rejects requests with more than 100,000
mutations total, and google-cloud-cpp sends a BulkMutation as a single
request -- it never splits by mutation count. writePending() built one
batch from every pending check-in (up to 5 SetCells per row), so a cold
node_info cache after a controller restart could push a single flush far
past the limit (observed: 124582). INVALID_ARGUMENT is permanent, so the
whole batch failed and those check-ins were dropped for the cycle.

Flush in chunks of at most 25,000 mutations. Failure handling is now
per-chunk: FailedMutation::original_index() aligns against a chunk-local
row-hash vector, and if BulkApply throws, only the not-yet-written tail
is re-queued so already-applied chunks are not replayed.
2026-07-21 02:15:46 +02:00
Grant Limberg 53acb42f11 controller: erase netconf request de-dup key if enqueue throws 2026-07-02 12:29:32 -07:00
Grant Limberg 54b61f3927 set max netconf queue length to 2M requests 2026-07-02 12:28:56 -07:00
Grant Limberg 4097f646a9 cap & de-dupe netconf request queue 2026-07-02 12:22:53 -07:00
Grant Limberg 9a0f635045 Set a minimum of 8 threads for netconf request handling in the Central controllers 2026-07-02 12:21:50 -07:00
Grant Limberg 50bfed682a don't do migrations via the startup script 2026-07-01 18:47:06 -07:00
Grant Limberg cb65567de3 Fix possible UB in calling operator[] on a const json& 2026-07-01 18:45:05 -07:00
Grant Limberg 5951981622 Merge pull request #2526 from zerotier/gl/ctl-pubsub
Add PubSub and BigTable support for Central hosted network controllers
2026-06-29 11:55:55 -07:00
Grant Limberg 1f6a1fd51f Remove ZT_CONTROLLER=1 from makefile. No longer used to build central
controllers
2026-06-29 11:13:40 -07:00
Grant Limberg edcc066d25 Add a SIGSEGV handler to print a stack trace for central controllers
Only enabled in the central controller builds.  Trying to find more info
about some mystery controller restarts in prod
2026-06-26 13:33:07 -07:00
Grant Limberg 0797626b3e Fix a potential crash on central controller shutdown 2026-06-26 13:32:09 -07:00
Grant Limberg 4bcff9d3e3 Fix the sso template for errors returned by IdP
Template was looking for {{networkId}} but that hadn't been populated
yet in the case where the IdP returned an error. This caused the
template parsing to crash and return its own error, disguising the error
from the IdP
2026-06-22 09:47:24 -07:00
Grant Limberg 64cc25baf6 bump actions to get rid of node warnings 2026-06-17 16:51:08 -07:00
Grant Limberg 8304ec79fe update documentation for ConnectionPool 2026-06-17 16:39:32 -07:00
Grant Limberg fcb016b9c1 standardize log formatting across the controller 2026-06-17 16:20:17 -07:00