46 lines
1.2 KiB
Go
46 lines
1.2 KiB
Go
package swoosh
|
|
|
|
import "fmt"
|
|
|
|
// Replay deterministically replays WAL records on top of a snapshot and returns the resulting state.
|
|
func Replay(wal WALStore, snapshot Snapshot) (OrchestratorState, error) {
|
|
base, err := cloneState(snapshot.State)
|
|
if err != nil {
|
|
return OrchestratorState{}, fmt.Errorf("clone snapshot state: %w", err)
|
|
}
|
|
|
|
if base.StateHash == "" {
|
|
hash, err := computeStateHash(base)
|
|
if err != nil {
|
|
return OrchestratorState{}, fmt.Errorf("compute snapshot hash: %w", err)
|
|
}
|
|
base.StateHash = hash
|
|
}
|
|
|
|
start := snapshot.LastAppliedIndex + 1
|
|
records, err := wal.Replay(start)
|
|
if err != nil {
|
|
return OrchestratorState{}, fmt.Errorf("replay wal: %w", err)
|
|
}
|
|
|
|
state := base
|
|
for _, record := range records {
|
|
if state.StateHash != record.StatePreHash {
|
|
return OrchestratorState{}, fmt.Errorf("wal pre-hash mismatch at index %d", record.Index)
|
|
}
|
|
|
|
next, err := Reduce(state, record.Transition, record.Guard)
|
|
if err != nil {
|
|
return OrchestratorState{}, fmt.Errorf("reduce wal record %d: %w", record.Index, err)
|
|
}
|
|
|
|
if next.StateHash != record.StatePostHash {
|
|
return OrchestratorState{}, fmt.Errorf("wal post-hash mismatch at index %d", record.Index)
|
|
}
|
|
|
|
state = next
|
|
}
|
|
|
|
return state, nil
|
|
}
|