Replace the ad-hoc tables/procedures install layout with versioned, ordered SQL migrations tracked in broker_schema_migrations. Add optional least-privilege role provisioning (--with-roles), multi-tenant row-level security, lease-based job claiming with stale-lease recovery, and job dependencies -- both by job id and by fan-in job group. Add Docker/Compose support for running the broker and its test suite.
57 lines
1.8 KiB
Go
57 lines
1.8 KiB
Go
package adapter
|
|
|
|
import (
|
|
"runtime/debug"
|
|
"time"
|
|
)
|
|
|
|
// RecoverAndLog recovers a panic (if any) and logs it with a stack trace.
|
|
// Call it via `defer adapter.RecoverAndLog(logger, "name")` at the top of
|
|
// any goroutine body that must never be allowed to crash the process.
|
|
func RecoverAndLog(logger Logger, name string) {
|
|
if r := recover(); r != nil {
|
|
logger.Error("recovered from panic", "component", name, "panic", r, "stack", string(debug.Stack()))
|
|
}
|
|
}
|
|
|
|
// SafeGo runs fn in a new goroutine, recovering any panic so it can never
|
|
// crash the process. Use for one-shot/fire-and-forget goroutines; a panic is
|
|
// logged (with its stack trace) and the goroutine simply ends.
|
|
func SafeGo(logger Logger, name string, fn func()) {
|
|
go func() {
|
|
defer RecoverAndLog(logger, name)
|
|
fn()
|
|
}()
|
|
}
|
|
|
|
// SupervisedGo runs fn in a new goroutine. If fn panics, the panic is logged
|
|
// and fn is restarted (after a short backoff) instead of letting the
|
|
// goroutine die permanently. Use for long-running loops (ticker routines,
|
|
// notification listeners, worker loops) that must keep running for the life
|
|
// of the process. fn must return normally, without panicking, once its own
|
|
// shutdown/context-done condition is met -- a clean return is not restarted.
|
|
func SupervisedGo(logger Logger, name string, fn func()) {
|
|
go func() {
|
|
for {
|
|
if runSupervised(logger, name, fn) {
|
|
return
|
|
}
|
|
time.Sleep(time.Second)
|
|
}
|
|
}()
|
|
}
|
|
|
|
// runSupervised runs fn once, recovering a panic if it occurs. It returns
|
|
// true if fn returned normally (no restart needed) and false if it panicked
|
|
// (caller should restart it).
|
|
func runSupervised(logger Logger, name string, fn func()) (clean bool) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
logger.Error("recovered from panic, restarting", "component", name, "panic", r, "stack", string(debug.Stack()))
|
|
clean = false
|
|
}
|
|
}()
|
|
fn()
|
|
return true
|
|
}
|