\Drupal\strata\Tier TierMigrator

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.

Summary

Methods
Properties
Constants
__construct
run
No public properties found
PAGE
No protected methods found
No protected properties found
No protected constants found
sourceTiers
roots
objects
promotion
promote
copy
reject
nearerDependent
framesIn
objectKeyFor
name
isUnder
router
policy
index
site
time
logger
verify
No private constants found

Constant

PAGE

PAGE = 500

How many objects to ask for per listing page.

Properties

$router

$router : \Drupal\strata\Tier\TieredProvider

Type

TieredProvider

$policy

$policy : \Drupal\strata\Tier\TierPolicy

Type

TierPolicy

$site

$site : \Drupal\strata\Site\SiteContext

Type

SiteContext

$time

$time : \Drupal\Component\Datetime\TimeInterface

Type

TimeInterface

$logger

$logger : \Psr\Log\LoggerInterface

Type

LoggerInterface

$verify

$verify : bool

Type

bool

Methods

__construct()

__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.

Parameters

\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.

Returns

mixed —

run()

run(int  $budget = 0) : \Drupal\strata\Tier\TierMigrationReport

Runs one migration pass.

Parameters

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.

Returns

\Drupal\strata\Tier\TierMigrationReport —

What was moved, what was left, and why.

sourceTiers()

sourceTiers() : list<int>

The tiers a pass can promote out of.

Every tier except the last, since nothing is further than the last one.

Returns

list

Tier indexes, nearest first.

roots()

roots() : list<string>

The key prefixes a pass walks.

This site's own namespace plus the prefixes every site in the bucket shares. Listing the whole bucket instead would move another site's commits on this site's schedule.

Returns

list

Prefixes.

objects()

objects(int  $tier, string  $prefix, list  $problems) : list<\Drupal\strata\Storage\ObjectMeta>

Every object under one prefix in one tier.

Parameters

int $tier

Tier index.

string $prefix

Key prefix.

list $problems

Collects a line when the listing itself fails.

Returns

list<\Drupal\strata\Storage\ObjectMeta> —

The objects.

promotion()

promotion(\Drupal\strata\Storage\ObjectMeta  $object, int  $from, int  $now, list  $refused) : int|null

The tier one object should be promoted into, if any.

Parameters

\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.

Returns

int|null —

The target tier index, or NULL when the object stays where it is.

promote()

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.

Parameters

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.

Returns

array{copied: bool, moved: bool, bytes: int}|null —

What happened, or NULL when there was nothing left to do.

copy()

copy(int  $target, string  $key, string  $body, list  $problems) : bool

Writes one object into a tier and proves it arrived intact.

Parameters

int $target

Tier index.

string $key

Object key.

string $body

The bytes.

list $problems

Collects a line when the write or the check fails.

Returns

bool —

TRUE when the copy is present and verified.

reject()

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.

Parameters

int $target

Tier index.

string $key

Object key.

string $reason

What went wrong.

list $problems

Collects the line.

Returns

void —

nearerDependent()

nearerDependent(string  $key, string  $body, int  $target) : string|null

Whether anything that decodes against this object still lives nearer than the target tier.

Parameters

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.

Returns

string|null —

Why the nearer copy has to stay, or NULL when nothing needs it there.

framesIn()

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.

Parameters

string $key

Object key.

string $body

The object's bytes.

Returns

list

Frame content addresses.

objectKeyFor()

objectKeyFor(\Drupal\strata\Cas\FrameRecord  $record) : string

The object key one frame record lives at.

Parameters

\Drupal\strata\Cas\FrameRecord $record

The record.

Returns

string —

The key of the pack holding it, or of its own object.

name()

name(int  $index) : string

One tier's name.

Parameters

int $index

Tier index.

Returns

string —

The name, or the index when the ladder no longer has that tier.

isUnder()

isUnder(string  $key, string  $prefix) : bool

Whether a key sits under a prefix, scoped or not.

Parameters

string $key

Object key.

string $prefix

Key prefix without slashes.

Returns

bool —

TRUE when it does.