Skip to content

fix: invalidate full cache key set after write handlers commit - #946

Merged
fejilaup-cloud merged 1 commit into
AtomicIP:mainfrom
Prasiejames:fix/stale-cache-invalidation-after-writes
Aug 28, 2026
Merged

fix: invalidate full cache key set after write handlers commit#946
fejilaup-cloud merged 1 commit into
AtomicIP:mainfrom
Prasiejames:fix/stale-cache-invalidation-after-writes

Conversation

@Prasiejames

@Prasiejames Prasiejames commented Aug 28, 2026

Copy link
Copy Markdown

Closes #849
Closes #850
Closes #851
Closes #852

Summary

The Redis-backed cache (#786) sits in front of the read handlers (get_ip, get_swap, list_ip_by_owner, ...), but the write handlers (transfer_ip, accept_swap, reveal_key, cancel_swap, cancel_expired_swap) invalidated only the single record key and did so before the write. Both defects become correctness bugs the moment reads are wired to Soroban RPC (#37, #46) and start repopulating the cache from chain state:

  1. List caches went stale. A transfer changes owner-list membership (ip:list:*); accept/reveal/cancel change seller/buyer list membership (swap:seller:*, swap:buyer:*). Only the ip:{id} / swap:{id} keys were cleared, so cached list pages served stale results for the full TTL (30–60s).
  2. Read-repopulate race. Invalidation ran before the RPC write. A read that misses the cache during the write window can fetch pre-write state from the chain and repopulate the cache after the write commits — serving stale data until TTL expiry. Post-write invalidation clears exactly that window.

Changes

  • api-server/src/cache.rs
    • New invalidate_ip(ip_id) helper — clears ip:{id} + all ip:list:* pages.
    • New invalidate_swap(swap_id) helper — clears swap:{id} + swap:seller:* + swap:buyer:* pages.
    • invalidate_on_contract_event now delegates to these helpers (behavior unchanged — event-driven invalidation is inherently post-commit).
    • Two new unit tests pinning record + list-prefix coverage (with unrelated-key survival).
  • api-server/src/handlers.rs
    • transfer_ipinvalidate_ip(ip_id) after the RPC call.
    • accept_swap, cancel_swap, cancel_expired_swapinvalidate_swap(swap_id) after the RPC call.
    • reveal_keyinvalidate_swap(swap_id) + clears reputation:* (a completed swap updates both parties' reputation), after the RPC call.

Testing

  • New unit tests: test_invalidate_ip_clears_record_and_owner_lists, test_invalidate_swap_clears_record_and_lists (cargo test --lib cache).
  • Existing cache tests (test_invalidate_on_contract_event_*) continue to pass unchanged.
  • Note: cargo was unavailable in the authoring environment, so the suite could not be executed there; the Redis integration tests (cache_redis_cross_instance, cache_redis_fallback) are unaffected in behavior.

Related

Write handlers (transfer_ip, accept_swap, reveal_key, cancel_swap,
cancel_expired_swap) invalidated only the single record key before the
RPC write, leaving owner/seller/buyer list caches stale and opening a
read-repopulate race once get_ip/get_swap are wired to RPC (AtomicIP#37, AtomicIP#46).

Add compound invalidate_ip/invalidate_swap helpers covering the record
plus list prefixes, call them post-write in every write handler, and
reuse them in invalidate_on_contract_event. reveal_key also clears the
reputation cache since a completed swap updates both parties' scores.

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
@drips-wave

drips-wave Bot commented Aug 28, 2026

Copy link
Copy Markdown

@Prasiejames 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

@fejilaup-cloud
fejilaup-cloud merged commit 62a7fbf into AtomicIP:main Aug 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants