Urgent.News

What's breaking now, across thousands of outlets.

Tech

My refund handler checked the ledger before paying. It still paid twice.

A refund handler commits the refund, then loses its reply. Maybe the HTTP response timed out. Maybe the worker died before it acknowledged the queue message. Maybe it was an AI agent's tool call, and the agent saw a timeout and called the tool again with the same arguments. The sender did nothing wrong by retrying. The question is what your handler does with the second delivery. The usual fix is…

Refund handlers must first check the ledger before issuing a refund. However, they may still end up paying twice due to various reasons such as timeout, lost acknowledgement, or repeated tool calls. To test this issue, a pytest plugin was created that delivers the same event multiple ways and counts how many times the effect lands in a shared ledger.

Four different handlers were tested: naive, check_then_act, guarded_raises, and guarded. The naive handler paid twice in every scenario, while the others paid correctly only once out of 200 trials. The webhook flow and an async version of the refund tool also yielded the same verdicts. Although reading the ledger before writing handles most re-deliveries, it fails when overlapping deliveries occur.

The only foolproof solution is to use a unique constraint on the key, as it enforces the desired behavior. However, this alone is not enough, as the handler still needs a pending and a done state to handle crashes between the claim and the API call. The drill doesn't cover real-world scenarios like using a database transaction for the refund call, but it still provides valuable insights into the issue.

Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.

Read the original at dev.to →

More in Tech

Traefik or Caddy? Choose a Reverse Proxy by How You Deploy

Adding a service behind a reverse proxy should be routine. But the workflow changes depending on whether routes live in one proxy config or are discovered from your containers.

  • Caddy is simpler for small, stable site configurations
  • Traefik suits frequently changing Docker services
  • Routing defined differently: Caddy explicitly, Traefik via metadata labels

Filter tcpdump by IP: Hosts, Direction, Subnets, and Ports

When a packet capture is full of traffic you don't care about, narrow it with a capture filter . For an IP address, the key distinction is whether you want traffic in both directions, only packets…

  • Use host keyword to filter by IP address in tcpdump
  • Apply net keyword with CIDR notation for subnet filtering
  • Combine host and tcp port numbers for port-based filtering

Node.js Feature Flag Rollouts: Stable Bucketing for 10% Notification Releases

Short answer: store the rollout percentage in the flag system, but make the release decision in the request path with a deterministic hash of an immutable user or account ID.

  • Store rollout percentage in flag system, decide release in request path
  • Use deterministic hash of immutable ID for consistent tenant delivery
  • Implement Go-based SHA-256 hashing to create 10,000 stable buckets

More from Tuesday 6 October →