blockqueue

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Jul 11, 2026 License: Apache-2.0 Imports: 34 Imported by: 0

README

BlockQueue logo

BlockQueue

A durable, embeddable message queue for Go, backed by SQLite or PostgreSQL.

Go Reference Apache-2.0 license

BlockQueue is an embeddable, at-least-once message queue for Go backed by SQLite or PostgreSQL. It stores one canonical message and one delivery row per subscriber, so fan-out state, retries, leases, DLQ transitions, and schedules remain transactional.

Turso/libSQL support is experimental.

[!IMPORTANT] v0.2.0 is a clean schema break and requires a new, empty database. It does not perform an in-place database upgrade from v0.1.

What v0.2.0 provides

  • Durable publish is the default in the Go API; explicit async publish is available when admission latency matters more than crash durability.
  • A weighted writer budget limits both pending message count and bytes. A reservation is released only after the database transaction finishes.
  • Claims use database locking, a new receipt token for every delivery lease, idempotent ACK, fenced stale receipts, delayed NACK, snooze, cancellation, and lease extension.
  • PublishTx, AckDeliveryTx, and the other *Tx methods can commit queue state atomically with application tables in the same SQLite or PostgreSQL database.
  • Delivery claims and processing failures are counted separately. Subscriber retry policy supports exponential backoff with bounded deterministic jitter, and every failure is retained in a paginated error history.
  • Priority, delayed delivery, absolute RFC3339 scheduling, recurring five-field cron schedules, IANA timezones, run history, and overlap protection.
  • Transactional, checksummed embedded schema migrations.
  • /livez, /readyz, bounded retention, adaptive SQLite checkpoints, and
  • /livez, /readyz, an embedded OpenAPI 3.1 document, RFC 9457 problem responses, bounded retention, adaptive SQLite checkpoints, and optional Prometheus metrics.

There is one queue engine and one current HTTP contract at /v1; the project does not maintain parallel v1/v2 engines or schemas. See the v0.2 migration guide for source-level changes and fresh-database rollout instructions.

Install

go get github.com/yudhasubki/blockqueue

The server binary is optional:

go build -o blockqueue ./cmd/blockqueue

Embed in a Go application

Only the root package and the selected storage driver are required:

package main

import (
	"context"
	"log"
	"time"

	"github.com/yudhasubki/blockqueue"
	"github.com/yudhasubki/blockqueue/store/sqlite"
)

func main() {
	driver, err := sqlite.Open("blockqueue.db", sqlite.Config{})
	if err != nil {
		log.Fatal(err)
	}

	queue := blockqueue.New(driver, blockqueue.Options{})
	if err := queue.Run(context.Background()); err != nil {
		log.Fatal(err)
	}
	defer queue.Close()

	topic := blockqueue.NewTopic("orders")
	worker := blockqueue.NewSubscriber(topic, "fulfillment", blockqueue.SubscriberOptions{
		MaxAttempts:        5,
		VisibilityDuration: "30s",
		DequeueBatchSize:   10,
	})
	if err := queue.CreateTopic(context.Background(), topic, blockqueue.Subscribers{worker}); err != nil {
		log.Fatal(err)
	}

	receipt, err := queue.Publish(context.Background(), topic, blockqueue.Message{
		Message:        `{"order_id":"1022"}`,
		IdempotencyKey: "order-1022",
		Priority:       10,
	})
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("persisted %s", receipt.MessageID)

	deliveries, err := queue.ClaimWait(context.Background(), topic, worker.Name, 10, time.Minute)
	if err != nil {
		log.Fatal(err)
	}
	for _, delivery := range deliveries {
		// Process the message before acknowledging this exact lease.
		if err := queue.AckDelivery(context.Background(), topic, worker.Name,
			delivery.ID, delivery.ReceiptToken); err != nil {
			log.Fatal(err)
		}
	}
}

Publish and BatchPublish wait for commit. PublishAsync and BatchPublishAsync return after bounded in-memory admission. If a durable caller's context expires after admission, it receives *CommitUnknownError with stable message IDs; the writer continues owning the admitted messages.

Transactional enqueue and completion

Use WithTx when application tables and BlockQueue use the same database. The producer can atomically create application data and enqueue later work:

err := queue.WithTx(ctx, nil, func(tx *sql.Tx) error {
	if err := insertOrder(ctx, tx, orderID, "pending"); err != nil {
		return err
	}
	_, err := queue.PublishTx(ctx, tx, topic, blockqueue.Message{
		Message:        `{"order_id":"1022","action":"fulfill"}`,
		IdempotencyKey: "fulfill-order-1022",
	})
	return err
})

That commit does not include consumer execution. The consumer claims the committed delivery later, then can atomically store its result and complete the delivery:

err := queue.WithTx(ctx, nil, func(tx *sql.Tx) error {
	if err := markOrderFulfilled(ctx, tx, orderID); err != nil {
		return err
	}
	return queue.AckDeliveryTx(
		ctx, tx, topic, worker.Name, delivery.ID, delivery.ReceiptToken,
	)
})

