Documentation
¶
Overview ¶
Package sideeffect provides at-most-once execution helpers for external side effects within dureq handlers.
When a handler calls an external API (payment, email, webhook), a crash after the API call but before job completion causes the handler to re-run, potentially executing the side effect twice. Step() prevents this by persisting the result in Redis before returning it to the handler.
Usage within a handler:
func handlePayment(ctx context.Context, order OrderPayload) error {
result, err := sideeffect.Step(ctx, "charge", func(ctx context.Context) (string, error) {
return paymentGateway.Charge(order.ID, order.Amount)
})
if err != nil {
return err
}
// result is the charge reference — cached on retry
...
}
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ExternalCall ¶
func ExternalCall(ctx context.Context, stepKey string, fn func(ctx context.Context, idempotencyKey string) error) error
ExternalCall wraps an external API call with a deterministic idempotency key. The callback receives the idempotency key to pass to the external service.
Usage:
err := sideeffect.ExternalCall(ctx, "stripe-charge", func(ctx context.Context, idempotencyKey string) error {
_, err := stripe.Charges.Create(&stripe.ChargeParams{
IdempotencyKey: &idempotencyKey,
...
})
return err
})
func Step ¶
func Step[T any](ctx context.Context, stepKey string, fn func(ctx context.Context) (T, error)) (T, error)
Step executes fn at most once per (runID, stepKey) pair.
On first call: claims the step, executes fn, stores the result, returns it. On retry (same runID): returns the cached result without re-executing fn.
The stepKey must be unique within the handler for a given run. Use descriptive names like "charge-payment", "send-email", "update-inventory".
Step requires that the context was created by the dureq worker (contains RunID and SideEffectStore). It will return an error if called outside a handler execution context.
Types ¶
This section is empty.