Skip to content

fix(lifecycle-service): invalidate cached account reads after merge - #384

Merged
davedumto merged 2 commits into
Vellar-Wallet:devfrom
onuibeblessing2019-hash:fix/287-lifecycle-cache-invalidation
Aug 30, 2026
Merged

fix(lifecycle-service): invalidate cached account reads after merge#384
davedumto merged 2 commits into
Vellar-Wallet:devfrom
onuibeblessing2019-hash:fix/287-lifecycle-cache-invalidation

Conversation

@onuibeblessing2019-hash

@onuibeblessing2019-hash onuibeblessing2019-hash commented Aug 30, 2026

Copy link
Copy Markdown

Closes #287

Investigation

lifecycle-service had zero caching of any kind — every getAccount call hit Horizon live. The only cache anywhere in this repo (wallet-service/src/cache.ts) is real, well-built infrastructure, but is wired into deps and never actually called from any route — unused scaffolding, and unrelated to account merges (wallet-service has no merge concept at all; merges are purely a lifecycle-service/classic-account thing). So #287's premise ("cached data is not invalidated") first needed a cache to exist before there was anything to invalidate.

Also found, while investigating: src/server.test.ts has literal syntax errors (TS1128, a stray unclosed describe block with a function illegally nested inside it) that prevent the whole file — and therefore the whole package's typecheck — from compiling, and src/account-merge.integration.test.ts references a AccountRecord type that doesn't exist anywhere and fails to even load (unrelated sac-sdk transitive-import error on top of that). Both are pre-existing and out of scope for this PR — left untouched and disclosed here rather than attempted, per the actual scope of #287.

What's here

  • src/account-cache.ts (new) — createCachedAccountReader, a short-TTL (default 30s) decorator around any AccountReader. Caches both successful lookups and not-found (undefined) results, so a merged-away account correctly reads as gone once invalidated. Exposes invalidate(accountId) and invalidateAll().
  • src/server.tsPOST /lifecycle/merge now invalidates both the source (merged away) and destination (balance changed) account's cache entries once the merge step is built and audit-logged, via a new isCachedAccountReader type guard (a plain, uncached AccountReader — what every existing test in this package uses — gets no invalidation call at all, since there's nothing to invalidate). Logs a new lifecycle.merge.cache_invalidated event.
  • Important, disclosed nuance: this endpoint only builds the unsigned merge transaction — the client still signs and submits it separately (via wallet-service). This service has no channel to observe that later on-chain confirmation, so invalidation here is optimistic (at the point the merge is committed to, not proven complete). The short TTL is the backstop for the rare case a given merge is later abandoned or fails to land — documented in both the code comment and README.md.
  • src/index.ts — production composition now wraps the real Horizon reader with the cache (previously just used the raw, uncached reader).
  • README.md — new "Account read caching and invalidation on merge" section per the issue's "document the invalidation step" requirement, including the optimistic-invalidation caveat.
  • Also fixes the same pre-existing workerProcessingLagSeconds is not defined" crash in packages/service-kit/src/metrics.tsalready fixed independently in #381 (#326/#327) — needed again here since this branch is based directly onupstream/dev, which doesn't have that fix yet; without it, importing @vellar/service-kit(and therefore this service's ownserver.ts`) throws at module load time.

Test plan

  • account-cache.test.ts (9 new tests) — cache hit/miss, TTL expiry, not-found caching, per-account-id isolation, and the exact end-to-end scenario [medium] Fix stale cache after account merge in lifecycle-service #287 describes: a stale cache entry showing the pre-merge source-exists/dest-old-balance state, confirmed still stale without invalidation, then confirmed correct (source gone, dest's new balance) after invalidate.
  • merge-cache-invalidation.test.ts (3 new tests) — exercises the real POST /lifecycle/merge route (not just the cache module in isolation): confirms both accounts are evicted after a successful merge, confirms a plain uncached reader is unaffected (no invalidation attempted, no throw), confirms a rejected merge (blockers remaining) does NOT invalidate anything.
  • Full lifecycle-service suite (excluding the two pre-existing broken files above, and worker/loop.test.ts's 4 pre-existing unrelated concurrency failures, confirmed identical with my changes stashed out): 58/58 passing.
  • packages/service-kit: 85/85 passing.
  • npx tsc --noEmit in lifecycle-service: clean except the pre-existing server.test.ts syntax errors (identical before/after this PR).

Closes #287

@drips-wave

drips-wave Bot commented Aug 30, 2026

Copy link
Copy Markdown

@onuibeblessing2019-hash 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

@vercel

vercel Bot commented Aug 30, 2026

Copy link
Copy Markdown

@onuibeblessing2019-hash is attempting to deploy a commit to the david's projects Team on Vercel.

A member of the Team first needs to authorize it.

@onuibeblessing2019-hash

Copy link
Copy Markdown
Author

closes #287

@davedumto
davedumto merged commit a844f38 into Vellar-Wallet:dev Aug 30, 2026
0 of 2 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.

2 participants