PublishTx returns state: staged; the rows become visible only if the transaction commits. AckDeliveryTx, NackDeliveryTx, SnoozeDeliveryTx, CancelDeliveryTx, and CancelMessageTx provide the same atomic boundary for consumer side effects. Keep callbacks short and free of network calls. SQLite has one writer, so an open caller transaction intentionally blocks queue writes until commit or rollback. PostgreSQL uses a shared topology fence, so publishers can proceed concurrently while destructive subscriber mutations wait for the transaction. Shutdown drains transactions created by WithTx; callers that begin a raw transaction themselves must coordinate its lifetime with shutdown.

The runnable transactional example shows both commits against one SQLite database, including receipt-fenced consumer completion.

Run the HTTP server

Copy config.yaml.example, then run:

./blockqueue migrate -config config.yaml
./blockqueue http -config config.yaml

Queue.Run also applies migrations, so the explicit migration command is optional for embedded deployments.

Configuration decoding rejects unknown YAML fields and expands ${NAME} from the process environment. This keeps PostgreSQL passwords out of committed configuration files; unset variables expand to an empty value and normal connection validation still applies.

Create a topic:

curl -X POST http://127.0.0.1:8080/v1/topics \
  -H 'Content-Type: application/json' \
  -d '{
    "name":"orders",
    "subscribers":[{
      "name":"fulfillment",
      "option":{
        "max_attempts":5,
        "visibility_duration":"30s",
        "dequeue_batch_size":10
      }
    }]
  }'

Async publish is the HTTP default and returns 202 with state: admitted:

curl -X POST http://127.0.0.1:8080/v1/topics/orders/messages \
  -H 'Content-Type: application/json' \
  -d '{"message":"order-1022","idempotency_key":"order-1022","priority":10}'

Wait for commit with ?wait_for=commit; this returns a definitive duplicate result:

curl -X POST 'http://127.0.0.1:8080/v1/topics/orders/messages?wait_for=commit' \
  -H 'Content-Type: application/json' \
  -d '{"message":"order-1022","idempotency_key":"order-1022"}'

Claim and ACK the returned receipt token:

curl -X POST 'http://127.0.0.1:8080/v1/topics/orders/subscribers/fulfillment/claim?timeout=30s&limit=10'

curl -X POST http://127.0.0.1:8080/v1/topics/orders/subscribers/fulfillment/messages/MESSAGE_ID/ack \
  -H 'Content-Type: application/json' \
  -d '{"receipt_token":"RECEIPT_TOKEN"}'

JSON decoding is strict. The HTTP limits are 1 MiB per message, 1,000 messages per batch, 16 MiB per request body, 16 KiB of headers, and 128 bytes per idempotency key.

The complete OpenAPI 3.1 contract is served at /openapi.json. Errors use application/problem+json with stable code values. The HTTP surface exposes message status, delivery/message cancellation, snooze, and delivery error history. Database transaction methods are intentionally Go-only: a remote HTTP request cannot join the caller's local transaction; cross-database systems should publish through an application outbox.

Delivery contract

  • Delivery is at-least-once. Consumers must make side effects idempotent.
  • A claim owns a delivery only for its current receipt token and lease. ACK, NACK, and lease extension reject stale receipts.
  • delivery_count increases when a lease is claimed; failure_count increases only on NACK or lease expiry. Dead-lettering is based on failures, with three failures and exponential retry delay as the default.
  • Snooze returns a claimed delivery to pending without consuming a failure. Cancellation is terminal and idempotent, and failure records remain queryable while the delivery is retained.
  • Publish waits for the canonical message and all subscriber delivery rows to commit. PublishAsync guarantees bounded process-local admission, not crash durability.
  • The database is authoritative. PostgreSQL notifications and in-memory wakeups reduce latency but are never required for correctness.
  • Built-in authentication and exactly-once execution are outside the project scope. Protect the HTTP server with a private network or reverse proxy.

v0.2 is the durable embedded/HTTP fan-out core. A typed Go worker runtime and multi-node maintenance leader election are intentionally roadmap items rather than hidden behavior in this release.

Storage and durability

Backend Status Coordination Default durability
SQLite Supported Single writer, immediate claim transactions WAL + synchronous=FULL
PostgreSQL Supported pgx, native UUID, FOR UPDATE SKIP LOCKED TLS required + synchronous_commit=on
Turso/libSQL Experimental Smoke-test scope only Backend dependent

Set store.DurabilityBalanced explicitly when lower latency is more important than the strict default. Async admission has no local disk spool; use durable publish when a successful response must imply a committed transaction. Processed deliveries are retained for seven days and schedule-run history for 30 days by default. Dead letters are retained indefinitely unless Options.DeadLetterRetention is set explicitly.

Development

go test ./...
go test -race ./...
go vet ./...
go test -run '^$' -bench . -benchmem ./...

The SQLite and PostgreSQL contract suite is shared. PostgreSQL tests create a random schema per run, remove it during cleanup, and refuse a database whose name does not end in _test:

createdb blockqueue_test
BLOCKQUEUE_TEST_POSTGRES_URL='postgres://postgres:postgres@127.0.0.1:5432/blockqueue_test?sslmode=disable' \
  go test -count=1 ./...

CI runs the complete suite and race detector against both storage backends, plus vet and a guarded PostgreSQL benchmark smoke. Benchmark scenarios and exact persisted-row checks are documented in benchmark/README.md.

For component boundaries and lock ownership, see docs/architecture.md.

Roadmap

  • v0.2.x: optional in-process event subscription, tracing hooks, and focused test helpers without changing the schema.
  • v0.3: typed Go workers and PostgreSQL maintenance leader election.
  • v0.4: versioned workflow/DAG orchestration as a separate layer over the queue.

License

Apache License 2.0.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrLeaseLost        = errors.New("delivery lease lost")
	ErrDeliveryNotFound = errors.New("delivery not found")
	ErrInvalidReceipt   = errors.New("receipt_token is required")
	ErrResourcePaused   = errors.New("topic or subscriber is paused")
)
View Source
var (
	ErrTopicNotFound      = errors.New("topic not found")
	ErrQueueNotRunning    = errors.New("blockqueue is not running")
	ErrQueueStopping      = errors.New("blockqueue is stopping")
	ErrNoActiveSubscriber = errors.New("topic has no active subscriber")
	ErrInvalidPublish     = errors.New("invalid publish request")
	ErrInvalidTopic       = errors.New("invalid topic")
	ErrInvalidSubscriber  = errors.New("invalid subscriber")
	ErrResourceConflict   = errors.New("resource already exists")
)
View Source
var (
	ErrScheduleNotFound  = errors.New("schedule not found")
	ErrScheduleVersion   = errors.New("stale schedule version")
	ErrScheduleOverlap   = errors.New("previous schedule run is still active")
	ErrScheduleLeaseLost = errors.New("schedule lease lost")
)
View Source
var (
	ErrSubscriberNotFound = errors.New("subscriber not found")
	ErrSubscriberDeleted  = errors.New("subscriber was deleted")
)
View Source
var (
	ErrWriterClosed          = errors.New("writer closed")
	ErrPendingBudgetExceeded = errors.New("pending write budget exceeded")
	ErrWriterDrainTimeout    = errors.New("writer shutdown with unpersisted messages")
	ErrIdempotencyConflict   = errors.New("idempotency key conflicts with a different message")
	ErrCommitUnknown         = errors.New("publish commit outcome unknown")
)
View Source
var ErrDeliveryTerminal = errors.New("delivery is already terminal")

ErrDeliveryTerminal reports an attempted cancellation of a delivery that was already processed or dead-lettered.

View Source
var ErrInvalidTransaction = errors.New("invalid blockqueue transaction")

ErrInvalidTransaction reports a missing or otherwise unusable caller transaction.

View Source
var ErrMigrationChecksum = errors.New("migration checksum mismatch")
View Source
var ErrUnsupportedDialect = errors.New("unsupported database dialect")

Functions

func DashboardFS added in v0.2.0

func DashboardFS() fs.FS

DashboardFS returns the embedded optional HTTP dashboard assets.

func Migrate added in v0.2.0

func Migrate(ctx context.Context, driver store.Driver) error

Migrate installs the v0.2 schema and applies future ordered migrations. v0.2 is a clean schema break: callers must provide a new database rather than a database created by v0.1.

Types

type BatchAckItem added in v0.2.0

type BatchAckItem struct {
	MessageID    string
	ReceiptToken string
}

type BatchNackItem added in v0.2.0

type BatchNackItem struct {
	MessageID    string
	ReceiptToken string
	RetryDelay   time.Duration
	Error        string
}

type Clock added in v0.2.0

type Clock interface {
	Now() time.Time
	After(time.Duration) <-chan time.Time
}

type CommitUnknownError added in v0.2.0

type CommitUnknownError struct {
	MessageIDs []string
	Cause      error
}

CommitUnknownError means the caller stopped waiting after admission. The writer still owns the messages and may already have committed them; callers can safely reconcile or retry using the included stable IDs/idempotency keys.

func (*CommitUnknownError) Error added in v0.2.0

func (err *CommitUnknownError) Error() string

func (*CommitUnknownError) Is added in v0.2.0

func (err *CommitUnknownError) Is(target error) bool

func (*CommitUnknownError) Unwrap added in v0.2.0

func (err *CommitUnknownError) Unwrap() error

type Deliveries added in v0.2.0

type Deliveries []Delivery

type Delivery added in v0.2.0

