\Drupal\strata\Verify Verifier

Reads the backup back and reports what does not come out.

The claim a backup system makes is that its history can be restored, and the only evidence for that is having read it. This walks a ref back through history and fetches every object each commit depends on: the parent commit, the base anchors its index resolves through, the frames each anchor entry names, the segment the flush wrote. In its deep mode it also decodes every frame and checks the bytes hash back to the address they are filed under, which catches silent corruption.

Nothing here repairs anything. Findings go to the ledger, where the repair ladder decides what runs automatically and what waits for a human. That separation is why this is safe to run from cron on a schedule.

Two things keep the cost sane on a large site. Frames and anchors are verified once no matter how many commits name them, and the commits between two anchors name the same one, so a pass over a day of history reads six anchors rather than one per flush. And a bounded pass walks a fixed number of commits back from the head and says so in the report, rather than reporting a clean result for a run that stopped early.

Summary

Methods
Properties
Constants
__construct
verify
verifyFrom
No public properties found
No public constants found
No protected methods found
No protected properties found
No protected constants found
verifyCommit
verifyAnchor
verifySegment
verifyFrame
decode
sweepTiers
sweep
raise
reportLostRef
reset
seenFrames
seenTrees
findings
counts
provider
index
store
commits
refs
bases
segments
tripwires
ledger
maxDeltaDepth
commitIndex
tiers
No private constants found

Properties

$seenFrames

$seenFrames : array

Frame addresses already verified in this pass.

Type

array<string, true> —

$seenTrees

$seenTrees : array

Base anchor addresses already walked in this pass.

Type

array<string, true> —

$findings

$findings : list<\Drupal\strata\Health\Finding>

Findings raised so far, in the order they were found.

Type

array<int, Finding> —

$counts

$counts : array{commits: int, trees: int, frames: int, segments: int, bytes: int}

Running counters for the report.

Type

array{commits: int, trees: int, frames: int, segments: int, bytes: int} —

$store

$store : \Drupal\strata\Cas\ObjectStore

Type

ObjectStore

$commits

$commits : \Drupal\strata\Tree\CommitLog

Type

CommitLog

$refs

$refs : \Drupal\strata\Tree\RefStore

Type

RefStore

$bases

$bases : \Drupal\strata\Tree\BaseReader

Type

BaseReader

$segments

$segments : \Drupal\strata\Segment\SegmentReader

Type

SegmentReader

$tripwires

$tripwires : \Drupal\strata\Health\TripwireRegistry

Type

TripwireRegistry

$maxDeltaDepth

$maxDeltaDepth : int

Type

int

$commitIndex

$commitIndex : ?\Drupal\strata\Tree\CommitIndex

Type

CommitIndex|null

Methods

__construct()

__construct(\Drupal\strata\Storage\StorageProviderInterface  $provider, \Drupal\strata\Cas\FrameIndexInterface  $index, \Drupal\strata\Cas\ObjectStore  $store, \Drupal\strata\Tree\CommitLog  $commits, \Drupal\strata\Tree\RefStore  $refs, \Drupal\strata\Tree\BaseReader  $bases, \Drupal\strata\Segment\SegmentReader  $segments, \Drupal\strata\Health\TripwireRegistry  $tripwires, \Drupal\strata\Health\HealthLedgerInterface  $ledger, int  $maxDeltaDepth = 32, \Drupal\strata\Tree\CommitIndex|null  $commitIndex = null, \Drupal\strata\Tier\TierStatusInterface|null  $tiers = null) : mixed

Constructs a verifier.

Parameters

\Drupal\strata\Storage\StorageProviderInterface $provider

Where objects live; asked whether each one is present.

\Drupal\strata\Cas\FrameIndexInterface $index

Consulted for what the site believes about each frame.

\Drupal\strata\Cas\ObjectStore $store

Fetches and decodes frames in a deep pass.

\Drupal\strata\Tree\CommitLog $commits

Walks history.

\Drupal\strata\Tree\RefStore $refs

Resolves the ref to start from.

\Drupal\strata\Tree\BaseReader $bases

Reads the base anchors history resolves against.

\Drupal\strata\Segment\SegmentReader $segments

Reads segment manifests.

\Drupal\strata\Health\TripwireRegistry $tripwires

The checks each observation is run through.

\Drupal\strata\Health\HealthLedgerInterface $ledger

Where findings are recorded.

int $maxDeltaDepth

The configured chain-depth cap, so a chain past it is reported.

\Drupal\strata\Tree\CommitIndex|null $commitIndex

