whspool

package
v1.147.0 Latest Latest
Warning

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

Go to latest
Published: Oct 17, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Overview

Package whspool is a webhook receiver's local write-ahead spool (#2121): the events of an acknowledged request are appended to a file on this replica's disk and synced before the request is answered, then drained to object storage in the background.

Without it a request is answered only once its events are in object storage, so a sender's wait is object storage's latency at that moment, and a slow object store fails deliveries at the sender while the platform is healthy. With it the wait is a local fsync.

The spool holds one directory per source. Each source has at most one active file, which requests append to, and any number of sealed files waiting to be drained, oldest first. A file is named for the process that created it and a sequence, which is also the name its segments are written under in object storage: draining a file a second time -- after a crash between its write and its removal -- overwrites the same objects instead of adding to them, so every acknowledged event lands once.

A record is its length, its CRC-32C and its JSON. A record whose sync did not finish was never acknowledged, so the torn tail a crash leaves is cut off when the spool is opened.

Index

Constants

View Source
const (
	DefaultMaxBytes int64 = 1 << 30
	DefaultMaxAge         = time.Hour
)

Defaults for the bounds past which a spool refuses new requests.

Variables

View Source
var ErrClosed = errors.New("the receiver's spool is closed")

ErrClosed is an append after the spool was closed.

View Source
var ErrFull = errors.New("the receiver's spool is full")

ErrFull is an append refused because the spool holds as much as it may: its files' bytes, or the age of its oldest event, are at their bound. The request is answered 503 so its sender retries.

Functions

This section is empty.

Types

type Options

type Options struct {
	// Dir is the spool's directory, created when it does not exist.
	Dir string
	// MaxBytes bounds the bytes of every file in the spool. Zero takes
	// DefaultMaxBytes.
	MaxBytes int64
	// MaxAge bounds the age of the oldest event not yet drained. Zero takes
	// DefaultMaxAge.
	MaxAge time.Duration
	// Replica is this process's identity, which the files it creates are
	// named for.
	Replica string
	Now     func() time.Time
}

Options open a spool.

type Record

type Record struct {
	Hash   string          `json:"hash"`
	Events []whevent.Event `json:"events"`
}

Record is one acknowledged request: the SHA-256 of its body and its events.

type Segment

type Segment struct {
	Source string
	// Writer and Seq name the file, and the objects it is drained to.
	Writer string
	Seq    uint64
	Bytes  int64
	Events int
	// Oldest is when the file's first event was received.
	Oldest time.Time
	// contains filtered or unexported fields
}

Segment is one sealed spool file waiting to be drained.

type Spool

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

Spool is one replica's spool directory.

func Open

func Open(o Options) (*Spool, error)

Open opens the spool at o.Dir, taking this process's lock on it, and reads what an earlier process left there: every file is sealed, a torn tail is cut off, and a file with no whole record is removed.

func (*Spool) Active

func (s *Spool) Active(source string) (Stats, bool)

Active reports what the source's active file holds, as the Stats of that one file, and false when it has none.

func (*Spool) Append

func (s *Spool) Append(source string, rec Record) (time.Duration, error)

Append writes rec to the source's active file and syncs it. Once it returns nil the record survives a crash, and the request may be answered. It returns ErrFull when the spool is at a bound and ErrClosed once closed, and how long the write and the sync took.

func (*Spool) Close

func (s *Spool) Close() error

Close seals every active file and releases the lock. Nothing is lost: the next process to open the spool drains what it holds.

func (*Spool) Dir

func (s *Spool) Dir() string

Dir is the spool's directory.

func (*Spool) Read

func (*Spool) Read(seg Segment) ([]Record, error)

Read returns a sealed file's records.

func (*Spool) Remove

func (s *Spool) Remove(seg Segment) error

Remove deletes a drained file.

func (*Spool) Seal

func (s *Spool) Seal(source string) error

Seal closes the source's active file, synced, and queues it to be drained. The next append opens a new one.

func (*Spool) Sealed

func (s *Spool) Sealed(source string) []Segment

Sealed returns the source's sealed files, oldest first.

func (*Spool) Sources

func (s *Spool) Sources() []string

Sources returns every source the spool holds a file for, sorted.

func (*Spool) Stats

func (s *Spool) Stats() Stats

Stats reports what the spool holds.

type Stats

type Stats struct {
	Bytes  int64
	Events int64
	Files  int
	// Oldest is when the oldest event not yet drained was received; zero
	// when the spool is empty.
	Oldest time.Time
}

Stats is what a spool holds: every file, active and sealed.

type Volume added in v1.147.0

type Volume struct {
	Total uint64
	Free  uint64
}

Volume is the filesystem a spool directory is on (#2148): its size and the bytes an unprivileged writer can still use. The spool refuses deliveries at its own max_bytes, but a volume shared with anything else can fill first.

func VolumeOf added in v1.147.0

func VolumeOf(dir string) (Volume, error)

VolumeOf reads the filesystem dir is on.

Jump to

Keyboard shortcuts

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