PAGE
PAGE = 500
How many objects to ask for per listing page.
Moves history into the tier its age says it belongs in.
The only thing that ever writes to a far tier, and it runs on a schedule rather than on the flush path. A far tier therefore takes one class-A request per object that has ever aged into it, which is what makes it cheap enough to be worth having.
Age is the object's own last-modified time in the tier it currently occupies, read out of the listing this pass has to do anyway. Nothing else about an object is a reliable clock: a frame is shared between commits a year apart, so no single commit dates it, and the reference count says nothing about when. A recompressed pack is a genuinely new object with a new address, so its clock starting again is correct rather than a reset - and recompression leaves a pack alone once it stops getting materially smaller, so a pack does not have its age refreshed forever.
A copy is verified before the nearer one is dropped. The bytes are read back out of the tier they were just written to and hashed against what was sent. A far tier that accepted a write and holds something else is the one failure a layer of safety must not paper over, so a mismatch drops the placement for that tier, records the problem, and leaves the nearer copy exactly where it was.
A delta parent is never left colder than its child. A frame whose child still lives in a nearer tier is copied outward but keeps its nearer copy, because a read of the hot child would otherwise have to reach into the cold bucket to decode - and would fail outright while that bucket is down. The report says so per object. Once the child ages out too, a later pass drops the parent's nearer copy. A dependent whose placement is not known yet counts as nearer, so the first pass after a rebuild holds back rather than guessing.
Refs never move. They are pinned to the nearest tier by TierPolicy, for the reasons stated there.
One caveat worth stating rather than hiding. Frames and packs are shared by every site in a bucket, because a frame is addressed by its content and content has no owner. Each site keeps its own placement table, so one site demoting a shared frame moves it out from under another site's hot history; that site's next read finds it in the far tier and records where it went, so nothing breaks, but the read is slower and costs more. A bucket shared between sites should configure every tier to retain the copy below it, which never removes a nearer copy at all.
$router : \Drupal\strata\Tier\TieredProvider
$policy : \Drupal\strata\Tier\TierPolicy
$index : \Drupal\strata\Cas\FrameIndexInterface
$site : \Drupal\strata\Site\SiteContext
__construct(\Drupal\strata\Tier\TieredProvider $router, \Drupal\strata\Tier\TierPolicy $policy, \Drupal\strata\Cas\FrameIndexInterface $index, \Drupal\strata\Site\SiteContext $site, \Drupal\Component\Datetime\TimeInterface $time, \Psr\Log\LoggerInterface $logger, bool $verify = true) : mixed
Constructs a migrator.
| \Drupal\strata\Tier\TieredProvider | $router | The store, asked for tier-explicit reads, writes and deletes. |
| \Drupal\strata\Tier\TierPolicy | $policy | Decides what belongs where. |
| \Drupal\strata\Cas\FrameIndexInterface | $index | Consulted for what deltas against a frame, which is what stops a parent going cold ahead of its child. |
| \Drupal\strata\Site\SiteContext | $site | Names the prefix this site's own objects sit under, so a pass over a shared bucket does not move another site's commits. |
| \Drupal\Component\Datetime\TimeInterface | $time | The clock ages are measured against. |
| \Psr\Log\LoggerInterface | $logger | Records what a pass did. |
| bool | $verify | TRUE to read every copy back and hash it. Off costs one class-B request per object less and gives up the only evidence that the far copy is the object it claims to be. |
run(int $budget = 0) : \Drupal\strata\Tier\TierMigrationReport
Runs one migration pass.
| int | $budget | Most bytes to copy; zero for no limit. A pass that reaches the budget stops where it is and says how far it got, because every object it handled is complete on its own. |
What was moved, what was left, and why.
objects(int $tier, string $prefix, list$problems) : list<\Drupal\strata\Storage\ObjectMeta>
Every object under one prefix in one tier.
| int | $tier | Tier index. |
| string | $prefix | Key prefix. |
| list |
$problems | Collects a line when the listing itself fails. |
The objects.
promotion(\Drupal\strata\Storage\ObjectMeta $object, int $from, int $now, list$refused) : int|null
The tier one object should be promoted into, if any.
| \Drupal\strata\Storage\ObjectMeta | $object | The object as the listing described it. |
| int | $from | Tier it is in now. |
| int | $now | Unix seconds. |
| list |
$refused | Collects a line when the object cannot be aged. |
The target tier index, or NULL when the object stays where it is.
promote(int $from, int $target, \Drupal\strata\Storage\ObjectMeta $object, list$refused, list $problems) : array{copied: bool, moved: bool, bytes: int}|null
Copies one object outward and decides whether the nearer copy goes.
| int | $from | Tier it is in now. |
| int | $target | Tier it belongs in. |
| \Drupal\strata\Storage\ObjectMeta | $object | The object. |
| list |
$refused | Collects a line when the nearer copy is kept deliberately. |
| list |
$problems | Collects a line when a copy could not be made or verified. |
What happened, or NULL when there was nothing left to do.
copy(int $target, string $key, string $body, list$problems) : bool
Writes one object into a tier and proves it arrived intact.
| int | $target | Tier index. |
| string | $key | Object key. |
| string | $body | The bytes. |
| list |
$problems | Collects a line when the write or the check fails. |
TRUE when the copy is present and verified.
reject(int $target, string $key, string $reason, list$problems) : void
Un-records a copy that cannot be trusted.
The object itself is left where it landed rather than deleted: a far tier that answered a write with something else is not a tier a delete should be aimed at on the strength of the same exchange, and the placement row is what would have made anything read it.
| int | $target | Tier index. |
| string | $key | Object key. |
| string | $reason | What went wrong. |
| list |
$problems | Collects the line. |
nearerDependent(string $key, string $body, int $target) : string|null
Whether anything that decodes against this object still lives nearer than the target tier.
| string | $key | Object key. |
| string | $body | The object's bytes, so a pack's own directory answers which frames it holds. |
| int | $target | Tier the object is being promoted into. |
Why the nearer copy has to stay, or NULL when nothing needs it there.
framesIn(string $key, string $body) : list<string>
The frame addresses one object holds.
Only frames carry a delta parent, so a commit, an anchor, a segment, a dictionary or a media block answers with nothing and skips the whole check.
| string | $key | Object key. |
| string | $body | The object's bytes. |
Frame content addresses.