type Delivery struct {
	ID             string            `json:"id"`
	Message        string            `json:"message"`
	Headers        map[string]string `json:"headers,omitempty"`
	CorrelationID  string            `json:"correlation_id,omitempty"`
	Status         string            `json:"status,omitempty"`
	DeliveryCount  int               `json:"delivery_count,omitempty"`
	FailureCount   int               `json:"failure_count,omitempty"`
	Priority       int               `json:"priority,omitempty"`
	ReceiptToken   string            `json:"receipt_token,omitempty"`
	LeaseExpiresAt *time.Time        `json:"lease_expires_at,omitempty"`
	VisibleAt      time.Time         `json:"visible_at"`
	CreatedAt      time.Time         `json:"created_at"`
	CancelledAt    *time.Time        `json:"cancelled_at,omitempty"`
	CancelReason   string            `json:"cancel_reason,omitempty"`
}

type DeliveryError added in v0.2.0

type DeliveryError struct {
	ID           string    `db:"id" json:"id"`
	MessageID    string    `db:"message_id" json:"message_id"`
	SubscriberID string    `db:"subscriber_id" json:"subscriber_id"`
	FailureCount int       `db:"failure_count" json:"failure_count"`
	Error        string    `db:"error" json:"error"`
	FailedAt     time.Time `db:"failed_at" json:"failed_at"`
}

DeliveryError is an append-only record of one NACK or lease expiry. A DLQ replay resets the delivery failure count but does not erase prior records.

type DeliveryErrorPage added in v0.2.0

type DeliveryErrorPage struct {
	Errors     []DeliveryError `json:"errors"`
	NextCursor string          `json:"next_cursor,omitempty"`
}

DeliveryErrorPage is a cursor-paginated delivery failure history.

type DeliveryPage added in v0.2.0

type DeliveryPage struct {
	Messages   Deliveries `json:"messages"`
	NextCursor string     `json:"next_cursor,omitempty"`
}

type DeliveryResult added in v0.2.0

type DeliveryResult struct {
	MessageID    string `json:"message_id"`
	SubscriberID string `json:"subscriber_id,omitempty"`
	Status       string `json:"status"`
	Error        string `json:"error,omitempty"`
}

type LifecycleState added in v0.2.0

type LifecycleState uint32
const (
	LifecycleNew LifecycleState = iota
	LifecycleRunning
	LifecycleStopping
	LifecycleStopped
)

func (LifecycleState) String added in v0.2.0

func (s LifecycleState) String() string

type Message added in v0.2.0

type Message struct {
	Message        string            `json:"message"`
	Headers        map[string]string `json:"headers,omitempty"`
	CorrelationID  string            `json:"correlation_id,omitempty"`
	IdempotencyKey string            `json:"idempotency_key,omitempty"`
	Priority       int               `json:"priority,omitempty"`
	Delay          string            `json:"delay,omitempty"`
	ScheduleAt     string            `json:"schedule_at,omitempty"`
}

type MessageDeliveryStatus added in v0.2.0

type MessageDeliveryStatus struct {
	SubscriberID  string     `db:"subscriber_id" json:"subscriber_id"`
	Subscriber    string     `db:"subscriber" json:"subscriber"`
	Status        string     `db:"status" json:"status"`
	DeliveryCount int        `db:"delivery_count" json:"delivery_count"`
	FailureCount  int        `db:"failure_count" json:"failure_count"`
	VisibleAt     time.Time  `db:"visible_at" json:"visible_at"`
	ProcessedAt   *time.Time `db:"processed_at" json:"processed_at,omitempty"`
	CancelledAt   *time.Time `db:"cancelled_at" json:"cancelled_at,omitempty"`
	CancelReason  string     `db:"cancel_reason" json:"cancel_reason,omitempty"`
}

MessageDeliveryStatus describes one subscriber's delivery state.

type MessageStatus added in v0.2.0

type MessageStatus struct {
	ID             string                  `json:"id"`
	TopicID        string                  `json:"topic_id"`
	Message        string                  `json:"message"`
	Headers        map[string]string       `json:"headers,omitempty"`
	CorrelationID  string                  `json:"correlation_id,omitempty"`
	IdempotencyKey string                  `json:"idempotency_key,omitempty"`
	Priority       int                     `json:"priority"`
	ScheduledAt    time.Time               `json:"scheduled_at"`
	CreatedAt      time.Time               `json:"created_at"`
	Deliveries     []MessageDeliveryStatus `json:"deliveries"`
}

MessageStatus is the canonical message and the current state of every subscriber delivery created with it.

type Options added in v0.2.0

type Options struct {
	Writer               WriterOptions
	CheckpointInterval   time.Duration         // Default: 30s
	RetentionPeriod      time.Duration         // Default: 7d
	DeadLetterRetention  time.Duration         // Default: disabled; operators opt in explicitly
	ScheduleRunRetention time.Duration         // Default: 30d
	ShutdownTimeout      time.Duration         // Default: 30s for Close
	ReadinessBacklog     int64                 // Default: 90% of pending message budget
	Clock                Clock                 // Optional deterministic scheduler clock
	DisableMetrics       bool                  // Skip per-message metric updates on the hot path
	MetricRegisterer     prometheus.Registerer // Optional collector registry; defaults to Prometheus global registry
}

type PublishReceipt added in v0.2.0

