package lease import ( "context" "errors" "time" ) // CheckpointReceipt describes a completed sync and close under a healthy // lease. It does not prove restored database semantics or authorize a command. // SafeUntil is useful only in-process, with its monotonic clock component. type CheckpointReceipt struct { CompletedAt time.Time SafeUntil time.Time } // Checkpoint keeps the same renewal running while one temporary replica store // opens, syncs and closes. Failure poisons the handle but does not release it; // the caller must retain its fence, record the failure and explicitly Release. func (h *Held) Checkpoint(ctx context.Context, dbPath string) (receipt CheckpointReceipt, err error) { if err = h.begin(false); err != nil { return receipt, err } defer func() { h.finish(false, err) }() opCtx, cancel := context.WithCancel(ctx) stop := context.AfterFunc(h.rn.ctx, cancel) defer stop() defer cancel() store, db, err := h.newStore(dbPath, 0) if err != nil { return receipt, err } // Short checkpoints need only the explicit L0 sync. Background snapshots // are unnecessary and v0.5.14 closes the DB before joining those monitors. db.MonitorInterval = 0 db.Replica.MonitorEnabled = false store.CompactionMonitorEnabled = false store.L0RetentionCheckInterval = 0 store.SetRetentionEnabled(false) openErr := store.Open(opCtx) var syncErr error if openErr == nil { syncErr = db.SyncAndWait(opCtx) } if openErr != nil || syncErr != nil { cancel() } closeErr := store.Close(opCtx) until, healthErr := h.rn.SafeUntil() err = errors.Join(openErr, syncErr, closeErr, healthErr, opCtx.Err()) if err != nil { return CheckpointReceipt{}, err } completed := time.Now() if !completed.Before(until) { return CheckpointReceipt{}, ErrLost } return CheckpointReceipt{CompletedAt: completed, SafeUntil: until}, nil }