fix(indexer): let deployment and activity workers pick up wallets linked mid-life - #334
Merged
blockchain-maxis merged 3 commits intoSep 2, 2026
Conversation
…store runDeploymentWorker and runActivityWorker imported the Prisma client directly, unlike runOperationsWorker's injectable OperationsStore, so their wallet/contract discovery was untestable — there was no way to prove a wallet linked mid-life gets scanned without a restart. Both already re-query wallet/contract state fresh on every tick call (no caching), but nothing verified that. Give deployment.ts a DeploymentStore and activity.ts an ActivityStore, mirroring the operations.ts pattern, and wire index.ts to pass the real Prisma client through both, same as it already does for operations. Add deployment.test.ts and activity.test.ts, each with a test that adds a wallet/contract to the store between two worker calls and asserts the new one is picked up on the second call — the "linked between cycles, indexed by the following cycle, no restart" behavior the issue asks for.
|
@ibochivincent-lang Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
✅ Deploy Preview for stellar-signet ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
blockchain-maxis
added a commit
to ibochielizabeth-spec/signet
that referenced
this pull request
Sep 2, 2026
blockchain-maxis#334 landed the same DeploymentStore seam this branch introduced, from the other direction (making the worker testable for mid-life wallet pickup), so both files conflicted. deployment.ts: - keep main's doc comment for the seam, plus this branch's note on why the seam also covers the two-wallet dedupe case; - findFirst takes `{ deployTxHash } | { address }` — this branch's widening, which is what the address dedupe needs; - `store` stays a required parameter (main's index.ts passes prisma explicitly), dropping this branch's default; - keep the address dedupe itself, the point of the PR. deployment.test.ts: took this branch's file (it carries the three dedupe tests plus main's other three) and ported main's 'wallet linked between cycles' test onto its fakeHorizon, which needed pubkey tracking added. 63/63 indexer tests pass; typecheck and lint clean.
blockchain-maxis
added a commit
to escaprt/signet
that referenced
this pull request
Sep 2, 2026
This branch rewrites the deployment worker's single 200-op scan into a paginated backfill, while blockchain-maxis#334 and blockchain-maxis#340 changed that same scan on main. Merged so both survive rather than picking a side: - DeploymentStore gains this branch's wallet.update alongside main's widened contract.findFirst (`{ deployTxHash } | { address }`). - `store` stays a required parameter (index.ts passes prisma explicitly), dropping this branch's default. - blockchain-maxis#340's address dedup is re-added inside handleOperation — the new shared handler both the quick-check and backfill paths run through, so it now covers more history than the scan it was written for, not less. Without this the merge would have silently reverted that fix. deployment.test.ts: took this branch's file (its fixtures match the paginated worker) and ported main's coverage onto them — cross-wallet dedup, per-wallet attribution, the cheap tx-hash guard firing before any transaction fetch, non-create ops ignored, and mid-life wallet pickup. Its in-memory store's findFirst also had to learn the address key shape; as written it read where.deployTxHash unconditionally, so every address lookup would have matched whichever row had an undefined hash. Checked the ported dedup test actually bites: stubbing out the address check fails it (65 pass / 1 fail), so it is a real guard rather than a passing assertion. 66/66 indexer tests pass; typecheck and lint clean. The migration (20260830000000_wallet_deployment_backfill) sorts after main's 20260829000000_drop_redundant_unique_indexes, and adds two nullable columns, so it is safe on an existing database.
blockchain-maxis
added a commit
to manchesternews98-jpg/signet
that referenced
this pull request
Sep 4, 2026
Closes blockchain-maxis#394. blockchain-maxis#279 — the issue this branch was opened against — is already fixed on main by blockchain-maxis#361, so the wholesale rewrite of deployment.ts is dropped: as authored it would have reverted the blockchain-maxis#340 address dedup, removed indexRequestedAt handling (blockchain-maxis#362/blockchain-maxis#334) and left two disagreeing backfill state machines on the Wallet table (blockchain-maxis#361). What is kept is the two ideas in it that main genuinely does not have, rebased as a small additive diff. **Forward catch-up.** Once a wallet is backfilled the worker checked a fixed newest-200 window each tick. That window is anchored to *now* rather than to how far the worker actually got, so anything that fell out of it between two ticks was never examined again — the backward walk is finished and does not revisit. A deploy script, a busy testnet key, or a long idle interval all reach it, and the symptom is invisible: the profile just quietly misses contracts, which is blockchain-maxis#279's complaint arriving by a different route. The new `Wallet.deploymentWatermark` records the newest operation actually examined, and the post-backfill check resumes forward from it. A wallet with no watermark yet reads one bounded newest-first page to establish one, which is the old behaviour for exactly one tick. **Per-page persistence.** `deploymentCursor` was written once at the end of a tick's walk, so a crash discarded up to MAX_PAGES_PER_TICK pages. Both cursor and watermark are now written after every page. On a wallet thousands of operations behind, that is the difference between making progress and never catching up. The docs claimed "Horizon is always newest-first, so nothing new can be missed", which is precisely the assumption that fails here; INDEXER.md now says why the obvious design is wrong instead. 5 tests added, 18 pass. Everything blockchain-maxis#361 established — the dedup, the indexRequestedAt clearing, the resumable backward walk, the explicit store seam — is untouched.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
closes #282
Summary
runOperationsWorkertakes an injectableOperationsStoreand re-queriesstore.wallet.findMany()fresh on every call — that's what already lets it pick up a wallet linked after the indexer started, on the very next tick, no restart.runDeploymentWorkerandrunActivityWorkerdidn't have that seam: they imported the Prisma client directly, so there was no way to write a test proving they do the same thing. In practice both already re-query fresh state each tick (prisma.wallet.findMany()/prisma.contract.findMany(), called inside the function body, not cached anywhere) — but "already works, probably" isn't the same as covered by a test, and the acceptance criteria for this issue specifically asks for one.Changes
apps/indexer/src/workers/deployment.ts: addedDeploymentWallet/ContractCreate/DeploymentStore(mirroringOperationsStore) and changedrunDeploymentWorkerto take astore: DeploymentStoreparameter instead of importingprismadirectly.apps/indexer/src/workers/activity.ts: same treatment —ActivityContract/ContractSnapshotCreate/ActivityStore, andrunActivityWorkernow takes astore: ActivityStore.apps/indexer/src/index.ts: passesprisma as unknown as DeploymentStore/... as ActivityStoreinto the two workers, the same way it already does forrunOperationsWorker.apps/indexer/src/workers/deployment.test.ts(new) andapps/indexer/src/workers/activity.test.ts(new): each has a test that calls the worker once, mutates the in-memory store's backing wallet/contract array (simulating a wallet the attestation worker just inserted, or a contract the deployment worker just found), and asserts the new one is scanned/snapshotted on the very next call — the "linked between cycles → indexed by the following cycle, no restart" behavior the issue describes. Also added coverage for the existing dedup logic (skip an already-knowndeployTxHash; skip a fresh snapshot; degrade to zero counts on a Horizon failure) that had no tests at all before this.No behavior changes to the actual indexing logic — this is a refactor for testability plus the tests that were missing, since the underlying discovery mechanism was already correct.
Verification
pnpm typecheck/pnpm lint/pnpm test— all pass across the workspace (one pre-existing, unrelated lint warning inapps/indexer/src/stellar.ts).apps/indexertest suite: 56/56 passing (was 47; +9 new tests acrossdeployment.test.tsandactivity.test.ts).