SECONDS_PER_MONTH
SECONDS_PER_MONTH = 2630016
Seconds in the month every projection is made against: 30.44 days.
Turns a window of store traffic into a monthly bill, and the bill into a rung.
A backup that quietly grows into a four-figure invoice is a worse outcome than one that told somebody at 80% and shortened its own retention at 100%. The guard reads what the store was actually asked to do over a window, extrapolates it to a month, and hands back an assessment the capture and compaction paths can act on.
Two ceilings, either of which may be left unset:
With both set, the rung is the worse of the two axes and BudgetAssessment::$reason names which one it came from. Zero on either axis means that axis does not constrain anything.
Prices default to Cloudflare R2: $0.015 per GB-month of storage, $4.50 per million class-A operations, $0.36 per million class-B operations, and no egress charge. AWS S3 charges $0.023, $5.00 and $0.40 for the same three lines and bills egress separately, so an S3 site sets its own prices through the constructor. R2's free allowance of 10 GB-months, 1 million class-A and 10 million class-B operations is recorded here as constants but is not subtracted from a projection: the allowance is per account, and Strata is rarely the only thing in the account.
Egress is not priced at all. R2 does not charge for it, and an S3 site's egress depends on where the reader is rather than on what Strata wrote.
A gigabyte is 2^30 bytes everywhere in this class.
__construct(int $bytesCeiling = 0, float $dollarsCeiling = 0.0, float $dollarsPerGigabyteMonth = \self::R2_DOLLARS_PER_GIGABYTE_MONTH, float $dollarsPerMillionClassA = \self::R2_DOLLARS_PER_MILLION_CLASS_A, float $dollarsPerMillionClassB = \self::R2_DOLLARS_PER_MILLION_CLASS_B, callable|null $clock = null) : mixed
Constructs the guard.
| int | $bytesCeiling | Bytes per month the store may be sent, or 0 for no ceiling on that axis. |
| float | $dollarsCeiling | Dollars per month the store may cost, or 0.0 for no ceiling on that axis. |
| float | $dollarsPerGigabyteMonth | Storage price. Defaults to R2's. |
| float | $dollarsPerMillionClassA | Price per million billed writes: put, delete and list. Defaults to R2's. |
| float | $dollarsPerMillionClassB | Price per million billed reads: get and head. Defaults to R2's. |
| callable|null | $clock | Returns a unix timestamp as an int. NULL uses time(). Read only by BudgetGuard::assessSince(). |
When a ceiling or a price is negative. A negative ceiling would put every reading above it at once, and a negative price would let traffic buy budget back.
assess(int $storedBytes, \Drupal\strata\Storage\ProviderStats $stats, int $secondsElapsed) : \Drupal\strata\Budget\BudgetAssessment
Reads a window of traffic and returns the rung it lands on.
The window is extrapolated to a 30.44-day month. Storage is priced on what the store holds now, held for the whole month; requests are priced on the observed rate extrapolated over it. Growth is not compounded into the storage line, so the dollars figure is what the current shape of the site costs to keep rather than a forecast of where it is heading.
| int | $storedBytes | What the store holds right now. |
| \Drupal\strata\Storage\ProviderStats | $stats | Traffic observed over the window. |
| int | $secondsElapsed | How long the window was. Zero or less means no rate can be read from it, and the reading comes back on the normal rung with nothing projected rather than dividing by zero. |
When $storedBytes is negative.
The reading.
assessSince(int $windowStartedAt, int $storedBytes, \Drupal\strata\Storage\ProviderStats $stats) : \Drupal\strata\Budget\BudgetAssessment
Reads a window that started at a given time and runs to now.
The default way to call the guard from a cron run or a status page, which knows when the counter was last reset but not how long ago that was. BudgetGuard::assess() takes the duration directly for a caller that already has it.
| int | $windowStartedAt | Unix timestamp the accumulator was last reset at. |
| int | $storedBytes | What the store holds right now. |
| \Drupal\strata\Storage\ProviderStats | $stats | Traffic observed since $windowStartedAt. |
When $storedBytes is negative.
When the injected clock does not return an int.
The reading.
dollarsFor(int $storedBytes, \Drupal\strata\Storage\ProviderStats $stats, float $months) : float
What a month of this traffic costs.
| int | $storedBytes | What the store holds right now. |
| \Drupal\strata\Storage\ProviderStats | $stats | Traffic observed over the window. |
| float | $months | How many months the window extrapolates to. |
Dollars, storage and requests together.
saturate(float $projected) : int
Narrows a projected byte count to an int without wrapping.
A short window multiplies the observed volume by a large factor, and a projection past PHP_INT_MAX cast straight to int produces a number with no relationship to the input. Pinning it at the maximum keeps the reading above every ceiling, which is where a projection that large belongs.
| float | $projected | The projected byte count. |
The count, at most PHP_INT_MAX.