Urgent.News

What's breaking now, across thousands of outlets.

Tech

Why we stopped retrying on timeout and built reconciliation instead

Early in StareBrain’s action dispatch layer, we had a simple rule: if an action times out, retry it. It felt safe. It felt standard. It was wrong. The problem isn’t the retry. The problem is what a timeout actually tells you — which is almost nothing. What a timeout means (and doesn’t mean) When a dispatch call times out, you know one thing: you didn’t get a response in time. That’s it. You don’t…

StareBrain, an action dispatch layer, previously had a simple rule: if an action timed out, retry it. However, this approach proved to be wrong. A timeout only indicates that a response wasn't received in time; it doesn't provide any other information about the action's status. Retrying in this state doubles the number of dispatches in flight without resolving the ambiguity. This could lead to multiple bookings, charges, or messages being created for a single user request.

To address this issue, the team stopped treating timeout as a signal to retry and began treating it as a signal to reconcile. They introduced a state model with two axes: `DeliveryState` and `AcceptanceState`. Delivery tracks whether the action reached the provider and received a response, while Acceptance tracks whether the provider processed the action correctly. These are independent, with Delivery confirming the provider's response and Acceptance verifying the provider's correct processing.

When an action times out, its Delivery state remains PENDING, indicating that the action hasn't been confirmed yet. The team assigned a new status, `DENIED_UNRESOLVED`, to actions that cannot be confirmed programmatically. This status never changes to success due to a timeout; it remains unresolved until a human review or a successful reconciliation pass clears it.

The team recognized that some providers have replication lag, meaning that a record written during a slow dispatch might not be queryable for several seconds after the call returns. To handle this, they introduced a concept called `eligibleForReconciliationAt`, which is set at dispatch time based on a consistency window duration. Reconciliation jobs only run after this gate, ensuring that the provider's query surface is used correctly.

Providers are categorized into different capability tiers based on their query capabilities. FULL supports idempotency keys and query endpoints, allowing for full reconciliation. RECONCILABLE providers have a query endpoint but no idempotency key support. PARTIAL providers only return artifact IDs, while BLIND providers offer no reconciliation possibility, requiring human review instead.

The switch from retry-on-timeout to reconciliation-on-timeout resulted in zero duplicate actions from network blips, a clear paper trail for unresolved actions, and provider integrations graded by their actual capabilities. Although this approach requires more infrastructure, state management, and explicit handling of ambiguity, it ultimately provides a more reliable and correct solution compared to pretending timeouts mean failures and retrying.

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

How to preview a coding benchmark run before spending on model calls

Use the --dry-run option to inspect a benchmark plan before launching model calls. In Weave Router's public weave-bench harness, the option resolves the pinned task manifest and prints the Harbor…

  • Use --dry-run option to preview benchmark plan without network impact.
  • --smoke selects smaller task set but may initiate jobs and incur costs.
  • Verify task selection, pinned versions, and comparison arms before removing --dry-run flag.

Building a Trading Engine in Java: Beyond BUY and SELL Signals

Generating a BUY or SELL signal is relatively straightforward. Building a system that can reliably execute that signal is a different engineering problem.

  • A trading signal and trade are distinct concepts in the NERV Trade engine.
  • Market analysis and broker execution are separated into distinct responsibilities.
  • Position and trade are separate entities, with trade owning its exposure.

i wrote a cookie policy for a product that sets no cookies

most cookie policies are confessions written by lawyers. mine is a confession written by me. i’m om yaduvanshi, 18, an applied ai developer building cloudline , a cookieless analytics tool.

  • 18-year-old developer Om Yaduvanshi created Cloudline, a cookieless analytics tool.
  • Cloudline's cookie policy page exists despite no cookies being set, to avoid appearing negligent.
  • The policy outlines four data points collected: page, referrer, screen size, and time.

Pushing Beats Polling Explained: Live Poll Trust Above a Handful of Viewers

Short answer: pushing beats polling above a handful of viewers because one state change should not trigger repeated reads for every screen, but treat delivery as an optimization, not as authorization.

  • Pushing poll results, not polling, reduces unnecessary load and scaling issues with more viewers
  • Trust must be established independently of transport layer for untrusted clients
  • Small, immutable poll state snapshots enable reconnects with correct, up-to-date view

How I built a live halftone portrait in React with just Canvas 2D

My portfolio is set like a newspaper, so my photo couldn't just be a photo. I wanted it to look like a printed halftone: a grid of dots, bigger where the picture is dark and smaller where it's light.

  • Image transformed into low-resolution grid of dots using Canvas 2D API
  • Dot radius determined by sample darkness with applied tone curve
  • Cursor interaction causes dots to swell and recede in a breathing effect

My users table reads one pagination contract: paginate(15), withQueryString, and a React pager that trusts meta

The first admin panel I built fetched all users, dumped them into state, and sliced pages client-side. It worked until a client hit 2,000 users and the list page took eleven seconds to load.

  • Implemented server-side pagination with Laravel's paginate(15) for 2,000+ users
  • React pager relied on backend meta data, fetching users based on page and meta
  • Consistent pagination contract extended to all admin panels in Laravel + React SaaS Starter Kit

More from Saturday 17 October →