Tenderseed v2.2.0
This release closes the audit of v2.1.1. Every point was checked against the
code before anything was changed, and the two that carried the most weight were
confirmed in their conclusion but wrong in their cause, which changed what was
written. None of them was a crash, a data loss or a deadlock.
Changed
- Verification remembers its verdicts. A failing address is now re-tried on
an exponential schedule, 2^n seconds capped at 4 hours, the same formula the
upstream crawler uses, instead of being re-dialled at every sweep for the 35
hours upstream takes to evict it. On a full book that was on the order of a
thousand futile connections an hour. An address just verified is no longer
dialled again immediately when another peer mentions it. The window during
which a successful verdict is trusted is derived frompeer_check_periodand
stays strictly below it, so the periodic re-verification the seed exists for
is never what gets skipped. No new configuration key. See FORK.md section 3.6. - A dial collision is no longer counted against the address. A peer that
connects to us while we are dialling it is reported as a duplicate rejection
by the transport and by the switch. That was marked as a failed attempt on a
live address, on a counter shared with the upstream crawler. Every other
rejection remains a verdict and is still marked.
Added
- Counters for the verification itself, exported on the existing metrics
endpoint under<namespace>_seed_verify_dials_total, with one label carrying
six outcomes that sum to the number of decisions taken. Nothing upstream
counts connection attempts. They are only registered when
metrics_listen_addris set, and registration failures are reported as
configuration errors rather than raised. - A
verification sweepline at info level, once per period, carrying the
same outcomes, for operators without Prometheus. golangci-lintruns in CI, pinned to a fixed version so an upgrade of
the tool cannot turn the build red on untouched code. Thelinttarget was
declared in the Makefile and executed nowhere.- The three integer limits are validated. A negative
max_num_inbound_peersormax_num_outbound_peers, or a
max_packet_msg_payload_sizeof zero or less, is now refused at startup with
the name of the key instead of being passed to CometBFT as is.
Fixed
- The listening socket is released when the switch fails to start. That
path stopped the metrics server but left the transport listening, unlike both
paths ofStop. - The shutdown signal is trapped after the switch is running. A signal
arriving duringSwitch.StartfoundIsRunningfalse and would have closed
the transport under a switch that was still starting. - The metrics server compares its sentinel error with
errors.Is. - The close error of the configuration file is checked.
makeno longer misses a source file added in a deeper directory:
$(wildcard internal/**/*.go)is not recursive in GNU make, it means*.
Documentation
- The worst case cost of a dial was wrong, and three places quoted it. A
dial costs at most 7 seconds, not 4: one second to connect, then two
consecutive three second handshake deadlines. A sequential sweep of a full
selection takes about 29 minutes, not 17, and about 3m40 with 8 workers, not
2.peer_check_periodis about 2.7 times the duration of one sweep, not five
times. Corrected inconfig.go, FORK.md section 4 and the README. - FORK.md gains section 3.6, which documents the interaction between the sweep
and theAttemptscounter of the address book, and why the sweep never made
an address evictable meaningfully sooner than upstream would have.
Updating
Only the binary is replaced. Node identity, configuration and address book are
untouched. No configuration key was added, removed or renamed. See the update
procedure in the README.