DEFAULT_BUDGET
DEFAULT_BUDGET = 500
Frames one pass re-seals when no budget is given.
Each one is a read, an open, a seal and a write, so the budget is a request budget as much as a time one.
Measures how much of the store still needs a retired key, and re-seals it a batch at a time.
A rotation is not an event, it is a state the store is in until every frame has been re-sealed. This is what reports on that state and what advances it.
Re-sealing is incremental and bounded, and it is safe to stop at any point. A frame is read, opened with whichever key works, sealed with the active key and written back to the same address - the content address is over the DECODED bytes, so re-sealing does not move a frame and nothing that references it has to be rewritten. A pass that runs out of budget leaves a consistent store where some frames are new and some are old, which is the same state it started in.
A rotation is only finished when nothing needs a retired key. Until then the retired keys must
stay configured; removing one makes every frame still sealed under it unreadable, and nothing can
tell that apart from corruption afterwards. isComplete() is the only safe signal for removing a
key, and the report says so.
$cipher : \Drupal\strata\Crypto\RotatingCipher
$store : \Drupal\strata\Cas\ObjectStore
$recompressor : \Drupal\strata\Compaction\Recompressor
$index : \Drupal\strata\Cas\FrameIndexInterface
$ledger : \Drupal\strata\Health\HealthLedgerInterface
__construct(\Drupal\strata\Crypto\RotatingCipher $cipher, \Drupal\strata\Cas\ObjectStore $store, \Drupal\strata\Compaction\Recompressor $recompressor, \Drupal\strata\Cas\FrameIndexInterface $index, \Drupal\strata\Health\HealthLedgerInterface $ledger, \Psr\Log\LoggerInterface $logger) : mixed
Constructs a rotation.
| \Drupal\strata\Crypto\RotatingCipher | $cipher | Opens a frame with whichever key works and reports which one that was. |
| \Drupal\strata\Cas\ObjectStore | $store | Reads the frames. |
| \Drupal\strata\Compaction\Recompressor | $recompressor | Rewrites an object under the active key. A packed frame is re-sealed with its pack, which is the rule compaction already follows and the reason a frame is not rewritten here. |
| \Drupal\strata\Cas\FrameIndexInterface | $index | Enumerates the frames to examine. |
| \Drupal\strata\Health\HealthLedgerInterface | $ledger | Where a mid-flush rotation is recorded. |
| \Psr\Log\LoggerInterface | $logger | Records what a pass did. |
measure(int $sample = \self::SAMPLE) : array{rotating: bool, keys: int, sampled: int, stale: int, unreadable: int, complete: bool, fingerprints: list<string>}
How much of the store still needs a retired key.
| int | $sample | Frames to examine. |
Whether a rotation is in progress, how many keys are on the ring, what the sample found, and whether it is safe to remove the retired keys.
run(int $budget = \self::DEFAULT_BUDGET) : array{examined: int, resealed: int, skipped: int, unreadable: int, problems: list<string>}
Re-seals frames that still open under a retired key.
| int | $budget | Frames to select at most. A packed frame is re-sealed with its whole pack, so a pass can re-seal more frames than the budget names: the budget bounds what is read and examined, and one object is the smallest unit that can be re-sealed. |
What the pass did.
frames(int $limit) : list<\Drupal\strata\Cas\FrameRecord>
The frames to examine, oldest first.
Every frame, referenced or not. A rotation is not a reachability question: a frame nothing points at yet is still sealed under a key, and a frame the tree does point at is exactly the one that must not stop opening.
| int | $limit | Most frames to return. |
Frame records.
read(string $hash, list$problems) : string|null
Reads one frame, recording rather than throwing when it will not open.
| string | $hash | The frame address. |
| list |
$problems | Problems collected so far, added to by reference. |
The decoded bytes, or NULL when the frame could not be read.
resealAll(list<\Drupal\strata\Cas\FrameRecord> $records, list$problems) : int
Writes a set of frames back under the active key.
A frame cannot be re-sealed by writing its bytes again: the store deduplicates on the content address, so a second write of the same bytes finds the frame already present and only takes a reference. The object itself has to be rewritten, which is what the recompressor does, and a packed frame is rewritten with its whole pack rather than pulled out of it.
The address never moves, so nothing that names a frame has to be rewritten. A pack that is rewritten leaves its predecessor behind for the prune, exactly as recompaction does.
| list<\Drupal\strata\Cas\FrameRecord> | $records | The frames that still open under a retired key. |
| list |
$problems | Problems collected so far, added to by reference. |
How many frames were re-sealed. A pack counts every frame it carried forward, because all of them are sealed under the active key afterwards.