Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2113ba2771 |
@@ -2187,7 +2187,7 @@ A **Resubmit** stores its copy of an event through the same `Engine.StoreEvent`,
|
|||||||
and a **Replay** adds one delivery of a stored event through
|
and a **Replay** adds one delivery of a stored event through
|
||||||
`Engine.CreateReplay`, which writes the delivery and its target's totals in one
|
`Engine.CreateReplay`, which writes the delivery and its target's totals in one
|
||||||
transaction and queues it. Those two methods are the only code that creates a
|
transaction and queues it. Those two methods are the only code that creates a
|
||||||
delivery; the handlers read and check the request, then make the one call.
|
delivery; the handlers parse the request and make the one call.
|
||||||
|
|
||||||
### Bounded Worker Pool
|
### Bounded Worker Pool
|
||||||
|
|
||||||
@@ -2198,9 +2198,8 @@ preventing goroutine explosions regardless of queue depth.
|
|||||||
**Architecture:**
|
**Architecture:**
|
||||||
|
|
||||||
- **Channels as queues:** Two buffered channels serve as bounded queues: a
|
- **Channels as queues:** Two buffered channels serve as bounded queues: a
|
||||||
delivery channel (the tasks of newly stored deliveries and of the `pending`
|
delivery channel (the tasks of deliveries the engine has just stored) and a
|
||||||
deliveries the recovery routine hands back) and a retry channel (tasks from
|
retry channel (tasks from backoff timers). Both are buffered to 10,000.
|
||||||
backoff timers). Both are buffered to 10,000.
|
|
||||||
- **Fan-out via channel, not goroutines:** When an event arrives with multiple
|
- **Fan-out via channel, not goroutines:** When an event arrives with multiple
|
||||||
targets, each `delivery.Task` is sent to the delivery channel. Workers pick
|
targets, each `delivery.Task` is sent to the delivery channel. Workers pick
|
||||||
them up and process them — no goroutine-per-target.
|
them up and process them — no goroutine-per-target.
|
||||||
@@ -3162,7 +3161,7 @@ Components are wired via Uber fx in this order:
|
|||||||
11. `metrics.New` — The delivery collectors, registered on that registry
|
11. `metrics.New` — The delivery collectors, registered on that registry
|
||||||
12. `middleware.New` — HTTP middleware
|
12. `middleware.New` — HTTP middleware
|
||||||
13. `delivery.New` — Event-driven delivery engine, which also stores each event
|
13. `delivery.New` — Event-driven delivery engine, which also stores each event
|
||||||
with its deliveries and creates replay deliveries
|
with its deliveries
|
||||||
14. `archive.NewWriters` — The database targets' archive writers, and the lock
|
14. `archive.NewWriters` — The database targets' archive writers, and the lock
|
||||||
every rename of their files runs under
|
every rename of their files runs under
|
||||||
15. `archive.NewSweeper` — Periodic pruning of idle archives
|
15. `archive.NewSweeper` — Periodic pruning of idle archives
|
||||||
|
|||||||
+10
-12
@@ -23,11 +23,10 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
// deliveryChannelSize is the buffer size for the delivery
|
// deliveryChannelSize is the buffer size for the delivery
|
||||||
// channel. The Tasks of newly stored deliveries, and of the
|
// channel. The Tasks of newly stored deliveries are sent
|
||||||
// pending deliveries the recovery routine hands back, are
|
// here. Workers drain this channel. Sized large enough
|
||||||
// sent here. Workers drain this channel. Sized large enough
|
// that storing an event should never block under normal
|
||||||
// that no task should be dropped under normal load: one that
|
// load.
|
||||||
// does not fit is left for the sweep.
|
|
||||||
deliveryChannelSize = 10000
|
deliveryChannelSize = 10000
|
||||||
|
|
||||||
// retryChannelSize is the buffer size for the retry
|
// retryChannelSize is the buffer size for the retry
|
||||||
@@ -152,13 +151,12 @@ type EngineParams struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Engine processes queued deliveries in the background
|
// Engine processes queued deliveries in the background
|
||||||
// using a bounded worker pool architecture. It also stores each
|
// using a bounded worker pool architecture. It owns only
|
||||||
// event with its deliveries and creates replay deliveries (see
|
// the cross-target machinery: the worker pool, the queue
|
||||||
// store.go). Beyond that it owns only the cross-target machinery:
|
// and retry channels, the recovery routine, and the persistence
|
||||||
// the worker pool, the delivery and retry channels, the recovery
|
// helpers and Scheduler that individual targets rely on.
|
||||||
// routine, and the persistence helpers and Scheduler that
|
// Each target type owns its own delivery, including retries,
|
||||||
// individual targets rely on. Each target type owns its own
|
// backoff, and circuit breaking.
|
||||||
// delivery, including retries, backoff, and circuit breaking.
|
|
||||||
type Engine struct {
|
type Engine struct {
|
||||||
database *database.Database
|
database *database.Database
|
||||||
dbManager *database.WebhookDBManager
|
dbManager *database.WebhookDBManager
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ func TestSweepLeavesAQueuedDeliveryAlone(t *testing.T) {
|
|||||||
)
|
)
|
||||||
rAgePending(t, s.WebhookDB, d.ID)
|
rAgePending(t, s.WebhookDB, d.ID)
|
||||||
|
|
||||||
// Queued exactly as StoreEvent queues it, and never dequeued:
|
// Queued exactly as the receiver queues it, and never dequeued:
|
||||||
// no workers are running in this engine.
|
// no workers are running in this engine.
|
||||||
s.Engine.ExportNotify([]delivery.Task{{
|
s.Engine.ExportNotify([]delivery.Task{{
|
||||||
DeliveryID: d.ID,
|
DeliveryID: d.ID,
|
||||||
@@ -248,9 +248,9 @@ func TestOwnershipIsReleasedAfterDelivery(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TestNotifyAfterRecoveryDoesNotSendAgain is the startup race of
|
// TestNotifyAfterRecoveryDoesNotSendAgain is the startup race of
|
||||||
// https://git.eeqj.de/sneak/webhooker/issues/299. StoreEvent has
|
// https://git.eeqj.de/sneak/webhooker/issues/299. The receiver has
|
||||||
// written a delivery, the recovery routine's run at start finds it
|
// written a delivery, the recovery routine's run at start finds it
|
||||||
// pending, sends it and releases it, and only then does StoreEvent's
|
// pending, sends it and releases it, and only then does the receiver's
|
||||||
// notify for it arrive. Nothing owns the delivery by then, so notify
|
// notify for it arrive. Nothing owns the delivery by then, so notify
|
||||||
// takes it.
|
// takes it.
|
||||||
func TestNotifyAfterRecoveryDoesNotSendAgain(t *testing.T) {
|
func TestNotifyAfterRecoveryDoesNotSendAgain(t *testing.T) {
|
||||||
|
|||||||
@@ -172,10 +172,8 @@ func loadResubmitSource(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// queueResubmit has the delivery engine store the copy, with a delivery
|
// queueResubmit has the delivery engine store the copy, with a delivery
|
||||||
// for each of the webhook's active targets as they stand now, through
|
// for each of the webhook's active targets as they stand now, exactly
|
||||||
// the same StoreEvent the receiver uses. The one difference is that the
|
// as it stores a received event.
|
||||||
// copy leaves its entrypoint's last event as it is, since it did not
|
|
||||||
// arrive on that URL.
|
|
||||||
func (h *Handlers) queueResubmit(
|
func (h *Handlers) queueResubmit(
|
||||||
w http.ResponseWriter,
|
w http.ResponseWriter,
|
||||||
r *http.Request,
|
r *http.Request,
|
||||||
|
|||||||
@@ -239,8 +239,8 @@ func newTestAppWithConfig(
|
|||||||
session.New,
|
session.New,
|
||||||
archive.NewWriters,
|
archive.NewWriters,
|
||||||
// The engine is built on a lifecycle that is never started,
|
// The engine is built on a lifecycle that is never started,
|
||||||
// so no worker runs: every delivery the engine creates for a
|
// so no worker runs: every delivery a handler creates stays
|
||||||
// handler stays pending, where the test can read it.
|
// pending, where the test can read it.
|
||||||
func(p delivery.EngineParams) *delivery.Engine {
|
func(p delivery.EngineParams) *delivery.Engine {
|
||||||
return delivery.New(fxtest.NewLifecycle(t), p)
|
return delivery.New(fxtest.NewLifecycle(t), p)
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user