fdbudget

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Aug 24, 2026 License: Apache-2.0 Imports: 2 Imported by: 0

Documentation

Overview

Package fdbudget apportions a process's file-descriptor limit among the consumers that hold an fd per unit of concurrent work: live WebSocket connections and in-flight plugin calls (each pins its inbound request fd for the call's duration). If either consumer sized itself against the whole limit independently, N of them could collectively exhaust it — the "accept: too many open files" cliff that takes down Postgres, Docker, and Redis together, before any single consumer's own admission control sheds load.

So the reserve for everything else (DB pool, Docker, Redis, S3, HTTP) is taken ONCE, and consumers Claim from the shared remainder in priority order: the essential, fixed-size consumers (plugin calls — auth/scoring) claim first and are guaranteed their budget; the elastic consumer (WebSockets, degradable to polling) claims last and absorbs whatever is left. RLIM_INFINITY / an unreadable limit leaves every claim unclamped.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Budget

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

Budget is the shared fd accountant. Construct once at startup, Claim in priority order.

func New

func New(fdSoftLimit uint64) *Budget

New computes the one-time reserve (a quarter of the limit, at least 256) and the claimable remainder. A zero or implausibly large soft limit yields an unbounded budget (claims pass through unchanged), matching a host with no meaningful fd constraint.

func (*Budget) Claim

func (b *Budget) Claim(name string, want int) (granted int, clamped bool)

Claim grants up to want fds to a named consumer, subtracting from the shared remainder. A want of 0 or less means "as much as available" and is always clamped to the remainder; a want above the remainder is clamped down to it. Returns the grant and whether the fd limit (not the configured value) was the binding constraint. Claim in priority order: essential fixed consumers first, the elastic one last.

func (*Budget) Split

func (b *Budget) Split() (soft, reserved, remaining uint64, claims []Claim)

Split returns the soft limit, the one-time reserve, the still-unclaimed remainder, and every claim in order — so startup can log exactly where the fds went.

func (*Budget) String

func (b *Budget) String() string

String renders the split as a single operator-readable line for the startup log.

type Claim

type Claim struct {
	Name    string // consumer, e.g. "plugin-inflight" or "websocket-conns"
	Want    int    // what it asked for (0 or negative means "as much as available")
	Granted int    // what it actually got after clamping to the remainder
	Clamped bool   // true if Granted < Want (the limit, not the config, is binding)
}

Claim records one consumer's apportionment, for logging the derived split at startup.

Jump to

Keyboard shortcuts

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