package main import ( "context" "fmt" "time" "github.com/google/uuid" ) type Queue struct { driver Driver } func NewQueue(ctx context.Context, driver Driver) (Queue, error) { _, err := driver.ExecContext(ctx, ` DROP TABLE IF EXISTS queue; CREATE TABLE IF NOT EXISTS queue ( id INTEGER PRIMARY KEY, updated INTEGER NOT NULL, reservation TEXT, payload TEXT ); `) return Queue{driver: driver}, err } func (q Queue) Enqueue(ctx context.Context, b []byte) error { _, err := q.driver.ExecContext(ctx, ` INSERT INTO queue (updated, payload) VALUES (?, ?) `, time.Now().Unix(), b, ) return err } func (q Queue) Dequeue(ctx context.Context) (string, []byte, error) { for { reservation, m, err := q.dequeue(ctx) if reservation != nil || err != nil { return string(reservation), m, err } select { case <-ctx.Done(): return "", nil, ctx.Err() case <-time.After(time.Second): } } } func (q Queue) dequeue(ctx context.Context) ([]byte, []byte, error) { now := time.Now().Unix() reservation := []byte(uuid.New().String()) var payload []byte if result, err := q.driver.ExecContext(ctx, ` UPDATE queue SET updated = ?, reservation = ? WHERE id IN ( SELECT id FROM queue WHERE reservation IS NULL OR ? - updated > 60 LIMIT 1 ) `, now, reservation, now); err != nil { return nil, nil, fmt.Errorf("failed to assign reservation: %w", err) } else if n, err := result.RowsAffected(); err != nil { return nil, nil, fmt.Errorf("failed to assign reservation: no count: %w", err) } else if n == 0 { return nil, nil, fmt.Errorf("failed to assign reservation: zero updates") } rows, err := q.driver.QueryContext(ctx, ` SELECT payload FROM queue WHERE reservation==? LIMIT 1 `, reservation) if err != nil { return nil, nil, fmt.Errorf("failed to query reservation: %w", err) } defer rows.Close() for rows.Next() { if err := rows.Scan(&payload); err != nil { return nil, nil, fmt.Errorf("failed to parse reservation: %w", err) } } if err := rows.Err(); err != nil { return nil, nil, fmt.Errorf("failed to page reservation: %w", err) } return reservation, payload, nil } func (q Queue) Ack(ctx context.Context, reservation string) error { _, err := q.driver.ExecContext(ctx, ` DELETE FROM queue WHERE reservation==? `, reservation) return err }