Supervised fire-and-forget in Go

These days, unmanaged go func() calls don’t appear as often as they used to in the early days of Go.

At this point, everyone knows Dave Cheney’s maxim to “never start a goroutine without knowing how it will stop”. Even so, I still occasionally run into unsynchronized go func() calls doing fire-and-forget work.

I’m not talking about jobs handed to a dedicated task queue such as Asynq. In that case, the task system owns the lifecycle of the tasks it starts. I mean smaller, best-effort jobs where it’s super tempting to start a task with go func() and just forget about it. Sending a notification or writing an expensive diagnostic log often falls into this category.

It typically looks like this:

func ( h handler ) createOrder ( w http. ResponseWriter, r http. Request ) { user:= r. URL. Query (). Get ( "user" ) ctx:= r. Context ()

go func () { H. tasks. SendNotification ( ctx, user ) // (1) }() go func () { H. tasks. WriteDiagnosticLog ( ctx, user ) // (2) }()

W. WriteHeader ( http. StatusAccepted ) }

In the handler above:

(1) sends a notification in a background goroutine

(2) writes a diagnostic log in another background goroutine

The response doesn’t wait for either one. On a long-running server this looks reasonable, as main will most likely outlive both calls. But this suffers from a few issues:

every request can start two more goroutines, with no limit on active work

both tasks get r.Context(), which net/http cancels when the handler returns. If the jobs are cancellation aware (ideally they should be), they can bail before they’re done

a panic in either goroutine crashes the whole process, because nothing recovers it

main has no way to wait for either task during shutdown. A restart kills whatever is still running

Every task goes through one worker pool #

One fix I’ve been using is a small worker pool backed by a buffered channel:

each background task is sent to the channel as a func() closure

workers started at the composition root, usually ma…

添加评论
点赞收藏
点踩分享查看原文
评论
?
参与讨论