react-native-onyx
Version:
State management for React Native
67 lines (66 loc) • 3.98 kB
TypeScript
import AbstractCircuitBreaker from './CircuitBreaker/AbstractCircuitBreaker';
/**
* Process-scoped circuit breaker for storage CAPACITY failures.
*
* The per-operation retry budget in `OnyxUtils.retryOperation` cannot stop a session-level storm:
* each evict -> OnyxDerived recompute -> new write starts its own fresh budget, so a full disk or
* exhausted quota can drive tens of thousands of evict+retry cycles that never make progress and
* freeze the app. This breaker is the session-level brake — `retryOperation` consults it before
* every eviction.
*
* It is ONE circuit (closed/open/half-open) fed by TWO failure-counting policies, both evaluated in
* {@link recordFailureInClosed}. It trips when EITHER:
* - capacity failures within {@link ROLLING_WINDOW_MS} exceed {@link FAILURE_THRESHOLD}, or
* - {@link NO_PROGRESS_CAP} consecutive evictions are each immediately followed by another capacity
* failure (the eviction freed nothing the next write could use — a no-progress cycle). This is a
* cheap proxy for `getDatabaseSize()`, which is costly and only reports origin-wide usage.
*
* Keeping both policies inside a single state machine — rather than composing two independent breakers
* — is deliberate: two breakers each with their own open/half-open/probe latch cannot share one
* coherent circuit state without races (stranded half-open probes, storms uncounted while the other
* probes, cross-contaminated counters).
*
* On trip it emits exactly ONE alert per incident (across reopen cycles). After {@link ROLLING_WINDOW_MS}
* the circuit moves to half-open and admits a single eviction+retry probe; a successful probe closes
* the circuit, a failed probe reopens it for another window.
*/
declare class StorageCircuitBreaker extends AbstractCircuitBreaker {
/** Timestamps of capacity failures still inside the rolling window. */
private failureTimestamps;
/** Consecutive evictions that each failed to free usable space. */
private consecutiveNoProgress;
/** Set when an eviction's retry is pending, so the next capacity failure counts as no-progress. */
private evictionAwaitingResult;
/** Guards the single alert per incident (the open→half-open→open cycle must not re-alert). */
private hasTripped;
constructor();
/**
* Record a CAPACITY failure. Call once per capacity failure in `retryOperation`, BEFORE deciding
* whether to evict. Returns `true` when the breaker is open and eviction must not proceed.
*/
recordCapacityFailure(): boolean;
/** Record that `retryOperation` just evicted a key, so the next capacity failure counts as no-progress. */
recordEviction(): void;
/**
* Record that a storage write succeeded. Fires on EVERY successful write, so it must only act on the
* one that carries capacity information: a write whose eviction was awaiting its verdict. Such a
* success means an eviction's retry actually landed — usable space was freed. In half-open that is
* the recovery probe succeeding (closes the circuit); in closed it clears the no-progress streak. A
* plain write that happens to succeed proves nothing about capacity and is a no-op (the common case).
*/
recordWriteSuccess(): void;
/**
* Record that the half-open recovery probe failed. `retryOperation` calls this when a write is
* rejected while a probe is in flight — the storage is still full, so reopen for another window.
* No-op while fully open (recordFailure short-circuits) and harmless while closed.
*/
recordProbeFailure(): void;
/** Wipe all state back to a fresh closed circuit. Process-scoped, so reset between tests/sessions. */
reset(): void;
protected recordFailureInClosed(): string | null;
protected recordSuccessInClosed(): void;
protected resetFailureState(): void;
private handleTrip;
}
declare const _default: StorageCircuitBreaker;
export default _default;