Summary
When two wallets simultaneously buy keys from the same creator, both read the same current supply, both compute cost at that supply, and both submit. One transaction will win on-chain and the other will fail — but the server has already committed both to the database optimistically, causing supply drift. This issue implements a per-creator in-process sequencer that serialises all mutating operations for a given creator through a single ordered queue, ensuring the database always reflects the true on-chain supply sequence.
Scope
1. Per-creator operation queue
- Maintain a
Map<creatorWallet, AsyncQueue> in process memory where each queue processes one operation at a time
- The
AsyncQueue accepts operations as promises and resolves them strictly in FIFO order
- Queues are created lazily on first use and garbage-collected after 5 minutes of inactivity to prevent unbounded memory growth
2. Request enqueuing
- When a buy or sell request arrives, enqueue it to the creator's queue before any database read or Soroban call
- The operation acquires the creator's current supply from the database inside the queue (not before enqueuing) so it always reads the post-previous-operation supply
- If a request waits in the queue for more than 10 seconds, dequeue it and return 503
sequencer_timeout
3. Cross-instance coordination
- In a multi-instance deployment, a distributed Redis lock per creator (
seq_lock:{creatorWallet}) must be held before processing any operation from the local queue
- Lock TTL: 15 seconds, renewed every 5 seconds while the operation is in flight
- If the lock cannot be acquired within 8 seconds, return 503
sequencer_contention
- Lock released immediately after the operation completes or fails
4. Supply verification
- After each confirmed on-chain transaction, compare the expected supply (pre-operation supply + delta) against the actual supply returned by the Soroban
get_total_supply view
- If they diverge, emit an error-level log
supply_drift_detected with expected and actual values and halt further operations for that creator until an operator clears the drift flag
- Drift flag stored in Redis:
drift:{creatorWallet}; cleared via POST /internal/sequencer/clear-drift/:creatorWallet
5. Integration tests
- Simulate 20 concurrent buy requests for the same creator — assert all succeed in order with no supply drift
- Simulate a sequencer timeout (slow operation) — assert the queued request returns 503
sequencer_timeout
- Simulate a Redis lock contention scenario — assert the losing instance returns 503
sequencer_contention
- Inject a supply mismatch after a confirmed transaction — assert the drift flag is set and subsequent operations return 503
Acceptance Criteria
ETA: 24 hours
Coordinate on Telegram
Summary
When two wallets simultaneously buy keys from the same creator, both read the same current supply, both compute cost at that supply, and both submit. One transaction will win on-chain and the other will fail — but the server has already committed both to the database optimistically, causing supply drift. This issue implements a per-creator in-process sequencer that serialises all mutating operations for a given creator through a single ordered queue, ensuring the database always reflects the true on-chain supply sequence.
Scope
1. Per-creator operation queue
Map<creatorWallet, AsyncQueue>in process memory where each queue processes one operation at a timeAsyncQueueaccepts operations as promises and resolves them strictly in FIFO order2. Request enqueuing
sequencer_timeout3. Cross-instance coordination
seq_lock:{creatorWallet}) must be held before processing any operation from the local queuesequencer_contention4. Supply verification
get_total_supplyviewsupply_drift_detectedwith expected and actual values and halt further operations for that creator until an operator clears the drift flagdrift:{creatorWallet}; cleared viaPOST /internal/sequencer/clear-drift/:creatorWallet5. Integration tests
sequencer_timeoutsequencer_contentionAcceptance Criteria
sequencer_timeoutETA: 24 hours
Coordinate on Telegram