Skip to main content

tollgate_core/
lease.rs

1//! Local quota leases: centrally allocated capacity, locally decremented.
2//!
3//! A [`LeaseGrant`] is what an allocator (the store or the quota server)
4//! returns after atomically debiting an account's balance. A [`LocalLease`]
5//! is the instance-side runtime form: one atomic counter by default, or an
6//! explicitly configured set of cache-isolated counters. Requests reserve
7//! units with CAS loops — no lock, no I/O — which is how one
8//! database transaction amortizes across thousands of requests. Central
9//! allocation bounds spend; the grant's lease-scoped capability prevents
10//! release or usage from being attributed to a different lease
11//! (INVARIANTS.md GL-1, GL-4).
12
13use std::mem::ManuallyDrop;
14use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
15use std::sync::{Arc, OnceLock};
16
17use jiff::Timestamp;
18
19use crate::deny::DenyReason;
20use crate::ids::{AccountId, FencingToken, LeaseId};
21use crate::sharding::{LocalSharding, Locality};
22use crate::units::CostUnits;
23
24/// An allocator's record of one lease: `units` were debited from
25/// `account_id`'s balance and belong exclusively to the holder until
26/// `expires_at`, after which the allocator reclaims whatever the holder did
27/// not spend (INVARIANTS.md GL-9).
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
30pub struct LeaseGrant {
31    /// The allocator's identity for this lease record.
32    pub lease_id: LeaseId,
33    /// The account whose balance funded the lease.
34    pub account_id: AccountId,
35    /// Capability token for this lease record, not an account-wide epoch.
36    pub fencing_token: FencingToken,
37    /// Units debited from the account at grant and spendable by the holder.
38    pub units: CostUnits,
39    /// When the allocator's grant ends. The holder stops spending earlier,
40    /// at `expires_at` minus its safety margin (see [`LocalLease::usable_until`]).
41    pub expires_at: Timestamp,
42}
43
44/// An account's unfunded spend on this instance, under
45/// [`EnforcementMode::Elastic`].
46///
47/// Three atomic counters, with the total shaped exactly like [`LocalLease`]'s:
48/// a CAS loop, no lock, no I/O, no clock. A lease counts *down* through units
49/// someone already paid for; `spent` counts *up* through units nobody has.
50/// `committed` distinguishes durable spend from pending reservations that can
51/// still be refunded. `commit_publications` closes the otherwise torn
52/// reservation-phase/occupancy transition, so a cap refusal never advertises
53/// irrevocable units as refundable. The ledger settles the difference by
54/// treating overage as a second funding term, so per-account conservation
55/// still closes exactly (INVARIANTS.md GL-1, GL-3).
56///
57/// **The cap is a parameter, not a field.** It arrives from the snapshot the
58/// request has already read, which means two things: a republished cap takes
59/// effect on the very next request with no reconciliation step, and — the
60/// load-bearing half — the cap comparison happens *inside* the same
61/// compare-exchange that claims the units. Checking a cap and then claiming
62/// against it in two steps would let two cores each observe room for a request
63/// that only one of them can have.
64///
65/// **Its lifetime is the account's, not a lease's.** It hangs off the
66/// per-account lease slot, which is created on first use and held for the
67/// life of the process. That is deliberate and is the difference between this
68/// and the rate-limiter registry: a limiter rebuilt after eviction costs a
69/// full bucket, but an overage counter rebuilt after eviction silently resets
70/// a spend cap.
71///
72/// [`EnforcementMode::Elastic`]: crate::snapshot::EnforcementMode::Elastic
73#[derive(Debug)]
74pub struct AccountOverage {
75    account_id: AccountId,
76    spent: AtomicU64,
77    committed: AtomicU64,
78    commit_publications: AtomicUsize,
79    /// Debit compare-exchanges on `spent` that lost to another writer (GL-139).
80    /// Beside `spent`, so the only thread that writes it is one already
81    /// contending for that line; added once per contended debit.
82    contended: AtomicU64,
83}
84
85/// Evidence that an overage commit publication is visible to cap observers.
86///
87/// The only way to publish committed occupancy is through this guard. Its
88/// lifetime surrounds the reservation's phase CAS and the occupancy update;
89/// dropping it is the release publication that makes the stable counters
90/// observable again.
91struct OverageCommitPublication<'a> {
92    overage: &'a AccountOverage,
93}
94
95impl OverageCommitPublication<'_> {
96    fn publish(self, units: CostUnits) {
97        let prior = self
98            .overage
99            .committed
100            .fetch_add(units.get(), Ordering::AcqRel);
101        debug_assert!(
102            prior
103                .checked_add(units.get())
104                .is_some_and(|committed| committed <= self.overage.spent.load(Ordering::Acquire)),
105            "committed overage exceeds total recorded spend"
106        );
107    }
108}
109
110impl Drop for OverageCommitPublication<'_> {
111    fn drop(&mut self) {
112        let prior = self
113            .overage
114            .commit_publications
115            .fetch_sub(1, Ordering::AcqRel);
116        debug_assert!(prior > 0, "overage commit publication count underflowed");
117    }
118}
119
120impl AccountOverage {
121    /// An empty overage counter for one account on this instance: nothing
122    /// extended, nothing committed.
123    #[must_use]
124    pub fn new(account_id: AccountId) -> Self {
125        AccountOverage {
126            account_id,
127            spent: AtomicU64::new(0),
128            committed: AtomicU64::new(0),
129            commit_publications: AtomicUsize::new(0),
130            contended: AtomicU64::new(0),
131        }
132    }
133
134    /// Overage debits that lost a compare-exchange to another writer, since
135    /// the account's counter was created. A lower bound, for the reasons
136    /// [`LocalLease::contended_debits`] gives. A control-plane read.
137    #[must_use]
138    pub fn contended_debits(&self) -> u64 {
139        self.contended.load(Ordering::Relaxed)
140    }
141
142    /// The account this counter belongs to.
143    #[must_use]
144    pub fn account_id(&self) -> AccountId {
145        self.account_id
146    }
147
148    /// Unfunded units extended on this instance so far.
149    #[must_use]
150    pub fn spent(&self) -> CostUnits {
151        CostUnits(self.spent.load(Ordering::Acquire))
152    }
153
154    /// Units still extendable under `cap`, saturating at zero.
155    ///
156    /// Read by readiness: an elastic account with headroom here is admissible
157    /// even when its lease is empty or absent, which is the whole point of the
158    /// mode (INVARIANTS.md GL-10).
159    #[must_use]
160    pub fn headroom(&self, cap: CostUnits) -> CostUnits {
161        CostUnits(cap.get().saturating_sub(self.spent.load(Ordering::Acquire)))
162    }
163
164    /// Extend `units` of unfunded credit if `cap` has room. Lock-free; the CAS
165    /// loop retries only under concurrent overage on the same account.
166    ///
167    /// Fails closed on both boundaries: a total that exceeds the cap and a
168    /// total that cannot be represented are the same refusal, because a
169    /// wrapped total would read as a tiny spend and reopen the cap
170    /// (INVARIANTS.md GL-11).
171    #[inline]
172    pub(crate) fn try_debit(&self, units: CostUnits, cap: CostUnits) -> Result<(), DenyReason> {
173        let want = units.get();
174        let mut current = self.spent.load(Ordering::Acquire);
175        // Counted in a register and recorded once on every exit.
176        let mut lost = 0u64;
177        let note = |lost: u64| {
178            if lost != 0 {
179                self.contended.fetch_add(lost, Ordering::Relaxed);
180            }
181        };
182        loop {
183            let refused = || {
184                // A zero-delta RMW, rather than a load, places this observer
185                // in the marker's modification order. It therefore either
186                // precedes publication (when the reservation is still
187                // refundable), overlaps it, or acquires the completed
188                // committed update; a stale zero cannot skip an already
189                // linearized publication start.
190                if self.commit_publications.fetch_add(0, Ordering::AcqRel) != 0 {
191                    return DenyReason::OverageCommitInProgress {
192                        spent: CostUnits(current),
193                        overage_cap: cap,
194                    };
195                }
196                let committed = self.committed.load(Ordering::Acquire);
197                if committed
198                    .checked_add(want)
199                    .is_some_and(|next| next <= cap.get())
200                {
201                    DenyReason::OverageCapTemporarilyExhausted {
202                        spent: CostUnits(current),
203                        overage_cap: cap,
204                    }
205                } else {
206                    DenyReason::OverageCapExhausted {
207                        spent: CostUnits(current),
208                        overage_cap: cap,
209                    }
210                }
211            };
212            let Some(next) = current.checked_add(want) else {
213                note(lost);
214                return Err(refused());
215            };
216            if next > cap.get() {
217                note(lost);
218                return Err(refused());
219            }
220            match self.spent.compare_exchange_weak(
221                current,
222                next,
223                Ordering::AcqRel,
224                Ordering::Acquire,
225            ) {
226                Ok(_) => {
227                    note(lost);
228                    return Ok(());
229                }
230                Err(observed) => {
231                    lost += 1;
232                    current = observed;
233                }
234            }
235        }
236    }
237
238    /// Publish the reservation's phase claim and committed occupancy as one
239    /// observer-safe transition.
240    ///
241    /// `claim` owns the single commit-vs-cancel CAS. The publication marker is
242    /// installed before that closure can run and removed only after a winning
243    /// claim has advanced `committed`. A cap observer that overlaps either
244    /// half therefore sees `OverageCommitInProgress`, never a stable claim
245    /// that the already-irrevocable units remain refundable.
246    ///
247    /// **Precondition: the caller already owns `units` in `spent`, and this
248    /// function never credits them back.** Publication is not a debit. There
249    /// are exactly two owners that satisfy that, and both are in this crate: a
250    /// pending overage [`Reservation`], whose cancel/drop refunds, and a
251    /// [`TentativeOverage`] guard, which refunds on drop. A caller that
252    /// publishes without holding one leaves committed occupancy above recorded
253    /// spend, which the `publish` assertion catches in debug and which silently
254    /// reopens the cap in release. Reach it through
255    /// [`TentativeOverage::publish_commit`] unless a pending reservation is
256    /// already the owner.
257    ///
258    /// [`Reservation`]: crate::reservation::Reservation
259    #[inline]
260    pub(crate) fn publish_claim<T, E>(
261        &self,
262        units: CostUnits,
263        claim: impl FnOnce() -> Result<T, E>,
264    ) -> Result<T, E> {
265        // One marker belongs to one live call stack, so exhausting usize would
266        // require more simultaneously executing publications than the process
267        // can address. The fetch is the bounded, lock-free hot-path operation.
268        let prior = self.commit_publications.fetch_add(1, Ordering::AcqRel);
269        debug_assert_ne!(
270            prior,
271            usize::MAX,
272            "live overage commit publications exceed the address space"
273        );
274        let publication = OverageCommitPublication { overage: self };
275        match claim() {
276            Ok(value) => {
277                publication.publish(units);
278                Ok(value)
279            }
280            Err(error) => Err(error),
281        }
282    }
283
284    /// Withdraw previously extended units (release of an uncommitted overage
285    /// reservation). Callers must return only units they extended, exactly
286    /// once — the reservation state machine guarantees this.
287    ///
288    /// Plain `fetch_sub`, and the choice of what happens if that contract were
289    /// ever broken is deliberate rather than accidental. Underflow wraps the
290    /// counter to near `u64::MAX`, which makes every later request exceed the
291    /// cap and deny: wrong, but wrong in the fail-closed direction. Clamping
292    /// to zero would be the fail-*open* direction — it would under-report
293    /// spend and hand the account a fresh cap — so the safer-looking
294    /// arithmetic is the more dangerous one here.
295    #[inline]
296    pub(crate) fn credit(&self, units: CostUnits) {
297        let prior = self.spent.fetch_sub(units.get(), Ordering::AcqRel);
298        debug_assert!(
299            prior >= units.get(),
300            "overage credit of {} exceeds recorded spend {prior}",
301            units.get()
302        );
303    }
304
305    /// Extend `units` as a *revocable* debit, owned by the returned guard.
306    ///
307    /// This is the commit-time half of elastic funding: a leased reservation
308    /// whose window lapsed needs overage capacity *before* it can claim the
309    /// phase, because a won claim with no funding term breaks the ledger
310    /// equation by exactly these units. But it cannot know yet whether it will
311    /// win — a canceller may already be resolving the same reservation — so
312    /// the debit must be revocable until the claim resolves.
313    ///
314    /// The guard is what makes "debited but never resolved" unrepresentable.
315    /// Without it the units sit in `spent` with no owner between the debit and
316    /// the claim; a panic unwinding through that window (or any early return a
317    /// later edit adds) leaves the reservation's own `Drop` refunding the
318    /// *lease* while these units are stranded for the life of the process,
319    /// silently shrinking the account's cap.
320    #[inline]
321    pub(crate) fn debit_tentatively(
322        &self,
323        units: CostUnits,
324        cap: CostUnits,
325    ) -> Result<TentativeOverage<'_>, DenyReason> {
326        self.try_debit(units, cap)?;
327        Ok(TentativeOverage {
328            overage: self,
329            units,
330        })
331    }
332}
333
334/// A revocable overage debit, held between the claim's funding and its
335/// resolution. Dropping it returns the units; [`publish_commit`] is the only
336/// way to make them irrevocable.
337///
338/// [`publish_commit`]: TentativeOverage::publish_commit
339#[derive(Debug)]
340pub(crate) struct TentativeOverage<'a> {
341    overage: &'a AccountOverage,
342    units: CostUnits,
343}
344
345impl TentativeOverage<'_> {
346    /// Publish `claim` and this debit as one observer-safe transition.
347    ///
348    /// Consuming `self` is the ordering guarantee: the debit is already in
349    /// `spent` before the marker is installed, so the publication's occupancy
350    /// assertion holds exactly as it does for a natively admitted overage, and
351    /// a cap observer that overlaps either half sees `OverageCommitInProgress`
352    /// rather than a stable claim that irrevocable units are refundable. A
353    /// caller cannot claim without first holding a debit, because there is no
354    /// other way to reach this function.
355    ///
356    /// A winning claim retains the units; a losing claim credits them back
357    /// before returning, because the winner was a cancellation that refunded
358    /// its own funding source and owes nothing for these.
359    #[inline]
360    pub(crate) fn publish_commit<T, E>(self, claim: impl FnOnce() -> Result<T, E>) -> Result<T, E> {
361        // The refund is this function's responsibility from here, so disarm
362        // the guard rather than letting it double-credit a retained debit.
363        let this = ManuallyDrop::new(self);
364        match this.overage.publish_claim(this.units, claim) {
365            Ok(value) => Ok(value),
366            Err(error) => {
367                this.overage.credit(this.units);
368                Err(error)
369            }
370        }
371    }
372}
373
374impl Drop for TentativeOverage<'_> {
375    fn drop(&mut self) {
376        self.overage.credit(self.units);
377    }
378}
379
380/// Somewhere for a draining lease to say so, without this crate learning what
381/// a task, a runtime, or a waker is.
382///
383/// The refill plane supplies the implementation; the request path only calls
384/// it. That keeps the hot-path crate's dependency policy intact — a trait
385/// declaration is not an async dependency — and leaves the wake mechanism free
386/// to change without touching a line of request-path code.
387///
388/// **Contract:** [`request_refill`](RefillSignal::request_refill) is invoked
389/// from inside a debit, on the request path. It must not block, wait on a
390/// lock, allocate, or perform I/O (INVARIANTS.md GL-5, GL-6). A single-counter
391/// lease calls it at most once. A sharded lease calls it at most once per
392/// shard between aggregate checks; an early shard signal is re-armed if the
393/// aggregate has not reached low water yet.
394pub trait RefillSignal: Send + Sync + core::fmt::Debug {
395    /// This lease has crossed its low-water mark and wants replacing.
396    fn request_refill(&self);
397}
398
399/// What the refill plane should do about a lease, from
400/// [`LocalLease::refill_due_or_rearm`].
401///
402/// The two active verdicts are not degrees of the same thing. `Draining` says
403/// the lease is still serving and should be replaced *before* it refuses
404/// anything; `Refused` says it already refused work the account could fund,
405/// which is a statement about the grant's size rather than its depletion and
406/// needs the holder's unspent units folded back in before the next one is
407/// sized (INVARIANTS.md GL-1, GL-6).
408#[derive(Debug, Clone, Copy, PartialEq, Eq)]
409pub enum RefillVerdict {
410    /// The lease is serving and has asked for nothing.
411    Idle,
412    /// Spending crossed low water. Rotate: acquire the next lease while this
413    /// one keeps serving, then release this one once it quiesces.
414    Draining,
415    /// A debit was refused for want of units. However far this lease is from
416    /// its low-water mark, it is too small for the work being offered, and
417    /// the units it still holds are the ones the next grant needs. Rotate by
418    /// *consolidating*: return them and re-grant against the restored
419    /// balance, atomically, so the exchange cannot shrink the holder or lose
420    /// the units to another instance in between.
421    Refused,
422}
423
424/// One lease shard on its own cache line.
425///
426/// The supported Apple Silicon hosts report 128-byte lines; aligning to 64
427/// would still let adjacent shards invalidate one another there. Over-aligning
428/// on a 64-byte-line target costs memory but does not weaken isolation.
429#[repr(align(128))]
430#[derive(Debug)]
431struct LeaseShard {
432    remaining: AtomicU64,
433    low_water: u64,
434    signalled: AtomicBool,
435    /// Debit compare-exchanges on `remaining` that lost to another writer.
436    ///
437    /// On this shard's own line, in padding it already had: the counter costs
438    /// no memory, and the only thread that writes it is one already contending
439    /// for this line. Added once per contended debit, never per failure, so a
440    /// contended debit adds one write rather than one per lost race.
441    contended: AtomicU64,
442}
443
444impl LeaseShard {
445    fn new(remaining: u64, low_water: u64) -> Self {
446        Self {
447            remaining: AtomicU64::new(remaining),
448            low_water,
449            signalled: AtomicBool::new(false),
450            contended: AtomicU64::new(0),
451        }
452    }
453
454    /// Record `lost` failed exchanges, if any. The branch is the whole cost
455    /// of the signal on an uncontended debit.
456    #[inline]
457    fn note_contention(&self, lost: u64) {
458        if lost != 0 {
459            self.contended.fetch_add(lost, Ordering::Relaxed);
460        }
461    }
462}
463
464#[derive(Debug)]
465enum LeaseBalance {
466    Single(LeaseShard),
467    Sharded(Box<[LeaseShard]>),
468}
469
470impl LeaseBalance {
471    fn as_slice(&self) -> &[LeaseShard] {
472        match self {
473            Self::Single(shard) => std::slice::from_ref(shard),
474            Self::Sharded(shards) => shards,
475        }
476    }
477}
478
479/// Fixed-size evidence for returning a pending debit.
480///
481/// A fragmented debit may draw from several counters, but cancellation may
482/// return the exact aggregate to any one counter: every counter is merely a
483/// partition of the same lease bound. Keeping only the refund destination
484/// avoids allocating a variable-length receipt on the request path.
485#[derive(Debug)]
486pub(crate) struct LeaseDebit {
487    shard: usize,
488    units: u64,
489}
490
491/// Instance-side lease state: the grant plus live remaining-unit counters.
492///
493/// Shared as `Arc<LocalLease>` between the request path (reserve/return) and
494/// the background refill task (`needs_refill`). Never mutated otherwise; a
495/// refill installs a *new* `LocalLease` rather than growing this one, so the
496/// request path never observes a counter that jumps upward mid-reservation.
497///
498/// Every fresh lease begins with clear shard-local refill signals. The refill
499/// plane re-arms an early signal only after checking the aggregate and closes
500/// the clear/debit race with a second aggregate read.
501#[derive(Debug)]
502struct LeaseInner {
503    grant: LeaseGrant,
504    balance: LeaseBalance,
505    /// Whom to tell when spending crosses `low_water`, if anyone. `None` for
506    /// a lease nobody refills — a test fixture, or a caller driving the
507    /// counter directly.
508    refill: OnceLock<Arc<dyn RefillSignal>>,
509    /// Refill trigger: when `remaining` falls to or below this, the holder
510    /// should acquire its next lease — in the background, never inline.
511    low_water: u64,
512    /// Set by a debit this lease could not fund, cleared by the refill plane
513    /// when it reads the verdict.
514    ///
515    /// Lease-wide rather than per-shard, because it reports a fact about the
516    /// *grant* and not about one counter: a refusal already walked every
517    /// shard (see [`LocalLease::try_reserve_at`]), so no sibling is holding
518    /// the units that would have funded it. It doubles as the refusal
519    /// doorbell's once-token, which is why a refusal wakes the plane even
520    /// when a low-water crossing already spent this lease's shard flags.
521    refused: AtomicBool,
522    /// The largest quote this lease refused for want of units: the demand a
523    /// consolidation may grow to (GL-131). Raised before the doorbell's swap,
524    /// which publishes it, and read by the plane only at quiescence. Zero
525    /// until a refusal, and never set by an expiry refusal, which rotates.
526    refused_quote: AtomicU64,
527    /// How much of the shards' contention a [`LocalLease::take_unreported_contention`]
528    /// caller has already been handed. Raised with `fetch_max`, so a lease that
529    /// is taken out of its slot and reinstalled — which a refused consolidation
530    /// does — is never reported twice.
531    contention_reported: AtomicU64,
532    /// Local end of life: `expires_at - safety margin`. Debits and commits
533    /// stop here, *before* the server-stamped expiry, so clock skew between
534    /// allocator and holder plus in-flight request time fit inside the
535    /// margin. Together with the allocator's reclaim grace (which starts
536    /// *after* `expires_at`) this closes the expiry race: the holder stops
537    /// spending strictly before the server starts reclaiming.
538    usable_until: Timestamp,
539}
540
541/// A local view of one lease's shared counters and metadata.
542///
543/// Sharded slots create one outer `Arc<LocalLease>` per locality. Those
544/// independently reference-counted handles all point to this shared inner
545/// state, so acquiring and dropping a routine request handle does not contend
546/// with other localities. The refill plane still observes every live alias
547/// through the inner `Arc` count before releasing a superseded lease.
548#[derive(Debug, Clone)]
549#[repr(align(128))]
550pub struct LocalLease {
551    inner: Arc<LeaseInner>,
552}
553
554fn usable_until(expires_at: Timestamp, margin: jiff::SignedDuration) -> Timestamp {
555    if margin < jiff::SignedDuration::ZERO {
556        // A negative margin would extend local use past allocator expiry and
557        // invert the expiry-safety protocol. Fail closed even if a caller
558        // bypasses validated LeaseManager configuration.
559        Timestamp::MIN
560    } else {
561        expires_at
562            .checked_sub(margin)
563            // A margin longer than the lease's life fails closed: never
564            // usable, settled by refill/reclaim.
565            .unwrap_or(Timestamp::MIN)
566    }
567}
568
569fn partition(total: u64, count: usize, index: usize) -> u64 {
570    let count = u64::try_from(count).expect("local shard count fits u64");
571    let index = u64::try_from(index).expect("local shard index fits u64");
572    total / count + u64::from(index < total % count)
573}
574
575impl LocalLease {
576    /// Wrap a grant for local spending with no safety margin (usable right
577    /// up to the grant's expiry). Prefer [`LocalLease::with_safety_margin`]
578    /// whenever the grant's clock is not the local clock.
579    ///
580    /// `low_water` is where background refill should begin; it must be below
581    /// the grant size to be useful, but any value is accepted (0 disables
582    /// early refill).
583    #[must_use]
584    pub fn new(grant: LeaseGrant, low_water: CostUnits) -> Self {
585        Self::with_safety_margin(grant, low_water, jiff::SignedDuration::ZERO)
586    }
587
588    /// Wrap a grant, refusing debits and commits once within `margin` of the
589    /// grant's expiry. Size the margin to cover worst-case allocator/holder
590    /// clock skew plus the longest request the service executes.
591    #[must_use]
592    pub fn with_safety_margin(
593        grant: LeaseGrant,
594        low_water: CostUnits,
595        margin: jiff::SignedDuration,
596    ) -> Self {
597        let usable_until = usable_until(grant.expires_at, margin);
598        LocalLease {
599            inner: Arc::new(LeaseInner {
600                balance: LeaseBalance::Single(LeaseShard::new(grant.units.get(), low_water.get())),
601                low_water: low_water.get(),
602                refused: AtomicBool::new(false),
603                refused_quote: AtomicU64::new(0),
604                contention_reported: AtomicU64::new(0),
605                usable_until,
606                refill: OnceLock::new(),
607                grant,
608            }),
609        }
610    }
611
612    /// Wrap a grant in an explicitly sharded local layout.
613    ///
614    /// Grant units and the low-water threshold are partitioned exactly across
615    /// `sharding`; their sums remain the original values. A one-shard request
616    /// retains the inline representation used by [`Self::with_safety_margin`].
617    #[must_use]
618    pub fn with_sharding(
619        grant: LeaseGrant,
620        low_water: CostUnits,
621        margin: jiff::SignedDuration,
622        sharding: LocalSharding,
623    ) -> Self {
624        if sharding == LocalSharding::SINGLE {
625            return Self::with_safety_margin(grant, low_water, margin);
626        }
627        let usable_until = usable_until(grant.expires_at, margin);
628        let count = sharding.get();
629        let shards = (0..count)
630            .map(|index| {
631                LeaseShard::new(
632                    partition(grant.units.get(), count, index),
633                    partition(low_water.get(), count, index),
634                )
635            })
636            .collect::<Vec<_>>()
637            .into_boxed_slice();
638        Self {
639            inner: Arc::new(LeaseInner {
640                grant,
641                balance: LeaseBalance::Sharded(shards),
642                refill: OnceLock::new(),
643                low_water: low_water.get(),
644                refused: AtomicBool::new(false),
645                refused_quote: AtomicU64::new(0),
646                contention_reported: AtomicU64::new(0),
647                usable_until,
648            }),
649        }
650    }
651
652    /// Attach the signal to raise when spending crosses `low_water`.
653    ///
654    /// Without one, a lease still records the crossing in `needs_refill` and
655    /// waits to be polled — which is the behaviour every caller had before
656    /// refill became demand-driven, and remains correct, just later.
657    #[must_use]
658    pub fn with_refill(self, signal: Arc<dyn RefillSignal>) -> Self {
659        // The first attachment owns the doorbell for every local view. A
660        // repeated attachment keeps that established signal; construction
661        // remains safe even if a caller created views before wiring refill.
662        let _already_attached = self.inner.refill.set(signal);
663        self
664    }
665
666    /// The instant this lease stops accepting debits and commits locally.
667    #[must_use]
668    pub fn usable_until(&self) -> Timestamp {
669        self.inner.usable_until
670    }
671
672    /// The allocator's grant this local lease spends.
673    #[must_use]
674    pub fn grant(&self) -> &LeaseGrant {
675        &self.inner.grant
676    }
677
678    /// Whether this is the only independently reference-counted local view of
679    /// the lease. The refill plane combines this with the outer `Arc` count;
680    /// only then can no request still hold any locality's handle.
681    #[must_use]
682    pub fn is_only_local_view(&self) -> bool {
683        Arc::strong_count(&self.inner) == 1
684    }
685
686    /// The largest quote this lease refused for want of units, or zero.
687    ///
688    /// Demand the refill plane has proof of: a consolidation may grow the
689    /// replacement to it when the account can fund it (GL-131). Exact only once
690    /// the lease has quiesced, the same condition [`Self::remaining`] needs.
691    #[must_use]
692    pub fn largest_refused_quote(&self) -> CostUnits {
693        CostUnits(self.inner.refused_quote.load(Ordering::Acquire))
694    }
695
696    /// Debits that lost a compare-exchange on a shard to another writer,
697    /// summed across shards, since this lease was created.
698    ///
699    /// Evidence that the account's funding line is being written from more
700    /// than one core at once, and a **lower bound** on contention rather than
701    /// a measure of its cost: only the debit loops can observe a lost race.
702    /// The rate bucket, reference counts and settlement use read-modify-write
703    /// operations that pay for a contended line without ever failing, and a
704    /// debit whose exchange lands between two rivals' records nothing. On a
705    /// load-linked/store-conditional target a spurious exchange failure also
706    /// counts; x86-64 and aarch64 with LSE atomics have none.
707    ///
708    /// A control-plane read that walks every shard. No decision reads it.
709    #[must_use]
710    pub fn contended_debits(&self) -> u64 {
711        self.inner
712            .balance
713            .as_slice()
714            .iter()
715            .fold(0u64, |total, shard| {
716                total.saturating_add(shard.contended.load(Ordering::Relaxed))
717            })
718    }
719
720    /// The contention recorded since the last call, handed out exactly once.
721    ///
722    /// A slot folds this into its account's running total when the lease
723    /// leaves it. Idempotent across removal and reinstallation of the same
724    /// lease: what was handed out is remembered on the lease itself, and a
725    /// concurrent or repeated call receives only what no earlier call did.
726    #[must_use]
727    pub fn take_unreported_contention(&self) -> u64 {
728        let total = self.contended_debits();
729        let reported = self
730            .inner
731            .contention_reported
732            .fetch_max(total, Ordering::AcqRel);
733        total.saturating_sub(reported)
734    }
735
736    /// The contention a slot has not yet been handed, without handing it out.
737    #[must_use]
738    pub fn unreported_contention(&self) -> u64 {
739        self.contended_debits()
740            .saturating_sub(self.inner.contention_reported.load(Ordering::Acquire))
741    }
742
743    /// Units still spendable, aggregated across every shard.
744    ///
745    /// Exact for a single-counter lease, and for a sharded lease once it has
746    /// quiesced — which is the state every accounting use requires, and the
747    /// one `release_quiesced` establishes before settling a grant.
748    ///
749    /// Under concurrency a sharded read is an *estimate in both directions*,
750    /// and deliberately so: a shard-by-shard walk is not one atomic instant,
751    /// and a failed fragmented reservation returns its whole aggregate to one
752    /// shard rather than to the shards it drew from (see
753    /// `Self::try_reserve_at`). A refund landing on an already-visited
754    /// shard is counted twice; one landing on a shard the walk has passed is
755    /// missed. Hence the clamp: the sum can exceed the grant, so it is
756    /// saturated and bounded rather than asserted, and no reader of a live
757    /// lease may treat the result as an exact balance.
758    #[must_use]
759    pub fn remaining(&self) -> CostUnits {
760        let remaining = self
761            .inner
762            .balance
763            .as_slice()
764            .iter()
765            .fold(0u64, |total, shard| {
766                total.saturating_add(shard.remaining.load(Ordering::Acquire))
767            })
768            .min(self.inner.grant.units.get());
769        CostUnits(remaining)
770    }
771
772    /// True once spending has crossed the low-water mark. Monotonic in
773    /// practice only between refills; the refill task polls or checks after
774    /// each reservation.
775    #[must_use]
776    pub fn needs_refill(&self) -> bool {
777        self.remaining().get() <= self.inner.low_water
778    }
779
780    /// What the refill plane should do about this lease, clearing whatever
781    /// the lease was holding to tell it.
782    ///
783    /// [`RefillVerdict::Refused`] is tested first and outranks a low-water
784    /// crossing, because the two verdicts ask for different actions and only
785    /// one of them is still preventable. A crossing is an *anticipatory*
786    /// signal — the lease can still serve, so the plane acquires alongside it
787    /// and no request is refused between ticks. A refusal is the failure that
788    /// crossing exists to avoid, already happened: there is nothing left to
789    /// preserve, and acquiring alongside a grant that could not fund the work
790    /// would install a *smaller* one beside it (see
791    /// `Self::signal_refusal`).
792    ///
793    /// Re-arming: for the crossing case the second aggregate read closes the
794    /// race with a debit that observed a still-set flag just before this
795    /// method cleared it — that debit is either included in the recheck, or a
796    /// later debit sees the cleared flag and rings the doorbell itself.
797    #[must_use]
798    pub fn refill_due_or_rearm(&self) -> RefillVerdict {
799        // Acquires the refused debit's counter reads before the plane acts on
800        // them, and consumes the doorbell so a plane that answers this
801        // verdict is not woken again for the same refusal.
802        if self.inner.refused.swap(false, Ordering::AcqRel) {
803            return RefillVerdict::Refused;
804        }
805        if self.needs_refill() {
806            return RefillVerdict::Draining;
807        }
808        for shard in self.inner.balance.as_slice() {
809            // Reading `true` acquires the debit published by the signal's
810            // release RMW before clearing its doorbell.
811            shard.signalled.swap(false, Ordering::AcqRel);
812        }
813        if self.needs_refill() {
814            RefillVerdict::Draining
815        } else {
816            RefillVerdict::Idle
817        }
818    }
819
820    /// Debit `units` if the lease is live and has capacity. Lock-free; the
821    /// CAS loop retries only under concurrent reservations on the same lease.
822    ///
823    /// This is the raw counter operation. Request code should prefer
824    /// [`crate::reservation::Reservation::reserve`], which pairs the debit
825    /// with the commit/release state machine.
826    #[inline]
827    pub fn try_debit(&self, units: CostUnits, now: Timestamp) -> Result<(), DenyReason> {
828        self.try_reserve_at(units, now, Locality::current())
829            .map(|_| ())
830    }
831
832    #[inline]
833    pub(crate) fn try_reserve_at(
834        &self,
835        units: CostUnits,
836        now: Timestamp,
837        locality: Locality,
838    ) -> Result<LeaseDebit, DenyReason> {
839        if now >= self.inner.usable_until {
840            // The same silence as the exhaustion exit below, for the same
841            // reason: a lease that can no longer serve must say so rather
842            // than wait to be discovered. Rotation keeps the poll interval as
843            // its backstop for a lease no request touches (INVARIANTS.md GL-6).
844            self.signal_refusal();
845            return Err(DenyReason::LeaseExpired);
846        }
847        let want = units.get();
848        let shards = self.inner.balance.as_slice();
849        let first = locality.index(LocalSharding::new(
850            std::num::NonZeroUsize::new(shards.len()).expect("lease has at least one shard"),
851        ));
852
853        // The routine path: one CAS on the caller's stable shard. Before
854        // assembling a split receipt, try every sibling for the whole debit;
855        // an idle shard can therefore be stolen without allocation.
856        for offset in 0..shards.len() {
857            let index = (first + offset) % shards.len();
858            if let Some(part) = self.try_whole(index, want) {
859                return Ok(part);
860            }
861        }
862
863        // Genuine fragmentation: reserve pieces in a stable circular order.
864        // The receipt remains fixed-size: all pieces belong to one lease, so
865        // cancellation can restore their exact aggregate to one shard without
866        // changing the lease bound. A failed attempt does the same before it
867        // returns, so denial remains zero-charge and no capacity is stranded.
868        let mut needed = want;
869        for offset in 0..shards.len() {
870            let index = (first + offset) % shards.len();
871            needed -= self.take_up_to(index, needed);
872            if needed == 0 {
873                // The first shard was necessarily exhausted (or already
874                // empty), so it is a sufficient shard-local refill doorbell
875                // for this cold fragmented path.
876                let next = shards[first].remaining.load(Ordering::Acquire);
877                self.maybe_signal_refill(first, next);
878                return Ok(LeaseDebit {
879                    shard: first,
880                    units: want,
881                });
882            }
883        }
884
885        self.credit_to(first, want - needed);
886        // Reported after the rollback, so the plane that answers this refusal
887        // reads the restored aggregate rather than a torn one. The quote is
888        // raised first so the doorbell's release carries it.
889        self.inner.refused_quote.fetch_max(want, Ordering::Relaxed);
890        self.signal_refusal();
891        Err(DenyReason::LeaseExhausted {
892            remaining: self.remaining(),
893        })
894    }
895
896    #[inline]
897    fn try_whole(&self, shard_index: usize, want: u64) -> Option<LeaseDebit> {
898        let shard = &self.inner.balance.as_slice()[shard_index];
899        let mut current = shard.remaining.load(Ordering::Acquire);
900        // Counted in a register and recorded once on every exit, so an
901        // uncontended debit pays one untaken branch.
902        let mut lost = 0u64;
903        loop {
904            let Some(next) = current.checked_sub(want) else {
905                shard.note_contention(lost);
906                return None;
907            };
908            match shard.remaining.compare_exchange_weak(
909                current,
910                next,
911                Ordering::AcqRel,
912                Ordering::Acquire,
913            ) {
914                Ok(_) => {
915                    shard.note_contention(lost);
916                    self.maybe_signal_refill(shard_index, next);
917                    return Some(LeaseDebit {
918                        shard: shard_index,
919                        units: want,
920                    });
921                }
922                Err(observed) => {
923                    lost += 1;
924                    current = observed;
925                }
926            }
927        }
928    }
929
930    #[cold]
931    fn take_up_to(&self, shard_index: usize, want: u64) -> u64 {
932        let shard = &self.inner.balance.as_slice()[shard_index];
933        let mut current = shard.remaining.load(Ordering::Acquire);
934        let mut lost = 0u64;
935        loop {
936            if current == 0 {
937                shard.note_contention(lost);
938                return 0;
939            }
940            let taken = current.min(want);
941            let next = current - taken;
942            match shard.remaining.compare_exchange_weak(
943                current,
944                next,
945                Ordering::AcqRel,
946                Ordering::Acquire,
947            ) {
948                Ok(_) => {
949                    shard.note_contention(lost);
950                    return taken;
951                }
952                Err(observed) => {
953                    lost += 1;
954                    current = observed;
955                }
956            }
957        }
958    }
959
960    #[inline]
961    fn maybe_signal_refill(&self, shard_index: usize, next: u64) {
962        if next <= self.inner.balance.as_slice()[shard_index].low_water {
963            self.signal_refill(shard_index);
964        }
965    }
966
967    /// Report a debit this lease could not fund, and ring the doorbell the
968    /// first time.
969    ///
970    /// A refusal is the one lease event that *proves* the grant can no longer
971    /// serve the work being offered, so it is the one event the refill plane
972    /// most needs and, before GL-109, the only one it was never told about.
973    /// Low water cannot stand in for it: the mark counts units and the
974    /// refusal is about a quote, so a lease holding more units than its mark
975    /// can refuse every request until its TTL while the account has the
976    /// balance to fund them.
977    ///
978    /// Cold, and once per lease: the flag is both the report the plane reads
979    /// and the token that keeps a refusal storm from becoming a wake storm.
980    /// A refusal arriving while an earlier one is still unanswered adds
981    /// nothing — the plane's response does not depend on how many there were.
982    #[cold]
983    #[inline(never)]
984    fn signal_refusal(&self) {
985        // Release publishes the preceding counter reads to the plane that
986        // clears this flag; a swap that observes `true` means an unanswered
987        // report already stands.
988        if self.inner.refused.swap(true, Ordering::AcqRel) {
989            return;
990        }
991        if let Some(signal) = self.inner.refill.get() {
992            signal.request_refill();
993        }
994    }
995
996    /// Raise the refill signal at most once per shard between control-plane
997    /// aggregate checks.
998    ///
999    /// Out of line and `#[cold]`: every debit tests the branch above, but only
1000    /// one debit per lease ever arrives here, so none of this belongs in the
1001    /// hot path's instruction stream.
1002    #[cold]
1003    #[inline(never)]
1004    fn signal_refill(&self, shard_index: usize) {
1005        let Some(signal) = self.inner.refill.get() else {
1006            return;
1007        };
1008        // Release publishes the preceding remaining-counter CAS to the
1009        // control plane when it clears this doorbell. The implementation
1010        // behind `request_refill` remains responsible for its own wake state.
1011        if !self.inner.balance.as_slice()[shard_index]
1012            .signalled
1013            .swap(true, Ordering::Release)
1014        {
1015            signal.request_refill();
1016        }
1017    }
1018
1019    /// Return previously debited units (release of an uncommitted
1020    /// reservation). Callers must return only units they debited, exactly
1021    /// once — the reservation state machine guarantees this.
1022    #[inline]
1023    pub(crate) fn credit(&self, debit: &LeaseDebit) {
1024        self.credit_to(debit.shard, debit.units);
1025    }
1026
1027    fn credit_to(&self, shard: usize, units: u64) {
1028        self.inner.balance.as_slice()[shard]
1029            .remaining
1030            .fetch_update(Ordering::AcqRel, Ordering::Acquire, |current| {
1031                current.checked_add(units)
1032            })
1033            .expect("a debit receipt cannot credit beyond its lease grant");
1034    }
1035}
1036
1037#[cfg(test)]
1038mod tests {
1039    use super::*;
1040    use std::num::NonZeroUsize;
1041
1042    fn t(secs: i64) -> Timestamp {
1043        Timestamp::from_second(secs).unwrap()
1044    }
1045
1046    fn overage() -> AccountOverage {
1047        AccountOverage::new(AccountId(1))
1048    }
1049
1050    /// The tentative debit's whole reason for existing: units taken but never
1051    /// resolved come back on their own. Without the guard they would sit in
1052    /// `spent` with no owner — a panic or an early return between the debit
1053    /// and the claim would strand them for the life of the process, silently
1054    /// shrinking the account's cap.
1055    #[test]
1056    fn dropping_an_unresolved_tentative_debit_returns_the_credit() {
1057        let o = overage();
1058        {
1059            let tentative = o.debit_tentatively(CostUnits(40), CostUnits(100)).unwrap();
1060            assert_eq!(o.spent(), CostUnits(40));
1061            drop(tentative);
1062        }
1063        assert_eq!(o.spent(), CostUnits::ZERO);
1064        assert_eq!(o.headroom(CostUnits(100)), CostUnits(100));
1065    }
1066
1067    /// A losing claim credits the debit back before returning, so the cap is
1068    /// immediately reusable and committed occupancy never moved.
1069    #[test]
1070    fn a_tentative_debit_whose_claim_loses_returns_the_credit() {
1071        let o = overage();
1072        let tentative = o.debit_tentatively(CostUnits(40), CostUnits(100)).unwrap();
1073        assert_eq!(o.spent(), CostUnits(40));
1074
1075        let lost: Result<(), ()> = tentative.publish_commit(|| Err(()));
1076        assert!(lost.is_err());
1077        assert_eq!(o.spent(), CostUnits::ZERO);
1078        assert_eq!(o.headroom(CostUnits(100)), CostUnits(100));
1079    }
1080
1081    /// A winning claim retains the debit and advances committed occupancy,
1082    /// which is what makes those units irrevocable to a cap observer.
1083    #[test]
1084    fn a_tentative_debit_whose_claim_wins_becomes_irrevocable() {
1085        let o = overage();
1086        let tentative = o.debit_tentatively(CostUnits(40), CostUnits(100)).unwrap();
1087        let won: Result<(), ()> = tentative.publish_commit(|| Ok(()));
1088        assert!(won.is_ok());
1089        assert_eq!(o.spent(), CostUnits(40));
1090        // Committed occupancy moved with it, so the remaining cap is stably
1091        // exhausted rather than temporarily so.
1092        assert_eq!(
1093            o.try_debit(CostUnits(61), CostUnits(100)),
1094            Err(DenyReason::OverageCapExhausted {
1095                spent: CostUnits(40),
1096                overage_cap: CostUnits(100),
1097            })
1098        );
1099    }
1100
1101    #[test]
1102    fn committed_overage_accumulates_up_to_the_cap_and_then_refuses() {
1103        let o = overage();
1104        o.try_debit(CostUnits(40), CostUnits(100)).unwrap();
1105        o.publish_claim(CostUnits(40), || Ok::<_, ()>(())).unwrap();
1106        o.try_debit(CostUnits(60), CostUnits(100)).unwrap();
1107        o.publish_claim(CostUnits(60), || Ok::<_, ()>(())).unwrap();
1108        assert_eq!(o.spent(), CostUnits(100));
1109        assert_eq!(o.headroom(CostUnits(100)), CostUnits::ZERO);
1110        assert_eq!(
1111            o.try_debit(CostUnits(1), CostUnits(100)),
1112            Err(DenyReason::OverageCapExhausted {
1113                spent: CostUnits(100),
1114                overage_cap: CostUnits(100),
1115            })
1116        );
1117        assert_eq!(o.spent(), CostUnits(100), "a refusal claims nothing");
1118    }
1119
1120    /// A pending debit changes which local overage state is reported when
1121    /// returning all pending credit would make this request fit. A request
1122    /// larger than the whole cap is stable local saturation, but it remains
1123    /// retryable because a background lease grant can fund it.
1124    #[test]
1125    fn pending_overage_is_transient_only_when_its_refund_would_make_room() {
1126        let o = overage();
1127        o.try_debit(CostUnits(60), CostUnits(100)).unwrap();
1128
1129        let pending_saturation = o.try_debit(CostUnits(50), CostUnits(100)).unwrap_err();
1130        assert_eq!(
1131            pending_saturation,
1132            DenyReason::OverageCapTemporarilyExhausted {
1133                spent: CostUnits(60),
1134                overage_cap: CostUnits(100),
1135            }
1136        );
1137        assert_eq!(pending_saturation.retry(), crate::deny::Retry::Transient);
1138
1139        let request_exceeds_cap = o.try_debit(CostUnits(101), CostUnits(100)).unwrap_err();
1140        assert_eq!(
1141            request_exceeds_cap,
1142            DenyReason::OverageCapExhausted {
1143                spent: CostUnits(60),
1144                overage_cap: CostUnits(100),
1145            }
1146        );
1147        assert_eq!(request_exceeds_cap.retry(), crate::deny::Retry::Transient);
1148    }
1149
1150    /// The cap is a parameter, so lowering it below what an account has
1151    /// already spent refuses immediately rather than waiting for the counter
1152    /// to catch up — and `headroom` saturates instead of underflowing.
1153    #[test]
1154    fn lowering_the_cap_below_current_spend_refuses_at_once() {
1155        let o = overage();
1156        o.try_debit(CostUnits(80), CostUnits(100)).unwrap();
1157        assert_eq!(o.headroom(CostUnits(50)), CostUnits::ZERO);
1158        assert!(o.try_debit(CostUnits(1), CostUnits(50)).is_err());
1159        o.try_debit(CostUnits(1), CostUnits(100)).unwrap();
1160    }
1161
1162    /// A total that cannot be represented is the same refusal as one that
1163    /// exceeds the cap: never a wrap to a small spend, which would reopen the
1164    /// cap (INVARIANTS.md GL-11).
1165    #[test]
1166    fn an_unrepresentable_total_refuses_rather_than_wrapping() {
1167        let o = overage();
1168        o.try_debit(CostUnits(u64::MAX - 1), CostUnits(u64::MAX))
1169            .unwrap();
1170        assert!(o.try_debit(CostUnits(2), CostUnits(u64::MAX)).is_err());
1171        assert_eq!(o.spent(), CostUnits(u64::MAX - 1));
1172    }
1173
1174    #[test]
1175    fn credit_returns_headroom_to_the_cap() {
1176        let o = overage();
1177        o.try_debit(CostUnits(100), CostUnits(100)).unwrap();
1178        o.credit(CostUnits(30));
1179        assert_eq!(o.spent(), CostUnits(70));
1180        assert_eq!(o.headroom(CostUnits(100)), CostUnits(30));
1181        o.try_debit(CostUnits(30), CostUnits(100)).unwrap();
1182        assert!(o.try_debit(CostUnits(1), CostUnits(100)).is_err());
1183    }
1184
1185    /// Concurrent debits that individually fit the cap must not jointly
1186    /// exceed it. The cap comparison lives inside the compare-exchange for
1187    /// exactly this reason; a check-then-claim would let both threads through.
1188    #[test]
1189    fn concurrent_debits_never_exceed_the_cap() {
1190        const THREADS: usize = 8;
1191        const EACH: usize = 500;
1192        const CAP: u64 = 1_000;
1193        let o = Arc::new(overage());
1194        let admitted = Arc::new(AtomicU64::new(0));
1195        std::thread::scope(|scope| {
1196            for _ in 0..THREADS {
1197                let o = Arc::clone(&o);
1198                let admitted = Arc::clone(&admitted);
1199                scope.spawn(move || {
1200                    for _ in 0..EACH {
1201                        if o.try_debit(CostUnits(1), CostUnits(CAP)).is_ok() {
1202                            admitted.fetch_add(1, Ordering::Relaxed);
1203                        }
1204                    }
1205                });
1206            }
1207        });
1208        assert_eq!(o.spent(), CostUnits(CAP));
1209        assert_eq!(
1210            admitted.load(Ordering::Relaxed),
1211            CAP,
1212            "every admitted unit is one the counter recorded, and vice versa"
1213        );
1214    }
1215
1216    fn lease(units: u64, expires: i64, low_water: u64) -> LocalLease {
1217        LocalLease::new(
1218            LeaseGrant {
1219                lease_id: LeaseId(7),
1220                account_id: AccountId(1),
1221                fencing_token: FencingToken(3),
1222                units: CostUnits(units),
1223                expires_at: t(expires),
1224            },
1225            CostUnits(low_water),
1226        )
1227    }
1228
1229    fn sharded_lease(units: u64, low_water: u64, shards: usize) -> LocalLease {
1230        LocalLease::with_sharding(
1231            LeaseGrant {
1232                lease_id: LeaseId(7),
1233                account_id: AccountId(1),
1234                fencing_token: FencingToken(3),
1235                units: CostUnits(units),
1236                expires_at: t(1_000),
1237            },
1238            CostUnits(low_water),
1239            jiff::SignedDuration::ZERO,
1240            LocalSharding::new(NonZeroUsize::new(shards).unwrap()),
1241        )
1242    }
1243
1244    #[test]
1245    fn sharded_grant_and_low_water_partitions_are_exact() {
1246        assert_eq!(align_of::<LeaseShard>(), 128);
1247        assert_eq!(size_of::<LeaseShard>(), 128);
1248        assert_eq!(align_of::<LocalLease>(), 128);
1249
1250        let l = sharded_lease(10, 3, 4);
1251        let shards = l.inner.balance.as_slice();
1252        assert_eq!(shards.len(), 4);
1253        assert_eq!(
1254            shards
1255                .iter()
1256                .map(|shard| shard.remaining.load(Ordering::Relaxed))
1257                .collect::<Vec<_>>(),
1258            [3, 3, 2, 2]
1259        );
1260        assert_eq!(
1261            shards
1262                .iter()
1263                .map(|shard| shard.low_water)
1264                .collect::<Vec<_>>(),
1265            [1, 1, 1, 0]
1266        );
1267        assert_eq!(l.remaining(), CostUnits(10));
1268    }
1269
1270    #[test]
1271    fn cloned_local_views_are_visible_to_quiescence_detection() {
1272        let lease = sharded_lease(10, 0, 2);
1273        assert!(lease.is_only_local_view());
1274        let sibling = lease.clone();
1275        assert!(!lease.is_only_local_view());
1276        drop(sibling);
1277        assert!(lease.is_only_local_view());
1278    }
1279
1280    #[test]
1281    fn fragmented_reservation_refunds_without_stranding_capacity() {
1282        let l = Arc::new(sharded_lease(10, 0, 4));
1283        assert_eq!(size_of::<LeaseDebit>(), 16, "the receipt is fixed-size");
1284
1285        let reservation = crate::Reservation::reserve(&l, CostUnits(8), t(0)).unwrap();
1286        assert_eq!(l.remaining(), CostUnits(2));
1287        assert_eq!(reservation.cancel(), crate::CancelOutcome::ZeroCharged);
1288        assert_eq!(l.remaining(), CostUnits(10));
1289    }
1290
1291    #[test]
1292    fn failed_fragmented_debit_reports_true_remaining_and_rolls_back() {
1293        let l = sharded_lease(10, 0, 4);
1294
1295        assert_eq!(
1296            l.try_debit(CostUnits(11), t(0)),
1297            Err(DenyReason::LeaseExhausted {
1298                remaining: CostUnits(10)
1299            })
1300        );
1301        assert_eq!(l.remaining(), CostUnits(10));
1302    }
1303
1304    #[test]
1305    fn rebalanced_refunds_are_exact_at_the_u64_boundary() {
1306        let l = Arc::new(sharded_lease(u64::MAX, 0, 2));
1307        let reservation = crate::Reservation::reserve(&l, CostUnits(u64::MAX), t(0)).unwrap();
1308        assert_eq!(l.remaining(), CostUnits::ZERO);
1309        assert_eq!(reservation.cancel(), crate::CancelOutcome::ZeroCharged);
1310        assert_eq!(l.remaining(), CostUnits(u64::MAX));
1311
1312        // The refund is deliberately allowed to concentrate the grant on one
1313        // shard. A second maximum-sized reserve proves that representation is
1314        // spendable and cannot overflow its refund destination.
1315        let reservation = crate::Reservation::reserve(&l, CostUnits(u64::MAX), t(0)).unwrap();
1316        assert_eq!(reservation.cancel(), crate::CancelOutcome::ZeroCharged);
1317        assert_eq!(l.remaining(), CostUnits(u64::MAX));
1318    }
1319
1320    /// No rival, no lost exchange: a debit that won its first compare-exchange
1321    /// records nothing, on the single and the sharded layout alike. Limited to
1322    /// targets whose weak exchange cannot fail spuriously; on a
1323    /// load-linked/store-conditional target a spurious failure also counts.
1324    #[cfg(any(
1325        target_arch = "x86_64",
1326        all(target_arch = "aarch64", target_feature = "lse")
1327    ))]
1328    #[test]
1329    fn an_uncontended_debit_records_no_contention() {
1330        for l in [lease(1_000, 1_000, 0), sharded_lease(1_000, 0, 4)] {
1331            for _ in 0..100 {
1332                let debit = l
1333                    .try_reserve_at(CostUnits(3), t(0), Locality::current())
1334                    .unwrap();
1335                l.credit(&debit);
1336            }
1337            // Fragmented debits walk `take_up_to` instead of `try_whole`.
1338            let fragmented = l
1339                .try_reserve_at(CostUnits(1_000), t(0), Locality::current())
1340                .unwrap();
1341            l.credit(&fragmented);
1342            assert_eq!(l.contended_debits(), 0);
1343        }
1344    }
1345
1346    /// Many writers on one line lose exchanges, and each loss is recorded on
1347    /// the shard it was lost on without disturbing the balance.
1348    #[test]
1349    fn contended_debits_are_recorded_without_disturbing_the_balance() {
1350        const THREADS: u64 = 8;
1351        const DEBITS: u64 = 20_000;
1352        let l = lease(THREADS * DEBITS, 1_000, 0);
1353        // Retries are a race, so the stress repeats until one is seen. Eight
1354        // threads on one line lose races within the first round on any
1355        // multi-core host; the bound keeps a single-core host from hanging.
1356        for _ in 0..50 {
1357            std::thread::scope(|scope| {
1358                for _ in 0..THREADS {
1359                    scope.spawn(|| {
1360                        for _ in 0..DEBITS {
1361                            let debit = l
1362                                .try_reserve_at(CostUnits(1), t(0), Locality::current())
1363                                .unwrap();
1364                            l.credit(&debit);
1365                        }
1366                    });
1367                }
1368            });
1369            if l.contended_debits() > 0 {
1370                break;
1371            }
1372        }
1373        assert!(
1374            l.contended_debits() > 0,
1375            "eight writers on one line never lost a race"
1376        );
1377        assert_eq!(
1378            l.remaining(),
1379            CostUnits(THREADS * DEBITS),
1380            "credits restored every debit"
1381        );
1382    }
1383
1384    /// The hand-out is exact and idempotent. A slot that takes the lease out,
1385    /// reinstalls it, and takes it out again must not report the same
1386    /// contention twice.
1387    #[test]
1388    fn unreported_contention_is_handed_out_exactly_once() {
1389        let l = sharded_lease(100, 0, 2);
1390        let shards = l.inner.balance.as_slice();
1391        shards[0].note_contention(3);
1392        shards[1].note_contention(4);
1393        assert_eq!(l.contended_debits(), 7);
1394        assert_eq!(l.unreported_contention(), 7);
1395        assert_eq!(l.take_unreported_contention(), 7);
1396        assert_eq!(
1397            l.take_unreported_contention(),
1398            0,
1399            "reinstalled and taken again"
1400        );
1401        assert_eq!(l.unreported_contention(), 0);
1402        shards[1].note_contention(2);
1403        assert_eq!(l.unreported_contention(), 2);
1404        assert_eq!(l.take_unreported_contention(), 2);
1405        assert_eq!(
1406            l.contended_debits(),
1407            9,
1408            "the lease's own total never resets"
1409        );
1410        // A handle cloned into another locality view shares the same record.
1411        assert_eq!(l.clone().take_unreported_contention(), 0);
1412        shards[0].note_contention(0);
1413        assert_eq!(l.contended_debits(), 9, "recording zero writes nothing");
1414    }
1415
1416    /// The overage counter records lost debit races (GL-139) and nothing when
1417    /// uncontended, without disturbing `spent`.
1418    #[cfg(any(
1419        target_arch = "x86_64",
1420        all(target_arch = "aarch64", target_feature = "lse")
1421    ))]
1422    #[test]
1423    fn an_uncontended_overage_debit_records_no_contention() {
1424        let overage = AccountOverage::new(AccountId(1));
1425        for _ in 0..100 {
1426            overage.try_debit(CostUnits(1), CostUnits(1_000)).unwrap();
1427        }
1428        // Refusals exit the loop on another path.
1429        assert!(
1430            overage
1431                .try_debit(CostUnits(1_000), CostUnits(1_000))
1432                .is_err()
1433        );
1434        assert_eq!(overage.contended_debits(), 0);
1435    }
1436
1437    #[test]
1438    fn contended_overage_debits_are_recorded_without_disturbing_spend() {
1439        const THREADS: u64 = 8;
1440        const DEBITS: u64 = 20_000;
1441        let overage = AccountOverage::new(AccountId(1));
1442        let mut rounds = 0;
1443        while overage.contended_debits() == 0 && rounds < 50 {
1444            rounds += 1;
1445            std::thread::scope(|scope| {
1446                for _ in 0..THREADS {
1447                    scope.spawn(|| {
1448                        for _ in 0..DEBITS {
1449                            overage
1450                                .try_debit(CostUnits(1), CostUnits(u64::MAX))
1451                                .unwrap();
1452                        }
1453                    });
1454                }
1455            });
1456        }
1457        assert!(
1458            overage.contended_debits() > 0,
1459            "eight writers on one line never lost a race"
1460        );
1461        assert_eq!(
1462            overage.spent.load(Ordering::Relaxed),
1463            rounds * THREADS * DEBITS,
1464            "every debit counted exactly once"
1465        );
1466    }
1467
1468    #[test]
1469    fn sharded_lease_spends_to_exact_exhaustion_without_stranding() {
1470        let l = sharded_lease(17, 0, 8);
1471        for _ in 0..17 {
1472            l.try_debit(CostUnits(1), t(0)).unwrap();
1473        }
1474        assert_eq!(l.remaining(), CostUnits::ZERO);
1475        assert_eq!(
1476            l.try_debit(CostUnits(1), t(0)),
1477            Err(DenyReason::LeaseExhausted {
1478                remaining: CostUnits::ZERO
1479            })
1480        );
1481    }
1482
1483    #[test]
1484    fn debit_search_starts_at_the_supplied_locality_and_wraps_in_order() {
1485        let l = sharded_lease(16, 0, 4);
1486        let debit = l
1487            .try_reserve_at(CostUnits(1), t(0), Locality::for_test(3))
1488            .unwrap();
1489        let remaining: Vec<_> = l
1490            .inner
1491            .balance
1492            .as_slice()
1493            .iter()
1494            .map(|shard| shard.remaining.load(Ordering::Relaxed))
1495            .collect();
1496        assert_eq!(remaining, [4, 4, 4, 3]);
1497        l.credit(&debit);
1498
1499        let fragmented = l
1500            .try_reserve_at(CostUnits(5), t(0), Locality::for_test(3))
1501            .unwrap();
1502        let remaining: Vec<_> = l
1503            .inner
1504            .balance
1505            .as_slice()
1506            .iter()
1507            .map(|shard| shard.remaining.load(Ordering::Relaxed))
1508            .collect();
1509        assert_eq!(remaining, [3, 4, 4, 0]);
1510        l.credit(&fragmented);
1511        assert_eq!(l.remaining(), CostUnits(16));
1512    }
1513
1514    /// A sibling that can satisfy the whole debit is the routine fallback,
1515    /// not fragmentation. Keeping the debit on one counter is what avoids an
1516    /// O(shards) gather and concentrates its cancellation on the counter that
1517    /// actually paid it.
1518    #[test]
1519    fn a_whole_sibling_is_used_before_fragmenting() {
1520        let l = sharded_lease(16, 0, 4);
1521
1522        // Leave locality 3 with three units while locality 0 still has four.
1523        let first = l
1524            .try_reserve_at(CostUnits(1), t(0), Locality::for_test(3))
1525            .unwrap();
1526        let sibling = l
1527            .try_reserve_at(CostUnits(4), t(0), Locality::for_test(3))
1528            .unwrap();
1529
1530        let remaining: Vec<_> = l
1531            .inner
1532            .balance
1533            .as_slice()
1534            .iter()
1535            .map(|shard| shard.remaining.load(Ordering::Relaxed))
1536            .collect();
1537        assert_eq!(
1538            remaining,
1539            [0, 4, 4, 3],
1540            "the whole sibling pays; the undersized local shard is untouched"
1541        );
1542
1543        l.credit(&sibling);
1544        l.credit(&first);
1545        assert_eq!(l.remaining(), CostUnits(16));
1546    }
1547
1548    #[test]
1549    fn a_torn_aggregate_above_the_grant_reads_as_the_grant() {
1550        let l = sharded_lease(4, 0, 2);
1551        // The state a concurrent walk can observe: the reader counted shard 0
1552        // at two units, then a failed fragmented reservation drained both
1553        // shards and returned their whole aggregate to shard 1 — which the
1554        // reader has not visited yet. Its sum is six against a grant of four.
1555        let LeaseBalance::Sharded(shards) = &l.inner.balance else {
1556            panic!("a two-shard lease is sharded");
1557        };
1558        shards[1].remaining.store(4, Ordering::Release);
1559
1560        assert_eq!(
1561            l.remaining(),
1562            CostUnits(4),
1563            "an over-counted walk is clamped to the grant, never asserted"
1564        );
1565    }
1566
1567    #[test]
1568    fn a_fragmenting_rollback_never_panics_a_concurrent_aggregate_read() {
1569        // The rollback path concentrates a failed reservation's aggregate on
1570        // one shard, so a reader partway through its walk can count the same
1571        // units twice. Before the clamp that tripped `remaining`'s debug
1572        // assertion here, and its checked-add in release.
1573        let l = Arc::new(sharded_lease(6_400, 0, 64));
1574        let stop = Arc::new(AtomicBool::new(false));
1575        let spenders: Vec<_> = (48..52)
1576            .map(|locality| {
1577                let l = Arc::clone(&l);
1578                let stop = Arc::clone(&stop);
1579                std::thread::spawn(move || {
1580                    while !stop.load(Ordering::Relaxed) {
1581                        // One unit more than the whole grant: every attempt
1582                        // drains all sixty-four shards, fails, and rolls the
1583                        // aggregate back onto this locality's own shard —
1584                        // far enough along the walk to be reached after a
1585                        // reader has already counted the shards before it.
1586                        drop(l.try_reserve_at(
1587                            CostUnits(6_401),
1588                            t(0),
1589                            Locality::for_test(locality),
1590                        ));
1591                    }
1592                })
1593            })
1594            .collect();
1595        for _ in 0..200_000 {
1596            assert!(l.remaining() <= CostUnits(6_400));
1597        }
1598        stop.store(true, Ordering::Relaxed);
1599        for spender in spenders {
1600            spender.join().unwrap();
1601        }
1602        assert_eq!(
1603            l.remaining(),
1604            CostUnits(6_400),
1605            "no units were lost or created"
1606        );
1607    }
1608
1609    #[test]
1610    fn an_early_shard_signal_is_rearmed_until_the_aggregate_crosses() {
1611        let signal = Arc::new(CountingSignal::default());
1612        let l = sharded_lease(100, 20, 2).with_refill(signal.clone());
1613
1614        l.try_debit(CostUnits(40), t(0)).unwrap();
1615        assert_eq!(signal.count(), 1, "the first shard crossed its share");
1616        assert_eq!(
1617            l.refill_due_or_rearm(),
1618            RefillVerdict::Idle,
1619            "sixty aggregate units remain"
1620        );
1621
1622        l.try_debit(CostUnits(1), t(0)).unwrap();
1623        assert_eq!(signal.count(), 2, "the control-plane check rearmed it");
1624        assert_eq!(l.refill_due_or_rearm(), RefillVerdict::Idle);
1625
1626        l.try_debit(CostUnits(40), t(0)).unwrap();
1627        assert_eq!(l.remaining(), CostUnits(19));
1628        assert_eq!(l.refill_due_or_rearm(), RefillVerdict::Draining);
1629    }
1630
1631    #[test]
1632    fn debit_decrements_and_credit_restores() {
1633        let l = lease(100, 1_000, 25);
1634        let debit = l
1635            .try_reserve_at(CostUnits(60), t(0), Locality::current())
1636            .unwrap();
1637        assert_eq!(l.remaining(), CostUnits(40));
1638        l.credit(&debit);
1639        assert_eq!(l.remaining(), CostUnits(100));
1640    }
1641
1642    #[test]
1643    fn exhaustion_denies_with_remaining() {
1644        let l = lease(10, 1_000, 0);
1645        assert_eq!(
1646            l.try_debit(CostUnits(11), t(0)),
1647            Err(DenyReason::LeaseExhausted {
1648                remaining: CostUnits(10)
1649            })
1650        );
1651        // Exact spend-to-zero is allowed.
1652        l.try_debit(CostUnits(10), t(0)).unwrap();
1653        assert_eq!(l.remaining(), CostUnits::ZERO);
1654    }
1655
1656    #[test]
1657    fn expiry_boundary_is_exclusive_of_expires_at() {
1658        let l = lease(10, 500, 0);
1659        assert_eq!(
1660            l.try_debit(CostUnits(1), t(500)),
1661            Err(DenyReason::LeaseExpired)
1662        );
1663        l.try_debit(CostUnits(1), t(499)).unwrap();
1664    }
1665
1666    #[test]
1667    fn low_water_triggers_refill_signal() {
1668        let l = lease(100, 1_000, 25);
1669        assert!(!l.needs_refill());
1670        l.try_debit(CostUnits(75), t(0)).unwrap();
1671        assert!(l.needs_refill());
1672    }
1673
1674    /// Counts calls so the exactly-once contract can be asserted rather than
1675    /// assumed.
1676    #[derive(Debug, Default)]
1677    struct CountingSignal(AtomicU64);
1678
1679    impl RefillSignal for CountingSignal {
1680        fn request_refill(&self) {
1681            self.0.fetch_add(1, Ordering::Relaxed);
1682        }
1683    }
1684
1685    impl CountingSignal {
1686        fn count(&self) -> u64 {
1687            self.0.load(Ordering::Relaxed)
1688        }
1689    }
1690
1691    /// The signal fires on the debit that *crosses* low water — not before,
1692    /// and, because the threshold is "at or below", not one debit late.
1693    #[test]
1694    fn the_crossing_debit_raises_the_signal() {
1695        let signal = Arc::new(CountingSignal::default());
1696        let l = lease(100, 1_000, 25).with_refill(signal.clone());
1697
1698        l.try_debit(CostUnits(74), t(0)).unwrap();
1699        assert_eq!(signal.count(), 0, "26 remaining is above low water");
1700        l.try_debit(CostUnits(1), t(0)).unwrap();
1701        assert_eq!(signal.count(), 1, "landing exactly on low water crosses it");
1702    }
1703
1704    /// GL-109: the refusal is the one lease event that *proves* the grant can
1705    /// no longer serve the work offered, and it was the one event the refill
1706    /// plane was never told about. A low-water crossing cannot stand in for
1707    /// it: this lease is comfortably above its mark and still cannot fund the
1708    /// quote, so nothing crosses, and before this the plane learned nothing.
1709    #[test]
1710    fn a_refused_debit_tells_the_refill_plane_rather_than_waiting_to_be_polled() {
1711        let signal = Arc::new(CountingSignal::default());
1712        let l = lease(49, 1_000, 25).with_refill(signal.clone());
1713
1714        assert!(matches!(
1715            l.try_debit(CostUnits(51), t(0)),
1716            Err(DenyReason::LeaseExhausted { .. })
1717        ));
1718        assert_eq!(signal.count(), 1, "the refusal rang the doorbell");
1719        assert!(
1720            !l.needs_refill(),
1721            "and it rang from above the mark, which is the whole point"
1722        );
1723        assert_eq!(
1724            l.refill_due_or_rearm(),
1725            RefillVerdict::Refused,
1726            "the plane is told the grant is mis-sized, not that it is draining"
1727        );
1728        assert_eq!(
1729            l.refill_due_or_rearm(),
1730            RefillVerdict::Idle,
1731            "reading the verdict consumes it; one refusal is one rotation"
1732        );
1733        assert_eq!(
1734            l.remaining(),
1735            CostUnits(49),
1736            "a refusal is still zero-charge"
1737        );
1738    }
1739
1740    /// GL-131: a consolidation may grow only to demand the lease has proven, so
1741    /// the refusal records its quote, keeps the largest across a storm, and an
1742    /// expiry refusal records nothing because it rotates rather than folds.
1743    #[test]
1744    fn a_refused_debit_records_its_largest_quote() {
1745        for l in [lease(49, 1_000, 25), sharded_lease(49, 25, 4)] {
1746            assert_eq!(l.largest_refused_quote(), CostUnits::ZERO);
1747            for quote in [51, 90, 60] {
1748                assert!(l.try_debit(CostUnits(quote), t(0)).is_err());
1749            }
1750            assert_eq!(l.largest_refused_quote(), CostUnits(90));
1751            l.try_debit(CostUnits(10), t(0)).unwrap();
1752            assert_eq!(
1753                l.largest_refused_quote(),
1754                CostUnits(90),
1755                "a funded debit is not demand the grant failed"
1756            );
1757        }
1758        let expired = lease(49, 1_000, 25);
1759        assert!(matches!(
1760            expired.try_debit(CostUnits(51), t(1_000)),
1761            Err(DenyReason::LeaseExpired)
1762        ));
1763        assert_eq!(expired.largest_refused_quote(), CostUnits::ZERO);
1764    }
1765
1766    /// The doorbell is rung once however hard the caller retries: the plane's
1767    /// response does not depend on how many requests were refused, and a
1768    /// refusal storm must not become a wake storm on the refill task.
1769    #[test]
1770    fn a_refusal_storm_rings_once_and_reports_once() {
1771        let signal = Arc::new(CountingSignal::default());
1772        let l = lease(49, 1_000, 25).with_refill(signal.clone());
1773
1774        for _ in 0..1_000 {
1775            assert!(l.try_debit(CostUnits(51), t(0)).is_err());
1776        }
1777        assert_eq!(signal.count(), 1);
1778        assert_eq!(l.refill_due_or_rearm(), RefillVerdict::Refused);
1779
1780        // Cleared, so a later refusal is a fresh report the plane must act on.
1781        assert!(l.try_debit(CostUnits(51), t(0)).is_err());
1782        assert_eq!(signal.count(), 2);
1783        assert_eq!(l.refill_due_or_rearm(), RefillVerdict::Refused);
1784    }
1785
1786    /// A refusal outranks a crossing because the two ask for different things
1787    /// and only one is still preventable. Rotating *alongside* a lease that
1788    /// already refused work would size the next grant against a balance this
1789    /// lease's unspent units are missing from (GL-109).
1790    #[test]
1791    fn a_refusal_outranks_a_low_water_crossing() {
1792        let l = lease(100, 1_000, 90);
1793
1794        // One debit both crosses low water and leaves too little for the next.
1795        l.try_debit(CostUnits(20), t(0)).unwrap();
1796        assert!(l.needs_refill(), "80 remaining is below the 90 mark");
1797        assert!(l.try_debit(CostUnits(81), t(0)).is_err());
1798
1799        assert_eq!(l.refill_due_or_rearm(), RefillVerdict::Refused);
1800        assert_eq!(
1801            l.refill_due_or_rearm(),
1802            RefillVerdict::Draining,
1803            "the crossing is still there once the refusal has been answered"
1804        );
1805    }
1806
1807    /// The expiry refusal was silent for the same reason the exhaustion one
1808    /// was, and is the same defect. Rollover keeps the poll interval as its
1809    /// backstop for a lease no request touches; a lease requests *are*
1810    /// reaching now says so on the first one (INVARIANTS.md GL-6).
1811    #[test]
1812    fn an_expired_lease_reports_its_refusal_rather_than_waiting_for_the_tick() {
1813        let signal = Arc::new(CountingSignal::default());
1814        let l = lease(100, 1_000, 25).with_refill(signal.clone());
1815
1816        assert_eq!(
1817            l.try_debit(CostUnits(1), t(2_000)),
1818            Err(DenyReason::LeaseExpired)
1819        );
1820        assert_eq!(signal.count(), 1);
1821        assert_eq!(l.refill_due_or_rearm(), RefillVerdict::Refused);
1822    }
1823
1824    /// A lease nobody refills still records the refusal, exactly as it records
1825    /// a crossing: the verdict is lease state, and attaching a doorbell only
1826    /// decides whether the plane hears about it sooner.
1827    #[test]
1828    fn a_lease_with_no_doorbell_still_records_its_refusal() {
1829        let l = lease(49, 1_000, 25);
1830        assert!(l.try_debit(CostUnits(51), t(0)).is_err());
1831        assert_eq!(l.refill_due_or_rearm(), RefillVerdict::Refused);
1832    }
1833
1834    /// A sharded lease refuses only after walking every sibling, so the report
1835    /// is about the grant and not about one counter — which is why the flag is
1836    /// lease-wide and one refusal is one report however many shards it tried.
1837    #[test]
1838    fn a_sharded_refusal_reports_once_for_the_whole_grant() {
1839        let signal = Arc::new(CountingSignal::default());
1840        let l = sharded_lease(40, 4, 4).with_refill(signal.clone());
1841
1842        assert!(matches!(
1843            l.try_debit(CostUnits(41), t(0)),
1844            Err(DenyReason::LeaseExhausted { .. })
1845        ));
1846        assert_eq!(signal.count(), 1);
1847        assert_eq!(l.remaining(), CostUnits(40), "the rollback restored it all");
1848        assert_eq!(l.refill_due_or_rearm(), RefillVerdict::Refused);
1849    }
1850
1851    /// A lease is replaced rather than refilled, so "at most once" needs no
1852    /// reset protocol — but it does need proving, since every later debit on
1853    /// a drained lease still tests the branch.
1854    #[test]
1855    fn a_lease_signals_at_most_once_however_long_it_drains() {
1856        let signal = Arc::new(CountingSignal::default());
1857        let l = lease(100, 1_000, 25).with_refill(signal.clone());
1858
1859        // Eighty single-unit debits against a hundred units: every one is
1860        // admissible, so a failure here would be the test lying, not the
1861        // lease refusing.
1862        for _ in 0..80 {
1863            l.try_debit(CostUnits(1), t(0)).unwrap();
1864        }
1865        assert_eq!(l.remaining(), CostUnits(20));
1866        assert_eq!(
1867            signal.count(),
1868            1,
1869            "one crossing, however many debits followed it"
1870        );
1871
1872        // A fresh lease is a fresh flag: this is the whole reset mechanism.
1873        let next = lease(100, 1_000, 25).with_refill(signal.clone());
1874        next.try_debit(CostUnits(80), t(0)).unwrap();
1875        assert_eq!(signal.count(), 2);
1876    }
1877
1878    /// A refused debit changes no counter, so it must never claim a
1879    /// *crossing* — and it must still report the refusal.
1880    ///
1881    /// This test used to assert the silence outright (`a_refused_debit_never
1882    /// _signals`), on the reasoning that a debit which moved no counter has
1883    /// nothing to announce. The premise held for crossings and hid GL-109: it
1884    /// read the doorbell as "low water was crossed" when what the refill
1885    /// plane needs is "act on this lease", and those differ exactly here.
1886    /// Both facts are now representable at once, so neither has to be given
1887    /// up: the verdict distinguishes them and the shard flags stay clear.
1888    #[test]
1889    fn a_refused_debit_reports_a_refusal_and_never_a_crossing() {
1890        let signal = Arc::new(CountingSignal::default());
1891        let l = lease(100, 1_000, 25).with_refill(signal.clone());
1892
1893        assert!(l.try_debit(CostUnits(500), t(0)).is_err(), "exhausted");
1894        assert_eq!(signal.count(), 1, "the plane is told once");
1895        assert!(
1896            l.try_debit(CostUnits(10), t(10_000)).is_err(),
1897            "past the usability window"
1898        );
1899        assert_eq!(
1900            signal.count(),
1901            1,
1902            "and not again while that report still stands"
1903        );
1904        assert_eq!(l.remaining(), CostUnits(100), "still zero-charge");
1905
1906        assert_eq!(l.refill_due_or_rearm(), RefillVerdict::Refused);
1907        assert_eq!(
1908            l.refill_due_or_rearm(),
1909            RefillVerdict::Idle,
1910            "seventy-five units above the mark: no crossing was ever claimed"
1911        );
1912    }
1913
1914    /// A lease with no signal attached is the pre-GL-10 behaviour: the crossing
1915    /// is still recorded for the poll loop, it simply arrives later.
1916    #[test]
1917    fn a_lease_without_a_signal_still_reports_the_crossing() {
1918        let l = lease(100, 1_000, 25);
1919        l.try_debit(CostUnits(80), t(0)).unwrap();
1920        assert!(l.needs_refill());
1921    }
1922
1923    #[test]
1924    fn negative_safety_margin_fails_closed() {
1925        let grant = LeaseGrant {
1926            lease_id: LeaseId(8),
1927            account_id: AccountId(1),
1928            fencing_token: FencingToken(4),
1929            units: CostUnits(10),
1930            expires_at: t(100),
1931        };
1932        let l = LocalLease::with_safety_margin(
1933            grant,
1934            CostUnits::ZERO,
1935            jiff::SignedDuration::from_secs(-10),
1936        );
1937        assert_eq!(
1938            l.try_debit(CostUnits(1), t(99)),
1939            Err(DenyReason::LeaseExpired)
1940        );
1941    }
1942
1943    #[test]
1944    fn concurrent_debits_never_overspend() {
1945        use std::sync::Arc;
1946        let l = Arc::new(lease(1_000, 1_000, 0));
1947        let mut handles = Vec::new();
1948        for _ in 0..8 {
1949            let l = Arc::clone(&l);
1950            handles.push(std::thread::spawn(move || {
1951                let mut granted = 0u64;
1952                for _ in 0..1_000 {
1953                    if l.try_debit(CostUnits(1), t(0)).is_ok() {
1954                        granted += 1;
1955                    }
1956                }
1957                granted
1958            }));
1959        }
1960        let total: u64 = handles.into_iter().map(|h| h.join().unwrap()).sum();
1961        // 8000 attempts against 1000 units: exactly the lease size is granted.
1962        assert_eq!(total, 1_000);
1963        assert_eq!(l.remaining(), CostUnits::ZERO);
1964    }
1965}