Documentation
¶
Overview ¶
Package budget owns the single admission and accounting choke point for GitHub requests (SYNC_ENGINE C-B1..C-B6).
Index ¶
- Variables
- func InsideAdmission(ctx context.Context) bool
- type AuthContext
- type Class
- type Clock
- type Doer
- type Gate
- type GraphQLRate
- type GraphQLRateObserver
- type LeaseOptions
- type LeaseStandbyHook
- type LeaseStore
- type Options
- type PostgresLeaseStore
- func (s *PostgresLeaseStore) Acquire(ctx context.Context, installationID int64, token string, ttl time.Duration) (Snapshot, time.Time, bool, error)
- func (s *PostgresLeaseStore) Release(ctx context.Context, installationID int64, token string) error
- func (s *PostgresLeaseStore) Renew(ctx context.Context, installationID int64, token string, ttl time.Duration) (time.Time, bool, error)
- func (s *PostgresLeaseStore) Save(ctx context.Context, installationID int64, token string, snapshot Snapshot) (bool, error)
- func (s *PostgresLeaseStore) SaveBackoff(ctx context.Context, installationID int64, token string, ...) (bool, error)
- type Request
- type RequestHook
- type RequestObservation
- type Resource
- type ResourceBudget
- type Response
- type Snapshot
- type Starvation
- type StarvationHook
Constants ¶
This section is empty.
Variables ¶
var ( // ErrClosed reports admission attempted after Gate.Close. ErrClosed = fmt.Errorf("GitHub budget gate is closed") // ErrLeaseLost reports proven loss or expiry of the budget lease. ErrLeaseLost = fmt.Errorf("GitHub budget gate lease lost") )
var ErrLeaseHeld = errors.New("GitHub installation budget lease is held")
ErrLeaseHeld reports that another process owns the unexpired installation budgeter lease.
Functions ¶
func InsideAdmission ¶
InsideAdmission reports whether ctx is executing a Gate before-send hook. Token providers use it only to avoid joining a non-admitted singleflight renewal that could be queued behind the caller's own concurrency slot.
Types ¶
type AuthContext ¶ added in v0.4.0
type AuthContext string
AuthContext identifies the credential pool GitHub uses to account a request. Installation tokens and App JWTs have independent REST budgets.
const ( // InstallationAuth is an installation access token. InstallationAuth AuthContext = "installation" // AppJWTAuth is a GitHub App JWT. AppJWTAuth AuthContext = "app_jwt" )
type Clock ¶
Clock is the time source for admission, backoff, and lease-expiry decisions. Tests inject a manual implementation so C-B2/C-B3 timing is deterministic.
type Doer ¶
Doer is the narrow dependency used by internal/gh. Implementations must preserve Gate.Do's C-B invariants.
type Gate ¶
type Gate struct {
// contains filtered or unexported fields
}
Gate is the C-B1 per-installation choke point. It owns admission, server-authoritative REST and GraphQL observations, per-auth-context secondary-limit backoff, and the C-B6 concurrency ceiling.
func New ¶
New constructs an in-process gate. Production callers should use NewLeased; New exists for conformance tests and single-process tooling.
func NewLeased ¶
func NewLeased( ctx context.Context, client *http.Client, gateOptions Options, store LeaseStore, leaseOptions LeaseOptions, ) (*Gate, error)
NewLeased acquires the Postgres-coordinated C-B1/C-O2 singleton before returning a usable gate. A live lease held by another runtime returns ErrLeaseHeld.
func NewLeasedStandby ¶ added in v0.5.0
func NewLeasedStandby( ctx context.Context, client *http.Client, gateOptions Options, store LeaseStore, leaseOptions LeaseOptions, onStandby LeaseStandbyHook, ) (*Gate, error)
NewLeasedStandby acquires the Postgres-coordinated C-B1/C-O2 singleton, waiting on a capped, jittered timer while another live runtime holds it. The first attempt is immediate. Only ErrLeaseHeld is retryable; store, configuration, token-generation, and invalid acquired-expiry errors remain fatal. No gate is returned, and therefore no GitHub call can be admitted, until an acquisition succeeds. If cancellation races with a successful acquisition, the acquired gate is closed and its token-checked lease is released before cancellation is returned.
func (*Gate) Close ¶
Close stops admission, keeps renewal alive while admitted calls drain, then snapshots and releases. If the caller's drain deadline expires, stragglers are canceled before bounded cleanup continues (C-B1/C-B6).
func (*Gate) Do ¶
Do admits and performs exactly one GitHub request (C-B1). REST state comes only from x-ratelimit-* headers; GraphQL state comes only from the supplied rateLimit observer (C-B2/C-B5).
Admission owns a C-B6 concurrency slot until the response body reaches EOF or is closed. Do may return a non-nil Response alongside a non-nil error; callers must still close every non-nil response body. Forgetting to close permanently leaks that slot, and enough leaks stop all installation traffic. Redirect following is disabled because a redirect would otherwise hide an unadmitted request inside http.Client.Do.
An installation-token mint issued from a BeforeSend hook reuses its outer request's concurrency slot so renewal cannot deadlock at MaxConcurrent=1. The mint still performs independent App-JWT REST admission, reservation, header observation, and backoff accounting.
type GraphQLRate ¶
GraphQLRate is extracted from a response's top-level data.rateLimit block. It is deliberately distinct from REST response-header accounting (C-B5).
type GraphQLRateObserver ¶
type GraphQLRateObserver func(*http.Response) (GraphQLRate, bool, error)
GraphQLRateObserver reads and restores a GraphQL response body, returning the authoritative rateLimit block when one is present.
type LeaseOptions ¶
type LeaseOptions struct {
InstallationID int64
Owner string
TTL time.Duration
RenewInterval time.Duration
SnapshotInterval time.Duration
StoreTimeout time.Duration
Clock Clock
}
LeaseOptions identifies and times a per-installation budgeter lease. Owner is a diagnostic process name only; an unguessable per-runtime token is the actual database ownership predicate.
type LeaseStandbyHook ¶ added in v0.5.0
LeaseStandbyHook reports one failed acquisition before NewLeasedStandby waits to retry. Owner is the caller-supplied diagnostic process name; the opaque lease token is never exposed.
type LeaseStore ¶
type LeaseStore interface {
Acquire(
context.Context,
int64,
string,
time.Duration,
) (Snapshot, time.Time, bool, error)
Renew(
context.Context,
int64,
string,
time.Duration,
) (time.Time, bool, error)
Save(context.Context, int64, string, Snapshot) (bool, error)
SaveBackoff(
context.Context,
int64,
string,
AuthContext,
time.Time,
) (bool, error)
Release(context.Context, int64, string) error
}
LeaseStore coordinates the one active budgeter for an installation and persists periodic C-P6 state snapshots. Acquire and Renew return Postgres's authoritative lease expiry; callers must never derive it from local time. Acquire false means the store proved that another unexpired owner holds the lease. For Renew, Save, and SaveBackoff, false means the store has proven that the caller no longer owns the lease. Transport failures must always be returned as errors; returning false for a transport failure violates this contract. Release returns any cleanup failure rather than translating it into ownership loss.
type Options ¶
type Options struct {
MaxConcurrent int
RESTLimit int64
GraphQLLimit int64
// SweepFloor and EventFloor are fractions in (0,1); EventFloor must be
// lower because event work has priority over sweep work.
SweepFloor float64
EventFloor float64
RESTRequestEstimate int64
GraphQLPointEstimate int64
SecondaryLimitFallback time.Duration
OnStarvation StarvationHook
OnRequest RequestHook
Clock Clock
Tracer trace.Tracer
}
Options configures a Gate. Limits are initial floor denominators only; an observed server limit always replaces them. Reservations are admission-side pessimism and never replace or persist server-authoritative remaining values (C-B2/C-B3).
type PostgresLeaseStore ¶
type PostgresLeaseStore struct {
// contains filtered or unexported fields
}
PostgresLeaseStore implements atomic lease acquire/renew/steal-on-expiry and periodic snapshots against installation_budgets.
func NewPostgresLeaseStore ¶
func NewPostgresLeaseStore(pool *pgxpool.Pool) *PostgresLeaseStore
NewPostgresLeaseStore constructs a Postgres-backed lease store.
func (*PostgresLeaseStore) Acquire ¶
func (s *PostgresLeaseStore) Acquire( ctx context.Context, installationID int64, token string, ttl time.Duration, ) (Snapshot, time.Time, bool, error)
Acquire obtains or steals an expired installation lease and returns its authoritative persisted snapshot.
func (*PostgresLeaseStore) Release ¶
func (s *PostgresLeaseStore) Release( ctx context.Context, installationID int64, token string, ) error
Release clears an installation lease only for the active owner.
func (*PostgresLeaseStore) Renew ¶
func (s *PostgresLeaseStore) Renew( ctx context.Context, installationID int64, token string, ttl time.Duration, ) (time.Time, bool, error)
Renew extends a lease only when token still proves ownership.
func (*PostgresLeaseStore) Save ¶
func (s *PostgresLeaseStore) Save( ctx context.Context, installationID int64, token string, snapshot Snapshot, ) (bool, error)
Save persists one budget snapshot only for the active owner.
func (*PostgresLeaseStore) SaveBackoff ¶
func (s *PostgresLeaseStore) SaveBackoff( ctx context.Context, installationID int64, token string, authContext AuthContext, until time.Time, ) (bool, error)
SaveBackoff immediately persists one auth context's secondary-limit deadline.
type Request ¶
type Request struct {
// contains filtered or unexported fields
}
Request wraps the only HTTP request shape accepted by Gate.Do. Callers use the constructors below so REST, GraphQL, and App-auth accounting cannot be confused.
func NewAppRESTRequest ¶ added in v0.4.0
NewAppRESTRequest wraps one App-JWT REST request.
func NewAuthRequest ¶
NewAuthRequest wraps one App-JWT installation-token exchange.
func NewGraphQLRequest ¶
func NewGraphQLRequest(req *http.Request, observer GraphQLRateObserver) *Request
NewGraphQLRequest wraps one GraphQL request and its rate observer.
func NewInstallationRESTRequest ¶ added in v0.4.0
NewInstallationRESTRequest wraps one installation-token REST request.
func NewRESTRequest ¶
NewRESTRequest wraps one installation-authenticated REST request for admission. New call sites should use NewInstallationRESTRequest so the credential context remains explicit.
func (*Request) BeforeSend ¶
BeforeSend installs work that must run after admission and immediately before the transport. GitHub clients use it to refresh and inject an installation token without letting a queued request carry a stale token.
type RequestHook ¶
type RequestHook func(RequestObservation)
RequestHook is M6's C-B1/C-B4 request-rate and conditional-hit seam.
type RequestObservation ¶
type RequestObservation struct {
Class Class
Resource Resource
AuthContext AuthContext
EndpointFamily string
StatusCode int
Conditional bool
NotModified bool
Err error
}
RequestObservation is emitted after one admitted network call. It contains only cardinality-bounded accounting data and never request URLs or headers.
type Resource ¶
type Resource string
Resource identifies GitHub's independently-accounted API resources. REST accounting is further partitioned by AuthContext.
type ResourceBudget ¶
ResourceBudget is the most recently observed server-authoritative budget. Known is false until a complete REST header set or GraphQL rateLimit block has been observed.
type Response ¶
type Response struct {
HTTP *http.Response
GraphQLRate *GraphQLRate
}
Response preserves the HTTP response and, for GraphQL, the extracted point accounting observed before the concurrency slot is released.
type Snapshot ¶
type Snapshot struct {
REST ResourceBudget
AppREST ResourceBudget
GraphQL ResourceBudget
BackoffUntil time.Time
AppJWTBackoffUntil time.Time
InFlight int
}
Snapshot is safe to expose to persistence and observability code. It never contains credentials or request data.
type Starvation ¶
type Starvation struct {
Class Class
Resource Resource
AuthContext AuthContext
Remaining int64
Limit int64
ResetAt time.Time
}
Starvation is emitted once for each request that queues behind a C-B3 reserved floor.
type StarvationHook ¶
type StarvationHook func(Starvation)
StarvationHook is the M1 observability seam; M6 will attach metrics.