backpressure

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Jul 31, 2026 License: MIT Imports: 8 Imported by: 0

README

Go Backpressure

Go Reference Go License

Unified client-side backpressure for Go services.

go-backpressure helps a client service stop overwhelming a degraded downstream. Instead of choosing between "send 100% of traffic" and "turn the dependency off", it continuously adjusts how much traffic is allowed through.

The core package is intentionally small and generic. It has no required metrics, logging, tracing, Redis, HTTP, RPC, or framework dependency. Consumers decide how to classify outcomes, expose observability, and connect it to their own systems.

What You Get

  • Controlled degradation instead of all-or-nothing switches. Keep sending a safe percentage of useful calls while a downstream is recovering.
  • Fast local decisions. Allow, Acquire, and direct Report complete in roughly 170-230 ns/op in the current benchmark suite.
  • No hot-path heap allocations. Direct decisions, reports, attrs, observers, and samplers stay at 0 allocs/op in the measured paths.
  • Cheaper failure handling. A local reject is hundreds of nanoseconds; a downstream timeout is usually milliseconds or seconds.
  • Unified integration model. The same controller API works for Redis, HTTP, RPC, databases, queues, and custom operations.
  • Caller-owned semantics. Your service decides whether cache miss, 404, validation errors, timeouts, overload signals, or partial results should affect backpressure.

Why This Matters

Downstream systems rarely fail cleanly. Redis gets slower before it disappears. An RPC service starts timing out on a subset of calls. An HTTP dependency begins returning 429 or 503 under load. If every client keeps sending the same volume of traffic, the degradation feeds itself:

  1. The downstream slows down or errors more often.
  2. Clients wait longer, retry more, and pile up goroutines.
  3. The downstream spends more work on requests that will time out anyway.
  4. Latency and error rate grow across the system.

A circuit breaker is useful when a dependency is plainly broken, but it is often too binary for partial degradation. This library gives you a middle ground: allow 100%, then 80%, then 50%, then 10%, and recover smoothly when the downstream becomes healthy again.

Local rejection is cheaper than a network timeout. A controlled fallback is better than a cascading outage.

What Problem It Solves

go-backpressure protects any client-side operation:

  • Redis and cache reads or writes
  • HTTP clients
  • RPC or gRPC calls
  • database calls
  • queue producers
  • any custom operation where overload should reduce traffic

The core package is intentionally unified. It does not know what Redis, HTTP, gRPC, queues, or databases are. Your code classifies each result as Success, Failure, Neutral, or Overload, and the controller adapts from those generic signals.

That design matters because err != nil is not always infrastructure failure:

  • cache miss: usually Neutral
  • HTTP 404: usually Neutral
  • validation error: usually Neutral
  • timeout: Failure
  • connection refused: Failure
  • HTTP 429/503: Overload

Install

go get github.com/istoliarov/go-backpressure

Requires Go 1.22 or newer.

Documentation is published on pkg.go.dev after the Go module proxy has seen the tagged version:

https://pkg.go.dev/github.com/istoliarov/go-backpressure@v0.1.1

If the page is not visible immediately after a release, request the version once:

GOPROXY=https://proxy.golang.org go list -m github.com/istoliarov/go-backpressure@v0.1.1

pkg.go.dev indexes modules from proxy.golang.org and normally adds new versions within a few minutes. See the official pkg.go.dev package adding docs for the details. The badge in this README is a documentation link, not a separate package registry.

Quick Start

package cache

import (
	"context"
	"errors"

	"github.com/istoliarov/go-backpressure"
)

var cacheBP = backpressure.New("cache_read", backpressure.DefaultConfig())

func GetUser(ctx context.Context, id string) (User, error) {
	permit, decision := cacheBP.Acquire(
		ctx,
		backpressure.AttrKey("operation", "cache_read"),
		backpressure.AttrKey("key", id),
	)
	if !decision.Allowed {
		return loadUserFromPrimary(ctx, id)
	}

	user, err := redisGetUser(ctx, id)
	permit.Report(classifyCacheRead(user, err))
	return user, err
}

