Skip to content

Move subsystem maintenance off the ringbuffer tick - #8535

Merged
Amaury Chamayou (achamayou) merged 42 commits into
mainfrom
agents/final-ringbuffer-tick-removal-implementation
Oct 9, 2026
Merged

Amaury Chamayou (achamayou) merged 42 commits into
mainfrom
agents/final-ringbuffer-tick-removal-implementation

Conversation

@eddyashton

Copy link
Copy Markdown
Member

Motivation

CCF no longer needs a single aggregate tick step. Following #8445 and #8446, move the remaining tick consumers onto independently scheduled work rather than delivering host-enclave ringbuffer tick messages.

This PR changes scheduling only. Removing the unused ringbuffer infrastructure and cleaning up its configuration are separate follow-ups.

Implementation summary

  • Give consensus, channel maintenance and indexing their own periodic tasks, using the existing owner-managed task mechanism and subsystem locks. Construct the components normally, then register their tasks.
  • Expose the usable committed transaction ID through a small read-only CommitPoint subsystem, so indexing does not depend on an injected callback or the whole node interface.
  • Use a local delayed task for recovered-service opening. It retries while opening remains uncommitted and stops once it is committed or no longer applicable.
  • Stop instantiating the host ringbuffer ticker and remove the enclave tick handler. Keep the ringbuffer infrastructure, declarations, tests and configuration for later cleanup.

Safety and compatibility

Maintenance no longer runs as one serial step or shares the inbound node-message lane. Tasks can run concurrently with other maintenance and inbound messages, protected by the existing Raft, channel and indexing locks. Executions of each individual periodic callback remain non-overlapping, with elapsed-time accounting based on the task clock. There is no thread affinity, new priority class or dedicated executor; shared-worker saturation can still delay consensus.

Indexing uses the committed, not speculative, transaction ID and retains the existing startup/private-recovery readiness gate. A backup can later become primary, and a local service opening can be rolled back before commit, so the recovery retry stops only after observing committed opening.

No configuration, public API, node wire-format or ledger-format changes. Mixed-version protocol compatibility is unchanged.

Validation:

  • After merging current main: fully linked logging/COSE/JS/programmability builds and six unit suites pass (tasks, ledger, Raft, node ingress/local retry, indexing and channels), as do full repository static checks.
  • Recovery and logging/forwarding e2e pass on the merged revision.
  • Five affected suites pass under TSAN before the main merge; nodes e2e and warning-as-error Sphinx validation also passed during implementation.
  • Independent whole-change adversarial review found no significant issues. A separate test-quality review led to failure-safe cleanup, authenticated traffic assertions and full committed-TxID checks. An injected blocking-lock regression now fails its assertion rather than hanging.

Replace the ledger_init/append/truncate/commit/open and ledger_get_range/
entry_range/no_entry_range ringbuffer messages with typed ledger interfaces.
A host-owned LedgerSubsystem runs mutations, range classification and reads
of uncommitted state in FIFO order on one OrderedTasks lane, and reads that
lie wholly within committed files as ordinary concurrent tasks. Entries are
moved into owned storage before submission and results are delivered as
owned values through typed callbacks.

Ledger::init now lowers the committed-file classification boundary when it
un-commits later files, so a read classified after init can never target a
file about to be replayed into. Range results distinguish an absent range
from an entry exceeding the read budget; recovery fails explicitly on the
latter rather than treating it as end-of-ledger.

The read budget remains derived from memory.max_msg_size so no
configuration changes. host may now depend on tasks (a leaf component).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ledger mutations now reach the host through the ledger OrderedTasks lane
rather than the ringbuffer, but node-to-node messages still arrive on the
ringbuffer. The two queues lost the emission order the enclave relies on: an
AppendEntries message could be processed before the append it refers to had
been applied, and the host dropped it.

Restore a single order by submitting each outbound node message to the same
lane as ledger mutations. The lane action reads any ledger entries and
assembles the complete frame; the result is queued for the libuv thread,
which owns the sockets and drains the queue on the existing 1ms cadence.
Address and close updates take the same path so their order relative to
sends is preserved. This is temporary until node-to-node transport leaves
the ringbuffer.

Also fix node_connections_test for the Ledger constructor change and the
messaging.h include that ledger.h no longer provides.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Review findings on the typed ledger subsystem:

