From 8f56e7dac68097fa8b0eabae09511cc9a2f26525 Mon Sep 17 00:00:00 2001 From: Brandur Date: Thu, 16 Jul 2026 15:34:14 -0500 Subject: [PATCH] Add additional example related to `JobStuckHandler` This one follows up #1291 to add one other example test related to the fairly new `JobStuckHandler` in which we demonstrate how to dump goroutine stacks for debugging purposes. --- ...e_job_stuck_handler_goroutine_dump_test.go | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 example_job_stuck_handler_goroutine_dump_test.go diff --git a/example_job_stuck_handler_goroutine_dump_test.go b/example_job_stuck_handler_goroutine_dump_test.go new file mode 100644 index 00000000..b524b3a1 --- /dev/null +++ b/example_job_stuck_handler_goroutine_dump_test.go @@ -0,0 +1,47 @@ +package river_test + +import ( + "context" + "log/slog" + "runtime" + + "github.com/riverqueue/river" +) + +// Example_jobStuckHandlerGoroutineDump demonstrates how to capture and log a +// goroutine dump from a JobStuckHandler. +func Example_jobStuckHandlerGoroutineDump() { + logger := slog.Default() + + config := &river.Config{ + JobStuckHandler: func(ctx context.Context, params river.JobStuckHandlerParams) river.JobStuckHandlerResult { + if logger.Enabled(ctx, slog.LevelWarn) { + logger.WarnContext(ctx, "River job stuck", + slog.Int64("job_id", params.ID), + slog.String("kind", params.Kind), + slog.String("queue", params.Queue), + slog.Int("total_stuck_jobs", params.TotalStuckJobs), + slog.String("goroutine_dump", captureGoroutineDump()), + ) + } + + return river.JobStuckHandlerResult{} + }, + } + + _ = config + + // Output: +} + +func captureGoroutineDump() string { + buf := make([]byte, 64*1024) + for { + n := runtime.Stack(buf, true) + if n < len(buf) { + return string(buf[:n]) + } + + buf = make([]byte, 2*len(buf)) + } +}