type PublishReceipt struct {
	MessageID   string    `json:"message_id"`
	State       string    `json:"state"`
	Duplicate   *bool     `json:"duplicate"`
	ScheduledAt time.Time `json:"scheduled_at"`
}

type PublishReceipts added in v0.2.0

type PublishReceipts []PublishReceipt

type Queue added in v0.2.0

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

Queue is the import-first BlockQueue engine. It owns the supplied database driver from construction until shutdown.

func New

func New(driver store.Driver, opt Options) *Queue

func (*Queue) AckDelivery added in v0.2.0

func (q *Queue) AckDelivery(ctx context.Context, topic Topic, subscriber, messageID, receipt string) error

func (*Queue) AckDeliveryTx added in v0.2.0

func (q *Queue) AckDeliveryTx(ctx context.Context, tx *sql.Tx, topic Topic, subscriber, messageID, receipt string) error

AckDeliveryTx atomically acknowledges a lease in a caller-owned transaction.

func (*Queue) BatchAckDeliveries added in v0.2.0

func (q *Queue) BatchAckDeliveries(ctx context.Context, topic Topic, subscriber string, requests []BatchAckItem) []DeliveryResult

func (*Queue) BatchNackDeliveries added in v0.2.0

func (q *Queue) BatchNackDeliveries(ctx context.Context, topic Topic, subscriber string, requests []BatchNackItem) []DeliveryResult

func (*Queue) BatchPublish added in v0.2.0

func (q *Queue) BatchPublish(ctx context.Context, topic Topic, requests []Message) (PublishReceipts, error)

func (*Queue) BatchPublishAsync added in v0.2.0

func (q *Queue) BatchPublishAsync(ctx context.Context, topic Topic, requests []Message) (PublishReceipts, error)

func (*Queue) BatchPublishDurable added in v0.2.0

func (q *Queue) BatchPublishDurable(ctx context.Context, topic Topic, requests []Message) (PublishReceipts, error)

func (*Queue) BatchPublishTx added in v0.2.0

func (q *Queue) BatchPublishTx(ctx context.Context, tx *sql.Tx, topic Topic, requests []Message) (PublishReceipts, error)

BatchPublishTx validates the complete batch before writing it to tx.

func (*Queue) CancelDelivery added in v0.2.0

func (q *Queue) CancelDelivery(ctx context.Context, topic Topic, subscriber, messageID, reason string) error

CancelDelivery terminally cancels one subscriber delivery. Repeating a successful cancellation is idempotent.

func (*Queue) CancelDeliveryTx added in v0.2.0

func (q *Queue) CancelDeliveryTx(ctx context.Context, tx *sql.Tx, topic Topic, subscriber, messageID, reason string) error

CancelDeliveryTx is CancelDelivery within a caller-owned transaction.

func (*Queue) CancelMessage added in v0.2.0

func (q *Queue) CancelMessage(ctx context.Context, topic Topic, messageID, reason string) ([]DeliveryResult, error)

CancelMessage cancels every pending or delivered subscriber delivery for a canonical message and returns the resulting state per subscriber.

func (*Queue) CancelMessageTx added in v0.2.0

func (q *Queue) CancelMessageTx(ctx context.Context, tx *sql.Tx, topic Topic, messageID, reason string) ([]DeliveryResult, error)

CancelMessageTx is CancelMessage within a caller-owned transaction.

func (*Queue) Claim added in v0.2.0

func (q *Queue) Claim(ctx context.Context, topic Topic, subscriber string, limit int, lease time.Duration) (Deliveries, error)

Claim atomically leases visible deliveries in canonical priority order. Every redelivery receives a fresh receipt token.

func (*Queue) ClaimWait added in v0.2.0

func (q *Queue) ClaimWait(ctx context.Context, topic Topic, subscriber string, limit int, lease time.Duration) (Deliveries, error)

ClaimWait long-polls without a polling backoff. Its timer is reset to the earliest pending visibility or expired lease deadline stored in the DB.

func (*Queue) Close added in v0.2.0

func (q *Queue) Close()

func (*Queue) CreateSchedule added in v0.2.0

func (q *Queue) CreateSchedule(ctx context.Context, topic Topic, input ScheduleInput) (Schedule, error)

func (*Queue) CreateSubscribers added in v0.2.0

func (q *Queue) CreateSubscribers(ctx context.Context, topic Topic, subscribers Subscribers) error

func (*Queue) CreateTopic added in v0.2.0

func (q *Queue) CreateTopic(ctx context.Context, topic Topic, subscribers Subscribers) error

func (*Queue) DeleteSchedule added in v0.2.0

func (q *Queue) DeleteSchedule(ctx context.Context, topic Topic, scheduleID string) error

func (*Queue) DeleteSubscriber added in v0.2.0

func (q *Queue) DeleteSubscriber(ctx context.Context, topic Topic, subscriber string) error

func (*Queue) DeleteTopic added in v0.2.0

func (q *Queue) DeleteTopic(ctx context.Context, topic Topic) error

