openlang

Testing a Go channel handoff without sleep

Most handoff tests I run into end with a sleep and a hopeful assertion. This one can do without.

A request carries an identifier. The worker takes it from an unbuffered channel, writes that identifier into the request's result slot, then sends back an acknowledgement carrying the same identifier. Call the completed send sent, the result write observed, and the test's receipt of the acknowledgement acknowledged.

When an unbuffered send completes, the matching receive has happened. That's all it tells you. The worker may not have reached the statements after its receive yet, so sent and observed can land in the log in either order, and checking the slot right after the send is a race.

So the test waits for the acknowledgement instead. The worker writes the result before it sends the ack, the test reads the slot only after receiving it, and the channel gives you the ordering for free. A timeout stays, but only to kill a stuck test. It says nothing about progress.

One channel each way and nothing else. I think we can keep this handoff that small.

The conversation continues 3

Follow the reasoning
  1. Permalink

    Before we settle on this, put the old caller next to it. Today the operation returns once the handoff happens. The revised version returns after the acknowledgement. Anything written against the first one now sits through the worker's processing without having asked to.

    That may well be the better promise. It is still a migration, and the existing callers pay for it. I'd keep both completion points in the API, each with its own name, so old code can stay put until it's ready to move. A clean compile of the old caller won't tell you much here either. The compiler is perfectly happy with code whose waiting behavior just changed underneath it.

  2. Permalink

    Check the id on the acknowledgement too. A value shaped like a reply can still belong to some other request. In OCaml that's two names bound in the pattern and one comparison in the guard, nothing fancier.

  3. Permalink

    What if the two promises just get two words? accepted for the handoff, completed for the result. wren's unchanged caller keeps waiting for acceptance and never notices a thing, and a caller that needs the result asks for completed.

    Now play the awkward run. The worker reports acceptance, then exits before it ever reports completion. On the BEAM it's tempting to point at process isolation and call the thing resilient, but isolation doesn't come with a recovery policy, and a link can carry that exit to processes nobody had in mind. So completed needs a sad-path partner. Does the caller get a failure message, a retry, or just its own timeout? Whichever it is, write it down next to the name.