- shutdown() ran after the enclave task workers had exited and only closed
  the gate, so an append or commit which had been accepted but not yet
  executed was never written. The ringbuffer design drained remaining
  messages before stopping the loop. Drain the ordered lane synchronously in
  shutdown() before closing the gate.

- Reads of committed files bypassed the ledger state lock. A read already
  dispatched before init() could then observe a file being un-committed and
  rewritten. The libuv threadpool read previously took that lock; do so
  again, so committed reads are dispatched off the lane but their file access
  is serialised against mutations as before.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
performance-enum-size on LedgerRangeStatus, a redundant access specifier in
NodeConnectionsImpl, and std::move of a const shared_ptr in Enclave.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ages

Review findings on #8405:

- The shutdown drain executed queued range reads as well as mutations. A
  recovery read callback submits the next batch, so stopping mid-recovery
  could keep the host thread recovering the whole ledger after the enclave
  threads had joined. Set a draining flag before the drain: it rejects new
  submissions and makes queued reads (and committed-read tasks) skip their
  callback, while mutations still reach disk.

- The oversized-entry log messages named ledger.max_read_size, which is not
  a setting. Describe the derived budget instead.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The test must SIGTERM the primary while it is reading the private ledger,
which takes about 0.8s for the 2000 entries the test writes. It did not
start polling until recover_with_shares() returned, which is 0.3-1.5s after
the final share is accepted depending on client request latency, so on a
slow client the read had finished before the first poll. Measured against
main with identical read start (about 180ms after the final share) and
replay rate (about 3.5k entries/s): the product behaviour is the same; only
the client-side slack differs.

Submit the shares from a thread and poll the primary immediately over a
pre-established connection, so the first observation is bounded by the poll
round trip rather than by the share-submission tail. Poll only the primary:
followers begin reading later, once the primary broadcasts the ledger
secrets, and need not be observed for this scenario.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extract the body of the leader-only branch at the end of private ledger
recovery into open_recovered_service(), which takes only the transaction,
share manager and service key it uses. It is now testable without a
NodeState harness, of which the repository has none.

open_recovered_service_test covers a store in the recovering state: the
service becomes OPEN, submitted shares are cleared, fresh shares are issued
and the previous identity is endorsed; and that it throws for any status
other than WAITING_FOR_RECOVERY_SHARES, which is the at-most-once guard
that prevents a second node from opening a service another has already
opened.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The test tried to SIGTERM the primary inside the window where it is reading
the private ledger, by polling quickly enough. Nothing guaranteed that: the
window is under a second and shrank relative to the client's turnaround
between accepting the final share and the first poll, so the test failed
deterministically in CI.

Instead, submit the shares to the primary and SIGTERM it as soon as the final
share is accepted, then assert the outcome: a new view is elected, every
survivor reaches PartOfNetwork, the service is open, and no survivor died
attempting a second opening. Whether the stop notice lands before or after
the primary finishes reading is a race the test no longer needs to win: if
the primary wrote the opening transaction and then stepped down before it
replicated, the new leader rolls it back and opens the service itself, which
both local runs exercised. A node which is not primary refusing to open, and
opening being at-most-once, are covered by open_recovered_service_test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PreviousServiceIdentityEndorsement became a map keyed by IdentityType in
#8401, so look up the CLASSICAL endorsement.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drive the task clock from a host-side libuv timer while retaining the existing tick semantics and configured interval. Let historical state caches and RPC frontends own their periodic task lifetimes, and remove the unused consensus periodic_end hook.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use the StubLedgerReader introduced by the ledger ringbuffer removal base instead of the removed ringbuffer StubWriter.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Satisfy the clang-tidy named-parameter check for the default no-op RPC handler implementation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Satisfy clang-tidy's member-initialization check in the shared periodic task owner.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the associate_node_address, node_outbound, node_inbound and
close_node_outbound ringbuffer messages with a typed, thread-safe node
transport implemented by the host's NodeConnections. Outbound sends stay
ordered behind earlier ledger mutations, preserving AppendEntries entry
attachment and per-peer nonce order. Inbound frames are copied into owned
buffers and run, with ticks and stop notices, on one critical OrderedTasks
lane.