func (*Queue) DeliveryErrors added in v0.2.0

func (q *Queue) DeliveryErrors(
	ctx context.Context,
	topic Topic,
	subscriber, messageID string,
	limit int,
	cursor string,
) (DeliveryErrorPage, error)

DeliveryErrors returns append-only NACK and lease-expiry history newest first. The cursor is opaque and scoped to the selected delivery.

func (*Queue) ExtendLease added in v0.2.0

func (q *Queue) ExtendLease(ctx context.Context, topic Topic, subscriber, messageID, receipt string, extension time.Duration) (time.Time, error)

func (*Queue) GetMessageStatus added in v0.2.0

func (q *Queue) GetMessageStatus(ctx context.Context, topic Topic, messageID string) (MessageStatus, error)

GetMessageStatus returns a canonical message and all of its delivery states.

func (*Queue) GetSchedule added in v0.2.0

func (q *Queue) GetSchedule(ctx context.Context, topic Topic, scheduleID string) (Schedule, error)

func (*Queue) GetSubscribersStatus added in v0.2.0

func (q *Queue) GetSubscribersStatus(ctx context.Context, topic Topic) (SubscriberStatuses, error)

func (*Queue) GetTopic added in v0.2.0

func (q *Queue) GetTopic(topicName string) (Topic, bool)

func (*Queue) GetTopics added in v0.2.0

func (q *Queue) GetTopics(ctx context.Context, filter TopicFilter) (Topics, error)

func (*Queue) ListDeliveries added in v0.2.0

func (q *Queue) ListDeliveries(ctx context.Context, topic Topic, subscriber string, deadLetter bool, limit int, cursor string) (DeliveryPage, error)

func (*Queue) ListSchedules added in v0.2.0

func (q *Queue) ListSchedules(ctx context.Context, topic Topic) ([]Schedule, error)

func (*Queue) Live added in v0.2.0

func (q *Queue) Live() bool

func (*Queue) NackDelivery added in v0.2.0

func (q *Queue) NackDelivery(ctx context.Context, topic Topic, subscriber, messageID, receipt string, retryDelay time.Duration, errorText string) error

func (*Queue) NackDeliveryTx added in v0.2.0

func (q *Queue) NackDeliveryTx(
	ctx context.Context,
	tx *sql.Tx,
	topic Topic,
	subscriber, messageID, receipt string,
	retryDelay time.Duration,
	errorText string,
) error

NackDeliveryTx atomically records a failed lease in a caller-owned transaction. A zero retryDelay selects the subscriber retry policy.

func (*Queue) PauseSchedule added in v0.2.0

func (q *Queue) PauseSchedule(ctx context.Context, topic Topic, scheduleID string, paused bool) error

func (*Queue) PauseSubscriber added in v0.2.0

func (q *Queue) PauseSubscriber(ctx context.Context, topic Topic, subscriber string) error

func (*Queue) PauseTopic added in v0.2.0

func (q *Queue) PauseTopic(ctx context.Context, topic Topic) error

func (*Queue) Publish added in v0.2.0

func (q *Queue) Publish(ctx context.Context, topic Topic, request Message) (PublishReceipt, error)

Publish is durable by default. A nil error means the canonical message and every subscriber delivery row committed successfully.

func (*Queue) PublishAsync added in v0.2.0

func (q *Queue) PublishAsync(ctx context.Context, topic Topic, request Message) (PublishReceipt, error)

PublishAsync returns the message identity at admission time. It is useful to Go callers that need the same receipt exposed by HTTP 202 responses.

func (*Queue) PublishDurable added in v0.2.0

func (q *Queue) PublishDurable(ctx context.Context, topic Topic, request Message) (PublishReceipt, error)

PublishDurable waits until the message and all subscriber delivery rows are committed. Duplicate is definitive in the returned receipt.

func (*Queue) PublishTx added in v0.2.0

func (q *Queue) PublishTx(ctx context.Context, tx *sql.Tx, topic Topic, request Message) (PublishReceipt, error)

PublishTx stages a canonical message and its fan-out in caller-owned tx. The caller owns commit or rollback; staged rows are not claimable before a successful commit.

func (*Queue) Ready added in v0.2.0

func (q *Queue) Ready(ctx context.Context) bool

func (*Queue) ReplayDeadLetters added in v0.2.0

func (q *Queue) ReplayDeadLetters(ctx context.Context, topic Topic, subscriber string, messageIDs []string) []DeliveryResult

func (*Queue) ResumeSubscriber added in v0.2.0

func (q *Queue) ResumeSubscriber(ctx context.Context, topic Topic, subscriber string) error

func (*Queue) ResumeTopic added in v0.2.0

func (q *Queue) ResumeTopic(ctx context.Context, topic Topic) error

func (*Queue) Run added in v0.2.0

func (q *Queue) Run(ctx context.Context) error

Run validates durable state and builds the complete runtime snapshot before starting background workers.

func (*Queue) RunScheduleNow added in v0.2.0

