\Drupal\strata\Restore Preflight

Works out what a restore can and cannot put back, before it writes anything.

Every subject in scope is replayed and classified. That is deliberately expensive: it reads the same objects the apply will read, so a plan that says restorable has proved it rather than assumed it, and an operator confirming a rollback is looking at a measurement.

The default for a degraded subject is to leave it alone. A partial field set written over a live value produces a record that is neither the old one nor the new one, in a system whose whole purpose is to be trusted about what the site used to look like. Filling the gaps with defaults is available per scope and is spelled out subject by subject before it happens.

Anything the preflight could not classify raises restore.partial, so a restore that was never run still leaves evidence of why.

Summary

Methods
Properties
Constants
__construct
plan
planSubjects
concurrentChanges
isRestorableRealm
No public properties found
PARTIAL
No protected methods found
No protected properties found
No protected constants found
planFor
record
replayer
commits
ledger
subjects
No private constants found

Constant

PARTIAL

PARTIAL = 'restore.partial'

Code raised when a restore would not be able to put everything back.

Properties

$replayer

$replayer : \Drupal\strata\Restore\Replayer

Type

Replayer

$commits

$commits : \Drupal\strata\Tree\CommitLog

Type

CommitLog

$subjects

$subjects : ?\Drupal\strata\Tree\SubjectIndex

Type

SubjectIndex|null

Methods

__construct()

__construct(\Drupal\strata\Restore\Replayer  $replayer, \Drupal\strata\Tree\CommitLog  $commits, \Drupal\strata\Health\HealthLedgerInterface  $ledger, \Drupal\strata\Tree\SubjectIndex|null  $subjects = null) : mixed

Constructs a preflight.

Parameters

\Drupal\strata\Restore\Replayer $replayer

Reconstructs each subject.

\Drupal\strata\Tree\CommitLog $commits

Resolves the target and reports the replay depth.

\Drupal\strata\Health\HealthLedgerInterface $ledger

Where a partial plan is recorded.

\Drupal\strata\Tree\SubjectIndex|null $subjects

Consulted for when each subject was last written, which is what makes a conflict detectable. NULL plans without conflict detection, which is what a store with no local index can do.

Returns

mixed —

plan()

plan(string  $target, bool  $fillDegraded = false, int|null  $limit = null, bool  $acceptConflicts = false) : \Drupal\strata\Restore\RestorePlan

Plans a restore of every subject a commit covers.

Parameters

string $target

Commit id to restore to.

bool $fillDegraded

TRUE to write partial reconstructions as well. Off unless a human asked.

int|null $limit

Most subjects to plan, or NULL for all of them.

bool $acceptConflicts

TRUE to write a subject somebody changed while the plan was waiting. Off unless a human asked.

Returns

\Drupal\strata\Restore\RestorePlan —

The plan.

planSubjects()

planSubjects(string  $target, list  $subjects, bool  $fillDegraded = false, bool  $acceptConflicts = false) : \Drupal\strata\Restore\RestorePlan

Plans a restore of named subjects only.

The surgical case: one node, one config object, one table row. A subject the target does not cover is planned anyway and comes back unrestorable, because "that node did not exist then" is an answer an operator needs rather than an error.

Parameters

string $target

Commit id to restore to.

list $subjects

Subject paths, such as "entity/node:42".

bool $fillDegraded

TRUE to write partial reconstructions as well.

bool $acceptConflicts

TRUE to write a subject somebody changed while the plan was waiting. Off unless a human asked.

Returns

\Drupal\strata\Restore\RestorePlan —

The plan.

concurrentChanges()

concurrentChanges(\Drupal\strata\Restore\RestorePlan  $plan) : array<string,\Drupal\strata\Restore\Conflict>

The subjects a plan covers that somebody has changed since it was built.

Called at apply time rather than at plan time, because at plan time nothing can have changed yet. A plan applied straight away finds nothing here; one that waited for a second approval may find several, and those are the writes nobody reviewing the plan saw.

Read from the local subject index, which records when each subject was last written, so this is one query and no object reads. A store with no local index detects nothing and says so by returning an empty set rather than by claiming there are none.

Parameters

\Drupal\strata\Restore\RestorePlan $plan

The plan being applied.

Returns

array

Subject path keyed to the conflict, empty when the plan carries no build time to compare against.

isRestorableRealm()

isRestorableRealm(string  $subject) : bool

Whether a restore writes subjects in a path's realm back at all.

Ephemeral state is captured so a restore can report what it invalidated, and never written back: the restored data rebuilds it, and writing a stale cache entry over a live bin costs more than the rebuild.

Parameters

string $subject

Subject path.

Returns

bool —

TRUE when the realm is restorable, and for a path whose realm is not one this release knows, since a subject from a newer capture is not something to silently drop.

planFor()

planFor(string  $target, list  $subjects, bool  $fillDegraded, list  $problems, float  $started, bool  $acceptConflicts = false) : \Drupal\strata\Restore\RestorePlan

Builds the plan for a resolved subject list.

Parameters

string $target

Commit id.

list $subjects

Subject paths.

bool $fillDegraded

Whether partial reconstructions are written.

list $problems

Problems collected so far.

float $started

When planning began.

bool $acceptConflicts

Whether subjects changed since the restore point are written anyway.

Returns

\Drupal\strata\Restore\RestorePlan —

The plan.

record()

record(\Drupal\strata\Restore\RestorePlan  $plan) : void

Records a plan that would not put everything back.

Parameters

\Drupal\strata\Restore\RestorePlan $plan

The plan.

Returns

void —