func classifyCacheRead(user User, err error) backpressure.Outcome {
	switch {
	case err == nil:
		return backpressure.Success()
	case errors.Is(err, ErrCacheMiss):
		return backpressure.Neutral()
	case errors.Is(err, context.DeadlineExceeded):
		return backpressure.Failure(err)
	default:
		return backpressure.Failure(err)
	}
}

Safer Wrapper

Use Do when you want the library to guarantee that Report happens exactly once. Panics are reported as failures and then re-thrown.

user, err := backpressure.Do(
	ctx,
	cacheBP,
	func(ctx context.Context) (User, error) {
		return redisGetUser(ctx, id)
	},
	func(ctx context.Context, decision backpressure.Decision) (User, error) {
		return loadUserFromPrimary(ctx, id)
	},
	func(user User, err error) backpressure.Outcome {
		return classifyCacheRead(user, err)
	},
	backpressure.AttrKey("operation", "cache_read"),
	backpressure.AttrKey("key", id),
)

Pseudocode

Cache Read
decision = backpressure.acquire("cache_read", key=user_id)

if decision.rejected:
    return primary_database.get(user_id)

value, error = redis.get(user_id)

if value.exists:
    decision.report(success)
elif error == cache_miss:
    decision.report(neutral)
elif error == timeout:
    decision.report(failure)
else:
    decision.report(failure)

return value, error
HTTP Client
decision = backpressure.acquire("http", host=api.example.com)

if decision.rejected:
    return cached_response_or_local_error()

response, error = http_client.do(request)

if error:
    decision.report(failure)
elif response.status in [429, 503]:
    decision.report(overload)
elif response.status >= 500:
    decision.report(failure)
elif response.status >= 400:
    decision.report(neutral)
else:
    decision.report(success)

Integration Examples

The core package is intentionally protocol agnostic. Examples use small local interfaces and fake clients so they compile without pulling in Redis, gRPC, database drivers, queue SDKs, metrics clients, or tracing libraries.

Scenario Example What it demonstrates
Cache read examples/cache Cache miss as Neutral, key-based sampling, fallback with Do
Generic operation examples/generic_operation Minimal manual Acquire/Report flow
gRPC-style RPC examples/grpc_style Mapping RPC status-like codes to Success, Failure, Neutral, Overload
Database read examples/database Treating no rows and retryable business errors differently from overload
Queue publish examples/queue Local buffering on reject and overload classification for a full broker
HTTP client httpbp Optional http.RoundTripper adapter for HTTP status classification

The examples are integration patterns rather than blessed adapters. In real services, keep the classification function close to the dependency owner: that team knows whether a 404, cache miss, duplicate message, retryable transaction, or resource-exhausted response should affect backpressure.

RPC Call
decision = backpressure.acquire("rpc", method=GetProfile)

if decision.rejected:
    return fallback_profile()

reply, error = rpc.call(GetProfile)

if error.code in [Unavailable, DeadlineExceeded]:
    decision.report(failure)
elif error.code == ResourceExhausted:
    decision.report(overload)
elif error:
    decision.report(neutral)
else:
    decision.report(success)

Unified By Design

The core API speaks in operational signals, not protocols.

type OutcomeClass int

const (
	OutcomeSuccess OutcomeClass = iota
	OutcomeFailure
	OutcomeNeutral
	OutcomeOverload
)

Your application, wrapper, or optional adapter can translate HTTP status codes, gRPC status codes, Redis errors, queue errors, database errors, or business-specific rules into these outcomes. The adaptive controller stays small, stable, and reusable.

What The Core Does Not Do

The core package does not:

  • import a metrics client;
  • import a logging or tracing SDK;
  • decide whether err != nil is always a failure;
  • assume HTTP, gRPC, Redis, queues, or databases;
  • start goroutines per request;
  • coordinate state between service instances.

Those choices belong to the consuming service. The library provides the controller, decisions, outcomes, snapshots, and observer hooks.

Algorithms

SRE Adaptive Throttling

The default strategy tracks requests and accepted responses in a rolling window. When attempted requests grow too far beyond accepted requests, the controller starts rejecting a controlled percentage locally.