The local index, consulted only to tell a store whose objects are gone from one that never had any. NULL leaves that question unasked, which is what a unit lane with no database does.

\Drupal\strata\Tier\TierStatusInterface|null $tiers

The store's tiers, asked at the start of every pass which of them can be reached. NULL is a store with one destination, where the provider's own reachability is the whole answer.

Returns

mixed —

verify()

verify(string  $ref = \Drupal\strata\Tree\RefStore::MAIN, int|null  $limit = null, bool  $deep = true) : \Drupal\strata\Verify\VerifyReport

Verifies history reachable from a ref.

Parameters

string $ref

Ref to walk back from.

int|null $limit

Stop after this many commits, or NULL to walk to the root of history.

bool $deep

TRUE to fetch and decode every frame; FALSE to check only that each object is present and indexed, which costs one HEAD per object instead of a GET and a decode.

Returns

\Drupal\strata\Verify\VerifyReport —

What the pass found.

verifyFrom()

verifyFrom(string  $head, int|null  $limit = null, bool  $deep = true) : \Drupal\strata\Verify\VerifyReport

Verifies history reachable from one commit.

Parameters

string $head

Commit id to start at.

int|null $limit

Stop after this many commits, or NULL to walk to the root.

bool $deep

TRUE to decode every frame.

Returns

\Drupal\strata\Verify\VerifyReport —

What the pass found.

verifyCommit()

verifyCommit(string  $id, \Drupal\strata\Tree\Commit  $commit, bool  $deep) : void

Verifies everything one commit depends on.

Parameters

string $id

The commit id.

\Drupal\strata\Tree\Commit $commit

The commit.

bool $deep

TRUE to decode every frame.

Returns

void —

verifyAnchor()

verifyAnchor(string  $commit, string  $address, bool  $deep) : void

Walks an anchor chain and verifies the frames its entries name.

Every link is verified, not only the resolved index: a frame named by an entry a later anchor replaced is still what that older anchor restores from, and an anchor is a restore target.

Parameters

string $commit

The commit that points at this anchor, for the finding's context.

string $address

Anchor address.

bool $deep

TRUE to decode every frame.

Returns

void —

verifySegment()

verifySegment(string  $key, bool  $deep) : void

Verifies one segment manifest and the payload frames it names.

Parameters

string $key

The segment's object key.

bool $deep

TRUE to decode every frame.

Returns

void —

verifyFrame()

verifyFrame(string  $hash, bool  $deep) : void

Verifies one frame.

The order is deliberate: what the index knows, then whether the object is there, then whether the things it needs to decode are there, and only then the decode itself. Each step's failure makes the next step meaningless, so reporting all of them for one broken frame would turn a single fault into a wall of findings.

Parameters

string $hash

Frame content address.

bool $deep

TRUE to fetch and decode.

Returns

void —

decode()

decode(string  $hash, int  $rawSize) : void

Fetches and decodes one frame, and checks the bytes against the address.

Parameters

string $hash

Frame content address.

int $rawSize

The decoded size the index records, so a clean-looking short read is counted correctly.

Returns

void —

sweepTiers()

sweepTiers() : void

Reports every tier of the store that cannot be reached.

Run before the walk rather than derived from it. A tier that is down produces a read failure per object, and the finding an operator needs is that one bucket is unreachable, not a thousand findings about the objects in it. A store with one destination raises nothing here, since its own reachability is reported by hook_requirements().

Returns

void —

sweep()

sweep(array  $observation) : bool

Runs one observation through every tripwire and records what fired.

Parameters

array $observation

What the caller had in hand.

Returns

bool —

TRUE when anything fired, so a caller can stop rather than pile findings on one fault.

raise()

raise(\Drupal\strata\Health\Finding  $finding) : void

Records one finding.

Parameters

\Drupal\strata\Health\Finding $finding

The finding.

Returns

void —

reportLostRef()

reportLostRef(string  $ref) : \Drupal\strata\Verify\VerifyReport

What to report when a ref resolves to nothing.

A ref that was never written and a ref whose object was deleted look the same from the bucket, and the difference is the whole answer: one is a site that has not flushed yet, the other is a site whose history is gone. The local index decides which, because it is derived from the bucket and cannot hold rows for commits that were never sealed.

Parameters

string $ref

The ref that did not resolve.

Returns

\Drupal\strata\Verify\VerifyReport —

An empty clean pass, or one carrying ref.missing.

reset()

reset() : void

Clears the state a previous pass left behind.

Returns

void —