Challenge 3 — Solution Task: Extend the worker loop from this chapter to dispatch to a different function depending on the job's "type" column (using match()), supporting both 'send_email' and 'resize_image' job types, each with its own try/catch and markJobComplete/markJobFailed calls. Explain in a comment what would happen if one job type's handler threw an uncaught exception, with no try/catch around the dispatch at all. query("SELECT * FROM jobs WHERE status = 'pending' LIMIT 1"); $job = $stmt->fetch(); if ($job) { $payload = json_decode($job['payload'], true); try { match ($job['type']) { 'send_email' => sendEmail($payload['to'], $payload['subject']), 'resize_image' => resizeImage($payload['source_path'], $payload['target_width'], $payload['target_height']), default => throw new InvalidArgumentException("Unknown job type: {$job['type']}"), }; markJobComplete($pdo, $job['id']); } catch (Exception $e) { markJobFailed($pdo, $job['id'], $e->getMessage()); } } else { sleep(2); } } // What would happen with no try/catch around the dispatch at all: // If either sendEmail() or resizeImage() threw an exception (a // real, plausible outcome - a mail server timeout, a corrupted // image file) and there were no try/catch wrapping the match() // call, the exception would propagate all the way up and out of // the while(true) loop entirely, since nothing in this script // would catch it. This would crash the ENTIRE worker process, not // just fail the one problematic job - every other pending job in // the queue (including completely unrelated, perfectly healthy // ones) would then sit unprocessed indefinitely, since the worker // that was supposed to keep polling for them is no longer running // at all. This is exactly the reasoning behind the chapter's own // warn-box: a failed job must be handled deliberately (caught, // marked 'failed', and the loop allowed to continue) rather than // allowed to silently (or catastrophically) take down the whole // background-processing system. ?> Notes: - match($job['type']) dispatches to the correct handler based purely on the job's own stored type column, with a default arm throwing a clear InvalidArgumentException for any genuinely unrecognised type - reusing the exact match()-with-a-default-throw pattern from Chapter 2's own NotificationFactory example. - Both sendEmail() and resizeImage() are called inside the SAME try/catch block here, rather than each needing its own separate try/catch - since match() itself already selects exactly one branch to run, a single surrounding try/catch correctly covers whichever one actually executes for a given job. - markJobComplete()/markJobFailed() are called exactly once per loop iteration, immediately after the try/catch resolves either way - this keeps the worker's own job-status bookkeeping consistent regardless of which job type or outcome occurred.