dropRatio = max(0, (requests - K * accepts) / (requests + 1))

This is useful for protecting dependencies that degrade gradually.

Pressure Strategy

The pressure strategy keeps a pressure score:

  • failures increase pressure
  • overload signals increase pressure faster
  • successes decrease pressure
  • time decay lets the system recover even at low traffic

The pressure score maps linearly to a pass percentage between MaxPassPercent and MinPassPercent.

Custom Strategies

If the built-in algorithms are not the right fit, consumers can register their own strategy while keeping the same public controller API.

cfg := backpressure.DefaultConfig()
cfg.Strategy = "my_strategy"

bp := backpressure.New(
	"search",
	cfg,
	backpressure.WithStrategy("my_strategy", myStrategy),
)

The custom strategy still receives generic Config, Attr, and Outcome values. The core package remains protocol agnostic.

Custom strategy support is experimental until v1. The stable integration path is the built-in controller API with caller-owned outcome classification.

Sampling

Built-in samplers cover the common generic cases:

  • SequenceSampler: even distribution across the request stream;
  • KeySampler: stable behavior for the same attr value, such as user, tenant, shard, or cache key;
  • RandomSampler: pseudo-random distribution without global math/rand.

Performance

The core hot path is designed to sit in front of high-volume client operations. In practice, that means a controller can protect a Redis call, RPC call, HTTP request, queue publish, or custom operation without adding a meaningful amount of local CPU overhead compared with the cost of the downstream call itself.

The current benchmark suite measures both the ideal path and common real-world paths: attrs, observers, rejected decisions, reports, wrappers, and samplers.

Example benchmark on AMD Ryzen 7 7800X3D, Go 1.24.4, Windows:

Benchmark ns/op B/op allocs/op
Baseline atomic add 1.6 0 0
Baseline mutex lock/unlock 3.7 0 0
Allow allowed 176.1 0 0
Allow allowed with attrs 168.2 0 0
Acquire allowed 178.1 2 0
Acquire allowed with attrs 179.7 5 0
Acquire rejected 183.2 0 0
Acquire with observer 195.5 2 0
Direct report success 215.3 0 0
Direct report failure 223.2 0 0
Direct report with observer 227.8 0 0
Do wrapper 359.1 48 1
Sequence sampler 1.6 0 0
Key sampler 7.9 0 0
Random sampler 4.7 0 0
How To Read These Numbers
  • Allow is the cheapest API when you only need a decision.
  • Acquire returns a Permit and is still allocation-free on the measured hot path.
  • Acquire with attrs remains allocation-free; attrs are only copied into the permit when the call is allowed so delayed Report sees stable values.
  • Acquire with observer shows the cost of synchronous observer callbacks without doing any logging or metrics work inside the callback.
  • Direct Report is an escape hatch that counts a standalone attempted operation. For normal request flow, prefer Acquire plus Permit.Report.
  • Do wrapper is the ergonomic safety API. It costs more than manual Acquire/Report, but still stays below a microsecond in this benchmark.
  • Samplers are effectively free compared with controller decisions.

The useful comparison is not against a single mutex or atomic operation. The useful comparison is against what this avoids: expensive network timeouts, retry storms, goroutine buildup, and cascading downstream degradation.

Run locally:

go test -run=^$ -bench=. -benchmem ./...

Exact numbers depend on hardware, operating system, CPU power mode, Go version, compiler behavior, and background load. Treat the table above as an illustrative snapshot, not a contractual performance guarantee. CI also runs the benchmark suite on Ubuntu so obvious performance regressions are visible in job logs, but GitHub-hosted runners are shared machines and are not suitable for precise microbenchmark claims.

The important property is the shape: local allow/reject/report decisions are measured in hundreds of nanoseconds, while avoided downstream timeouts are normally configured in milliseconds or seconds.

For example, even a 10 ms timeout is roughly 50,000x slower than a 200 ns local decision. Backpressure does not make downstream calls faster; it helps you avoid sending calls that are likely to waste time and amplify overload.

Runtime Configuration

