\Drupal\strata\Restore PhysicalRestore

Replaces whole tables with what was captured for them.

The operation for the realms a logical restore refuses: raw table rows, and anything whose correctness depends on constraints, sequences and triggers rather than on an entity API.

The honest constraint, stated first because everything else follows from it. Strata stores CHURN, not snapshots - that is the whole reason a year of history fits in 10 GB. So the rows it can put back into a table are the rows it captured for that table, and nothing else. A table whose changes were all captured restores exactly; a table that existed before capture started, or whose drift exceeded the reconciler's per-pass bound, restores to fewer rows than it has now.

That difference is measured before anything is written, and a restore that would leave a table with materially fewer rows than it holds REFUSES unless an operator says otherwise. Silently truncating a table to the subset that happened to be captured is the single worst thing this code could do, and it is exactly what a naive implementation does.

A pre-restore snapshot is forced, as it is for a logical restore, and maintenance mode is the caller's decision: the truncate strategy needs it and the shadow-swap strategy does not, which is why the strategy describes its own cost.

Summary

Methods
Properties
Constants
__construct
addStrategy
available
unavailable
plan
restore
No public properties found
COVERAGE_FLOOR
No protected methods found
No protected properties found
No protected constants found
rowsAt
run
refusalFor
enterMaintenance
leaveMaintenance
snapshot
actor
strategies
database
replayer
flusher
audit
state
currentUser
logger
notifier
No private constants found

Constant

COVERAGE_FLOOR

COVERAGE_FLOOR = 0.9

How far below the live row count a restore may land before it refuses.

A table restoring to 95% of its rows is plausibly a table with a few uncaptured rows; one restoring to 5% is a table about to be emptied by a backup that never saw it.

Properties

$database

$database : \Drupal\Core\Database\Connection

Type

Connection

$replayer

$replayer : \Drupal\strata\Restore\Replayer

Type

Replayer

$flusher

$flusher : \Drupal\strata\Flush\Flusher

Type

Flusher

$audit

$audit : \Drupal\strata\Restore\RestoreAudit

Type

RestoreAudit

$state

$state : \Drupal\Core\State\StateInterface

Type

StateInterface

$currentUser

$currentUser : \Drupal\Core\Session\AccountProxyInterface

Type

AccountProxyInterface

$logger

$logger : \Psr\Log\LoggerInterface

Type

LoggerInterface

$notifier

$notifier : ?\Drupal\strata\Event\Notifier

Type

Notifier|null

Methods

__construct()

__construct(\Drupal\Core\Database\Connection  $database, \Drupal\strata\Restore\Replayer  $replayer, \Drupal\strata\Flush\Flusher  $flusher, \Drupal\strata\Restore\RestoreAudit  $audit, \Drupal\Core\State\StateInterface  $state, \Drupal\Core\Session\AccountProxyInterface  $currentUser, \Psr\Log\LoggerInterface  $logger, \Drupal\strata\Event\Notifier|null  $notifier = null) : mixed

Constructs a physical restore.

Parameters

\Drupal\Core\Database\Connection $database

The connection tables are replaced on.

\Drupal\strata\Restore\Replayer $replayer

Reconstructs the captured rows.

\Drupal\strata\Flush\Flusher $flusher

Takes the snapshot that makes the restore undoable.

\Drupal\strata\Restore\RestoreAudit $audit

Records what was done.

\Drupal\Core\State\StateInterface $state

Used to put the site into maintenance mode when a strategy needs it.

\Drupal\Core\Session\AccountProxyInterface $currentUser

Attributes the restore to whoever ran it.

\Psr\Log\LoggerInterface $logger

Records the outcome.

\Drupal\strata\Event\Notifier|null $notifier

Announces the outcome, or NULL to announce nothing.

Returns

mixed —

addStrategy()

addStrategy(\Drupal\strata\Restore\RestoreStrategyInterface  $strategy) : void

Registers a strategy.

Collected from the container, so a contributed strategy is a tagged service.

Parameters

\Drupal\strata\Restore\RestoreStrategyInterface $strategy

The strategy.

Returns

void —

available()

available() : array<string,\Drupal\strata\Restore\RestoreStrategyInterface>

Strategies that can run on this connection.

Returns

array

Id keyed to the strategy.

unavailable()

unavailable() : array<string,string>

Why each unavailable strategy cannot run.

Returns

array

Id keyed to the reason.

plan()

plan(string  $table, string  $target) : \Drupal\strata\Restore\PhysicalPlan

Works out what restoring a table would do, before anything is written.

Parameters

string $table

The table.

string $target

Commit id to restore to.

Returns

\Drupal\strata\Restore\PhysicalPlan —

What the restore would leave behind, and whether that is safe.

restore()

restore(string  $table, string  $target, string  $strategy = 'truncate', bool  $apply = false, bool  $acceptRowLoss = false) : \Drupal\strata\Restore\RestoreResult

Restores one table.

Parameters

string $table

The table.

string $target

Commit id to restore to.

string $strategy

Which strategy to use.

bool $apply

FALSE to plan and record without writing.

bool $acceptRowLoss

TRUE to proceed when the restore would leave the table with materially fewer rows than it holds. Off by default, because that is the case where a backup empties a table it never saw.

Returns

\Drupal\strata\Restore\RestoreResult —

What happened.

rowsAt()

rowsAt(string  $table, string  $target) : array{rows: list<array<string,mixed>>, columns: list<string>, unreadable: int}

The rows captured for a table at a commit.

Every row subject the target's tree covers for this table is replayed. A subject the replay could not complete is counted rather than half-written: a table restored from a mix of complete and partial rows is not the table as it was at any moment.

Parameters

string $table

The table.

string $target

Commit id.

Returns

array{rows: list>, columns: list, unreadable: int} —

The rows, the union of their columns, and how many could not be read.

run()

run(string  $table, string  $target, string  $strategy, bool  $apply, bool  $acceptRowLoss) : \Drupal\strata\Restore\RestoreResult

The restore itself, before anything is announced.

Parameters

string $table

The logical table name.

string $target

The commit to restore to.

string $strategy

Which strategy writes the rows.

bool $apply

FALSE to plan without writing.

bool $acceptRowLoss

TRUE to proceed when the table would lose rows.

Returns

\Drupal\strata\Restore\RestoreResult —

What happened.

refusalFor()

refusalFor(\Drupal\strata\Restore\PhysicalPlan  $plan, string  $strategy, bool  $acceptRowLoss) : string|null

Why a restore would not run.

Parameters

\Drupal\strata\Restore\PhysicalPlan $plan

What the restore would do.

string $strategy

The strategy asked for.

bool $acceptRowLoss

Whether the caller accepted losing rows.

Returns

string|null —

The reason, or NULL when it may proceed.

enterMaintenance()

enterMaintenance(string  $strategy) : bool

Puts the site into maintenance mode when the strategy needs it.

Parameters

string $strategy

The strategy id.

Returns

bool —

TRUE when this call turned maintenance mode on, so only it turns it back off. A site already in maintenance mode when the restore started stays in it afterwards.

leaveMaintenance()

leaveMaintenance(bool  $entered) : void

Takes the site back out of maintenance mode.

Parameters

bool $entered

Whether this restore turned it on.

Returns

void —

snapshot()

snapshot() : string|null

Seals the site's current state so the restore can be undone.

Returns

string|null —

The snapshot commit id, or NULL when nothing could be sealed.

actor()

actor() : int|null

The user the restore is attributed to.

Returns

int|null —

A Drupal user id, or NULL for an unattended run.