wevm / wevm/mppx

Subscription activation can remain permanently in flight after a crash during commit

Open
#674 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
176
Forks
66
Avg merge
1d 1h
Merged PRs (30d)
52

Description

Context

SubscriptionStore.activate() writes a non-expiring committingAt marker before persisting the subscription record and lookup index.

This protects a settled activation from being replaced by another activation, but a process exit during the following multi-key commit leaves the lookup locked indefinitely.

Reproduction

I added deterministic fault injection at two boundaries:

  1. Writing the subscription record
  2. Writing the lookup index

Reproduction commit: cc1e84b

In both cases:

  • create() completes and the committingAt marker is persisted.
  • The selected store write fails.
  • A new store wrapper simulates a process restart.
  • activationTimeoutMs is set to 0.
  • Multiple fresh challenge IDs still return inFlight.
  • create() is never called again.
  • The original committing marker remains.

A record-write failure leaves no record or lookup. A lookup-write failure leaves an orphaned record with no resolvable lookup.

Why not simply expire the marker?

create() may already have completed the first-period payment. Clearing or expiring the marker can let another challenge call create() and charge again.

Persisting the result in the marker only covers failures after that write. It does not cover a crash after the external payment succeeds but before the result is persisted, especially for custom activation hooks.

Design question

What recovery guarantee should the store provide once settlement may have happened?

A safe implementation appears to require:

  • A constrained, versioned activation journal
  • Authoritative reconciliation when settlement may have succeeded
  • CAS-based record and lookup materialization
  • Fail-closed handling for conflicting lookup ownership
  • Explicit handling for legacy committing markers

For automatic Tempo activation, could reconciliation use the challenge attribution written onchain? Should custom activation hooks receive an idempotency or reconciliation contract?

I have deterministic tests ready and can implement the agreed recovery semantics once this boundary is decided.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with SubscriptionStore.activate() and the deterministic fault-injection reproduction in commit cc1e84b. Review the failures at the subscription-record and lookup-index write boundaries, including the restart behavior with activationTimeoutMs set to 0. Done requires agreed recovery semantics for committing markers and tests covering the observed orphaned and permanently in-flight states.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
payments
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.