Controllers can read configuration from a provider on every Acquire and Report, so you can tune behavior without recreating the controller.

Start from DefaultConfig() for normal use. A zero-value Config{} normalizes to a disabled fail-open controller, so partially initialized config does not accidentally enable shedding.

Explicit zero values are preserved where zero is meaningful. For example, MaxPassPercent = 0 can intentionally close traffic, and MinSamples = 0 can intentionally disable warm-up.

cfg := backpressure.DefaultConfig()
cfg.MinPassPercent = 10
cfg.MaxPassPercent = 100
cfg.ShadowMode = true

bp := backpressure.New("cache_read", cfg)

// Later:
cfg.ShadowMode = false
cfg.MinPassPercent = 5
bp.UpdateConfig(cfg)

Observability

The core package has an observer interface instead of depending on a specific metrics library. Use it to connect decisions and reports to whatever your service already uses.

Observer callbacks are synchronous and run on the caller's hot path. Keep them fast and non-blocking; if you need buffering, batching, logging, or network I/O, do that behind your own adapter.

Useful metrics to expose:

  • backpressure_decisions_total{controller,reason,allowed}
  • backpressure_reports_total{controller,outcome}
  • backpressure_local_rejects_total{controller,reason}
  • backpressure_pass_percent{controller}
  • backpressure_drop_ratio{controller}
  • backpressure_pressure{controller}
  • backpressure_window_requests{controller}
  • backpressure_window_accepts{controller}
  • backpressure_shadow_rejects{controller}

Every controller also exposes a Snapshot for debug endpoints.

snapshot := bp.Snapshot()

Optional Packages

Optional packages may provide thin examples for common protocols, but the core library does not require them. You can ignore every adapter and call Acquire/Report directly around your own operation.

Production Rollout

  1. Start in shadow mode and observe what would have been rejected.
  2. Enable with a conservative floor, for example MinPassPercent = 80.
  3. Watch local rejects, pass percent, drop ratio, downstream latency, and errors.
  4. Lower MinPassPercent gradually to production values.
  5. Keep fallback behavior explicit and cheap.

FAQ

Is this a circuit breaker?

No. A circuit breaker is usually binary: closed, open, half-open. This library is percentage-based. It can pass 92%, 70%, 35%, or 5% of traffic depending on the health signals it receives.

Should cache miss be a failure?

Usually no. A cache miss is a normal business result. Treating it as failure can make the controller reduce cache traffic when the cache is actually behaving correctly.

When should I use key-based sampling?

Use key-based sampling when you want stable behavior for the same user, tenant, or cache key. Use sequence sampling when you want an even distribution across the request stream and do not want the same keys to be skipped repeatedly.

What happens on bad config?

The library is fail-open by default. Invalid critical configuration allows traffic and emits an observer event rather than breaking the caller.

Status

This project is being built as a small, production-oriented core with optional adapters around it. The public API is intended to stay compact and protocol agnostic.

Documentation

Overview

Package backpressure provides unified client-side adaptive throttling for Go services.

A controller sits before a downstream operation, decides whether the operation should be attempted, and learns from caller-classified outcomes. The package is protocol agnostic: Redis, HTTP, RPC, database calls, queues, and custom operations all report the same generic Success, Failure, Neutral, and Overload signals.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func AttrValue

func AttrValue(attrs []Attr, key string) (string, bool)

AttrValue returns the first value matching key.

func Do

func Do[T any](
	ctx context.Context,
	controller *Controller,
	call func(context.Context) (T, error),
	fallback func(context.Context, Decision) (T, error),
	classify func(T, error) Outcome,
	attrs ...Attr,
) (value T, err error)

Do wraps an operation with Acquire, fallback, classification, and exactly-once reporting. If call panics, Do reports a failure and re-panics.

Example
package main

import (
	"context"
	"fmt"

	"github.com/istoliarov/go-backpressure"
)

