// Package main is the entry point for the webhooker application. package main import ( "time" "go.uber.org/fx" "sneak.berlin/go/webhooker/internal/config" "sneak.berlin/go/webhooker/internal/database" "sneak.berlin/go/webhooker/internal/delivery" "sneak.berlin/go/webhooker/internal/globals" "sneak.berlin/go/webhooker/internal/handlers" "sneak.berlin/go/webhooker/internal/healthcheck" "sneak.berlin/go/webhooker/internal/logger" "sneak.berlin/go/webhooker/internal/middleware" "sneak.berlin/go/webhooker/internal/server" "sneak.berlin/go/webhooker/internal/session" ) // stopTimeout bounds the whole fx stop sequence, not each hook. // // fx defaults to 15s, which is longer than Docker's 10s default // stop grace: the container would be SIGKILLed before the bound // could fire, so nothing bounded by it would ever be observed. // 5s leaves headroom inside that grace for signal delivery and // process exit; the observed wedge case already exits at ~5.3s, // so a larger bound would trade a rare skipped database close for // a more common hard kill. // // The server's stop hook must fit inside it with room to spare: a // hook that used the whole budget would exhaust it at that instant, // and fx would skip every hook after the server — the delivery // engine, the healthcheck, the webhook DB manager and the database // close. That hook is the 3s HTTP drain plus the Sentry flush that // follows it in the same hook, so the flush is clamped to the stop // context's remaining time less server.TailHookReserve rather than // running for its own fixed 2s; the reserve is what the tail hooks // live on, and they are microsecond-scale in normal operation. // TestStopTimeout_LeavesHeadroomForTailHooks pins the arithmetic // across every drain length. // // This does not make the database close unconditional: the // ArchiveSweeper and RetentionReaper hooks run before the server // and can still consume the whole budget on their own. const stopTimeout = 5 * time.Second // Build-time variables set via -ldflags. // //nolint:gochecknoglobals // Build-time variables injected by the linker. var ( version = "dev" appname = "webhooker" ) func main() { globals.Appname = appname globals.Version = version newApp().Run() } // newApp builds the application graph. It is separate from main so // a test can assert the options it carries. func newApp() *fx.App { return fx.New( fx.StopTimeout(stopTimeout), fx.Provide( globals.New, logger.New, config.New, database.New, database.NewWebhookDBManager, database.NewRetentionReaper, healthcheck.New, session.New, handlers.New, middleware.New, delivery.New, delivery.NewArchiveSweeper, // Wire *delivery.Engine as delivery.Notifier so the // webhook handler can notify the engine of new deliveries. func(e *delivery.Engine) delivery.Notifier { return e }, // Wire *delivery.Engine as delivery.WebhookEvictor so // deleting a webhook releases its archive writer. func(e *delivery.Engine) delivery.WebhookEvictor { return e }, server.New, ), fx.Invoke( func( *server.Server, *delivery.Engine, *database.RetentionReaper, *delivery.ArchiveSweeper, ) { }, ), ) }