Debarshi Das

Work, original

allot

A mutual fund order engine in Go that changes each order once, while payment gateways, exchanges, and registrars retry, reorder, and go silent.

Role
Designer and developer
With
On my own
Status
Active
Years
App, gateway and registrar messages change one order once; two duplicates stop in ember; a relay submits to the exchange.Hover a part to see its name
allot: three outside systems feed one order state machine, and a relay talks to the exchange.

The problem

An investor taps Invest on a weak network. One purchase now crosses four systems that share no transaction: the app, the payment gateway, the exchange order platform, and the registrar that allots units. Each one delivers messages at least once and in any order. The failures an investor sees are two orders for one tap, and money taken with no units allotted.

allot is a backend that prevents both. Delivery between systems stays at least once; the effect on an order happens once.

What I built

  • A pure state machine. order.Apply takes an order and an event and returns the next order, its ledger postings, and its outbox messages. It performs no I/O, so one transaction writes all of it.
  • Idempotency keys on order placement, per the IETF Idempotency-Key draft: a retry gets the stored response, a different body with the same key gets 422, and a request whose key is in use gets 409.
  • An inbox that applies each gateway webhook once, keyed by event ID.
  • A transactional outbox with leases and FOR UPDATE SKIP LOCKED, so a relay that dies mid-send loses nothing.
  • A timeout state. When the exchange does not answer, the order waits in submit_uncertain and the relay asks for its status under the same reference.
  • Reconciliation of the registrar's file, which checks units against amount over the net asset value (NAV) and the NAV date against the cut-off rules before it applies a row.
  • A chaos harness that runs all of it against fakes that duplicate, delay, reorder, drop, and go silent, and then checks six guarantees.

Evidence

Fifty chaos runs of 200 orders each, every fault on, placed 10,000 orders through 30,446 HTTP requests. The gateway delivered 28,893 webhooks, duplicates included. The exchange received 9,410 submissions, retries included, and the check for exchange orders under unknown references found none. No run broke a guarantee.

The harness also catches the bugs it is built to catch. The repository plants two of them behind build tags. In one run of 150 orders, a new exchange reference on every retry produced 232 violations, and a timeout treated as a rejection produced 17 refunds for orders the exchange had accepted. Every run of either bug fails.

These numbers come from one laptop: an AMD Ryzen AI 7 350, PostgreSQL 18 in Docker, and Go 1.27.

Decisions

  • A timeout is not a failure. Refunding on a timeout returns money for orders the exchange may have accepted. Resending under a new reference creates a second order.
  • Evidence moves an order forward. Units in the registrar's file prove acceptance, even if that message never came.
  • Contradictions go to a person. An event that conflicts with what an order already knows opens a finding and changes nothing.
  • Plain SQL, no ORM. The queries are the subject of the project.

The repository records each decision with the alternative it rejected. The essay Effectively once explains each technique with two interactive figures.

My role

I designed and developed allot: the scope, the six guarantees, the failure modes, the code, the tests, and the chaos harness.

Current limits

  • The exchange, gateway, and registrar are fakes, modeled on public descriptions of these systems, not on private APIs or file formats.
  • It covers purchases only. Redemptions follow different rules.
  • It describes no company's systems.

Go, PostgreSQL, Distributed systems, Payments

Choose a style to copy