refactor: event-driven delivery engine with channel notifications and timer-based retries
All checks were successful
check / check (push) Successful in 58s
All checks were successful
check / check (push) Successful in 58s
Replace the polling-based delivery engine with a fully event-driven architecture using Go channels and goroutines: - Webhook handler notifies engine via buffered channel after creating delivery records, with inline event data for payloads < 16KB - Large payloads (>= 16KB) use pointer semantics (Body *string = nil) and are fetched from DB on demand, keeping channel memory bounded - Failed retry-target deliveries schedule Go timers with exponential backoff; timers fire into a separate retry channel when ready - On startup, engine scans DB once to recover interrupted deliveries (pending processed immediately, retrying get timers for remaining backoff) - DB stores delivery status for crash recovery only, not for inter-component communication during normal operation - delivery.Notifier interface decouples handlers from engine; fx wires *Engine as Notifier No more periodic polling. No more wasted cycles when idle.
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"sneak.berlin/go/webhooker/internal/database"
|
||||
"sneak.berlin/go/webhooker/internal/delivery"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -117,12 +118,12 @@ func (h *Handlers) HandleWebhook() http.HandlerFunc {
|
||||
|
||||
// Create delivery records for each active target
|
||||
for i := range targets {
|
||||
delivery := &database.Delivery{
|
||||
dlv := &database.Delivery{
|
||||
EventID: event.ID,
|
||||
TargetID: targets[i].ID,
|
||||
Status: database.DeliveryStatusPending,
|
||||
}
|
||||
if err := tx.Create(delivery).Error; err != nil {
|
||||
if err := tx.Create(dlv).Error; err != nil {
|
||||
tx.Rollback()
|
||||
h.log.Error("failed to create delivery",
|
||||
"target_id", targets[i].ID,
|
||||
@@ -139,6 +140,23 @@ func (h *Handlers) HandleWebhook() http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
// Notify the delivery engine with inline event data so it can
|
||||
// process deliveries immediately without a DB round-trip.
|
||||
// Large bodies (>= 16KB) are left nil to keep channel memory
|
||||
// bounded; the engine fetches them from DB on demand.
|
||||
n := delivery.Notification{
|
||||
WebhookID: entrypoint.WebhookID,
|
||||
EventID: event.ID,
|
||||
Method: event.Method,
|
||||
Headers: event.Headers,
|
||||
ContentType: event.ContentType,
|
||||
}
|
||||
bodyStr := string(body)
|
||||
if len(body) < delivery.MaxInlineBodySize {
|
||||
n.Body = &bodyStr
|
||||
}
|
||||
h.notifier.Notify(n)
|
||||
|
||||
h.log.Info("webhook event created",
|
||||
"event_id", event.ID,
|
||||
"webhook_id", entrypoint.WebhookID,
|
||||
|
||||
Reference in New Issue
Block a user