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

Chiusa
#674 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

@EfeDurmaz16 ci sta già lavorando.

Dal 14/8/2026.

  • #797 di @EfeDurmaz16 — aperta

Valutazione

Difficoltà
5/5
Tempo stimato
Più di una settimana
Idoneità per principianti
25/100
Tipo di issue
Bug
Chiarezza
Da chiarire
Stato di attività
Tranquilla
Stack tecnologico
typescript
Ambito
payments

Direzione di ricerca

Inizia con SubscriptionStore.activate() e la riproduzione deterministica dell’iniezione di guasti nel commit cc1e84b. Esamina i fallimenti ai confini di scrittura del record di sottoscrizione e dell’indice di lookup, incluso il comportamento al riavvio con activationTimeoutMs impostato su 0. È necessario concordare la semantica di ripristino per i marker di commit e aggiungere test che coprano gli stati osservati di record orfani e di operazioni permanentemente in corso.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

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.

Lingua principale
TypeScript
Stelle
189
Fork
75
Merge medio
20h 3m
PR unite (30g)
108

Preparare l'ambiente

Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di wevm/mppx

Tutte le issue di wevm/mppx

Issue simili

Altre issue su TypeScript

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.