store

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 10, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package store is the PostgreSQL persistence layer: the event archive and the delivery queue. The queue is plain SQL — due rows are claimed with FOR UPDATE SKIP LOCKED, so any number of workers can run concurrently without double delivery and without any broker beside Postgres.

Index

Constants

View Source
const (
	VerifyVerified   = "verified"
	VerifyRejected   = "rejected"
	VerifyUnverified = "unverified"
)

Event statuses (verify_status column).

View Source
const (
	StatusPending   = "pending"
	StatusRetrying  = "retrying"
	StatusInflight  = "inflight"
	StatusDelivered = "delivered"
	StatusDead      = "dead"
)

Delivery statuses.

Variables

View Source
var ErrNotFound = errors.New("not found")

ErrNotFound is returned when an event id does not exist.

Functions

This section is empty.

Types

type Claimed

type Claimed struct {
	DeliveryID  uuid.UUID
	EventID     uuid.UUID
	Target      string
	Attempt     int // attempts before this one
	Source      string
	Body        []byte
	ContentType string
}

Claimed is a delivery leased to a worker, joined with what the worker needs to perform the HTTP call.

type Delivery

type Delivery struct {
	ID             uuid.UUID  `json:"id"`
	EventID        uuid.UUID  `json:"event_id"`
	Target         string     `json:"target"`
	Status         string     `json:"status"`
	Kind           string     `json:"kind"`
	Attempt        int        `json:"attempt"`
	NextAttemptAt  time.Time  `json:"next_attempt_at"`
	LastError      string     `json:"last_error,omitempty"`
	ResponseStatus *int       `json:"response_status,omitempty"`
	DeliveredAt    *time.Time `json:"delivered_at,omitempty"`
	CreatedAt      time.Time  `json:"created_at"`
}

type Event

type Event struct {
	ID           uuid.UUID           `json:"id"`
	Source       string              `json:"source"`
	ReceivedAt   time.Time           `json:"received_at"`
	Headers      map[string][]string `json:"headers,omitempty"`
	Body         []byte              `json:"-"`
	ContentType  string              `json:"content_type"`
	BodySize     int                 `json:"body_size"`
	VerifyStatus string              `json:"verify_status"`
	VerifyReason string              `json:"verify_reason,omitempty"`
}

type EventFilter

type EventFilter struct {
	Source string
	Status string
	Since  time.Time
	Until  time.Time
	Limit  int
	Offset int
}

EventFilter narrows ListEvents. Status filters by delivery state (pending/delivered/dead) or by verify_status (rejected).

type EventSummary

type EventSummary struct {
	ID           uuid.UUID `json:"id"`
	Source       string    `json:"source"`
	ReceivedAt   time.Time `json:"received_at"`
	ContentType  string    `json:"content_type"`
	BodySize     int       `json:"body_size"`
	VerifyStatus string    `json:"verify_status"`
	InProgress   int       `json:"in_progress"`
	Delivered    int       `json:"delivered"`
	Dead         int       `json:"dead"`
}

EventSummary is a list row: the event plus aggregate delivery counts.

type Store

type Store struct {
	// contains filtered or unexported fields
}

func New

func New(ctx context.Context, dsn string) (*Store, error)

New connects to Postgres and pings it.

func (*Store) ClaimDue

func (s *Store) ClaimDue(ctx context.Context, limit int, visibility time.Duration) ([]Claimed, error)

ClaimDue leases up to limit due deliveries to the calling worker.

Due means: pending/retrying with next_attempt_at in the past, or inflight rows whose lease expired (claimed_at older than visibility) — the worker that claimed them crashed mid-delivery. FOR UPDATE SKIP LOCKED makes concurrent claimers never block or double-claim; the visibility timeout makes crashed claims re-deliverable (at-least-once, never lost).

func (*Store) Close

func (s *Store) Close()

func (*Store) GetEvent

func (s *Store) GetEvent(ctx context.Context, id uuid.UUID) (*Event, []Delivery, error)

GetEvent returns the full event including body, headers and its deliveries.

func (*Store) InsertEvent

func (s *Store) InsertEvent(ctx context.Context, ev *Event, targets []string) error

InsertEvent persists the event and, unless it was rejected, fans out one pending delivery per target — all in a single transaction, so an event is never visible without its deliveries.

func (*Store) ListEvents

func (s *Store) ListEvents(ctx context.Context, f EventFilter) ([]EventSummary, error)

ListEvents returns newest-first event summaries with delivery counts. The LEFT JOIN is aggregated per event id, so the join cannot multiply rows.

func (*Store) MarkDelivered

func (s *Store) MarkDelivered(ctx context.Context, id uuid.UUID, attempt int, responseStatus int) error

MarkDelivered finalizes a successful delivery.

func (*Store) MarkFailed

func (s *Store) MarkFailed(ctx context.Context, id uuid.UUID, attempt int, lastErr string,
	responseStatus *int, dead bool, nextAttemptAt time.Time) error

MarkFailed records a failed attempt. When dead is false the delivery is rescheduled for nextAttemptAt; when true it goes to the dead letter state and stays visible until replayed or pruned.

func (*Store) Migrate

func (s *Store) Migrate(ctx context.Context) ([]string, error)

Migrate applies embedded migrations in filename order. Migrations are forward-only; each file runs once inside its own transaction and is recorded in schema_migrations.

func (*Store) PendingMigrations

func (s *Store) PendingMigrations(ctx context.Context) ([]string, error)

PendingMigrations lists embedded migrations not yet applied (for --dry-run).

func (*Store) Ping

func (s *Store) Ping(ctx context.Context) error

Ping is used by /healthz.

func (*Store) Prune

func (s *Store) Prune(ctx context.Context, cutoff time.Time, dryRun bool) (int64, error)

Prune deletes events received before cutoff (deliveries cascade). With dryRun it only counts.

func (*Store) QueueDepth

func (s *Store) QueueDepth(ctx context.Context) (int64, error)

QueueDepth returns how many deliveries are waiting or in flight. Used by tests and by serve's periodic queue log line.

func (*Store) ReplayEvent

func (s *Store) ReplayEvent(ctx context.Context, eventID uuid.UUID, targets []string) (int64, error)

ReplayEvent enqueues fresh deliveries for one event, one per target. Rejected events can be replayed too — replay is an explicit operator decision made after inspecting the event.

func (*Store) ReplayRange

func (s *Store) ReplayRange(ctx context.Context, source string, from, to time.Time, targets []string) (int64, error)

ReplayRange enqueues replay deliveries for every event of a source in [from, to]. Rejected events are skipped: they failed signature verification and never earned deliveries — replay them individually if you really mean it.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL