memlimit

package
v0.48.0 Latest Latest
Warning

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

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

Documentation

Overview

Package memlimit reports the memory budget the process actually has, so a size derived from it (the merge cap) tracks the container it runs in rather than a constant that is right at exactly one deployment size.

It is deliberately dependency-free: the answer comes from GOMEMLIMIT, the cgroup, or the host's total memory, in that order of authority.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Bytes

func Bytes() int64

Bytes returns the memory the process may use, or 0 when nothing reports one.

GOMEMLIMIT wins when set: it is what the embedder promised the Go heap, and an embedder that applies a cgroup limit to it (automemlimit and friends) has already reserved its own headroom. Otherwise the cgroup limit is used, since a container's limit — not the host's RAM — is what the kernel kills against, and the host total is the last resort.

func MergeBudget added in v0.48.0

func MergeBudget(configured int64) int64

MergeBudget is how many bytes every concurrent merge may hold together: the caller's configured allowance, or a share of the detected process budget (Bytes) when it is 0. A negative value opts out, returning math.MaxInt64 so the caller's other bounds decide alone.

func MergeConcurrency added in v0.48.0

func MergeConcurrency(configured int64, cpuLimit int) int

MergeConcurrency is how many merges the budget supports: enough that each gets at least [minMergeBytes], never more than cpuLimit, never fewer than one.

Deriving it from memory rather than from cores is the point. A merge's *allowance* is the budget divided by this number, so taking the divisor from the core count prices a memory quantity in CPUs: a 16-core pod with a 4 GiB limit would give each merge 32 MiB and produce a great many merges too small to keep up with ingest. cpuLimit still caps it from above — merging is CPU-bound work, and there is nothing to gain from more merges than there are cores to run them.

func MergeShare

func MergeShare(configured int64, concurrency, amplification int) int64

MergeShare is how many bytes one merge may hold: MergeBudget divided across the merges that may run at once and by amplification, the peak resident per byte of the bound (a merge that holds its output twice over passes 2).

func QueryShare added in v0.43.0

func QueryShare(configured int64) int64

QueryShare is how many bytes one query may read before it is refused, derived from the process budget the same way MergeShare derives the merge allowance.

configured is the caller's cap: 0 takes a share of the detected process budget (Bytes), and a negative value opts out. Opting out returns 0 rather than math.MaxInt64, because the consumer spells "unbounded" as "install no limiter at all" — an effectively-infinite ceiling would still pay to count every byte it will never reject.

Types

type Pool added in v0.48.0

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

Pool admits work by the bytes it intends to hold, so a budget divided across concurrent merges is one they actually have.

Without it the per-merge share is a promise nobody keeps: the facade fans merges out on its own schedule, every one of them sizes itself against "the budget divided by the concurrency", and nothing stops more than that many from running. The division then understates what a merge may hold *and* overstates how many may hold it — wrong in both directions at once.

A zero or negative total, and the nil Pool, admit everything: an embedder that opts out of the memory bound gets no gate rather than a gate of size zero.

func NewPool added in v0.48.0

func NewPool(total int64) *Pool

NewPool returns a pool admitting total bytes at once.

func (*Pool) Acquire added in v0.48.0

func (p *Pool) Acquire(ctx context.Context, n int64) (release func(), err error)

Acquire blocks until n bytes are free, then returns the function that hands them back. The release is idempotent, so `defer release()` is the correct use.

A request larger than the whole pool is clamped to it: such a merge runs alone rather than never, because a bound that can deadlock its own work is worse than one that is occasionally exceeded.

Waiters are served strictly in arrival order, so a queued large request delays smaller ones behind it rather than being starved by them. Requests are near-uniform — each caller asks for its share of one budget — but not identical: the share is divided by a concurrency that rises as engines appear, so a waiter queued when the process held one engine asks for more than one queued later.

func (*Pool) Total added in v0.48.0

func (p *Pool) Total() int64

Total reports the pool's size, 0 for a pool that admits everything.

func (*Pool) TryAcquire added in v0.48.0

func (p *Pool) TryAcquire(n int64) (release func(), ok bool)

TryAcquire takes n bytes if they are free right now, reporting whether it got them. It never queues, so a caller on a shared goroutine — the maintenance loop, which also services flush pressure — can decline the work and come back next cycle instead of parking everything behind it.

func (*Pool) Waiting added in v0.48.0

func (p *Pool) Waiting() int

Waiting reports how many callers are queued for bytes. It exists for tests that need to race the grant itself rather than the waiter's arrival, and for an operator counter should one be wanted.

Jump to

Keyboard shortcuts

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