SECONDS_PER_DAY
SECONDS_PER_DAY = 86400
Seconds in a day.
Projects what a site's history will cost, from measured constants rather than guesses.
Every number this class multiplies was measured and is named, so a projection can be argued with rather than believed. The operation sizes come from mutating a real corpus and serializing only the changed field keys; the compression ratios come from the codec catalog's own reference measurements, taken frame by frame through the shipped codecs; the delta-coding reduction comes from re-encoding the corpus against each subject's previous version; the access-churn rate comes from core's own session write interval.
Three findings this model makes, which matter more than the totals.
Two thirds of a Drupal site's write volume is user access-timestamp churn, so how that one field is treated moves the bill more than anything else about the site. Recording it as a compact login event costs 24% less than a full field delta and less than dropping it, because dropping the operation leaves the row dirty for the reconciler to pick up later at full price.
Files are most of the stored bytes and get almost none of the compression benefit, which is why they have their own ladder, their own budget line and a different default: store each unique content once, forever, by digest, rather than versioning it.
The tree is what a large quiet site pays for. Base manifests scale with how many subjects a site has and how many of them change, not with how busy any one of them is, so a site with a million mostly-idle users pays more than a small site under constant load.
What the base interval does, stated carefully. Lengthening it divides the number of bases, but each base then covers a longer window and records more changed subjects, so the two effects largely cancel. The interval is a real dial on restore latency - a deeper replay walks more segments - and only a weak one on stored bytes. A projection that claimed otherwise would send an operator to tune the wrong thing.
ACCESS_WRITES_PER_ACTIVE_USER = 12.0
Access writes a day per daily-active user.
Core writes the access field only when requestTime - lastAccessedTime exceeds
session_write_interval, which defaults to 180 seconds. At three sessions a day of about
twelve minutes each that is twelve writes.
OP_BYTES = ['access_event' => 48, 'access_delta' => 184, 'user_edit' => 593, 'node_edit' => 870, 'row_write' => 217, 'state_write' => 217, 'config_write' => 2000]
Operation record sizes in bytes, including the 120-byte header.
Measured by mutating a real corpus and serializing only the changed top-level field keys. Where a realm had several measured cases the mean of the day-to-day ones is used and the first-capture case is excluded, since a first capture happens once and a daily edit happens daily. The table and configuration realms had no measured case of their own; the small-field figure stands in for a row write, and a whole configuration object is taken at two kilobytes.
DELTA_REDUCTION = 0.257
Share of journal bytes delta coding removes before compression.
25.7% in aggregate. On the rewrite class alone it is 93%: a 5,967-byte blob with one flag flipped encodes to 94 bytes against its previous version. Small operations dominate by count, which is why the aggregate figure is so much lower than the headline one.
TREE_BYTES_PER_CHANGED_SUBJECT = 240.0
Bytes a base manifest spends per subject that changed inside its window.
Calibrated against one measurement: 7.23 GB a year of manifests for a site of 262,000 subjects
writing 82,892 operations a day with hourly bases. One measured point fixes one constant, and
strata:calibrate replaces it with the site's own.
WRITES_PER_FLUSH = 4
Write requests one flush performs.
A pack of frames, a segment manifest, a commit and the ref that names it. Frames below a megabyte are batched into one pack object, which is the difference between a few million requests a month and tens of millions, and the index of subjects belongs to a base anchor on its own interval rather than to every flush.
__construct(array$opBytes = [], float|null $compactionRatio = null) : mixed
Constructs an estimator.
| array |
$opBytes | Operation sizes overriding the measured ones, keyed as |
| float|null | $compactionRatio | The compression ratio to apply, or NULL to read it from the codec catalog. |
populations(\Drupal\strata\Estimate\Measurement $measurement) : list<array{name: string, realm: string, ops: float, bytes: int}>
Every write population a site has, each with its own rate and its own operation size.
Populations rather than realms, because the entity realm is three different things: access churn at 48 bytes and two thirds of the volume, profile edits at 593, and content edits at 870. One mean over that mix would be wrong in both directions at once, and it is exactly the mix the access-churn decision turns on.
| \Drupal\strata\Estimate\Measurement | $measurement | The site. |
The populations.