The valet's location queue: saved first, sent second; offline waits, refused is dropped; ten tests.

You've handed your car keys to a stranger. That's what a valet is. They drive off to park it, and the only thing between you and mild panic is a dot on a map, moving.

Then the valet drives into a basement. Or under a flyover. Or into one of the many parts of Bangalore where 4G just... isn't.

The dot stops.

Yesterday I asked what happens next. Here's the answer, and it's the most careful forty lines in the whole app.

Why the obvious version loses data

The valet app reports a location fix every few seconds, from a background task. The obvious code is: on each fix, send it to the server.

That works right up until the send fails. The background task runs in a headless context where the connection may not even be up. A fire-and-forget send from there doesn't throw anywhere you can see. The fix is just gone, and the route on the driver's map has a hole in it exactly where they were most worried.

Saved first, sent second

So nothing is ever sent before it's stored. Every fix goes into a small on-phone queue, and then the queue tries to drain:

async enqueue(fix) { const queued = ordered([...(await read()), fix]).slice(-maxFixes); await write(queued); 

It still sends immediately. My first version enqueued and waited, which meant nothing reached the server between going online and going offline, and the valet's "last seen" went stale within a minute. The queue is for when a send fails, not a substitute for trying.

The queue has a ceiling, because a phone in a long tunnel shouldn't fill its storage with coordinates:

/** Roughly 15 minutes at the configured interval. */ export const MAX_QUEUED_FIXES = 200; 

Past that, the oldest fixes go and the newest always stay. Where the car is now matters more than where it was a quarter of an hour ago.

Offline waits. Refused is dropped.

Here's the subtle part. A send can fail for two very different reasons:

  • Offline. Worth trying again. Stop here, keep everything, in order.
  • Rejected. The server said no: say, a fix for a job this valet isn't assigned to. That will be a no forever. Retrying it would block every good fix queued behind it.
if (result.ok) continue; // A refused fix is consumed; an unsendable one halts the drain so the // rest keep their order for the next attempt. if (result.reason === 'rejected') continue; break; } await write(queued.slice(index)); 

And when the signal comes back, the backlog replays oldest first, so the driver's map redraws the route in the order it was actually driven:

/** Oldest first, so a drain replays the route in the order it was travelled. */ function ordered(fixes: readonly LocationFix[]): LocationFix[] { return [...fixes].sort((a, b) => a.recordedAt - b.recordedAt); 

Two more things the queue survives: two drains racing each other (only one runs at a time, or the same backlog gets sent twice), and a corrupted stored value (the queue starts empty instead of crashing a background task nobody is watching).

Ten tests

Every one of those decisions has a test: send on capture, hold on failure, oldest first, the bound, refused-versus-offline, stop at the first unsendable fix, a corrupt store, a single malformed entry. Ten tests, all passing, and none of them need a phone.

What I'd tell myself before starting

"Offline" isn't an error to handle. It's a state the app spends real time in. Design for it first, and the happy path falls out for free.

Tomorrow: you've seen what happens in the tunnel. Tomorrow, the valet's side of the screen: taking a job, every step one tap, and the photo of the parked car that has to exist before the job can be confirmed. Follow the series.

ParkEase is a peer-to-peer parking marketplace for India, built solo and launching soon on Android.
Code: https://github.com/Deonkar/parkease · Music: ende.app (CC BY 4.0)