crates/monetize-server/src/oversell.rs
//! **The signup gate's second wall: capacity already SOLD, and a hold on the
//! slot while the customer types a card.**
//!
//! `Signup.Start` asked one question before this module — `Product::can_absorb`,
//! which is `Admin.Capacity` against FREE space. Free space is what is not
//! written yet, and a fleet of tenants holding little writes nothing while
//! owning everything: measured on the front 2026-09-18, eleven tenants with
//! 10 GiB caps on a data set of 37.8 GiB, every one of them nearly empty, so
//! every `can_absorb` answered yes and the shop went on selling. The caps are
//! the promise, and the moment the customers use what they bought the promise
//! is the thing that breaks.
//!
//! `monetize::fleet::Oversell` has computed `sum(sold) − servable` since it was
//! written and **gated nothing**: it is rendered on the Usage page
//! (`services::usage::fleet_meter_to_pb`) for an operator to look at. This
//! module is that same arithmetic, asked before a checkout is minted, which is
//! the last moment a capacity refusal is free — after the webhook the money has
//! moved and the only way out is a refund by hand (`crate::signup::Owed`).
//!
//! ```text
//! Signup.Start
//! │ name · e-mail · plan (services::signup)
//! ▼
//! can_absorb "could you serve N bytes that are not here yet" ── no ─▶ NO_ROOM
//! │ yes: the box has the FREE SPACE
//! ▼
//! admit sum(caps) + held reservations + this plan
//! │ vs capacity × max_sold_percent_of_capacity ── over ─▶ NO_ROOM
//! ▼
//! vendor.start_signup the checkout, with the slot HELD until it settles
//! ```
//!
//! # Overselling is a business choice, so it is the operator's number
//!
//! A hosting business that never oversells buys disk it is not using; one that
//! oversells without a number finds out when a customer's push fails. So there
//! is a knob, `[signups.<product>] max_sold_percent_of_capacity`, and — exactly like
//! `[clouds.<name>] cap` — **there is no spelling of "unlimited"**. The default
//! is [`DEFAULT_MAX_SOLD_PERCENT`]; see it for why that number and not 100.
//!
//! # What the hold guards, and what it does not
//!
//! Two `Signup.Start` calls arriving together both read the same stored caps,
//! and before this both got a yes for the same last slot. The fold and the hold
//! happen **under one mutex** ([`Reservations::admit`]), so the loser of that
//! race counts the winner's bytes and is refused.
//!
//! What is still unguarded, exactly:
//!
//! * **A restart forgets the holds.** They are in memory, like
//! `SignupStore::in_flight` and for the same reason — one process serves this
//! store — but unlike an in-flight sink call a hold spans a human typing a
//! card number, so a restart inside that window releases slots that are being
//! paid for. A persisted hold would have to be expired by a timer nobody can
//! see, and an unexpired one blocks the shop.
//! * **A hold expires after [`Reservations::TTL_MS`]**, and a customer who pays
//! after that was, from this gate's point of view, never holding anything.
//! The webhook is still honoured — money that has moved is never refused for
//! capacity — so the fleet can go one plan past the allowance that way.
//! * **The fold reads STORED caps**, so a signup created seconds ago is counted
//! only once its row is in the store (it is, before the hold is released) and
//! a purchase whose entitlement push is still in flight is counted from the
//! ledger, not from the appliance.
//!
//! None of those is a race two concurrent checkouts can lose money in, which is
//! the one this closes.
use std::sync::Mutex;
use monetize::fleet::Oversell;
use monetize_product::Servable;
use crate::state::AppState;
/// **How much of the iron a deployment may sell, as a percentage of what the
/// product says it can hold.** 100 is "never promise a byte that does not
/// exist"; 300 is "sell it three times over".
///
/// The default is 300 and it is a MEASURED compromise, not a round number.
/// Overselling storage is ordinary and legitimate — tenants hold a fraction of
/// their caps, and a shop that refused to promise a byte twice would buy disk
/// to leave empty. But a default of "no limit" is the thing this module exists
/// to remove, and a default of 100 would have stopped the live front selling
/// the moment it shipped: measured 2026-09-18, it holds eleven tenants at 10
/// GiB against a 37.8 GiB set, which is 2.9× — already past 100 and inside 300.
///
/// So the default keeps the deployment that exists selling, refuses its twelfth
/// tenant, and makes an operator who wants a fourth tenant per gigabyte write
/// the number down. An operator who wants the old behaviour has to type a
/// number for it, which is the point.
pub const DEFAULT_MAX_SOLD_PERCENT: u32 = 300;
/// **One held slot**: bytes promised to a checkout that has been minted and not
/// yet settled. See the module doc for what the hold does and does not close.
#[derive(Clone, Debug)]
struct Reservation {
id: u64,
product: String,
bytes: u64,
expires_unix_ms: u64,
/// The vendor session this hold became, once the checkout was minted. While
/// it is `None` the hold is a `Signup.Start` still inside the gate; once it
/// is set, the hold is dropped as soon as the store has DECIDED that
/// session — the caps are then counted from the record instead, and
/// counting both would refuse a customer for their own purchase.
session: Option<(String, String)>,
}
/// **The held slots**, in memory. One process serves the signup store (redb
/// holds the file lock), so one `Mutex<Vec<_>>` is the whole of it; the list is
/// bounded by how many checkouts can be minted inside one TTL.
#[derive(Default)]
pub struct Reservations {
held: Mutex<Vec<Reservation>>,
next_id: std::sync::atomic::AtomicU64,
}
/// A held slot, alive until the checkout settles or this is dropped.
///
/// **Dropping it releases the slot**, which is what makes every error path
/// between the gate and the vendor's answer correct without a line of its own:
/// a refused name, a vendor that would not mint, a panic. A checkout that WAS
/// minted calls [`Hold::minted`] and the hold then waits for the webhook (or
/// the TTL).
pub struct Hold<'a> {
reservations: &'a Reservations,
id: u64,
/// The bytes this hold is for — for the log line, so an operator can see
/// what a refusal was measured against.
pub bytes: u64,
}
impl Hold<'_> {
/// The checkout exists at the vendor: keep the slot until the session is
/// decided or the TTL runs out.
pub fn minted(self, vendor: &str, session: &str) {
self.reservations.name_session(self.id, vendor, session);
std::mem::forget(self);
}
}
impl Drop for Hold<'_> {
fn drop(&mut self) {
self.reservations.release(self.id);
}
}
impl Reservations {
/// **How long a minted checkout holds its slot.** 30 minutes.
///
/// The vendor's own session lives far longer — Stripe's is 24 hours — and
/// holding a slot that long for every abandoned checkout would let a
/// handful of people who changed their minds close the shop. 30 minutes is
/// longer than any real card form and short enough that an abandoned
/// checkout costs one slot for one half-hour. A customer who pays after it
/// is still served: the webhook never asks this gate anything.
pub const TTL_MS: u64 = 30 * 60 * 1000;
/// **The fold and the hold, under one lock.**
///
/// `decide` is handed the bytes ALREADY held for this product by checkouts
/// in flight and answers whether one more may be sold. It runs inside the
/// mutex on purpose: a check-then-hold with a gap between them is the race
/// that told two concurrent buyers yes for the last slot.
///
/// Expired holds, and holds whose session the store has already decided,
/// are dropped here — this is the only place the list is pruned, so a
/// deployment that stops selling stops paying for the bookkeeping.
pub fn admit<E>(
&self,
store: &crate::signup::SignupStore,
product: &str,
bytes: u64,
now_unix_ms: u64,
decide: impl FnOnce(u64) -> Result<(), E>,
) -> Result<Hold<'_>, E> {
let mut held = self.held.lock().unwrap_or_else(|p| p.into_inner());
held.retain(|r| {
if r.expires_unix_ms <= now_unix_ms {
return false;
}
match &r.session {
// Decided: the store now carries the caps (or the refusal), and
// a slot counted twice refuses a customer for their own money.
// A store that will not answer keeps the hold — the cautious
// side of the arithmetic, and it costs at most one TTL.
Some((vendor, session)) => store.get(vendor, session).ok().flatten().map(|rec| rec.state.is_pending()).unwrap_or(true),
None => true,
}
});
let already: u64 = held.iter().filter(|r| r.product == product).map(|r| r.bytes).sum();
decide(already)?;
let id = self.next_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
held.push(Reservation { id, product: product.to_owned(), bytes, expires_unix_ms: now_unix_ms.saturating_add(Self::TTL_MS), session: None });
Ok(Hold { reservations: self, id, bytes })
}
fn name_session(&self, id: u64, vendor: &str, session: &str) {
let mut held = self.held.lock().unwrap_or_else(|p| p.into_inner());
if let Some(r) = held.iter_mut().find(|r| r.id == id) {
r.session = Some((vendor.to_owned(), session.to_owned()));
}
}
fn release(&self, id: u64) {
self.held.lock().unwrap_or_else(|p| p.into_inner()).retain(|r| r.id != id);
}
/// Bytes currently held for a product. For the tests and for a log line;
/// nothing decides on it outside [`Reservations::admit`], where it is read
/// under the same lock as the write.
pub fn held_bytes(&self, product: &str) -> u64 {
// Every site's holds (ST-11: a site's hold is kept as `<product>@<estate>`).
let site = format!("{product}@");
self.held.lock().unwrap_or_else(|p| p.into_inner()).iter().filter(|r| r.product == product || r.product.starts_with(&site)).map(|r| r.bytes).sum()
}
}
/// **What the fleet arithmetic says about one more sale.** The arms are
/// [`Oversell`]'s own, plus the one it cannot have: a fold that did not happen.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Room {
/// Within the allowance. `promised` includes the sale being considered.
Fits { promised: u64, capacity: u64, allowed: u64, floor: Floor },
/// **Past it.** `over_allowance` is what this sale would promise beyond
/// `allowed`; `by` is the raw [`Oversell::Oversold`] figure, which is the
/// number the Usage page renders, so the gate and the report can be shown
/// to agree.
Oversold { by: u64, over_allowance: u64, promised: u64, capacity: u64, allowed: u64, floor: Floor },
/// **The fold did not happen** — the product did not measure, the meter is
/// not bytes of disk, or the product does not declare it at all. NOT a
/// verdict: see [`room`].
NotChecked(String),
}
/// **How much of the fold behind a verdict is a FLOOR rather than a total** —
/// the tenants the capacity arithmetic could not see. STINK S5.
///
/// `monetize::fleet::FleetTotal::capacity()` is `used + servable`, where `used`
/// is the sum over the USAGE STORE. A tenant with no reading contributes
/// nothing to it — correctly, because a reading that did not arrive is never
/// folded in as a zero — and the consequence is that **capacity SHRINKS by
/// exactly what the tenants have written whenever the poller goes quiet**.
/// Measured in `arrival_rate.rs`: identical iron, identical bill, identical
/// tenants, and the shop sells 87 accounts with the poller working and 15 with
/// it silent.
///
/// The refusal is the safe direction to be wrong in, which is why this was
/// never an outage. What made it a defect is that it was SILENT: a monitoring
/// failure changed commercial behaviour and the sentence the operator read
/// named an allowance, a percentage and a disk, with nothing to say the disk
/// figure was reconstructed from readings that never arrived.
///
/// So the verdict carries the fact, the refusal says it, and a refusal issued
/// on a floor raises a RED row. **The arithmetic is unchanged**: what capacity
/// SHOULD be when a reading is missing is a different question — the
/// appliance's own `total_bytes` sits beside `servable_bytes` but includes a
/// reserve monetize must not sell, so there is no correct substitute to be had
/// from the two numbers on hand. That one is written up as a ruling.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub struct Floor {
/// Tenants with a cap and no reading of the signup meter.
pub tenants_without_reading: u32,
/// Tenants whose signed fact names no cap on it.
pub tenants_without_cap: u32,
}
impl Floor {
/// Is any part of this fold a floor?
pub fn is_a_floor(self) -> bool {
self.tenants_without_reading > 0 || self.tenants_without_cap > 0
}
/// The sentence a refusal appends, or empty when the fold was complete.
pub fn say(self) -> String {
if !self.is_a_floor() {
return String::new();
}
let mut parts = Vec::new();
if self.tenants_without_reading > 0 {
parts.push(format!(
"{} tenant(s) have no usage reading, so the iron this was measured against is a FLOOR and may be larger \
(a silent usage poller shrinks it by exactly what the tenants have written)",
self.tenants_without_reading
));
}
if self.tenants_without_cap > 0 {
parts.push(format!("{} tenant(s) hold a fact that names no cap, so the promise is a floor too", self.tenants_without_cap));
}
format!(". MEASURED FROM AN INCOMPLETE FOLD: {}", parts.join("; "))
}
}
/// **The oversell fold for one more sale of `cap_bytes`**, including
/// `also_promised` bytes already held by checkouts in flight.
///
/// It is `monetize::fleet::total` and `FleetTotal::oversell` — the same two
/// functions the Usage page renders — with the prospective sale added to the
/// allowance before the comparison. There is deliberately no second arithmetic:
/// a gate that computed its own would be a number that could disagree with the
/// screen an operator is looking at while they argue with it.
///
/// **An unmeasured box is not refused here**, and that is not a silence turned
/// into a yes: `Product::can_absorb` has already asked the box a live question
/// about this very sale and answered it, and this gate needs a FIGURE rather
/// than a verdict. When there is no figure there is no second opinion to give,
/// so it says which measurement did not happen and the caller reports it.
/// **[`room`] for ONE site of the product** (STINKY ST-11): the caps sold on `estate` (else
/// the product's home) against that estate's own box — never another site's sales or iron.
pub fn room_on(state: &AppState, product: monetize_product::Served<'_>, cap_bytes: u64, also_promised: u64, max_sold_percent: u32, estate: Option<&str>) -> Room {
let site = site_of_sale(state, product.id(), estate);
let servable = servable_of_site(state, product, site.as_deref());
room_with_on(state, product, cap_bytes, also_promised, max_sold_percent, servable, site.as_deref())
}
/// **Which site a sale is folded on** (ST-11): the caller's estate, else the product's home;
/// `None` for a product that names no home (one box: the whole product, as before).
pub fn site_of_sale(state: &AppState, product: &str, estate: Option<&str>) -> Option<String> {
let home = state.registry.home_of(product)?;
Some(estate.map(str::trim).filter(|e| !e.is_empty()).map(str::to_owned).unwrap_or(home))
}
/// The servable figure of `site`'s box: the home's as [`servable_now`] (cached), another
/// site's from its own main ([`Product::servable_on`]).
pub fn servable_of_site(state: &AppState, product: monetize_product::Served<'_>, site: Option<&str>) -> Servable {
match site {
Some(e) if state.registry.home_of(product.id()).as_deref() != Some(e) => product.servable_on(e),
_ => servable_now(state, product),
}
}
pub fn room(state: &AppState, product: monetize_product::Served<'_>, cap_bytes: u64, also_promised: u64, max_sold_percent: u32) -> Room {
let servable = servable_now(state, product);
room_with(state, product, cap_bytes, also_promised, max_sold_percent, servable)
}
/// The product's servable figure: the poller's cached answer first — it is one
/// tick old at most and costs nothing. A cache that has never been filled (a
/// server inside its first tick) asks the product itself rather than reporting
/// "nobody has asked yet" as though it were a measurement. That ask is a call
/// to the appliance, so [`admit`] makes it BEFORE it takes the reservations'
/// lock, never under it.
pub fn servable_now(state: &AppState, product: monetize_product::Served<'_>) -> Servable {
match state.health.get(product.id()).servable {
Some(s @ Servable::Measured { .. }) => s,
_ => product.servable(),
}
}
/// ★ **A READ-ONLY ACCOUNT'S ROOM IS RELEASED, ITS DATA KEPT** (owner,
/// 2026-10-05). A tenant the ladder has carried past the grace period
/// (Suspended, Retention) no longer holds the room it bought: each of
/// `meters` counts only what it measurably STORES (0 with no reading), so the
/// room it was sold is free to sell again and to grow for, while its bytes —
/// still on the disk, still clonable — stay counted. Paying restores the cap
/// with the payment's own fact.
pub fn released(facts: Vec<monetize_product::EntitlementFact>, usages: &[(monetize_product::TenantId, monetize_product::Usage)], meters: &[&str]) -> Vec<monetize_product::EntitlementFact> {
facts
.into_iter()
.map(|mut f| {
if matches!(f.state, monetize_product::State::Suspended | monetize_product::State::Retention) {
let used = usages.iter().find(|(t, _)| t.0 == f.tenant.0).map(|(_, u)| &u.meters);
for m in meters {
if let Some(cap) = f.caps.get_mut(*m) {
*cap = used.and_then(|u| u.get(*m)).copied().unwrap_or(0).min(*cap);
}
}
}
f
})
.collect()
}
/// [`room`] over a servable figure already in hand: local reads and arithmetic
/// only, so it may run under a lock.
pub fn room_with(state: &AppState, product: monetize_product::Served<'_>, cap_bytes: u64, also_promised: u64, max_sold_percent: u32, servable: Servable) -> Room {
room_with_on(state, product, cap_bytes, also_promised, max_sold_percent, servable, None)
}
/// [`room_with`] folding only `site`'s tenants (ST-11; `None`: every tenant of the product).
pub fn room_with_on(state: &AppState, product: monetize_product::Served<'_>, cap_bytes: u64, also_promised: u64, max_sold_percent: u32, servable: Servable, site: Option<&str>) -> Room {
let on_site = |tenant: &str| site.is_none_or(|s| state.registry.estate_of_tenant(product.id(), tenant).as_deref() == Some(s));
let meter_name = state.signup_plans.cap_meter_of(product.id());
let meter_name = meter_name.as_str();
let Some(meter) = product.meters().iter().find(|m| m.name == meter_name) else {
return Room::NotChecked(format!("{} declares no {meter_name} meter, so there is no fleet sum to compare a cap against", product.id()));
};
let usages = match state.usage.all(product.id()) {
Ok(v) => v,
Err(e) => return Room::NotChecked(format!("the usage store could not be read: {e}")),
};
let facts = match state.ledger.entitlements(product.id()).map_err(|e| e.to_string()).and_then(|f| {
crate::signup::with_signup_facts(state, product.id(), f).map_err(|e| e.to_string())
}) {
Ok(v) => v,
Err(e) => return Room::NotChecked(format!("the entitlements could not be folded: {e}")),
};
let usages: Vec<_> = usages.into_iter().filter(|(t, _)| on_site(&t.0)).collect();
let facts: Vec<_> = facts.into_iter().filter(|f| on_site(&f.tenant.0)).collect();
let facts = released(facts, &usages, &[meter.name]);
let Some(mut total) = monetize::fleet::total(meter, &usages, &facts, &servable) else {
return Room::NotChecked(format!("{meter_name} is not a per-tenant meter on {}, so it has no fleet total", product.id()));
};
// The sale, and every checkout being paid for right now, counted as though
// they had already landed. That is the whole difference between this and
// the report: the report says where the fleet IS, the gate says where this
// sale would put it.
total.allowance = total.allowance.saturating_add(cap_bytes).saturating_add(also_promised);
let Some(capacity) = total.capacity() else {
return Room::NotChecked(match total.oversell() {
Oversell::Unmeasured(why) => why,
other => format!("the fleet total gave no capacity figure: {other:?}"),
});
};
// `allowed` is the whole promise this deployment permits, not the overhang:
// percent OF capacity, so 300 % of a 37.8 GiB set is 113.4 GiB of caps.
let allowed = (u128::from(capacity) * u128::from(max_sold_percent) / 100).min(u128::from(u64::MAX)) as u64;
// What the fold could NOT see, carried on the verdict (STINK S5). The fleet
// total has counted it all along — `is_a_floor` is rendered on the Usage
// page — and the gate threw it away.
let floor = Floor {
tenants_without_reading: total.tenants_without_reading as u32,
tenants_without_cap: total.tenants_without_cap as u32,
};
match total.oversell() {
Oversell::Oversold { by } => {
let promised = total.allowance;
if promised > allowed {
Room::Oversold { by, over_allowance: promised - allowed, promised, capacity, allowed, floor }
} else {
Room::Fits { promised, capacity, allowed, floor }
}
}
Oversell::Fits { .. } => Room::Fits { promised: total.allowance, capacity, allowed, floor },
// Reachable only if `capacity()` answered and `oversell()` did not,
// which cannot happen today; kept as a refusal to measure rather than a
// yes, because that is what it would mean if it ever could.
Oversell::Unmeasured(why) => Room::NotChecked(why),
Oversell::NotStorage => Room::NotChecked(format!("a cap on {meter_name} is not bytes of storage on {}, so it cannot oversell a disk", product.id())),
}
}
/// **How far the SHOP has run past the WALLET**, in bytes of promise that no
/// budget on this estate can ever back. STINK S2, measured rather than argued.
///
/// The two ceilings are different units and they walk apart by construction:
/// `max_sold_percent_of_capacity` is a percentage OF THE IRON, so every growth
/// raises it, and `[clouds.<n>] cap` is a fixed number that only falls. The
/// relationship nobody had written down is this one —
///
/// ```text
/// reachable = the iron now + the iron the wallet can still buy
/// (crate::grow::affordable_bytes)
/// gap = promised − reachable (None when promised ≤ reachable)
/// ```
///
/// — and `gap > 0` means the deployment has sold caps it could not honour even
/// after spending its last euro on disk.
///
/// **This refuses nothing.** It is a reading, said out loud by [`admit`] and
/// carried here so a test can measure it as ONE number. Whether the shop should
/// be REFUSED at `reachable` (and at what multiple of it — overselling is a
/// legitimate business choice, and this estate's operator chose 600 %) is a
/// commercial ruling and is deliberately not invented in code.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct PastTheWallet {
/// Caps promised, including the sale being considered.
pub promised: u64,
/// The iron the product serves from today.
pub capacity: u64,
/// What the wallet can still buy on top of it.
pub affordable: u64,
/// `promised − (capacity + affordable)`, always > 0.
pub over: u64,
}
/// [`PastTheWallet`] for one prospective sale, or `None` when the promise fits
/// inside the last affordable disk — or when the wallet cannot be asked at all
/// (no growth configured, no ceiling, a set that will not price). A question
/// that could not be asked is never rendered as an alarm.
///
/// **Never waits for the cloud** on an armed server: the reading is the last
/// one [`crate::grow::affordable_within`] made on its own thread (a refresh is
/// started when it is stale), because this is asked inside `Signup.Start`,
/// between the customer's click and the card form. With no reading yet the
/// question is not asked this time, and — like every question that could not
/// be asked — that is not an alarm.
pub fn past_the_wallet(state: &AppState, product: &str, promised: u64, capacity: u64) -> Option<PastTheWallet> {
let affordable = match crate::grow::wallet_reading(state, product) {
crate::grow::Wallet::Read(v) => v?,
crate::grow::Wallet::NotInTime => return None,
};
let reachable = capacity.saturating_add(affordable);
// `then`, never `then_some`: the latter evaluates its argument before the
// condition is looked at, and `promised − reachable` underflows on every
// healthy estate — which is most of them, most of the time.
(promised > reachable).then(|| PastTheWallet { promised, capacity, affordable, over: promised - reachable })
}
/// **The gate: fold, refuse or hold the slot.**
///
/// `Ok` carries the [`Hold`] — the caller mints the checkout and calls
/// [`Hold::minted`], or drops it and the slot is free again. `Err` is the
/// OPERATOR's sentence; the customer is told
/// [`crate::signup::words::NO_ROOM`] and nothing about disks.
pub fn admit<'a>(state: &'a AppState, product: monetize_product::Served<'_>, cap_bytes: u64) -> Result<Hold<'a>, String> {
admit_on(state, product, cap_bytes, None)
}
/// [`admit`] for a sale on `estate` (ST-11): folded on that site ([`site_of_sale`]) against
/// its own box, and its checkouts held apart from every other site's.
pub fn admit_on<'a>(state: &'a AppState, product: monetize_product::Served<'_>, cap_bytes: u64, estate: Option<&str>) -> Result<Hold<'a>, String> {
let site = site_of_sale(state, product.id(), estate);
let held_as = match &site {
Some(e) => format!("{}@{e}", product.id()),
None => product.id().to_owned(),
};
let now = state.now();
let percent = state.max_sold_percent_of(product.id());
// **Nothing that asks anybody runs under the reservations' lock** (owner
// 2026-09-30, after test2's 25-33 s signups): the servable figure is taken
// here, and the wallet reading and every line are said after the lock is
// let go. Inside it is the fold — local reads and arithmetic — and the hold.
let servable = servable_of_site(state, product, site.as_deref());
let mut seen: Option<Room> = None;
let held = state.signup_reservations.admit(&state.signups, &held_as, cap_bytes, now, |already_held| {
let r = room_with_on(state, product, cap_bytes, already_held, percent, servable, site.as_deref());
let verdict = match &r {
Room::Oversold { by, over_allowance, promised, capacity, allowed, floor } => Err(format!(
"{}: this deployment has already sold {} of caps against {} of iron ({} oversold), and one more {} plan \
would put it {} past the {} it permits (max_sold_percent_of_capacity={percent}). \
Raise the number only if the overselling is deliberate, or give the box more disk{}",
product.id(),
monetize_product::bytes(promised.saturating_sub(cap_bytes).saturating_sub(already_held)),
monetize_product::bytes(*capacity),
monetize_product::bytes(*by),
monetize_product::bytes(cap_bytes),
monetize_product::bytes(*over_allowance),
monetize_product::bytes(*allowed),
// ── STINK S5 ──────────────────────────────────────────
// A customer is being turned away on a capacity figure
// that the fold could not complete. The figure is the
// honest one available — a reading that did not arrive is
// never folded in as a zero — but an operator reading this
// sentence has to be told which of the two things to go
// and fix: the disk, or the poller.
floor.say(),
)),
_ => Ok(()),
};
seen = Some(r);
verdict
});
// ── said after the lock ─────────────────────────────────────────────
match (seen, &held) {
(Some(Room::Fits { promised, capacity, allowed, floor }), _) => {
if promised > capacity {
// Selling past the iron, inside the allowance the operator
// wrote down. Said out loud every time, because it is a
// choice and not a fact about the disk.
eprintln!(
"monetize-server: signup capacity: {} is oversold by {} and the sale is allowed \
({} promised of {} permitted, {} of iron, max_sold_percent_of_capacity={percent})",
product.id(),
monetize_product::bytes(promised - capacity),
monetize_product::bytes(promised),
monetize_product::bytes(allowed),
monetize_product::bytes(capacity),
);
}
// An ALLOWED sale measured on an incomplete fold says so too:
// the figure the shop decided on is a floor (STINK S5). Not a
// RED row — nobody was turned away — but said, because the
// next sale on the same fold is the one that is refused.
if floor.is_a_floor() {
eprintln!("monetize-server: signup capacity: {}: the sale was allowed on an incomplete fold{}", product.id(), floor.say());
}
// ── STINK S2: past the LAST AFFORDABLE DISK ────────────────
// Oversold against the iron is a choice. Oversold against the
// iron this estate can EVER buy is a promise nothing can keep. A
// RED row rather than a refusal — refusing here is the owner's
// commercial ruling. The reading is the SNAPSHOT
// (`grow::wallet_reading`): until test2 2026-09-30 this line
// bisected the price at the cloud, inline, under this gate's lock
// — 25-33 s of every signup, and >100 s behind a growth.
if let Some(gap) = past_the_wallet(state, product.id(), promised, capacity) {
let why = format!(
"{}: SELLING PAST THE LAST AFFORDABLE DISK — {} of caps promised against {} of iron plus {} the \
{} ceiling can still buy, so {} is promised that no budget on this estate can ever back. \
The sale was ALLOWED: nothing refuses on this (STINK S2, owner ruling open)",
product.id(),
monetize_product::bytes(gap.promised),
monetize_product::bytes(gap.capacity),
monetize_product::bytes(gap.affordable),
state.grow.get(product.id()).map(|pg| pg.set.cloud.clone()).unwrap_or_default(),
monetize_product::bytes(gap.over),
);
skidbladnir::functional_status("monetize-server/signup", "shop-past-the-wallet", false, &why);
eprintln!("monetize-server: signup capacity: {why}");
}
}
(Some(Room::Oversold { floor, .. }), Err(why)) => {
if floor.is_a_floor() {
skidbladnir::functional_status("monetize-server/signup", "capacity-from-a-floor", false, why);
eprintln!("monetize-server: signup capacity: REFUSED ON AN INCOMPLETE FOLD: {why}");
}
}
// Counted as neither a yes nor a no by this gate: `can_absorb` has
// already put a live question to the box and answered it, and this
// fold simply has no figure to add. Reported so that a deployment
// running for weeks without the second wall cannot do it quietly.
(Some(Room::NotChecked(why)), _) => {
skidbladnir::functional_status("monetize-server/signup", "capacity-unchecked", true, &format!("{}: the sold-capacity gate did not run: {why}", product.id()));
eprintln!("monetize-server: signup capacity: {}: the sold-capacity gate did not run: {why}", product.id());
}
_ => {}
}
held
}
/// **The same wall for an ORDER** (a-ceiling-s3, owner 2026-09-28): a bundle
/// that raises a tenant's `pack_bytes` from `current` to `target` is a new
/// promise of the difference, folded exactly as a signup's is — against every
/// cap sold and every checkout held — and refused past
/// `max_sold_percent_of_capacity`. `Err` is the operator's sentence; the
/// quote turns it into a refusal the customer reads in their own words. An
/// order that promises nothing new on that meter is not this gate's, and a
/// fold that could not be made is not a refusal (`can_absorb` has answered).
/// An order the oversell ceiling refuses: why (the operator's words), and the
/// room still left under the ceiling (what the offer's sentence names).
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OverCeiling {
pub why: String,
pub room_left_bytes: u64,
}
pub fn admit_order(
state: &AppState,
product: monetize_product::Served<'_>,
current: &std::collections::BTreeMap<String, u64>,
target: &std::collections::BTreeMap<String, u64>,
) -> Result<(), OverCeiling> {
let meter = crate::signup::SIGNUP_CAP_METER;
let delta = target.get(meter).copied().unwrap_or(0).saturating_sub(current.get(meter).copied().unwrap_or(0));
if delta == 0 {
return Ok(());
}
// The poller's cached figure only: the order's own `can_absorb` has just
// asked the product a live question, and a second live ask here would be
// a second capacity call per quote. No cached figure = no second opinion.
if !matches!(state.health.get(product.id()).servable, Some(Servable::Measured { .. })) {
return Ok(());
}
let percent = state.max_sold_percent_of(product.id());
let held = state.signup_reservations.held_bytes(product.id());
match room(state, product, delta, held, percent) {
Room::Oversold { over_allowance, promised, capacity, allowed, floor, .. } => {
let why = format!(
"{}: this order would promise {} more, {} in all against {} of iron — {} past the {} this deployment permits \
(max_sold_percent_of_capacity={percent}). Raise the number only if the overselling is deliberate, or give the box more disk{}",
product.id(),
monetize_product::bytes(delta),
monetize_product::bytes(promised),
monetize_product::bytes(capacity),
monetize_product::bytes(over_allowance),
monetize_product::bytes(allowed),
floor.say()
);
// Said by the CALLER (round 5.3, coordinator 2026-10-08): a refusal
// with [`say_order_refused`]; an order a set that grows QUEUES is not a
// refusal, and the window quotes every row on every load — a WARN here
// was 7 a minute on test2's mock ([log-verdict] spam).
// What can still be sold under the ceiling: the permitted total
// less what is promised without this order.
let room_left_bytes = allowed.saturating_sub(promised.saturating_sub(delta));
Err(OverCeiling { why, room_left_bytes })
}
_ => Ok(()),
}
}
/// **An order the oversell ceiling REFUSES** (a box whose set is never
/// resized): the operator's status and log line — a WARN, said once per
/// refused quote.
pub fn say_order_refused(why: &str) {
skidbladnir::functional_status("monetize-server/purchase", "oversell-ceiling", false, why);
eprintln!("monetize-server: order capacity: REFUSED {why}");
}
/// The references whose queueing was said ([`queue_note`]): a bounded set, so
/// a shop that runs for months forgets the oldest.
static QUEUE_SAID: std::sync::Mutex<std::collections::BTreeSet<String>> = std::sync::Mutex::new(std::collections::BTreeSet::new());
const QUEUE_SAID_KEPT: usize = 4096;
/// ★ **An order past the room, QUEUED — said ONCE per order** (round 5.3,
/// coordinator 2026-10-08: "a queued order is not a refusal; log the queue
/// once per order, at the moment it queues, as INFO with the order id and its
/// reason"). The line for `reference` the first time it queues, `None` after.
/// It carries none of the words a captured stderr line is made a WARN by
/// (gunnar-common `stderr_capture::is_warning`: FAILED, REFUSED, panicked,
/// " error"). The estate is told by its `grow_room` order
/// ([`crate::grow::queue_past_room`]).
pub fn queue_note(reference: &str, who: &str, why: &str) -> Option<String> {
let mut said = QUEUE_SAID.lock().unwrap_or_else(|p| p.into_inner());
if said.contains(reference) {
return None;
}
if said.len() >= QUEUE_SAID_KEPT {
said.clear();
}
said.insert(reference.to_owned());
let why = why.replace("REFUSED", "refused").replace("FAILED", "failed").replace("panicked", "stopped").replace(" error", " fault").replace(" Error", " fault");
Some(format!("monetize-server: order {reference} for {who}: queued: waits for room — the next growth window resizes the set ({why})"))
}
#[cfg(test)]
mod tests;