func (q *Queue) RunScheduleNow(ctx context.Context, topic Topic, scheduleID string, force bool) (ScheduleRun, error)

func (*Queue) ScheduleRunHistory added in v0.2.0

func (q *Queue) ScheduleRunHistory(ctx context.Context, topic Topic, scheduleID string, limit int, cursor string) (ScheduleRunPage, error)

func (*Queue) Shutdown added in v0.2.0

func (q *Queue) Shutdown(ctx context.Context) error

Shutdown stops admission, drains the writer, stops listeners and maintenance workers, performs the final checkpoint, and closes the database driver.

func (*Queue) SnoozeDelivery added in v0.2.0

func (q *Queue) SnoozeDelivery(
	ctx context.Context,
	topic Topic,
	subscriber, messageID, receipt string,
	delay time.Duration,
) (time.Time, error)

SnoozeDelivery returns an active lease to pending without recording a failure or consuming the subscriber's failure budget.

func (*Queue) SnoozeDeliveryTx added in v0.2.0

func (q *Queue) SnoozeDeliveryTx(
	ctx context.Context,
	tx *sql.Tx,
	topic Topic,
	subscriber, messageID, receipt string,
	delay time.Duration,
) (time.Time, error)

SnoozeDeliveryTx is SnoozeDelivery within a caller-owned transaction.

func (*Queue) State added in v0.2.0

func (q *Queue) State() LifecycleState

func (*Queue) UpdateSchedule added in v0.2.0

func (q *Queue) UpdateSchedule(ctx context.Context, topic Topic, scheduleID string, expectedVersion int, input ScheduleInput) (Schedule, error)

func (*Queue) WithTx added in v0.2.0

func (q *Queue) WithTx(ctx context.Context, options *sql.TxOptions, fn func(*sql.Tx) error) error

WithTx runs fn in a transaction owned by the queue. It is the preferred way to atomically change application tables and publish or complete deliveries: the queue can notify local waiters only after commit succeeds.

func (*Queue) WriterHealthy added in v0.2.0

func (q *Queue) WriterHealthy() bool

type RetryPolicy added in v0.2.0

type RetryPolicy struct {
	InitialDelay string  `json:"initial_delay,omitempty"`
	MaxDelay     string  `json:"max_delay,omitempty"`
	Multiplier   float64 `json:"multiplier,omitempty"`
	Jitter       float64 `json:"jitter,omitempty"`
}

RetryPolicy controls the delay applied after a NACK or expired lease. Empty fields use the documented exponential-backoff defaults.

type Schedule added in v0.2.0

type Schedule struct {
	ID             string         `db:"id" json:"id"`
	TopicID        string         `db:"topic_id" json:"topic_id"`
	Name           string         `db:"name" json:"name"`
	CronExpression string         `db:"cron_expression" json:"cron"`
	Timezone       string         `db:"timezone" json:"timezone"`
	Message        string         `db:"message" json:"message"`
	Headers        string         `db:"headers" json:"-"`
	CorrelationID  sql.NullString `db:"correlation_id" json:"-"`
	Priority       int            `db:"priority" json:"priority"`
	MisfirePolicy  string         `db:"misfire_policy" json:"misfire_policy"`
	OverlapPolicy  string         `db:"overlap_policy" json:"overlap_policy"`
	Paused         bool           `db:"paused" json:"paused"`
	Version        int            `db:"version" json:"version"`
	NextRunAt      time.Time      `db:"next_run_at" json:"next_run_at"`
	OwnerID        sql.NullString `db:"owner_id" json:"-"`
	LeaseExpiresAt sql.NullTime   `db:"lease_expires_at" json:"-"`
	FencingToken   int64          `db:"fencing_token" json:"-"`
	CreatedAt      time.Time      `db:"created_at" json:"created_at"`
	UpdatedAt      time.Time      `db:"updated_at" json:"updated_at"`
}

func (Schedule) MarshalJSON added in v0.2.0

func (schedule Schedule) MarshalJSON() ([]byte, error)

func (Schedule) PublicHeaders added in v0.2.0

func (schedule Schedule) PublicHeaders() map[string]string

type ScheduleInput added in v0.2.0

type ScheduleInput struct {
	Name           string            `json:"name"`
	CronExpression string            `json:"cron"`
	Timezone       string            `json:"timezone,omitempty"`
	Message        string            `json:"message"`
	Headers        map[string]string `json:"headers,omitempty"`
	CorrelationID  string            `json:"correlation_id,omitempty"`
	Priority       int               `json:"priority,omitempty"`
	MisfirePolicy  string            `json:"misfire_policy,omitempty"`
	OverlapPolicy  string            `json:"overlap_policy,omitempty"`
}

type ScheduleRun added in v0.2.0

