Files
Baradb/formal-verification/backup.tla
T
dimgigov 422df08ab9 feat: clean build + crossmodal.tla + TLA+ symmetry (v1.0.0-prep)
Build warnings cleanup (0 warnings):
- Suppress threadpool deprecation warning (baradadb.nim)
- Remove unused 'os' import (logging.nim)
- Fix ImplicitDefaultValue for newNode params (ast.nim)
- Add explicit cstring cast for posix.open (wal.nim)
- Fix HoleEnumConv for MsgKind parsing (server.nim)

TLA+ Formal Verification:
- Add symmetry reduction (Permutations) to all 9 existing specs
- Add SYMMETRY directive to all .cfg files
- New crossmodal.tla: cross-modal consistency spec
  * MetadataVectorConsistency, HybridResultValid
  * CommittedAtomicity, AbortedAtomicity, TxnStateValid
- New models/crossmodal.cfg

Tests:
- Add Cross-Modal TLA+ Faithfulness tests (4 tests)

Docs:
- Update PLAN.md and BUG_AUDIT.md with completed tasks
2026-05-13 09:24:04 +03:00

184 lines
6.6 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-------------------------------- MODULE backup --------------------------------
(*
TLA+ specification of the Backup & Restore subsystem as implemented
in BaraDB (core/backup.nim).
Key properties verified:
- BackupSnapshotsValid: every backup snapshot is a valid file->content mapping.
- RestoreIntegrity : restoring from a valid backup yields the exact
data directory that was backed up at restore time.
- VerifyIntegrity : a verified backup is a known backup.
- RetentionInvariant : after cleanup, at most keepCount backups exist.
- HistoryConsistency : restore history only records known archive names.
- BackupIdValid : all backup IDs are less than the next ID counter.
*)
EXTENDS Integers, Sequences, FiniteSets, TLC
CONSTANTS Files, \* set of file IDs
Contents, \* set of possible file contents
MaxBackups, \* bound number of backups for model checking
KeepCount, \* retention count (must be >= 1)
MaxSteps, \* bound total actions for model checking
Nil \* distinguished nil value (model value)
ASSUME IsFiniteSet(Files) /\ IsFiniteSet(Contents)
ASSUME KeepCount >= 1 /\ MaxSteps >= 1
VARIABLES
dataDir, \* dataDir[f] ∈ Contents {Nil} — current live data
backups, \* backups ⊆ 1..MaxBackups — existing backup IDs
backupContent, \* backupContent[b][f] ∈ Contents {Nil} — snapshot of dataDir
verified, \* verified ⊆ 1..MaxBackups — backups that passed verify
history, \* history ∈ Seq(1..MaxBackups {Nil}) — restore history
nextBackupId, \* nextBackupId ∈ 1..MaxBackups+1 — monotonic ID counter
steps \* steps ∈ 0..MaxSteps — action counter bound
vars == <<dataDir, backups, backupContent, verified, history, nextBackupId, steps>>
\* Helper operators
Max(a, b) == IF a > b THEN a ELSE b
-----------------------------------------------------------------------------
\* Helper operators
\* Is backup b a valid, known backup?
IsKnownBackup(b) == b \in backups
\* Does backup b contain exactly the dataDir at the time it was taken?
BackupMatchesData(b, dir) ==
\A f \in Files : backupContent[b][f] = dir[f]
-----------------------------------------------------------------------------
\* Initial state
Init ==
/\ dataDir = [f \in Files |-> Nil]
/\ backups = {}
/\ backupContent = [b \in 1..MaxBackups |-> [f \in Files |-> Nil]]
/\ verified = {}
/\ history = << >>
/\ nextBackupId = 1
/\ steps = 0
-----------------------------------------------------------------------------
\* State transitions
\* Modify a file in the live data directory.
ModifyFile(f, c) ==
/\ f \in Files
/\ c \in Contents
/\ steps < MaxSteps
/\ dataDir' = [dataDir EXCEPT ![f] = c]
/\ steps' = steps + 1
/\ UNCHANGED <<backups, backupContent, verified, history, nextBackupId>>
\* Create a backup of the current data directory.
CreateBackup ==
/\ nextBackupId <= MaxBackups
/\ steps < MaxSteps
/\ backups' = backups \cup {nextBackupId}
/\ backupContent' = [backupContent EXCEPT ![nextBackupId] = dataDir]
/\ nextBackupId' = nextBackupId + 1
/\ steps' = steps + 1
/\ UNCHANGED <<dataDir, verified, history>>
\* Verify a backup: mark it as verified if it is known.
VerifyBackup(b) ==
/\ b \in backups
/\ steps < MaxSteps
/\ verified' = verified \cup {b}
/\ steps' = steps + 1
/\ UNCHANGED <<dataDir, backups, backupContent, history, nextBackupId>>
\* Restore from a verified backup: overwrite dataDir with backup contents.
RestoreBackup(b) ==
/\ b \in verified
/\ steps < MaxSteps
/\ dataDir' = backupContent[b]
/\ history' = Append(history, b)
/\ steps' = steps + 1
/\ UNCHANGED <<backups, backupContent, verified, nextBackupId>>
\* Cleanup old backups, keeping only the KeepCount most recent ones.
\* The "most recent" are the largest IDs (monotonic counter).
CleanupBackups ==
/\ steps < MaxSteps
/\ LET toKeep == IF Cardinality(backups) <= KeepCount
THEN backups
ELSE CHOOSE s \in SUBSET backups :
/\ Cardinality(s) = KeepCount
/\ \A b1 \in s, b2 \in backups \ s : b1 > b2
IN backups' = toKeep
/\ verified' = verified \cap backups'
/\ steps' = steps + 1
/\ UNCHANGED <<dataDir, backupContent, history, nextBackupId>>
-----------------------------------------------------------------------------
\* Next-state relation
Next ==
\/ \E f \in Files : \E c \in Contents : ModifyFile(f, c)
\/ CreateBackup
\/ \E b \in 1..MaxBackups : VerifyBackup(b)
\/ \E b \in 1..MaxBackups : RestoreBackup(b)
\/ CleanupBackups
-----------------------------------------------------------------------------
\* Safety properties
\* All backup snapshots are valid file->content mappings (enforced by TypeOk).
\* This property ensures backups only contain valid Contents or Nil.
BackupSnapshotsValid ==
\A b \in backups :
\A f \in Files : backupContent[b][f] \in Contents \cup {Nil}
\* After restore, dataDir matches the backup that was restored.
\* This is only true immediately after restore; we express it as a
\* temporal property: whenever a restore happens, dataDir equals backup.
RestoreIntegrity ==
\A b \in verified :
(history /= << >> /\ history[Len(history)] = b) =>
BackupMatchesData(b, dataDir)
\* Verified backups are always known backups.
VerifyIntegrity ==
verified \subseteq backups
\* After cleanup, the number of backups is at most KeepCount.
RetentionInvariant ==
Cardinality(backups) <= Max(KeepCount, nextBackupId - 1)
\* History only records valid backup IDs (may reference deleted backups).
HistoryConsistency ==
\A i \in 1..Len(history) : history[i] \in 1..(nextBackupId - 1)
\* All backup IDs are valid (less than the monotonic counter).
BackupIdValid ==
\A b \in backups : b < nextBackupId
\* Type invariant
TypeOk ==
/\ dataDir \in [Files -> Contents \cup {Nil}]
/\ backups \subseteq 1..MaxBackups
/\ backupContent \in [1..MaxBackups -> [Files -> Contents \cup {Nil}]]
/\ verified \subseteq 1..MaxBackups
/\ history \in Seq(1..MaxBackups \cup {Nil})
/\ nextBackupId \in 1..(MaxBackups + 1)
/\ steps \in 0..MaxSteps
\* Liveness properties
\* Any backup that is created can eventually be verified.
VerifyProgress ==
\A b \in 1..MaxBackups : b \in backups ~> b \in verified
\* Specification with weak fairness.
Spec == Init /\ [][Next]_vars /\ WF_vars(Next)
\* Symmetry reduction for model checking.
Symmetry == Permutations({f1, f2}) \cup Permutations({c1, c2})
=============================================================================