Skip to content

fix(indexer): let deployment and activity workers pick up wallets linked mid-life - #334

Merged
blockchain-maxis merged 3 commits into
blockchain-maxis:mainfrom
ibochivincent-lang:fix/index-newly-linked-wallets
Sep 2, 2026
Merged

fix(indexer): let deployment and activity workers pick up wallets linked mid-life#334
blockchain-maxis merged 3 commits into
blockchain-maxis:mainfrom
ibochivincent-lang:fix/index-newly-linked-wallets

Conversation

@ibochivincent-lang

Copy link
Copy Markdown
Contributor

closes #282

Summary

runOperationsWorker takes an injectable OperationsStore and re-queries store.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. runDeploymentWorker and runActivityWorker didn'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: added DeploymentWallet/ContractCreate/DeploymentStore (mirroring OperationsStore) and changed runDeploymentWorker to take a store: DeploymentStore parameter instead of importing prisma directly.
  • apps/indexer/src/workers/activity.ts: same treatment — ActivityContract/ContractSnapshotCreate/ActivityStore, and runActivityWorker now takes a store: ActivityStore.
  • apps/indexer/src/index.ts: passes prisma as unknown as DeploymentStore / ... as ActivityStore into the two workers, the same way it already does for runOperationsWorker.
  • apps/indexer/src/workers/deployment.test.ts (new) and apps/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-known deployTxHash; 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 in apps/indexer/src/stellar.ts).
  • apps/indexer test suite: 56/56 passing (was 47; +9 new tests across deployment.test.ts and activity.test.ts).

…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.
@drips-wave

drips-wave Bot commented Aug 30, 2026

Copy link
Copy Markdown

@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! 🚀

Learn more about application limits

@netlify

netlify Bot commented Aug 30, 2026

Copy link
Copy Markdown

Deploy Preview for stellar-signet ready!

Name Link
🔨 Latest commit 99b0b5f
🔍 Latest deploy log https://app.netlify.com/projects/stellar-signet/deploys/6a97f9c5bf8c190008447567
😎 Deploy Preview https://deploy-preview-334--stellar-signet.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@vercel

vercel Bot commented Aug 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
signet-web Skipped Skipped Sep 2, 2026 10:26am UTC

@blockchain-maxis
blockchain-maxis merged commit efc02e0 into blockchain-maxis:main Sep 2, 2026
11 checks passed
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.
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.

Operations and activity workers do not pick up newly linked wallets

2 participants