type ScheduleRun struct {
	ID           string         `db:"id" json:"id"`
	ScheduleID   string         `db:"schedule_id" json:"schedule_id"`
	MessageID    sql.NullString `db:"message_id" json:"-"`
	ScheduledFor time.Time      `db:"scheduled_for" json:"scheduled_for"`
	StartedAt    time.Time      `db:"started_at" json:"started_at"`
	FinishedAt   sql.NullTime   `db:"finished_at" json:"-"`
	Status       string         `db:"status" json:"status"`
	Error        sql.NullString `db:"error" json:"-"`
	CreatedAt    time.Time      `db:"created_at" json:"created_at"`
}

func (ScheduleRun) MarshalJSON added in v0.2.0

func (run ScheduleRun) MarshalJSON() ([]byte, error)

type ScheduleRunPage added in v0.2.0

type ScheduleRunPage struct {
	Runs       []ScheduleRun `json:"runs"`
	NextCursor string        `json:"next_cursor,omitempty"`
}

type Subscriber added in v0.2.0

type Subscriber struct {
	ID        uuid.UUID         `db:"id" json:"id"`
	TopicID   uuid.UUID         `db:"topic_id" json:"topic_id"`
	TopicName string            `db:"topic_name" json:"topic_name,omitempty"`
	Name      string            `db:"name" json:"name"`
	Options   SubscriberOptions `db:"option" json:"options"`
	Paused    bool              `db:"paused" json:"paused"`
	CreatedAt time.Time         `db:"created_at" json:"created_at"`
	DeletedAt *time.Time        `db:"deleted_at" json:"deleted_at,omitempty"`
}

func NewSubscriber added in v0.2.0

func NewSubscriber(topic Topic, name string, options SubscriberOptions) Subscriber

type SubscriberOptions added in v0.2.0

type SubscriberOptions struct {
	MaxAttempts        int         `json:"max_attempts"`
	VisibilityDuration string      `json:"visibility_duration"`
	DequeueBatchSize   int         `json:"dequeue_batch_size,omitempty"`
	RetryPolicy        RetryPolicy `json:"retry_policy,omitempty"`
}

func (*SubscriberOptions) Scan added in v0.2.0

func (options *SubscriberOptions) Scan(source any) error

func (SubscriberOptions) Value added in v0.2.0

func (options SubscriberOptions) Value() (driver.Value, error)

type SubscriberQueueStats

type SubscriberQueueStats struct {
	Pending   int `db:"pending"`
	Delivered int `db:"delivered"`
}

type SubscriberStatus added in v0.2.0

type SubscriberStatus struct {
	TopicID            uuid.UUID `json:"topic_id"`
	Name               string    `json:"name"`
	UnpublishedMessage int       `json:"unpublished_message"`
	UnackedMessage     int       `json:"unacked_message"`
}

type SubscriberStatuses added in v0.2.0

type SubscriberStatuses []SubscriberStatus

type Subscribers added in v0.2.0

type Subscribers []Subscriber

type Topic added in v0.2.0

type Topic struct {
	ID        uuid.UUID  `db:"id" json:"id"`
	Name      string     `db:"name" json:"name"`
	Paused    bool       `db:"paused" json:"paused"`
	CreatedAt time.Time  `db:"created_at" json:"created_at"`
	DeletedAt *time.Time `db:"deleted_at" json:"deleted_at,omitempty"`
}

func NewTopic added in v0.2.0

func NewTopic(name string) Topic

type TopicFilter added in v0.2.0

type TopicFilter struct {
	Names       []string
	WithDeleted bool
}

type Topics added in v0.2.0

type Topics []Topic

type WriterOptions added in v0.2.0

type WriterOptions struct {
	BatchSize          int
	FlushInterval      time.Duration
	MaxPendingMessages int64
	MaxPendingBytes    int64
	RetryMin           time.Duration
	RetryMax           time.Duration
	// contains filtered or unexported fields
}

WriterOptions controls batching and the weighted pending budget.

func DefaultWriterOptions added in v0.2.0

func DefaultWriterOptions() WriterOptions

Directories

Path Synopsis
cmd
blockqueue command
example
basic command
Package main demonstrates using BlockQueue as an imported Go library.
Package main demonstrates using BlockQueue as an imported Go library.
transactional command
Package main demonstrates transactional enqueueing and transactional delivery completion with BlockQueue and an application table in one SQLite database.
Package main demonstrates transactional enqueueing and transactional delivery completion with BlockQueue and an application table in one SQLite database.
internal
testdb
Package testdb provides guarded, schema-isolated PostgreSQL databases for integration tests and benchmarks.
Package testdb provides guarded, schema-isolated PostgreSQL databases for integration tests and benchmarks.
pkg
Package store defines the small database boundary used by BlockQueue.
Package store defines the small database boundary used by BlockQueue.
postgres
Package postgres provides the production PostgreSQL storage driver.
Package postgres provides the production PostgreSQL storage driver.
sqlite
Package sqlite provides the production SQLite storage driver.
Package sqlite provides the production SQLite storage driver.
turso
Package turso provides experimental libSQL/Turso storage support.
Package turso provides experimental libSQL/Turso storage support.

Jump to

Keyboard shortcuts

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