Add a critical task class to JobBoard: every worker runs critical tasks
first, and the enclave dispatch thread now runs only critical tasks, so
blocking general tasks cannot delay consensus. Frames declaring a size
above memory.max_msg_size close the connection instead of terminating the
node. The host closes node sockets explicitly at shutdown, since the
enclave retains the transport.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Clarify that JobBoard may enqueue on later ticks, while PeriodicTaskOwner skips concurrent executions and carries their elapsed time into the next successful run.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ion-ccf' into agents/node-to-node-ringbuffer-removal
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drop TaskClass::Critical and the dispatch thread's critical-task loop. The
node ingress lane is now an ordinary OrderedTasks on the main job board,
and the dispatch thread is ingress-only again, as after #8404.

Inbound node messages, ticks and stop notices remain FIFO and mutually
exclusive on their lane, but now queue behind other ready tasks and need a
free worker. Blocking general tasks occupying every worker can therefore
delay consensus. Measure this in CI before considering a dedicated
consensus executor.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
enclave_shutdown_tasks() (added by #8420) shuts down the main job board,
which abandons the pending actions of every registered OrderedTasks lane,
including the ledger lane. The host called LedgerSubsystem::shutdown()
after it, so the drain always found an empty queue and mutations which
append()/commit() had accepted were silently discarded on graceful stop.
The ringbuffer design drained remaining ledger messages before stopping
the loop, so this was a regression.

Drain the ledger subsystem inside run_enclave_threads, after the enclave
threads join and before enclave_shutdown_tasks(), and document the
ordering requirement at both the call site and on shutdown().

Remove the shutdown() call from ~Enclave: the Enclave is never destroyed
on the normal exit path, so it only looked like a safety net.

Extend the shutdown unit test with the production call order, and add a
test which pins the hazard (board shutdown first loses the mutations) so
the ordering comments cannot go stale unnoticed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…-implementation' into agents/node-to-node-ringbuffer-removal
…ion-ccf' into agents/node-to-node-ringbuffer-removal
Merge artefact: this branch forked from an intermediate state of #8405
that still had this call, which #8405 then removed in f2985f2 before
merging. The host owns the ledger lane drain in run_enclave_threads.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ion-ccf' into agents/node-to-node-ringbuffer-removal
Schedule subsystem maintenance independently and use a bounded delayed retry for recovered service opening. Retain ringbuffer infrastructure and configuration for a separate cleanup.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Make periodic callback failure cleanup reachable, verify real consensus and authenticated-channel results, and trim redundant timing coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…inal-ringbuffer-tick-removal-implementation
@eddyashton
Eddy Ashton (eddyashton) requested a review from a team as a code owner October 8, 2026 13:22
Copilot AI balanced review requested due to automatic review settings October 8, 2026 13:22
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The architecture threading documentation still promises that consensus ticks are serialized through NodeIngress.

1 open finding
What changed in this PR

Moves consensus, channel, indexing, and recovery maintenance from the aggregate ringbuffer tick onto independently scheduled tasks.

Changes:

  • Adds owner-managed periodic maintenance for consensus, channels, and indexing.
  • Introduces committed-TxID and recovery-opening task abstractions.
  • Removes host/enclave aggregate tick handling and adds concurrency/lifetime tests.

Custom instructions used: .github/copilot-instructions.md, .github/instructions/reviewing.instructions.md, .github/instructions/changelog.instructions.md

File Description
CMakeLists.txt Links channel tests with task support.
CHANGELOG.md Documents the scheduling change.
doc/​operations/​resource_usage.rst Describes periodic-task resource behavior.
scripts/​source-dependencies.json Allows consensus to depend on tasks.
src/​consensus/​aft/​raft.h Registers consensus maintenance periodically.
src/​consensus/​aft/​test/​main.cpp Tests scheduling and concurrency.
src/​enclave/​enclave.h Installs commit-point/index tasks and removes tick dispatch.
src/​host/​run.cpp Stops creating the ringbuffer ticker.
src/​indexing/​indexer.h Drives indexing from committed TxIDs.
src/​indexing/​test/​indexing.cpp Tests indexing scheduling and ownership.
src/​node/​commit_point_interface.h Defines the commit-point subsystem API.
src/​node/​commit_point_subsystem.h Implements committed-TxID access.
src/​node/​node_inbound_message.h Updates NodeIngress documentation.
src/​node/​node_state.h Registers maintenance and recovery retry tasks.
src/​node/​node_to_node_channel_manager.h Owns channel maintenance scheduling.
src/​node/​recovered_service_opening_task.h Implements delayed recovery retries.
src/​node/​test/​channels.cpp Tests channel maintenance concurrency.
src/​node/​test/​node_inbound_message.cpp Tests recovery retry behavior.
src/​tasks/​test/​delayed_tasks.cpp Tests independent periodic-task ownership.

🧠 Review effort: Balanced


Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.

Comment thread doc/operations/resource_usage.rst
Mark the task name accessor nodiscard and document consensus maintenance separately from the ordered node ingress lane.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…inal-ringbuffer-tick-removal-implementation
@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Description

Comparing 3 available runs from this branch (#8535) against the trend of the last 30 main runs.

Each chart plots every benchmark as an axis, with values normalized so 100 is the EWMA baseline of recent main runs, using a 7-run half-life. The 3 orange branch lines run from the oldest (faintest) to the latest (darkest and thickest); the darker blue band is the main baseline +/- 1 std dev and the lighter blue band around it is +/- 2 std dev.

Axis labels show the latest branch value and its difference from the main EWMA baseline, where 0% is on the baseline. They are coloured green where the latest run improves on the baseline, red where it regresses, and grey where the difference is within one std dev of the baseline (within noise). Higher is better for throughput and rate, lower for latency and memory.

A benchmark which does not exist on main yet has no baseline of its own, so its earliest available run from this branch is used as its reference and its band is measured across this branch's runs. Its axis is normalized, scaled and coloured like any other, but the comparison is against this branch rather than against main.

Throughput (tx/s)

---
config:
  radar:
    width: 620
    height: 620
    marginTop: 90
    marginRight: 220
    marginBottom: 60
    marginLeft: 220
    axisLabelFactor: 1.12
    curveTension: 0.08
  theme: base
  themeCSS: |
    .radarCurve-0{fill:color-mix(in srgb, #62B5E5 13%, var(--color-canvas-default,var(--bgColor-default,#fff)))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarCurve-1{fill:color-mix(in srgb, #62B5E5 40%, var(--color-canvas-default,var(--bgColor-default,#fff)))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarCurve-2{fill:color-mix(in srgb, #62B5E5 13%, var(--color-canvas-default,var(--bgColor-default,#fff)))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarCurve-3{fill:var(--color-canvas-default,var(--bgColor-default,#fff))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarAxisLabel,.radarTitle{fill:var(--color-fg-default,var(--fgColor-default,#111827))!important;color:var(--color-fg-default,var(--fgColor-default,#111827))!important}
    .radarCurve-4{stroke-width:1.5px!important;stroke-opacity:0.40!important}
    .radarCurve-5{stroke-width:1.5px!important;stroke-opacity:0.50!important}
    .radarCurve-6{stroke-width:1.75px!important;stroke-opacity:1.00!important}
    .radarAxisLabel:nth-of-type(1){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(2){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(3){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(4){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(5){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(6){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(7){fill:#808A94!important}
  themeVariables:
    cScale0: "#62B5E5"
    cScale1: "#62B5E5"
    cScale2: "#62B5E5"
    cScale3: "#62B5E5"
    cScale4: "#F97316"
    cScale5: "#F97316"
    cScale6: "#F97316"
    radar:
      axisColor: "#9CA3AF"
      graticuleColor: "#E5E7EB"
      graticuleOpacity: 0
      axisStrokeWidth: 1
      curveOpacity: 0
---
radar-beta
  axis b0["Basic Blocking 100ms: 3,100 tx/s ▬ 0%"]
  axis b1["Basic Blocking 20ms: 15,332 tx/s ▬ 0%"]
  axis b2["Basic Blocking 2ms: 49,309 tx/s ▬ +2%"]
  axis b3["Basic JS: 16,217 tx/s ▬ +2%"]
  axis b4["Historical Queries: 948,887 tx/s ▬ +1%"]
  axis b5["L…g Certificate Blocking: 28,666 tx/s ▬ 0%"]
  axis b6["Logging JWT Blocking: 15,304 tx/s ▬ 0%"]
  curve stddev2_high["main EWMA + 2 std dev"]{100.38, 100.28, 108.56, 104.75, 111.79, 100.50, 100.32}
  curve stddev1_high["main EWMA + 1 std dev"]{100.19, 100.14, 104.28, 102.38, 105.90, 100.25, 100.16}
  curve stddev1_low["main EWMA - 1 std dev"]{99.81, 99.86, 95.72, 97.62, 94.10, 99.75, 99.84}
  curve stddev2_low["main EWMA - 2 std dev"]{99.62, 99.72, 91.44, 95.25, 88.21, 99.50, 99.68}
  curve branch_0["#8535 (2 runs earlier)"]{100.00, 99.75, 97.97, 99.87, 94.49, 99.35, 99.92}
  curve branch_1["#8535 (1 run earlier)"]{100.13, 100.00, 102.69, 99.45, 99.48, 99.55, 99.97}
  curve branch_2["#8535"]{100.20, 99.95, 101.90, 101.78, 101.32, 99.54, 99.99}
  graticule polygon
  max 121
  min 79
  ticks 0
  showLegend false
Loading

Latency (ms)

---
config:
  radar:
    width: 620
    height: 620
    marginTop: 90
    marginRight: 220
    marginBottom: 60
    marginLeft: 220
    axisLabelFactor: 1.12
    curveTension: 0.08
  theme: base
  themeCSS: |
    .radarCurve-0{fill:color-mix(in srgb, #62B5E5 13%, var(--color-canvas-default,var(--bgColor-default,#fff)))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarCurve-1{fill:color-mix(in srgb, #62B5E5 40%, var(--color-canvas-default,var(--bgColor-default,#fff)))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarCurve-2{fill:color-mix(in srgb, #62B5E5 13%, var(--color-canvas-default,var(--bgColor-default,#fff)))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarCurve-3{fill:var(--color-canvas-default,var(--bgColor-default,#fff))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarAxisLabel,.radarTitle{fill:var(--color-fg-default,var(--fgColor-default,#111827))!important;color:var(--color-fg-default,var(--fgColor-default,#111827))!important}
    .radarCurve-4{stroke-width:1.5px!important;stroke-opacity:0.40!important}
    .radarCurve-5{stroke-width:1.5px!important;stroke-opacity:0.50!important}
    .radarCurve-6{stroke-width:1.75px!important;stroke-opacity:1.00!important}
    .radarAxisLabel:nth-of-type(1){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(2){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(3){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(4){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(5){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(6){fill:#2DA44E!important}
    .radarAxisLabel:nth-of-type(7){fill:#808A94!important}
  themeVariables:
    cScale0: "#62B5E5"
    cScale1: "#62B5E5"
    cScale2: "#62B5E5"
    cScale3: "#62B5E5"
    cScale4: "#F97316"
    cScale5: "#F97316"
    cScale6: "#F97316"
    radar:
      axisColor: "#9CA3AF"
      graticuleColor: "#E5E7EB"
      graticuleOpacity: 0
      axisStrokeWidth: 1
      curveOpacity: 0
---
radar-beta
  axis b0["Basic Blocking 100ms: 98 ms ▬ 0%"]
  axis b1["Basic Blocking 20ms: 19 ms ▬ 0%"]
  axis b2["Basic Blocking 2ms: 5 ms ▬ -5%"]
  axis b3["Basic JS: 19 ms ▬ 0%"]
  axis b4["Historical Queries: 32 ms ▬ -1%"]
  axis b5["Logging Certificate Blocking: 18 ms ▼ 5%"]
  axis b6["Logging JWT Blocking: 19 ms ▬ 0%"]
  curve stddev2_high["main EWMA + 2 std dev"]{100.51, 100.00, 118.31, 104.96, 112.96, 100.00, 100.00}
  curve stddev1_high["main EWMA + 1 std dev"]{100.25, 100.00, 109.15, 102.48, 106.48, 100.00, 100.00}
  curve stddev1_low["main EWMA - 1 std dev"]{99.75, 100.00, 90.85, 97.52, 93.52, 100.00, 100.00}
  curve stddev2_low["main EWMA - 2 std dev"]{99.49, 100.00, 81.69, 95.04, 87.04, 100.00, 100.00}
  curve branch_0["#8535 (2 runs earlier)"]{99.92, 100.00, 113.97, 99.70, 108.15, 94.74, 100.00}
  curve branch_1["#8535 (1 run earlier)"]{99.92, 100.00, 94.97, 99.70, 98.88, 94.74, 100.00}
  curve branch_2["#8535"]{99.92, 100.00, 94.97, 99.70, 98.88, 94.74, 100.00}
  graticule polygon
  max 132
  min 68
  ticks 0
  showLegend false
Loading

Memory (bytes)

---
config:
  radar:
    width: 620
    height: 620
    marginTop: 90
    marginRight: 220
    marginBottom: 60
    marginLeft: 220
    axisLabelFactor: 1.12
    curveTension: 0.08
  theme: base
  themeCSS: |
    .radarCurve-0{fill:color-mix(in srgb, #62B5E5 13%, var(--color-canvas-default,var(--bgColor-default,#fff)))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarCurve-1{fill:color-mix(in srgb, #62B5E5 40%, var(--color-canvas-default,var(--bgColor-default,#fff)))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarCurve-2{fill:color-mix(in srgb, #62B5E5 13%, var(--color-canvas-default,var(--bgColor-default,#fff)))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarCurve-3{fill:var(--color-canvas-default,var(--bgColor-default,#fff))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarAxisLabel,.radarTitle{fill:var(--color-fg-default,var(--fgColor-default,#111827))!important;color:var(--color-fg-default,var(--fgColor-default,#111827))!important}
    .radarCurve-4{stroke-width:1.5px!important;stroke-opacity:0.40!important}
    .radarCurve-5{stroke-width:1.5px!important;stroke-opacity:0.50!important}
    .radarCurve-6{stroke-width:1.75px!important;stroke-opacity:1.00!important}
    .radarAxisLabel:nth-of-type(1){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(2){fill:#E5484D!important}
    .radarAxisLabel:nth-of-type(3){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(4){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(5){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(6){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(7){fill:#808A94!important}
  themeVariables:
    cScale0: "#62B5E5"
    cScale1: "#62B5E5"
    cScale2: "#62B5E5"
    cScale3: "#62B5E5"
    cScale4: "#F97316"
    cScale5: "#F97316"
    cScale6: "#F97316"
    radar:
      axisColor: "#9CA3AF"
      graticuleColor: "#E5E7EB"
      graticuleOpacity: 0
      axisStrokeWidth: 1
      curveOpacity: 0
---
radar-beta
  axis b0["Basic Blocking 100ms: 88.3 MiB ▬ -1%"]
  axis b1["Basic Blocking 20ms: 90.6 MiB ▲ 1%"]
  axis b2["Basic Blocking 2ms: 92 MiB ▬ 0%"]
  axis b3["Basic JS: 96.2 MiB ▬ -1%"]
  axis b4["Historical Queries: 137 MiB ▬ -1%"]
  axis b5["Logging Certificate Blocking: 114 MiB ▬ 0%"]
  axis b6["Logging JWT Blocking: 87.2 MiB ▬ -1%"]
  curve stddev2_high["main EWMA + 2 std dev"]{102.40, 101.80, 104.09, 101.77, 103.13, 103.26, 101.99}
  curve stddev1_high["main EWMA + 1 std dev"]{101.20, 100.90, 102.05, 100.88, 101.57, 101.63, 101.00}
  curve stddev1_low["main EWMA - 1 std dev"]{98.80, 99.10, 97.95, 99.12, 98.43, 98.37, 99.00}
  curve stddev2_low["main EWMA - 2 std dev"]{97.60, 98.20, 95.91, 98.23, 96.87, 96.74, 98.01}
  curve branch_0["#8535 (2 runs earlier)"]{99.43, 100.65, 100.43, 99.72, 98.78, 99.59, 97.78}
  curve branch_1["#8535 (1 run earlier)"]{98.32, 100.70, 99.11, 99.56, 98.61, 100.43, 102.39}
  curve branch_2["#8535"]{99.40, 101.01, 99.76, 99.31, 98.72, 99.54, 99.40}
  graticule polygon
  max 109
  min 91
  ticks 0
  showLegend false
Loading

Rate (ops/s)

---
config:
  radar:
    width: 620
    height: 620
    marginTop: 90
    marginRight: 220
    marginBottom: 60
    marginLeft: 220
    axisLabelFactor: 1.12
    curveTension: 0.08
  theme: base
  themeCSS: |
    .radarCurve-0{fill:color-mix(in srgb, #62B5E5 13%, var(--color-canvas-default,var(--bgColor-default,#fff)))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarCurve-1{fill:color-mix(in srgb, #62B5E5 40%, var(--color-canvas-default,var(--bgColor-default,#fff)))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarCurve-2{fill:color-mix(in srgb, #62B5E5 13%, var(--color-canvas-default,var(--bgColor-default,#fff)))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarCurve-3{fill:var(--color-canvas-default,var(--bgColor-default,#fff))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarAxisLabel,.radarTitle{fill:var(--color-fg-default,var(--fgColor-default,#111827))!important;color:var(--color-fg-default,var(--fgColor-default,#111827))!important}
    .radarCurve-4{stroke-width:1.5px!important;stroke-opacity:0.40!important}
    .radarCurve-5{stroke-width:1.5px!important;stroke-opacity:0.50!important}
    .radarCurve-6{stroke-width:1.75px!important;stroke-opacity:1.00!important}
    .radarAxisLabel:nth-of-type(1){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(2){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(3){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(4){fill:#2DA44E!important}
    .radarAxisLabel:nth-of-type(5){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(6){fill:#2DA44E!important}
    .radarAxisLabel:nth-of-type(7){fill:#2DA44E!important}
    .radarAxisLabel:nth-of-type(8){fill:#2DA44E!important}
    .radarAxisLabel:nth-of-type(9){fill:#808A94!important}
  themeVariables:
    cScale0: "#62B5E5"
    cScale1: "#62B5E5"
    cScale2: "#62B5E5"
    cScale3: "#62B5E5"
    cScale4: "#F97316"
    cScale5: "#F97316"
    cScale6: "#F97316"
    radar:
      axisColor: "#9CA3AF"
      graticuleColor: "#E5E7EB"
      graticuleOpacity: 0
      axisStrokeWidth: 1
      curveOpacity: 0
---
radar-beta
  axis b0["CCF c…n c…t lifecycle: 21,475 ops/s ▬ +1%"]
  axis b1["CCF fresh JS invocation: 19,507 ops/s ▬ 0%"]
  axis b2["CHAMP get: 63,824,483 ops/s ▬ -1%"]
  axis b3["CHAMP put: 8,368,132 ops/s ▲ 2%"]
  axis b4["KV deserialisation: 2,804,262 ops/s ▬ +2%"]
  axis b5["KV serialisation: 2,508,781 ops/s ▲ 3%"]
  axis b6["KV s…t deserialisation: 6,411 ops/s ▲ 3%"]
  axis b7["KV snapshot serialisation: 4,966 ops/s ▲ 4%"]
  axis b8["Q…S s…d context lifecycle: 26,245 ops/s ▬ 0%"]
  curve stddev2_high["main EWMA + 2 std dev"]{103.68, 103.67, 103.38, 103.68, 104.34, 104.07, 103.86, 105.57, 103.61}
  curve stddev1_high["main EWMA + 1 std dev"]{101.84, 101.84, 101.69, 101.84, 102.17, 102.03, 101.93, 102.79, 101.80}
  curve stddev1_low["main EWMA - 1 std dev"]{98.16, 98.16, 98.31, 98.16, 97.83, 97.97, 98.07, 97.21, 98.20}
  curve stddev2_low["main EWMA - 2 std dev"]{96.32, 96.33, 96.62, 96.32, 95.66, 95.93, 96.14, 94.43, 96.39}
  curve branch_0["#8535 (2 runs earlier)"]{100.99, 100.90, 98.71, 103.18, 98.36, 101.86, 100.18, 95.36, 101.87}
  curve branch_1["#8535 (1 run earlier)"]{101.24, 101.48, 102.28, 100.18, 101.66, 101.39, 100.94, 97.74, 102.47}
  curve branch_2["#8535"]{100.54, 99.58, 98.80, 102.40, 101.92, 102.91, 103.14, 104.12, 100.35}
  graticule polygon
  max 110
  min 90
  ticks 0
  showLegend false
Loading

…inal-ringbuffer-tick-removal-implementation
@achamayou
Amaury Chamayou (achamayou) merged commit 3c28f8a into main Oct 9, 2026
14 checks passed
@achamayou
Amaury Chamayou (achamayou) deleted the agents/final-ringbuffer-tick-removal-implementation branch October 9, 2026 14:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bench-ab run-long-test Run Long Test job

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants