github.com/celestiaorg/celestia-node@v0.15.0-beta.1/pruner/checkpoint.go (about) 1 package pruner 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "sync/atomic" 8 9 "github.com/ipfs/go-datastore" 10 11 "github.com/celestiaorg/celestia-node/header" 12 ) 13 14 var ( 15 storePrefix = datastore.NewKey("pruner") 16 checkpointKey = datastore.NewKey("checkpoint") 17 ) 18 19 // checkpoint contains information related to the state of the 20 // pruner service that is periodically persisted to disk. 21 type checkpoint struct { 22 lastPrunedHeader atomic.Pointer[header.ExtendedHeader] 23 24 LastPrunedHeight uint64 `json:"last_pruned_height"` 25 FailedHeaders map[uint64]string `json:"failed"` 26 } 27 28 // initializeCheckpoint initializes the checkpoint, storing the earliest header in the chain. 29 func (s *Service) initializeCheckpoint(ctx context.Context) error { 30 firstHeader, err := s.getter.GetByHeight(ctx, 1) 31 if err != nil { 32 return fmt.Errorf("failed to initialize checkpoint: %w", err) 33 } 34 35 return s.updateCheckpoint(ctx, firstHeader, nil) 36 } 37 38 // loadCheckpoint loads the last checkpoint from disk, initializing it if it does not already exist. 39 func (s *Service) loadCheckpoint(ctx context.Context) error { 40 bin, err := s.ds.Get(ctx, checkpointKey) 41 if err != nil { 42 if err == datastore.ErrNotFound { 43 return s.initializeCheckpoint(ctx) 44 } 45 return fmt.Errorf("failed to load checkpoint: %w", err) 46 } 47 48 var cp *checkpoint 49 err = json.Unmarshal(bin, &cp) 50 if err != nil { 51 return fmt.Errorf("failed to unmarshal checkpoint: %w", err) 52 } 53 s.checkpoint = cp 54 55 // load last pruned header based off height 56 lastPruned, err := s.getter.GetByHeight(ctx, cp.LastPrunedHeight) 57 if err != nil { 58 return fmt.Errorf("failed to load last pruned header at height %d: %w", cp.LastPrunedHeight, err) 59 } 60 61 s.checkpoint.lastPrunedHeader.Store(lastPruned) 62 return nil 63 } 64 65 // updateCheckpoint updates the checkpoint with the last pruned header height 66 // and persists it to disk. 67 func (s *Service) updateCheckpoint( 68 ctx context.Context, 69 lastPruned *header.ExtendedHeader, 70 failedHeights map[uint64]error, 71 ) error { 72 for height, failErr := range failedHeights { 73 // if the height already exists, just update the error 74 s.checkpoint.FailedHeaders[height] = failErr.Error() 75 } 76 77 s.checkpoint.lastPrunedHeader.Store(lastPruned) 78 s.checkpoint.LastPrunedHeight = lastPruned.Height() 79 80 bin, err := json.Marshal(s.checkpoint) 81 if err != nil { 82 return err 83 } 84 85 return s.ds.Put(ctx, checkpointKey, bin) 86 } 87 88 func (s *Service) lastPruned() *header.ExtendedHeader { 89 return s.checkpoint.lastPrunedHeader.Load() 90 }