M5: retention policy, prune with repacking, append-only locations

- RetentionPolicy (last/daily/weekly/monthly) and PruneService: forget
  snapshots the policy drops, delete packs nothing uses and repack mostly
  unused ones by copying encrypted blobs as-is; new packs are indexed
  before old ones are removed
- a location lock keeps prune and backups apart; append-only locations
  are never pruned (cleanup via the location's lifecycle rules)
- weekly prune from the background job; occ backup:prune [--dry-run],
  backup:retention, backup:target:append-only
- tests: forget+prune frees space and the remaining snapshot restores
  identically, repack of mixed packs, dry-run preview, retention buckets
This commit is contained in:
2026-10-02 02:21:48 +02:00
parent e5eaf6df14
commit 7b3c9cabf6
16 changed files with 606 additions and 1 deletions
+10
View File
@@ -49,6 +49,16 @@ final class PackWriter {
return strlen($encrypted);
}
/** Add an already encrypted blob as-is (repacking: no decryption needed, the blob is bound to its id, not to a pack). */
public function addRaw(string $id, string $encrypted, int $rawLength, int $flags): void {
fwrite($this->buffer, $encrypted);
$this->entries[] = ['id' => $id, 'offset' => $this->size, 'length' => strlen($encrypted), 'raw' => $rawLength, 'flags' => $flags];
$this->size += strlen($encrypted);
if ($this->size >= $this->targetSize) {
$this->flush();
}
}
/** Upload the current pack (if any) and record it in the index. */
public function flush(): void {
if ($this->entries === []) {