Skip to content

Implement a write-ahead log (WAL) for all Soroban transaction submissions to guarantee exactly-once execution and full crash recovery #756

Description

@Chucks1093

Summary

The current buy/sell flow submits a Soroban transaction and immediately updates the database. If the server crashes between submission and the database write, the transaction may have executed on-chain but the database reflects the pre-transaction state — leading to permanent inconsistency. This issue implements a write-ahead log that guarantees the server can always recover to a consistent state after a crash, even mid-submission.

Scope

1. WAL table

  • Add a SorobanWALEntry table: id, operation (buy/sell), wallet, creatorWallet, amount, expectedSupplyBefore, xdrPayload, state (PENDING | SUBMITTED | CONFIRMED | FAILED | ROLLED_BACK), txHash, submittedAt, confirmedAt, error, createdAt
  • Write the WAL entry with state PENDING inside the same database transaction that validates the request — before any Soroban call is made
  • Only proceed to Soroban submission after the WAL entry is durably written

2. Submission and confirmation

  • After WAL entry is written, submit the Soroban transaction and update the WAL entry to SUBMITTED with the txHash
  • Poll Horizon for the transaction result; on confirmation update to CONFIRMED and apply all database side effects (balance update, holder count, price snapshot, transaction history) in a single atomic database transaction
  • On submission failure: update WAL entry to FAILED with the error, roll back any optimistic state

3. Recovery worker

  • On server startup, scan for WAL entries in PENDING or SUBMITTED state older than 30 seconds
  • For PENDING entries: the transaction was never submitted — mark as ROLLED_BACK
  • For SUBMITTED entries: query Horizon for the txHash result; if confirmed apply side effects and mark CONFIRMED; if failed mark FAILED; if still pending re-poll up to 10 times with exponential backoff before marking FAILED
  • Recovery worker runs once at startup and then every 60 seconds

4. Idempotency guard

  • Before applying database side effects from a confirmed transaction, check whether a WAL entry for that txHash has already been applied — if so skip (idempotent confirmation)
  • Apply all side effects in a single database transaction with the WAL state update to CONFIRMED as the last operation so a crash during side-effect application results in a re-applied confirmation on next recovery

5. Integration tests

  • Simulate a crash between WAL PENDING write and Soroban submission — run recovery worker — assert WAL entry is ROLLED_BACK and no side effects applied
  • Simulate a crash between SUBMITTED and CONFIRMED database write — run recovery worker — assert side effects are applied exactly once
  • Submit two requests with the same idempotency key — assert only one WAL entry and one set of side effects
  • Confirm the final database state after recovery matches what a crash-free execution would produce

Acceptance Criteria

  • WAL entry written before any Soroban call
  • PENDING entries on startup rolled back by recovery worker
  • SUBMITTED entries resolved against Horizon by recovery worker
  • All side effects applied atomically with WAL state transition
  • Idempotency guard prevents double-application of confirmed transactions
  • Database state after crash recovery identical to crash-free execution

ETA: 24 hours


Coordinate on Telegram

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardThird CampaignCampaign: Third Campaign

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions