Help us shape the future of Go! Take the 2026 Go Developer Survey

filegate

package
v0.24.0 Latest Latest
Warning

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

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

Documentation

Overview

Package filegate queues the writers in one process that share a database file, first come first served, with lease statements ahead of the rest.

SQLite lets one writer at a time hold a file's write lock, and a writer that finds it taken waits in SQLite's busy handler: a sleep, then another try, with no queue. A writer that just released the lock tries again at once and usually wins it back from the writers asleep in the handler, so under a burst of writers a single unlucky one can wait for everyone else's whole run, which a long busy_timeout only postpones failing on. A gate in front of the write makes the writers in one process take turns, so the number of writers that contend for the file is the number of processes, and a slow disk (where each write is long) cannot starve one of them.

A lease renewal has a deadline (a fraction of the lease's TTL) that an ordinary write does not, so EnterFirst puts it ahead of every waiting ordinary writer; it waits only for the writer already inside.

Handles are matched by the identity of the file (device and inode, or volume and file index), so every spelling of a path (a relative path, a symlink, a different case on a case-insensitive file system) shares one gate. A gate does not order writers in different processes: they still contend through SQLite.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Gate

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

A Gate is a handle on the queue of one database file.

func For

func For(path string) *Gate

For returns the gate of the database file at path, shared by every handle in the process that opens the same file. A file that does not exist yet is created empty first (SQLite takes an empty file for a new database), so that it has an identity; if it cannot be, the gate is keyed by the cleaned absolute path. An in-memory database (":memory:" or a "mode=memory" URI) is never shared between handles, so its gate admits writers without queueing. Release the handle with Close.

func (*Gate) Close

func (h *Gate) Close()

Close drops the handle; the gate is forgotten once every handle on it is closed. Closing a handle twice is the same as once. A closed handle still admits writers.

func (*Gate) Enter

func (h *Gate) Enter(ctx context.Context) (leave func(), err error)

Enter waits for this handle's turn to write and returns the function that ends it, which the caller must call exactly once. It returns ctx's error if ctx ends first, without taking a turn.

func (*Gate) EnterFirst

func (h *Gate) EnterFirst(ctx context.Context) (leave func(), err error)

EnterFirst is Enter for a statement with a deadline: it is admitted ahead of every writer waiting in Enter, behind only the writer inside and the EnterFirst callers that asked before it.

func (*Gate) Waiting added in v0.19.0

func (h *Gate) Waiting() int

Waiting is the number of writers queued behind the one inside, in Enter and EnterFirst. A test uses it to hold the gate until every writer it started is queued.

Jump to

Keyboard shortcuts

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