Skip to content

feat: sync operator app skeleton - #16

Open
sadiq1971 wants to merge 5 commits into
feat/dedicated-syncfrom
feat/sync-operator-node
Open

feat: sync operator app skeleton#16
sadiq1971 wants to merge 5 commits into
feat/dedicated-syncfrom
feat/sync-operator-node

Conversation

@sadiq1971

@sadiq1971 sadiq1971 commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

What this does

  • New apps/syncoperator module, modelled on apps/splitwell: config, store, automation host, metrics, bootstrap, health.
  • SyncOperatorStore ingests MemberTraffic where this operator is the observer, and exposes getTotalPurchasedMemberTraffic(member) off an indexed column. The query itself is now shared with DbSvDsoStore via MemberTrafficQueries.
  • SyncOperatorApp connects to Scan and its own sequencer, and takes its synchronizer id from that sequencer rather than from config, so there is nothing to keep in sync at bootstrap.
  • New sync_operator_acs_store table and two SpliceDbLockCounters.

How it's verified

compile, Test/compile and scalafmtCheckAll pass across the tree.

SyncOperatorStoreTest covers the filter: sums across purchases, keeps members apart, and ignores purchases for another synchronizer, another operator, no operator, and a non-zero migration id. 7/7 pass locally against Postgres, and it is now listed in test-full-class-names-non-integration.log so CI runs it.

Not covered: that the store survives a decentralized-synchronizer migration without dropping records. That is part of the acceptance for canton-network#35 and needs a fixture that changes the migration id mid-test.

Tracked in

Implements P2-E5.2. Unblocks P2-E5.3, which also needs P2-E5.1 (#15 here). Relies on the migration-id policy in P2-E1.5.

@sadiq1971 sadiq1971 changed the title feat: sync operator app skeleton (Scala) feat: sync operator app skeleton Aug 19, 2026
@sadiq1971
sadiq1971 force-pushed the feat/sync-operator-node branch from dea0d60 to bf74782 Compare August 19, 2026 22:46
@sadiq1971 sadiq1971 self-assigned this Aug 20, 2026

@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, this looks mostly reasonable. I left a few comments. I would also suggest adding an integration test directly in this PR that tests that you can at least start and stop the app successfully.

Adds the apps/syncoperator module: config, store, automation host and
health, ingesting the MemberTraffic purchases the operator observes for
its own synchronizer and holding the sequencer admin connection the
reconciliation will grant on.

Ingestion is deliberately not filtered by the node's own migration id,
since a registered synchronizer is pinned to migration id 0.

Signed-off-by: sadiq1971 <sadiqurr8@gmail.com>
Drop the synchronizer id and sequencer list from the config and take the
synchronizer id from the sequencer instead. Share the MemberTraffic sum query
with the DSO store.

Also fixes the store test, which never ran: the operator was missing as an
observer on the ingested contracts, and the suite was absent from the
non-integration test list.

Signed-off-by: sadiq1971 <sadiqurr8@gmail.com>
@sadiq1971
sadiq1971 force-pushed the feat/sync-operator-node branch from 681ff2f to 75cbcb9 Compare August 25, 2026 19:48
Add package_name to the sync_operator_acs_store index, matching the shape V049
rebuilt the dso and scan indexes into; the shared MemberTraffic query filters
on it.

Drop trafficBalanceReconciliationDelay, which nothing reads, and the scalapb
runtime deps, which the module has no generated code for. Add the module to
clean-splice.

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

Copy link
Copy Markdown
Collaborator Author

thanks, this looks mostly reasonable. I left a few comments. I would also suggest adding an integration test directly in this PR that tests that you can at least start and stop the app successfully.

I have addressed the comments. For integration test I have added it on PR #17 to keep this PR clean and less changes.

@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

}
// Only used to partition the store's ingestion offsets; purchases are ingested regardless
// of it, see SyncOperatorStore.contractFilter.
domainMigrationId <- appInitStep(s"Resolving domain migration id") {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I don't understand why you need this here, there is no migration id for dedicated synchronizers/it's always zero?

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.

I think it's different migration id. it's mainnet's migration id, not the dedicated one's, and the store re-ingests when it changes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

mainnet migration id will never change again so I don't think we need this

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.

It was following what the sibling apps was doing - reading from scan. But if it doesn't change we may not need to fetch it anymore. But is it safe to set 0?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

yes it's safe to set to 0

Comment thread build.sbt
Drop the speculative "yet" from the domain time comment, and call the init step
what the variable already calls it.

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

Copy link
Copy Markdown
Collaborator Author

@moritzkiefer-da Can you check this CI issue?
artifactory_user and artifactory_password must be provided if not using OSS only dependencies.

@moritzkiefer-da

Copy link
Copy Markdown

is this based on some pretty old branch? that should have been fixed ages ago

@sadiq1971

Copy link
Copy Markdown
Collaborator Author

is this based on some pretty old branch? that should have been fixed ages ago

Yeah- It's way behind the main splice repo. The main of the fork is also behind. But the ci issue is happening from tomorrow.

@sadiq1971

Copy link
Copy Markdown
Collaborator Author

is this based on some pretty old branch? that should have been fixed ages ago

Yeah- It's way behind the main splice repo. The main of the fork is also behind. But the ci issue is happening from tomorrow.

Is it fine if I place PR for updating the repo main and also the current working branch for the phase 2 implementation? Also It might be better if we just not mainnet a different branch rather than main as it already is a fork.

@moritzkiefer-da

Copy link
Copy Markdown

Is it fine if I place PR for updating the repo main and also the current working branch for the phase 2 implementation?

yep definitely

MIGRATION_ID is frozen network wide and logical synchronizer upgrades carry a
serial id instead, so the store's partition can never move and resolving it
bought nothing. Drops resolveDomainMigrationId and its scan fallback.

It also lines the store's stamp up with the payload.migrationId == 0L check in
the contract filter, so there is no longer a second migration id a few lines
away meaning something different.

Signed-off-by: sadiq1971 <sadiqurr8@gmail.com>
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.

3 participants