func main() {
	bp := backpressure.New("rpc", backpressure.DefaultConfig())

	value, err := backpressure.Do(
		context.Background(),
		bp,
		func(ctx context.Context) (string, error) {
			return "value", nil
		},
		func(ctx context.Context, decision backpressure.Decision) (string, error) {
			return "fallback", nil
		},
		func(value string, err error) backpressure.Outcome {
			if err != nil {
				return backpressure.Failure(err)
			}
			return backpressure.Success()
		},
		backpressure.AttrKey("operation", "lookup"),
	)
	fmt.Println(value, err)

}
Output:
value <nil>

Types

type Attr

type Attr struct {
	Key   string
	Value string
}

Attr carries optional low-cardinality context for sampling, snapshots, and observer callbacks.

func AttrKey

func AttrKey(key, value string) Attr

AttrKey creates an attribute from a string key and value.

type Config

type Config struct {
	Enabled    bool
	ShadowMode bool

	Strategy StrategyType

	MinPassPercent float64
	MaxPassPercent float64

	Window      time.Duration
	BucketCount int
	MinSamples  int64

	SREK float64

	PressureLimit    float64
	ErrorIncrease    float64
	OverloadIncrease float64
	SuccessDecrease  float64
	DecayPerSecond   float64

	NeutralPolicy NeutralPolicy
}

Config controls a controller. Start from DefaultConfig for normal use.

The zero value is fail-open disabled after normalization. This avoids accidentally enabling traffic shedding from a partially initialized config. Explicit zero values are preserved for fields where zero is meaningful, such as MinSamples, MinPassPercent, MaxPassPercent, and pressure weights.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns conservative production defaults.

type ConfigError

type ConfigError struct {
	Issues []string
}

ConfigError describes non-fatal configuration normalization issues.

func (ConfigError) Error

func (e ConfigError) Error() string

type ConfigProvider

type ConfigProvider interface {
	Snapshot() Config
}

ConfigProvider supplies runtime configuration snapshots.

type Controller

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

Controller decides whether client-side operations should be attempted and adapts from caller-reported outcomes.

func New

func New(name string, cfg Config, opts ...Option) *Controller

New creates a controller. Pass DefaultConfig() unless you intentionally use a custom configuration.

func (*Controller) Acquire

func (c *Controller) Acquire(ctx context.Context, attrs ...Attr) (Permit, Decision)

Acquire records an attempted operation, decides whether it should run, and returns a Permit for reporting the operation outcome when it is allowed.

Example
package main

import (
	"context"
	"errors"
	"fmt"
	"time"

	"github.com/istoliarov/go-backpressure"
)

func main() {
	bp := backpressure.New("cache_read", backpressure.DefaultConfig())

	permit, decision := bp.Acquire(context.Background(), backpressure.AttrKey("operation", "cache_read"))
	if !decision.Allowed {
		fmt.Println("fallback")
		return
	}

	value, err := readCache(context.Background(), "item:42")
	permit.Report(classifyCacheRead(value, err))
	fmt.Println("done")

}

func readCache(_ context.Context, key string) (string, error) {
	if key == "" {
		return "", errors.New("empty key")
	}
	return "cached", nil
}

func classifyCacheRead(value string, err error) backpressure.Outcome {
	switch {
	case value != "":
		return backpressure.Success()
	case err == nil:
		return backpressure.Neutral()
	case errors.Is(err, context.DeadlineExceeded):
		return backpressure.Failure(err).WithLatency(50 * time.Millisecond)
	default:
		return backpressure.Failure(err)
	}
}
Output:
done

func (*Controller) Allow

func (c *Controller) Allow(ctx context.Context, attrs ...Attr) Decision

Allow records an attempted operation and returns only a decision. Prefer Acquire when the caller can report a result through the returned Permit.

func (*Controller) Name

func (c *Controller) Name() string

Name returns the controller name used in snapshots and observer callbacks.

func (*Controller) Report

func (c *Controller) Report(outcome Outcome)

Report reports an outcome without a Permit and counts it as an attempted operation for built-in strategies. Prefer Permit.Report when the result belongs to a specific Acquire call.

func (*Controller) Snapshot

func (c *Controller) Snapshot() Snapshot

Snapshot returns the current controller state for debug endpoints and observability adapters.

func (*Controller) UpdateConfig

