Skip to content

feat: let the sync operator upgrade its dedicated synchronizer - #58

Merged
sadiq1971 merged 12 commits into
mainfrom
feat/sync-operator-lsu
Oct 2, 2026
Merged

sadiq1971 merged 12 commits into
mainfrom
feat/sync-operator-lsu

Conversation

@sadiq1971

@sadiq1971 sadiq1971 commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

Closes ChainSafe/canton-extending-mainnet#36

Summary: adds the operator side of a logical synchronizer upgrade, so a dedicated synchronizer upgrades on its operator's own schedule rather than the DSO's. The schedule lives in the operator's config, the announcement is published at the topology freeze time through the sequencer, and the successor's nodes and traffic state follow. Without a successor configured the upgrade automation does not register.

Stacked on #57. The SV's LsuAnnouncementTrigger and LsuTrigger are both rewritten here onto shared bases in apps-common. LsuAnnouncementTriggerBase also moves the parse of the voted protocol version from completeTask into listReadyTasks; both still run only past the topology freeze time.

BREAKING: the sync operator's sequencer moves from sequencer.admin-api to synchronizer-nodes.current.sequencer.admin-api.

@sadiq1971
sadiq1971 force-pushed the refactor/share-lsu-building-blocks branch from 3f096a1 to 5e5ec35 Compare September 23, 2026 18:13
@sadiq1971
sadiq1971 force-pushed the feat/sync-operator-lsu branch from dce29a0 to 996ea62 Compare September 23, 2026 18:15
@sadiq1971
sadiq1971 force-pushed the refactor/share-lsu-building-blocks branch from babd6ad to a9bcf14 Compare September 24, 2026 16:05
@sadiq1971
sadiq1971 force-pushed the feat/sync-operator-lsu branch from b0f905d to cb92f47 Compare September 24, 2026 16:05
The SV's node initialization, state export and traffic transfer are not
SV-specific. Move them so another app that operates a synchronizer can
reuse them, take what the initializer needs from a node through a trait
instead of LocalSynchronizerNode, and let the traffic transfer trigger
take the common SynchronizerNode.

Signed-off-by: sadiq1971 <sadiqurr8@gmail.com>
@sadiq1971
sadiq1971 force-pushed the refactor/share-lsu-building-blocks branch from a9bcf14 to 990a3d7 Compare September 24, 2026 20:40
@sadiq1971
sadiq1971 force-pushed the feat/sync-operator-lsu branch 2 times, most recently from 8ba6e9c to f9638f2 Compare September 24, 2026 20:59
@sadiq1971
sadiq1971 marked this pull request as ready for review September 24, 2026 21:00
Adds the operator side of a logical synchronizer upgrade: a schedule in
the operator's own config, an announcement trigger that publishes it at
the topology freeze time through the sequencer, and the node
initialization and traffic transfer that follow.

The operator's existing triggers now resolve their sequencer through the
active node, so they follow the upgrade, and parameter reconciliation
pauses while topology is frozen. Without a successor node configured the
upgrade automation does not register.

The SV's announcement trigger moves onto the shared base this adds.

BREAKING: the sync operator's sequencer moves from sequencer.admin-api to
synchronizer-nodes.current.sequencer.admin-api.

Signed-off-by: sadiq1971 <sadiqurr8@gmail.com>
@sadiq1971
sadiq1971 force-pushed the feat/sync-operator-lsu branch from f9638f2 to 0b82a2e Compare September 24, 2026 21:10
Signed-off-by: sadiq1971 <sadiqurr8@gmail.com>
…ator-lsu [ci]

Signed-off-by: sadiq1971 <sadiqurr8@gmail.com>
Signed-off-by: sadiq1971 <sadiqurr8@gmail.com>
…ator-lsu [ci]

Signed-off-by: sadiq1971 <sadiqurr8@gmail.com>

@moritzkiefer-da moritzkiefer-da left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

thanks, overall looks good. a few questions on details and this should be a regular integration test not compose.

Comment thread test-full-class-names-docker-no-canton.log Outdated
…st [ci]

Pull the duplicated half of the LSU transfer trigger into
LsuTransferTriggerBase, leaving the SV trigger with its cometbft, manual LSU
and reconcile steps and the operator trigger with its type and dump path.

Replace the localnet upgrade test with a regular integration test that starts
the successor's sequencer and mediator only while it runs, and take the
successor nodes back out of the localnet compose setup.

Signed-off-by: sadiq1971 <sadiqurr8@gmail.com>

@moritzkiefer-da moritzkiefer-da left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

thanks, getting there

val successorNode =
syncOperatorBackend.appState.synchronizerNodes.successor.value

clue(s"the successor's nodes are initialized from the predecessor before $upgradeTime") {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

let's run some transaction on the new physical synchronizer after upgrade time to check that it worked fully

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

added

.listLsuAnnouncements(psid.logical)
superseded = announcements.filter(announcement =>
announcement.mapping.successorSynchronizerId.serial > psid.serial &&
!clock.now.isBefore(announcement.mapping.upgradeTime)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

same here these clock.now checks are dodgy.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This one only runs when no successor is configured, so there's nothing new to query.

I think we can just drop the time part and keep the rest — "an upgrade to a higher serial is announced and no successor configured" is enough on its own, and it's pure state. The announcement trigger only runs when a successor is configured, so the only way to get there is removing the successor while an upgrade is pending, which is worth refusing anyway. Once current points at the successor the serials match and it stops firing.

Only difference is it gets stricter, refusing from freeze time instead of upgrade time. Fine by me unless you see a case I'm missing.

…eze gate [ci]

The upgrade test now crosses the upgrade time: the participant follows onto the
successor, a further purchase is granted there, and it still transacts.

Reconciling synchronizer parameters no longer skips while an upgrade is
announced. A rejected change is just retried, which is what the gsync trigger
does.

Signed-off-by: sadiq1971 <sadiqurr8@gmail.com>
The upgrade leaves the splitwell synchronizer on a new serial, which the other
sync operator tests cannot start against, so it gets its own job and test list.

Signed-off-by: sadiq1971 <sadiqurr8@gmail.com>
@sadiq1971 sadiq1971 self-assigned this Sep 30, 2026
…c state [ci]

The successor rejects traffic reads until the predecessor's traffic state has
been transferred onto it, which is the point from which grants have to go to
the successor to survive the upgrade. Reading it back is a better signal than
either a local clock or the participant's registered serial, and it works the
same on the global and on a dedicated synchronizer, so the sv, scan and sync
operator apps now share one SynchronizerNodeService.

Signed-off-by: sadiq1971 <sadiqurr8@gmail.com>
@sadiq1971
sadiq1971 changed the base branch from refactor/share-lsu-building-blocks to main September 30, 2026 17:08
Signed-off-by: sadiq1971 <sadiqurr8@gmail.com>

# Conflicts:
#	apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/lsu/LsuTrigger.scala

@moritzkiefer-da moritzkiefer-da left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

thanks

The announcement is only ever published with a successor configured, so an
announced serial ahead of the one this node serves is enough to refuse. Drops
the last comparison against the local clock.

Signed-off-by: sadiq1971 <sadiqurr8@gmail.com>
@sadiq1971
sadiq1971 merged commit 9c6ceaf into main Oct 2, 2026
295 of 309 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[P2-E8.4] Operator-driven LSU for the dedicated synchronizer

2 participants