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
 | |
| }
 | 