func (c *Controller) UpdateConfig(cfg Config)

UpdateConfig updates the controller config when it uses the default StaticConfig provider. Controllers created with WithConfigProvider should be updated through that provider instead.

type Decision

type Decision struct {
	Allowed     bool
	Reason      Reason
	PassPercent float64
	DropRatio   float64
	RetryAfter  time.Duration
	WouldReject bool
}

Decision describes the result of an Acquire or Allow call.

type ErrRejected

type ErrRejected struct {
	Decision Decision
}

ErrRejected is returned by Do and optional adapters when a request is locally rejected and no fallback is provided.

func (ErrRejected) Error

func (e ErrRejected) Error() string

type KeySampler

type KeySampler struct {
	Key string
	// contains filtered or unexported fields
}

KeySampler makes stable decisions for the same attribute value.

func NewKeySampler

func NewKeySampler(key string) *KeySampler

NewKeySampler creates a sampler keyed by the given attribute name.

func (*KeySampler) Allow

func (s *KeySampler) Allow(passPercent float64, attrs []Attr) bool

type MultiObserver

type MultiObserver []Observer

MultiObserver fans callbacks out to several observers.

func (MultiObserver) OnConfigError

func (m MultiObserver) OnConfigError(name string, err error)

func (MultiObserver) OnDecision

func (m MultiObserver) OnDecision(name string, decision Decision, attrs []Attr)

func (MultiObserver) OnReport

func (m MultiObserver) OnReport(name string, outcome Outcome, snapshot Snapshot, attrs []Attr)

type NeutralPolicy

type NeutralPolicy string
const (
	NeutralDoesNotAffect NeutralPolicy = "does_not_affect"
	NeutralCountsSuccess NeutralPolicy = "counts_success"
	NeutralCountsFailure NeutralPolicy = "counts_failure"
)

type Observer

type Observer interface {
	OnDecision(name string, decision Decision, attrs []Attr)
	OnReport(name string, outcome Outcome, snapshot Snapshot, attrs []Attr)
	OnConfigError(name string, err error)
}

Observer receives synchronous best-effort callbacks for decisions, reports, and config normalization errors. Observer panics are recovered by Controller, but slow callbacks still add latency to the caller's hot path.

type ObserverFunc

type ObserverFunc struct {
	Decision    func(name string, decision Decision, attrs []Attr)
	Report      func(name string, outcome Outcome, snapshot Snapshot, attrs []Attr)
	ConfigError func(name string, err error)
}

ObserverFunc adapts individual functions into an Observer.

func (ObserverFunc) OnConfigError

func (o ObserverFunc) OnConfigError(name string, err error)

func (ObserverFunc) OnDecision

func (o ObserverFunc) OnDecision(name string, decision Decision, attrs []Attr)

func (ObserverFunc) OnReport

func (o ObserverFunc) OnReport(name string, outcome Outcome, snapshot Snapshot, attrs []Attr)

type Option

type Option func(*Controller)

Option customizes a Controller.

func WithConfigProvider

func WithConfigProvider(provider ConfigProvider) Option

WithConfigProvider makes the controller read runtime config from provider.

func WithObserver

func WithObserver(observer Observer) Option

WithObserver installs synchronous observer callbacks. Observer panics are recovered, but callbacks still run on the caller's hot path and should be fast and non-blocking.

func WithSampler

func WithSampler(sampler Sampler) Option

WithSampler installs the sampler used by all built-in strategies.

func WithStrategy

func WithStrategy(strategyType StrategyType, strategy Strategy) Option

WithStrategy registers a custom strategy type. The controller will use it when Config.Strategy matches strategyType.

type Outcome

type Outcome struct {
	Class   OutcomeClass
	Err     error
	Latency time.Duration
	Weight  float64
}

Outcome is the caller-classified result of an attempted operation.

func Failure

func Failure(err error) Outcome

Failure reports infrastructure-like failure such as timeout or unavailable.

func Neutral

func Neutral() Outcome

Neutral reports a valid result that should not worsen backpressure state.

func Overload

func Overload(err error) Outcome

Overload reports an explicit overload signal such as 429, 503, or resource exhausted.

