openlang

Choose the failure lifecycle before the syntax

Partly reviewed

Suppose a small Python application fetches measurements and weights concurrently, then combines them into one score. One fetch raises an exception while the other is still running. Before comparing how languages spell concurrent work, which lifecycle should this calculation require?

I would first decide whether a failed fetch makes the whole calculation unusable. With asyncio.TaskGroup, a member failure other than CancelledError causes the remaining members to be cancelled, and the group waits for its tasks before finishing its exit. Calling a coroutine function alone does not schedule that work. The fetches need to become tasks in the group.

Next comes a different readiness decision. If both tasks return normally but the weights are unsuitable, collection has completed without establishing that calculation should begin. Should the application reject those inputs, request replacements, or produce a partial result? TaskGroup does not choose that policy.

I would also check what caller cancellation should do and whether fetch cleanup preserves cancellation by propagating CancelledError. Swallowing it can disrupt the group's cancellation behavior.

For this particular application, I would choose TaskGroup if either fetch failing should end the combined request and the scope must wait for its members. Then I would validate the returned data before calculating. Those requirements give a language comparison something concrete to preserve.

The conversation continues 4

Follow the reasoning
  1. Permalink

    What happens if we keep the measurements fixed but replace the weight fetch's exception with a normal return of []? That no longer triggers TaskGroup's failure cancellation. Now change the measurements to an empty collection too. Perhaps the same empty weights represent a legitimate empty calculation. The interesting boundary is whether those two empty results mean missing required data or simply no work. I would settle that distinction before treating every unsuitable result as a task failure.

  2. Permalink

    There are only two fetches here. That already bounds this example's work population, so I would not add a worker pool just to make the design look controlled.

    Follow the remaining fetch instead. Its sibling fails, the group requests cancellation, and the group still waits for its members. A cancellation request is not evidence that the fetch has finished. The useful requirement is that the combined request cannot leave its scope while member work remains unfinished. If partial results matter, state separately whether they may be used after the group reports failure.

  3. Permalink

    The word coroutine does not settle the handoff. In Lua 5.4, creation leaves a coroutine unstarted, and the host drives it through resume and explicit yields. For your Python calculation, I would compare facilities against TaskGroup's waiting and failure cancellation requirements before letting similar vocabulary suggest a shared lifecycle.