refactor: self-contained delivery tasks — engine delivers without DB reads in happy path
All checks were successful
check / check (push) Successful in 58s
All checks were successful
check / check (push) Successful in 58s
The webhook handler now builds DeliveryTask structs carrying all target config and event data inline (for bodies ≤16KB) and sends them through the delivery channel. In the happy path, the engine delivers without reading from any database — it only writes to record delivery results. For large bodies (≥16KB), Body is nil and the engine fetches it from the per-webhook database on demand. Retry timers also carry the full DeliveryTask, so retries avoid unnecessary DB reads. The database is used for crash recovery only: on startup the engine scans for interrupted pending/retrying deliveries and re-queues them. Implements owner feedback from issue #15: > the message in the <=16KB case should have everything it needs to do > its delivery. it shouldn't touch the db until it has a success or > failure to record.
This commit is contained in:
@@ -22,7 +22,7 @@ import (
|
||||
// noopNotifier is a no-op delivery.Notifier for tests.
|
||||
type noopNotifier struct{}
|
||||
|
||||
func (n *noopNotifier) Notify(delivery.Notification) {}
|
||||
func (n *noopNotifier) Notify([]delivery.DeliveryTask) {}
|
||||
|
||||
func TestHandleIndex(t *testing.T) {
|
||||
var h *Handlers
|
||||
|
||||
@@ -17,7 +17,9 @@ const (
|
||||
|
||||
// HandleWebhook handles incoming webhook requests at entrypoint URLs.
|
||||
// Only POST requests are accepted; all other methods return 405 Method Not Allowed.
|
||||
// Events and deliveries are stored in the per-webhook database.
|
||||
// Events and deliveries are stored in the per-webhook database. The handler
|
||||
// builds self-contained DeliveryTask structs with all target and event data
|
||||
// so the delivery engine can process them without additional DB reads.
|
||||
func (h *Handlers) HandleWebhook() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
@@ -116,7 +118,16 @@ func (h *Handlers) HandleWebhook() http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
// Create delivery records for each active target
|
||||
// Prepare body pointer for inline transport (≤16KB bodies are
|
||||
// included in the DeliveryTask so the engine needs no DB read).
|
||||
var bodyPtr *string
|
||||
if len(body) < delivery.MaxInlineBodySize {
|
||||
bodyStr := string(body)
|
||||
bodyPtr = &bodyStr
|
||||
}
|
||||
|
||||
// Create delivery records and build self-contained delivery tasks
|
||||
tasks := make([]delivery.DeliveryTask, 0, len(targets))
|
||||
for i := range targets {
|
||||
dlv := &database.Delivery{
|
||||
EventID: event.ID,
|
||||
@@ -132,6 +143,22 @@ func (h *Handlers) HandleWebhook() http.HandlerFunc {
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
tasks = append(tasks, delivery.DeliveryTask{
|
||||
DeliveryID: dlv.ID,
|
||||
EventID: event.ID,
|
||||
WebhookID: entrypoint.WebhookID,
|
||||
TargetID: targets[i].ID,
|
||||
TargetName: targets[i].Name,
|
||||
TargetType: targets[i].Type,
|
||||
TargetConfig: targets[i].Config,
|
||||
MaxRetries: targets[i].MaxRetries,
|
||||
Method: event.Method,
|
||||
Headers: event.Headers,
|
||||
ContentType: event.ContentType,
|
||||
Body: bodyPtr,
|
||||
AttemptNum: 1,
|
||||
})
|
||||
}
|
||||
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
@@ -140,22 +167,14 @@ 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,
|
||||
// Notify the delivery engine with self-contained delivery tasks.
|
||||
// Each task carries all target config and event data inline so
|
||||
// the engine can deliver without touching any database (in the
|
||||
// ≤16KB happy path). The engine only writes to the DB to record
|
||||
// delivery results after each attempt.
|
||||
if len(tasks) > 0 {
|
||||
h.notifier.Notify(tasks)
|
||||
}
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user