snapshot.go RawHistoryFolder
1package daemon
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "sync"
8 "time"
9
10 vt "github.com/bnema/vev-vt"
11 "github.com/bnema/vev/internal/domain"
12 "github.com/bnema/vev/internal/usecase/layout"
13 snapcodec "github.com/bnema/vev/internal/usecase/snapshot"
14)
15
16// snapshotQueueCapacity bounds retained immutable captures. A full queue never
17// stalls session producers; the session remains dirty for the next interval.
18const snapshotQueueCapacity = 1
19
20// snapshotFinalQueueCapacity is a global bound for terminal captures retained
21// while the worker is blocked. On saturation a new terminal capture is rejected
22// (and left dirty) rather than silently dropping a persistence acknowledgement.
23const snapshotFinalQueueCapacity = 32
24
25// snapshotAttemptKind distinguishes rate-limited routine publications from
26// mandatory terminal checkpoints. Forced attempts never consume the routine
27// completion window.
28type snapshotAttemptKind uint8
29
30const (
31 snapshotAttemptRoutine snapshotAttemptKind = iota
32 snapshotAttemptForced
33)
34
35type snapshotCapture struct {
36 session *session
37 attemptKind snapshotAttemptKind
38 generation uint64 // repository publication generation
39 parentCheckpoint *domain.CheckpointRef
40 checkpoint domain.CheckpointRef // set by the encoder before publication
41 mutationRevision uint64
42 name string
43 incarnation domain.IncarnationID
44 createdAt uint64
45 defaultTab uint16
46 tabs []snapshotCaptureTab
47 publicationContext context.Context
48 sealedRefs map[*vt.HistoryChunk]snapcodec.ObjectRef // set by the single encoder worker
49 coordinatorDiscarded bool // guarded by session.snapshotMu
50 // normalWorkerAdmitted is set before snapshotJobs admission and immutable
51 // until completion; final-queue captures leave it false.
52 normalWorkerAdmitted bool
53 finishOnce sync.Once
54}
55
56type snapshotCaptureTab struct {
57 stableID string
58 cols uint16
59 rows uint16
60 nextPaneID uint64
61 focus layout.PaneID
62 tree *layout.Tree
63 panes []snapshotCapturePane
64}
65
66type snapshotCapturePane struct {
67 id layout.PaneID
68 stableID string
69 cwd string
70 // Sealed identities and the copied mutable tail are retained separately, so
71 // capture never rotates a live mutable tail merely to persist it.
72 sealed vt.HistorySnapshotView
73 tail vt.HistoryView
74 // transcript is an owned copy taken while pane.mu is held. It must be
75 // marshaled only after that lock has been released.
76 transcript vt.RecoveryTranscriptSnapshot
77 process *snapcodec.Process
78}
79
80// snapshotFailureSignature classifies a persistence failure without retaining
81// error text. Error strings often contain object content or filesystem paths,
82// neither of which is stable enough for deduplication or suitable for a global
83// notice history.
84func snapshotFailureSignature(phase string, err error) string {
85 if err == nil {
86 return phase + ":none"
87 }
88 switch {
89 case errors.Is(err, context.Canceled):
90 return phase + ":canceled"
91 case errors.Is(err, context.DeadlineExceeded):
92 return phase + ":deadline"
93 default:
94 return fmt.Sprintf("%s:%T", phase, err)
95 }
96}
97
98const (
99 // Routine checkpoints are rate limited from completion; forced teardown
100 // checkpoints use the same bounded worker but never consume this window.
101 snapshotInterval = 2 * time.Minute
102 snapshotFinalFlushTimeout = time.Second
103)
104
105// SnapshotShutdownTimeout reports the shared final-checkpoint shutdown budget.
106func SnapshotShutdownTimeout() time.Duration { return snapshotFinalFlushTimeout }