github.com/m3db/m3@v1.5.0/specs/dbnode/snapshots/SnapshotsSpec.tla (about)

     1  ------------------------------- MODULE SnapshotsSpec -------------------------------
     2  EXTENDS Integers, Sequences, TLC
     3  
     4  CONSTANTS numClients
     5  CONSTANTS numWrites
     6  
     7  \* Set to reasonable values to limit the search space that TLC needs to check. Otherwise,
     8  \* the search space is infinite because its always valid for the server to perform a
     9  \* persistence step.
    10  CONSTANTS minNumWritesForPersistence
    11  CONSTANTS minNumWritesForCleanup
    12  
    13  AllExceptLast(seq) == SubSeq(seq, 1, Len(seq)-1)
    14  
    15  (***************************************************************************
    16  --algorithm SnapshotsSpec
    17  
    18  \* Writes issued and acked by the client.
    19  variable
    20      \* Unique identifier for the next write.
    21      CurrentIndex = 0;
    22      \* Writes issued by a client, acked or not.
    23      IssuedWrites = {};
    24      \* Writes that have been acked by M3DB.
    25      AckedWrites = {};
    26      \* Sequence of files, each of which is represented by a set which stores
    27      \* the writes contained by the commitlog.
    28      CommitLogFiles = << {} >>;
    29      \* Sequence of snapshot checkpoint files, each of which points to the index
    30      \* in the CommitLogFiles sequence which it contains all the writes up to.
    31      SnapshotCheckpointFiles = << >>;
    32      \* Writes persisted outside of the commitlog.
    33      PersistedWrites = {};
    34  
    35  macro write_to_commitlog_and_ack(writes)
    36  begin
    37      \* Store the writes in the last (more recent / active) commitlog file.
    38      CommitLogFiles[Len(CommitLogFiles)] := CommitLogFiles[Len(CommitLogFiles)] \union (writes);
    39      \* Mark all writes as Acked.
    40      AckedWrites := AckedWrites \union writes;
    41  end macro
    42  
    43  macro handle_snapshot()
    44  begin
    45      \* We haven't already started a snapshot, so rotate the commitlog and mark a snapshot as in progress.
    46      if snapshotInProgress = FALSE /\ CurrentIndex-lastPersistIndex >= minNumWritesForPersistence then
    47          \* "Rotate" the commitlog by adding a new one.
    48          CommitLogFiles := Append(CommitLogFiles, {});
    49          snapshotInProgress := TRUE;
    50          lastPersistIndex := CurrentIndex;
    51      \* We've already started a snapshot, so complete it.
    52      elsif snapshotInProgress = TRUE then
    53          \* Sanity checks.
    54          assert(Len(CommitLogFiles) >= 2);
    55  
    56          either
    57              \* Snapshot success
    58              \*
    59              \* Since we rotate the commitlog at the beginning of every Snapshot before doing
    60              \* anything else, we know that when snapshotting is complete we can add to PersistedWrites
    61              \* all the writes in all the commitlog files except for the (most recent) rotated one.
    62              with allCommitlogFilesExceptLast = AllExceptLast(CommitLogFiles);
    63                  writesToPersist = (UNION {allCommitlogFilesExceptLast[x]: x \in DOMAIN allCommitlogFilesExceptLast});
    64              do
    65                  PersistedWrites := PersistedWrites \union writesToPersist;
    66                  \* Add a new snapshot checkpoint file which points to the commitlog file up until
    67                  \* which it contains all the data for.
    68                  SnapshotCheckpointFiles := Append(SnapshotCheckpointFiles, Len(CommitLogFiles)-1);
    69                  snapshotInProgress := FALSE;
    70              end with
    71          or
    72              \* Snapshot failure
    73              snapshotInProgress := FALSE;
    74          end either
    75      end if
    76  end macro
    77  
    78  macro handle_cleanup()
    79  begin
    80      if Len(SnapshotCheckpointFiles) >=1 /\
    81          CurrentIndex-lastCleanupIndex >= minNumWritesForCleanup
    82      then
    83          with lastSnapshottedCommitlogIndex = SnapshotCheckpointFiles[Len(SnapshotCheckpointFiles)];
    84          do
    85              \* Identify the most recent snapshot metadata file, and delete all commitlogs up to
    86              \* and including that one because all of thoes writes should have been snapshotted already.
    87              CommitLogFiles := SubSeq(CommitLogFiles, lastSnapshottedCommitlogIndex+1, Len(CommitLogFiles));
    88              SnapshotCheckpointFiles := << >>;
    89              lastCleanupIndex := CurrentIndex;
    90          end with
    91      end if
    92  end macro
    93  
    94  \* Server process.
    95  process M3DB = 0
    96  variable
    97      \* Variables used for persistence state (flushing / snapshotting)
    98      snapshotInProgress = FALSE;
    99  
   100      \* Variables used for preventing background operations from occurring
   101      \* infinitely.
   102      lastPersistIndex = 0;
   103      lastCleanupIndex = 0;
   104  
   105  begin
   106      server_loop: while TRUE do
   107          either
   108              \* Take all the unacked writes in IssuedWrites and put them in the commitlog and ack them.
   109              write_to_commitlog_and_ack(IssuedWrites \ AckedWrites);
   110          or
   111              handle_snapshot();
   112          or
   113              handle_cleanup();
   114          end either
   115      end while
   116  end process
   117  
   118  \* Client processes.
   119  process n \in 1..numClients
   120  begin
   121      client_loop: while CurrentIndex < numWrites do
   122          IssuedWrites := IssuedWrites \union {CurrentIndex};
   123          CurrentIndex := CurrentIndex+1;
   124      end while
   125  end process
   126  
   127  end algorithm;
   128   ***************************************************************************)
   129  \* BEGIN TRANSLATION
   130  VARIABLES CurrentIndex, IssuedWrites, AckedWrites, CommitLogFiles,
   131            SnapshotCheckpointFiles, PersistedWrites, pc, snapshotInProgress,
   132            lastPersistIndex, lastCleanupIndex
   133  
   134  vars == << CurrentIndex, IssuedWrites, AckedWrites, CommitLogFiles,
   135             SnapshotCheckpointFiles, PersistedWrites, pc, snapshotInProgress,
   136             lastPersistIndex, lastCleanupIndex >>
   137  
   138  ProcSet == {0} \cup (1..numClients)
   139  
   140  Init == (* Global variables *)
   141          /\ CurrentIndex = 0
   142          /\ IssuedWrites = {}
   143          /\ AckedWrites = {}
   144          /\ CommitLogFiles = << {} >>
   145          /\ SnapshotCheckpointFiles = << >>
   146          /\ PersistedWrites = {}
   147          (* Process M3DB *)
   148          /\ snapshotInProgress = FALSE
   149          /\ lastPersistIndex = 0
   150          /\ lastCleanupIndex = 0
   151          /\ pc = [self \in ProcSet |-> CASE self = 0 -> "server_loop"
   152                                          [] self \in 1..numClients -> "client_loop"]
   153  
   154  server_loop == /\ pc[0] = "server_loop"
   155                 /\ \/ /\ CommitLogFiles' = [CommitLogFiles EXCEPT ![Len(CommitLogFiles)] = CommitLogFiles[Len(CommitLogFiles)] \union ((IssuedWrites \ AckedWrites))]
   156                       /\ AckedWrites' = (AckedWrites \union (IssuedWrites \ AckedWrites))
   157                       /\ UNCHANGED <<SnapshotCheckpointFiles, PersistedWrites, snapshotInProgress, lastPersistIndex, lastCleanupIndex>>
   158                    \/ /\ IF snapshotInProgress = FALSE /\ CurrentIndex-lastPersistIndex >= minNumWritesForPersistence
   159                             THEN /\ CommitLogFiles' = Append(CommitLogFiles, {})
   160                                  /\ snapshotInProgress' = TRUE
   161                                  /\ lastPersistIndex' = CurrentIndex
   162                                  /\ UNCHANGED << SnapshotCheckpointFiles,
   163                                                  PersistedWrites >>
   164                             ELSE /\ IF snapshotInProgress = TRUE
   165                                        THEN /\ Assert((Len(CommitLogFiles) >= 2),
   166                                                       "Failure of assertion at line 54, column 9 of macro called at line 111, column 13.")
   167                                             /\ \/ /\ LET allCommitlogFilesExceptLast == AllExceptLast(CommitLogFiles) IN
   168                                                        LET writesToPersist == (UNION {allCommitlogFilesExceptLast[x]: x \in DOMAIN allCommitlogFilesExceptLast}) IN
   169                                                          /\ PersistedWrites' = (PersistedWrites \union writesToPersist)
   170                                                          /\ SnapshotCheckpointFiles' = Append(SnapshotCheckpointFiles, Len(CommitLogFiles)-1)
   171                                                          /\ snapshotInProgress' = FALSE
   172                                                \/ /\ snapshotInProgress' = FALSE
   173                                                   /\ UNCHANGED <<SnapshotCheckpointFiles, PersistedWrites>>
   174                                        ELSE /\ TRUE
   175                                             /\ UNCHANGED << SnapshotCheckpointFiles,
   176                                                             PersistedWrites,
   177                                                             snapshotInProgress >>
   178                                  /\ UNCHANGED << CommitLogFiles,
   179                                                  lastPersistIndex >>
   180                       /\ UNCHANGED <<AckedWrites, lastCleanupIndex>>
   181                    \/ /\ IF Len(SnapshotCheckpointFiles) >=1 /\
   182                              CurrentIndex-lastCleanupIndex >= minNumWritesForCleanup
   183                             THEN /\ LET lastSnapshottedCommitlogIndex == SnapshotCheckpointFiles[Len(SnapshotCheckpointFiles)] IN
   184                                       /\ CommitLogFiles' = SubSeq(CommitLogFiles, lastSnapshottedCommitlogIndex+1, Len(CommitLogFiles))
   185                                       /\ SnapshotCheckpointFiles' = << >>
   186                                       /\ lastCleanupIndex' = CurrentIndex
   187                             ELSE /\ TRUE
   188                                  /\ UNCHANGED << CommitLogFiles,
   189                                                  SnapshotCheckpointFiles,
   190                                                  lastCleanupIndex >>
   191                       /\ UNCHANGED <<AckedWrites, PersistedWrites, snapshotInProgress, lastPersistIndex>>
   192                 /\ pc' = [pc EXCEPT ![0] = "server_loop"]
   193                 /\ UNCHANGED << CurrentIndex, IssuedWrites >>
   194  
   195  M3DB == server_loop
   196  
   197  client_loop(self) == /\ pc[self] = "client_loop"
   198                       /\ IF CurrentIndex < numWrites
   199                             THEN /\ IssuedWrites' = (IssuedWrites \union {CurrentIndex})
   200                                  /\ CurrentIndex' = CurrentIndex+1
   201                                  /\ pc' = [pc EXCEPT ![self] = "client_loop"]
   202                             ELSE /\ pc' = [pc EXCEPT ![self] = "Done"]
   203                                  /\ UNCHANGED << CurrentIndex, IssuedWrites >>
   204                       /\ UNCHANGED << AckedWrites, CommitLogFiles,
   205                                       SnapshotCheckpointFiles, PersistedWrites,
   206                                       snapshotInProgress, lastPersistIndex,
   207                                       lastCleanupIndex >>
   208  
   209  n(self) == client_loop(self)
   210  
   211  Next == M3DB
   212             \/ (\E self \in 1..numClients: n(self))
   213  
   214  Spec == Init /\ [][Next]_vars
   215  
   216  \* END TRANSLATION
   217  
   218  \* Invariants - Add these to the model checker when running.
   219  AllAckedWritesAreBootstrappable == AckedWrites \subseteq ( (UNION { CommitLogFiles[x] : x \in DOMAIN CommitLogFiles }) \union PersistedWrites)
   220  =============================================================================
   221  \* Modification History
   222  \* Last modified Sun Nov 25 21:19:27 EST 2018 by richardartoul
   223  \* Created Sat Nov 24 16:19:03 EST 2018 by richardartoul