func Success

func Success() Outcome

Success reports that the downstream handled the operation well.

func (Outcome) WithLatency

func (o Outcome) WithLatency(latency time.Duration) Outcome

WithLatency attaches downstream latency to an outcome.

func (Outcome) WithWeight

func (o Outcome) WithWeight(weight float64) Outcome

WithWeight adjusts the outcome impact for strategies that use weights.

type OutcomeClass

type OutcomeClass int

OutcomeClass classifies an operation result for backpressure purposes.

const (
	OutcomeSuccess OutcomeClass = iota
	OutcomeFailure
	OutcomeNeutral
	OutcomeOverload
)

func (OutcomeClass) String

func (c OutcomeClass) String() string

type Permit

type Permit interface {
	Report(outcome Outcome)
}

Permit reports the result of an allowed operation. Implementations are safe to call more than once; only the first report is applied.

type RandomSampler

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

RandomSampler makes pseudo-random decisions without using the global math/rand source.

func NewRandomSampler

func NewRandomSampler(seed uint64) *RandomSampler

NewRandomSampler creates a sampler with the provided seed. A zero seed uses the current time.

func (*RandomSampler) Allow

func (s *RandomSampler) Allow(passPercent float64, _ []Attr) bool

type Reason

type Reason string

Reason explains why a decision was made.

const (
	ReasonAllowed       Reason = "allowed"
	ReasonDisabled      Reason = "disabled"
	ReasonMinSamples    Reason = "min_samples"
	ReasonMaxPass       Reason = "max_pass"
	ReasonPressure      Reason = "pressure"
	ReasonAdaptiveDrop  Reason = "adaptive_drop"
	ReasonInvalidConfig Reason = "invalid_config"
)

type Sampler

type Sampler interface {
	Allow(passPercent float64, attrs []Attr) bool
}

Sampler decides whether a given pass percentage should allow an operation.

type SequenceSampler

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

SequenceSampler distributes decisions evenly across the request stream.

func NewSequenceSampler

func NewSequenceSampler() *SequenceSampler

NewSequenceSampler creates a stream-oriented sampler.

func (*SequenceSampler) Allow

func (s *SequenceSampler) Allow(passPercent float64, _ []Attr) bool

type Snapshot

type Snapshot struct {
	Name     string
	Enabled  bool
	Strategy StrategyType

	PassPercent float64
	DropRatio   float64
	Pressure    float64

	WindowRequests int64
	WindowAccepts  int64
	WindowFailures int64
	LocalRejects   int64
	// ShadowRejects counts requests that would have been rejected while
	// ShadowMode allowed them through.
	ShadowRejects int64

	Config    Config
	UpdatedAt time.Time
}

Snapshot is a point-in-time view of controller state.

type StaticConfig

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

func NewStaticConfig

func NewStaticConfig(cfg Config) *StaticConfig

NewStaticConfig creates a concurrency-safe in-memory ConfigProvider.

func (*StaticConfig) Snapshot

func (c *StaticConfig) Snapshot() Config

Snapshot returns the latest stored configuration.

func (*StaticConfig) Store

func (c *StaticConfig) Store(cfg Config)

Store replaces the current configuration.

type Strategy

type Strategy interface {
	Allow(now time.Time, cfg Config, attrs []Attr) Decision
	Report(now time.Time, cfg Config, outcome Outcome, attrs []Attr)
	Snapshot(now time.Time, cfg Config) Snapshot
}

Strategy is the algorithm contract implemented by built-in strategies.

Custom strategies are supported for advanced consumers, but this interface is experimental until v1 and may change based on real integrations.

type StrategyType

type StrategyType string
const (
	StrategySREAdaptive StrategyType = "sre_adaptive"
	StrategyPressure    StrategyType = "pressure"
)

Directories

Path Synopsis
examples
cache command
database command
grpc_style command
queue command
Package httpbp provides an optional net/http RoundTripper adapter for backpressure controllers.
Package httpbp provides an optional net/http RoundTripper adapter for backpressure controllers.

Jump to

Keyboard shortcuts

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