@@ -22,6 +22,9 @@ export const DEFAULT_SNAPSHOT_COUNTS = {
22
22
monthly : 4 ,
23
23
} as SnapshotCounts ;
24
24
25
+ // We have at least one snapshot for each interval, assuming
26
+ // there are actual changes since the last snapshot, and at
27
+ // most the listed number.
25
28
export interface SnapshotCounts {
26
29
frequent : number ;
27
30
daily : number ;
@@ -32,9 +35,12 @@ export interface SnapshotCounts {
32
35
export async function updateRollingSnapshots ( {
33
36
snapshots,
34
37
counts,
38
+ opts,
35
39
} : {
36
40
snapshots : SubvolumeSnapshots ;
37
41
counts ?: Partial < SnapshotCounts > ;
42
+ // options to create
43
+ opts ?;
38
44
} ) {
39
45
counts = { ...DEFAULT_SNAPSHOT_COUNTS , ...counts } ;
40
46
@@ -44,46 +50,71 @@ export async function updateRollingSnapshots({
44
50
counts,
45
51
changed,
46
52
} ) ;
47
- if ( ! changed ) {
48
- // definitely no data written since most recent snapshot, so nothing to do
49
- return ;
50
- }
51
53
52
54
// get exactly the iso timestamp snapshot names:
53
55
const snapshotNames = ( await snapshots . readdir ( ) ) . filter ( ( name ) =>
54
56
DATE_REGEXP . test ( name ) ,
55
57
) ;
56
58
snapshotNames . sort ( ) ;
57
- if ( snapshotNames . length > 0 ) {
58
- const age = Date . now ( ) - new Date ( snapshotNames . slice ( - 1 ) [ 0 ] ) . valueOf ( ) ;
59
+ let needNewSnapshot = false ;
60
+ if ( changed ) {
61
+ const timeSinceLastSnapshot =
62
+ snapshotNames . length == 0
63
+ ? 1e12 // infinitely old
64
+ : Date . now ( ) - new Date ( snapshotNames . slice ( - 1 ) [ 0 ] ) . valueOf ( ) ;
59
65
for ( const key in SNAPSHOT_INTERVALS_MS ) {
60
- if ( counts [ key ] ) {
61
- if ( age < SNAPSHOT_INTERVALS_MS [ key ] ) {
62
- // no need to snapshot since there is already a sufficiently recent snapshot
63
- logger . debug ( "updateRollingSnapshots: no need to snapshot" , {
64
- name : snapshots . subvolume . name ,
65
- } ) ;
66
- return ;
67
- }
68
- // counts[key] nonzero and snapshot is old enough so we'll be making a snapshot
66
+ if ( counts [ key ] && timeSinceLastSnapshot > SNAPSHOT_INTERVALS_MS [ key ] ) {
67
+ // there is NOT a sufficiently recent snapshot to satisfy the constraint
68
+ // of having at least one snapshot for the given interval.
69
+ needNewSnapshot = true ;
69
70
break ;
70
71
}
71
72
}
72
73
}
73
74
74
- // make a new snapshot
75
- const name = new Date ( ) . toISOString ( ) ;
76
- await snapshots . create ( name ) ;
75
+ // Regarding error reporting we try to do everything below and throw the
76
+ // create error or last delete error...
77
+
78
+ let createError : any = undefined ;
79
+ if ( changed && needNewSnapshot ) {
80
+ // make a new snapshot -- but only bother
81
+ // definitely no data written since most recent snapshot, so nothing to do
82
+ const name = new Date ( ) . toISOString ( ) ;
83
+ logger . debug (
84
+ "updateRollingSnapshots: creating snapshot of" ,
85
+ snapshots . subvolume . name ,
86
+ ) ;
87
+ try {
88
+ await snapshots . create ( name , opts ) ;
89
+ snapshotNames . push ( name ) ;
90
+ } catch ( err ) {
91
+ createError = err ;
92
+ }
93
+ }
94
+
77
95
// delete extra snapshots
78
- snapshotNames . push ( name ) ;
79
96
const toDelete = snapshotsToDelete ( { counts, snapshots : snapshotNames } ) ;
80
- for ( const expired of toDelete ) {
97
+ let deleteError : any = undefined ;
98
+ for ( const name of toDelete ) {
81
99
try {
82
- await snapshots . delete ( expired ) ;
83
- } catch {
84
- // some snapshots can't be deleted, e.g., they were used for the last send.
100
+ logger . debug (
101
+ "updateRollingSnapshots: deleting snapshot of" ,
102
+ snapshots . subvolume . name ,
103
+ name ,
104
+ ) ;
105
+ await snapshots . delete ( name ) ;
106
+ } catch ( err ) {
107
+ // ONLY report this if create doesn't error, to give both delete and create a chance to run.
108
+ deleteError = err ;
85
109
}
86
110
}
111
+
112
+ if ( createError ) {
113
+ throw createError ;
114
+ }
115
+ if ( deleteError ) {
116
+ throw deleteError ;
117
+ }
87
118
}
88
119
89
120
function snapshotsToDelete ( { counts, snapshots } ) : string [ ] {
0 commit comments