UNPKG

react-native-onyx

Version:

State management for React Native

67 lines (66 loc) 3.98 kB
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;