ChainSafe / ChainSafe/canton-extending-mainnet
[P2-E5.7] SyncOperatorStore pins its partition to migration 0 with no test at a non-zero generation
- Dominant language
- Shell
- Stars
- 0
- Forks
- 0
- Avg merge
- 22h 58m
- Merged PRs (30d)
- 1
Description
`SyncOperatorApp` constructs the store with a hardcoded partition id rather than the resolved network value (`SyncOperatorApp.scala:119-127`):
```scala
store = SyncOperatorStore(
storeKey, storage, loggerFactory, retryProvider,
// MIGRATION_ID is frozen network-wide and logical synchronizer upgrades carry a serial id
// instead, so the store's partition never has to move.
0L,
...
)
```
The reasoning is sound for a dedicated synchronizer, which upgrades via LSU and never hard-migrates. It is also self-consistent: rows are tagged 0 and queries filter on 0.
What is untested is the interaction with the ledger the store actually reads. `MemberTraffic` lives on the global synchronizer, so the operator app ingests from a ledger whose generation can advance independently of this hardcoded 0. #60 named that risk directly when it rejected the same approach for other stores: "The store would claim generation 0 while reading a generation-3 ledger", specifically calling out `store_last_ingested_offsets` and backfilling, since ledger offsets reset at a hard migration.
`SyncOperatorStoreTest` defaults `migrationId = 0L` (`:43`) and never varies it, so nothing exercises this. That is precisely the hole #60's fourth acceptance criterion closed for `ScanStore` and `SvDsoStore`, both of which now test at `nextDomainMigrationId`.
Not a live bug. Every network today is at generation 0, and the ingestion filter is immune to the #60 mechanism because it pins `payload.migrationId == 0L` directly rather than comparing against `domainMigrationId` (`SyncOperatorStore.scala:100`).
**Acceptance criteria:**
- [ ] A `SyncOperatorStoreTest` case runs at a non-zero network generation and still ingests registered-sync purchases
- [ ] Decide and record whether the store partition should track the global synchronizer's generation or stay pinned, and why
- [ ] If it stays pinned, confirm what happens to `store_last_ingested_offsets` when the global synchronizer hard-migrates
**Key files:** `apps/syncoperator/src/main/scala/.../SyncOperatorApp.scala:119-127`, `apps/syncoperator/src/main/scala/.../store/SyncOperatorStore.scala:100`, `apps/syncoperator/src/test/scala/.../SyncOperatorStoreTest.scala:43`
**Related:** found while verifying #60 and #83 were complete. Both are; this is the residue neither covered.
**Epic:** ChainSafe/canton-extending-mainnet#68
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with SyncOperatorApp.scala:119-127 and SyncOperatorStore.scala:100, then run SyncOperatorStoreTest.scala with its migrationId varied from 0L. Compare the non-zero-generation coverage in ScanStoreTest and SvDsoStoreTest. Done means the test covers registered-sync purchases at a non-zero generation and the partition and store_last_ingested_offsets behavior is documented and justified.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- scala
- Domain
- distributed-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100