fix: delivery engine nil pointer crash on startup (closes #17)

Store the *database.Database wrapper instead of calling .DB() eagerly
at construction time. The GORM *gorm.DB is only available after the
database's OnStart hook runs, but the engine constructor runs during
fx resolution (before OnStart). Accessing .DB() lazily via the wrapper
avoids the nil pointer panic.
This commit is contained in:
clawbot
2026-03-01 16:34:16 -08:00
parent 7f8469a0f2
commit d4fbd6c110

View File

@@ -12,7 +12,6 @@ import (
"time"
"go.uber.org/fx"
"gorm.io/gorm"
"sneak.berlin/go/webhooker/internal/database"
"sneak.berlin/go/webhooker/internal/logger"
)
@@ -46,18 +45,18 @@ type EngineParams struct {
// Engine processes queued deliveries in the background.
type Engine struct {
db *gorm.DB
log *slog.Logger
client *http.Client
cancel context.CancelFunc
wg sync.WaitGroup
database *database.Database
log *slog.Logger
client *http.Client
cancel context.CancelFunc
wg sync.WaitGroup
}
// New creates and registers the delivery engine with the fx lifecycle.
func New(lc fx.Lifecycle, params EngineParams) *Engine {
e := &Engine{
db: params.DB.DB(),
log: params.Logger.Get(),
database: params.DB,
log: params.Logger.Get(),
client: &http.Client{
Timeout: httpClientTimeout,
},
@@ -110,7 +109,7 @@ func (e *Engine) run(ctx context.Context) {
func (e *Engine) processPending(ctx context.Context) {
var deliveries []database.Delivery
result := e.db.
result := e.database.DB().
Where("status IN ?", []database.DeliveryStatus{
database.DeliveryStatusPending,
database.DeliveryStatusRetrying,
@@ -196,13 +195,13 @@ func (e *Engine) deliverRetry(_ context.Context, d *database.Delivery) {
// Determine attempt number from existing results
var resultCount int64
e.db.Model(&database.DeliveryResult{}).Where("delivery_id = ?", d.ID).Count(&resultCount)
e.database.DB().Model(&database.DeliveryResult{}).Where("delivery_id = ?", d.ID).Count(&resultCount)
attemptNum := int(resultCount) + 1
// Check if we should wait before retrying (exponential backoff)
if attemptNum > 1 {
var lastResult database.DeliveryResult
lookupErr := e.db.Where("delivery_id = ?", d.ID).Order("created_at DESC").First(&lastResult).Error
lookupErr := e.database.DB().Where("delivery_id = ?", d.ID).Order("created_at DESC").First(&lastResult).Error
if lookupErr == nil {
shift := attemptNum - 2
if shift > 30 {
@@ -330,7 +329,7 @@ func (e *Engine) recordResult(d *database.Delivery, attemptNum int, success bool
Duration: durationMs,
}
if err := e.db.Create(result).Error; err != nil {
if err := e.database.DB().Create(result).Error; err != nil {
e.log.Error("failed to record delivery result",
"delivery_id", d.ID,
"error", err,
@@ -339,7 +338,7 @@ func (e *Engine) recordResult(d *database.Delivery, attemptNum int, success bool
}
func (e *Engine) updateDeliveryStatus(d *database.Delivery, status database.DeliveryStatus) {
if err := e.db.Model(d).Update("status", status).Error; err != nil {
if err := e.database.DB().Model(d).Update("status", status).Error; err != nil {
e.log.Error("failed to update delivery status",
"delivery_id", d.ID,
"status", status,