The __destruct that ran after the response was sent

A profiler showed 140 ms of work happening after the response had been flushed, which is invisible to the user and entirely visible in the worker’s throughput.

final class BatchWriter
{
    private array $pending = [];

    public function __destruct()
    {
        $this->flush();   // 140 ms of inserts, at shutdown
    }
}

// which is fine under php-fpm and wrong in a worker:
//   fpm      the request has ended, the connection closed
//   worker   the next job is waiting, and this is on its clock

Destructor-based flushing is a reasonable pattern under a request lifecycle that ends with the process, and a bad one anywhere the process is reused. Under a queue worker the destructor runs when the object is collected, which is somewhere in the middle of the next job, and the time is attributed to whatever happened to be running. Making the flush explicit and calling it from a terminating hook moved the cost to a place a graph could see.