64818938 Andy McCall 1 month ago ↶
1
2
3
4
d02e545d Andy McCall 1 month ago ↶
5
64818938 Andy McCall 1 month ago ↶
6
7
8
9
10
11
12
13
14
15
d02e545d Andy McCall 1 month ago ↶
16
17
18
19
64818938 Andy McCall 1 month ago ↶
20
21
22
23
24
25
26
767ac6cd Andy McCall 17 days ago
27
28
ef7d4ea6 Andy McCall 16 days ago ↶
29
30
31
32
33
ff0af1ee unknown 1 month ago
34
5b9c6ad4 Andy McCall 1 month ago ↶
35
use crate :: attest:: Attest;
ff0af1ee unknown 1 month ago
36
use crate :: hash:: { hash_object, header, ObjectRef, ObjectType} ;
1983686f Andy McCall 1 month ago ↶
37
use crate :: identity:: { Identity, Signature} ;
6f8aeb99 Andy McCall 1 month ago ↶
38
use crate :: objects:: { Blob, Change, ObjectError, StateObject} ;
fc1a466a Andy McCall 1 month ago ↶
39
use crate :: receipt:: Receipt;
64818938 Andy McCall 1 month ago ↶
40
use crate :: strata:: { self , Error as BackendError, LocalStore, MemoryStore} ;
ff0af1ee unknown 1 month ago
41
use rand:: RngCore;
d02e545d Andy McCall 1 month ago ↶
42
use std:: cell:: RefCell;
ff0af1ee unknown 1 month ago
43
use std:: fs;
64818938 Andy McCall 1 month ago ↶
44
use std:: io:: { self , Write} ;
ff0af1ee unknown 1 month ago
45
use std:: path:: { Path, PathBuf} ;
64818938 Andy McCall 1 month ago ↶
46
use std:: sync:: Arc;
ff0af1ee unknown 1 month ago
47
d02e545d Andy McCall 1 month ago ↶
48
const PACK_MAGIC : & [ u8 ; 8 ] = b " OXYPACK1" ;
49
const IDX_MAGIC : & [ u8 ; 8 ] = b " OXYIDX01" ;
50
const IDX_ENTRY : usize = 32 + 8 + 4 + 4 + 1 + 1 ;
51
const FLAG_ZSTD : u8 = 1 ;
a17fc622 Andy McCall 1 month ago ↶
52
53
54
55
const FLAG_DICT : u8 = 2 ;
56
57
58
const DICT_BYTES : usize = 110 < < 10 ;
59
const DICT_MIN_OBJECTS : usize = 1000 ;
60
61
const DICT_OBJECT_MAX : usize = 64 < < 10 ;
d02e545d Andy McCall 1 month ago ↶
62
const ZSTD_LEVEL : i32 = 3 ;
da652d63 Andy McCall 1 month ago ↶
63
64
pub const REPACK_ZSTD_LEVEL : i32 = 9 ;
2b872d07 Andy McCall 1 month ago ↶
65
66
67
const MAX_OBJECT_LEN : u32 = 1 < < 30 ;
767ac6cd Andy McCall 17 days ago
68
69
70
71
const NO_DICT : & str = " packed with a dictionary the pack lacks" ;
d02e545d Andy McCall 1 month ago ↶
72
767ac6cd Andy McCall 17 days ago
73
74
75
76
77
78
79
80
81
const RETIRED_KEY : & str = " pack-retired" ;
82
83
84
85
86
pub const RETIRED_FOR : std:: time:: Duration = std:: time:: Duration:: from_secs( 60 * 60 ) ;
87
88
const RETIRED_TRIES : usize = 32 ;
89
90
91
92
93
const HAS_RELIST_AFTER : std:: time:: Duration = std:: time:: Duration:: from_secs( 1 ) ;
ef7d4ea6 Andy McCall 16 days ago ↶
94
95
96
97
98
99
100
101
pub const JOURNAL_KEY : & str = " pack-entries" ;
102
103
const LIST_SUFFIX : & str = " .entries" ;
104
105
const PART_SUFFIX : & str = " .entries-part" ;
106
107
const LIST_MAGIC : & str = " oxy-entries 1" ;
108
109
110
const LEGACY_FOLDED_KEY : & str = " pack-legacy-folded" ;
111
112
113
const JOURNAL_FOLD_AT : usize = 1000 ;
771661aa Andy McCall 16 days ago ↶
114
115
116
117
118
pub const ENTRIES_FRESH : std:: time:: Duration = std:: time:: Duration:: from_secs( 30 ) ;
ef7d4ea6 Andy McCall 16 days ago ↶
119
120
121
const ENTRY_INDEXES : [ ( & str , & str ) ; 6 ] = [
122
( " sig" , " sigs" ) ,
123
( " seal" , " seals" ) ,
124
( " receipt" , " receipts" ) ,
125
( " approval" , " approvals" ) ,
126
( " cert" , " certs" ) ,
127
( " accepted" , " accepted-lists" ) ,
128
] ;
767ac6cd Andy McCall 17 days ago
129
130
131
type Listed = Vec< ( String, u64 , Option< u64 > ) > ;
132
133
134
fn parse_retired ( bytes : & [u8 ]) -> std:: collections:: BTreeMap< String, u64 > {
135
String :: from_utf8_lossy( bytes)
136
.lines ( )
137
.filter_map ( | l | {
138
let ( t, k) = l.split_once ( ' ' ) ? ;
139
Some ( ( k.to_string ( ) , t.parse ( ) .ok ( ) ? ) )
140
} )
141
.collect ( )
142
}
143
144
fn encode_retired ( m : & std:: collections:: BTreeMap< String, u64 > ) -> Vec< u8 > {
145
m.iter ( )
146
.map ( | ( k , t ) | format! ( " {t} {k} \n " ) )
147
.collect:: < String> ( )
148
.into_bytes ( )
149
}
150
151
152
# [ derive ( Debug, Clone, Default, PartialEq, Eq ) ]
153
pub struct RetiredReport {
154
155
pub packs : usize ,
156
157
pub loose : usize ,
158
159
160
pub kept : usize ,
161
}
162
afaba5f1 Andy McCall 1 month ago ↶
163
164
pub type Object = ( ObjectType, Vec< u8 > ) ;
165
9ae4d15a Andy McCall 1 month ago ↶
166
167
168
pub type Progress < 'a > = & 'a mut dyn FnMut ( & str ) ;
64818938 Andy McCall 1 month ago ↶
169
170
171
pub type Backend = dyn strata:: Store;
9ae4d15a Andy McCall 1 month ago ↶
172
173
fn silent ( ) -> impl FnMut( & str ) {
174
| _ | { }
64818938 Andy McCall 1 month ago ↶
175
}
176
177
178
179
fn backend_err ( e : BackendError) -> StoreError {
180
match e {
181
BackendError:: Io( e) => StoreError:: Io( e) ,
182
other => StoreError:: Io( io:: Error:: other( other.to_string ( ) ) ) ,
183
}
184
}
185
186
187
fn put_new_or_existing ( backend : & Backend, key : & str , bytes : & [u8 ]) -> Result< ( ) > {
188
match backend.put_new ( key, bytes) {
189
Ok ( ( ) ) | Err ( BackendError:: Exists( _ ) ) => Ok ( ( ) ) ,
190
Err ( e) => Err ( backend_err ( e) ) ,
191
}
192
}
193
194
195
fn get_optional ( backend : & Backend, key : & str ) -> Result< Option< Vec< u8 > > > {
196
match backend.get ( key) {
197
Ok ( b) => Ok ( Some ( b) ) ,
198
Err ( BackendError:: NotFound( _ ) ) => Ok ( None ) ,
199
Err ( e) => Err ( backend_err ( e) ) ,
200
}
201
}
202
203
fn delete ( backend : & Backend, key : & str ) -> Result< ( ) > {
204
backend.delete ( key) .map_err ( backend_err)
205
}
206
207
208
fn loose_key ( r : & ObjectRef) -> String {
209
let h = r.to_hex ( ) ;
210
format! ( " loose/{} /{} " , & h[ .. 2 ] , & h[ 2 .. ] )
211
}
212
213
214
fn loose_ref ( key : & str ) -> Option< ObjectRef> {
215
let rest = key.strip_prefix ( " loose/" ) ? ;
216
let ( hh, tail) = rest.split_once ( ' /' ) ? ;
217
if hh.len ( ) ! = 2 | | tail.contains ( ' /' ) {
218
return None ;
219
}
220
ObjectRef:: from_hex( & format! ( " {hh} {tail} " ) )
221
}
222
223
224
fn fanout_prefix ( index : & str , r : & ObjectRef) -> String {
225
let h = r.to_hex ( ) ;
226
format! ( " {index} /{} /{} /" , & h[ .. 2 ] , & h[ 2 .. ] )
ef7d4ea6 Andy McCall 16 days ago ↶
227
}
228
229
230
fn is_entry_list ( key : & str ) -> bool {
231
key.starts_with ( " pack-" ) & & ( key.ends_with ( LIST_SUFFIX ) | | key.ends_with ( PART_SUFFIX ) )
232
}
233
234
235
236
fn encode_entry_list < 'a > ( keys : impl Iterator< Item = & 'a String> ) -> Result< Vec< u8 > > {
237
let mut text = String :: from( LIST_MAGIC ) ;
238
text.push ( ' \n ' ) ;
239
for k in keys {
240
text.push_str ( k) ;
241
text.push ( ' \n ' ) ;
242
}
243
Ok ( zstd:: bulk:: compress( text.as_bytes ( ) , ZSTD_LEVEL ) ? )
244
}
245
246
247
fn decode_entry_list ( key : & str , bytes : & [u8 ]) -> Result< Vec< String> > {
248
let bad = | why : & str | StoreError:: Io( io:: Error:: other( format! ( " {key} : {why} " ) ) ) ;
249
let text = zstd:: stream:: decode_all( bytes) .map_err ( | _| bad ( " not zstd" ) ) ? ;
250
let text = String :: from_utf8( text) .map_err ( | _| bad ( " not UTF-8" ) ) ? ;
251
let mut lines = text.lines ( ) ;
252
if lines.next ( ) ! = Some ( LIST_MAGIC ) {
253
return Err ( bad ( " not an entry list" ) ) ;
254
}
255
Ok ( lines
256
.filter ( | l | ! l.is_empty ( ) )
257
.map ( str :: to_string)
258
.collect ( ) )
64818938 Andy McCall 1 month ago ↶
259
}
260
ef7d4ea6 Andy McCall 16 days ago ↶
261
262
263
fn expire_folded ( m : & mut std:: collections:: BTreeMap< String, u64 > , now : u64 ) {
264
let hour = RETIRED_FOR .as_millis ( ) as u64 ;
265
m.retain ( | _, t | * t = = 0 | | now.saturating_sub ( * t) < hour) ;
266
}
267
64818938 Andy McCall 1 month ago ↶
268
269
fn legacy_key ( index : & str , r : & ObjectRef) -> String {
270
let h = r.to_hex ( ) ;
271
format! ( " {index} /{} /{} " , & h[ .. 2 ] , & h[ 2 .. ] )
9ae4d15a Andy McCall 1 month ago ↶
272
}
273
64818938 Andy McCall 1 month ago ↶
274
fn now_ms ( ) -> u64 {
275
std:: time:: SystemTime:: now( )
276
.duration_since ( std:: time:: UNIX_EPOCH )
277
.map ( | d | d.as_millis ( ) as u64 )
278
.unwrap_or ( 0 )
279
}
280
d02e545d Andy McCall 1 month ago ↶
281
# [ derive ( Clone, Copy, Debug ) ]
282
struct PackEntry {
283
offset : u64 ,
284
stored_len : u32 ,
285
raw_len : u32 ,
286
ty : ObjectType,
287
flags : u8 ,
288
}
289
290
291
struct PackIndex {
64818938 Andy McCall 1 month ago ↶
292
293
key : String,
d02e545d Andy McCall 1 month ago ↶
294
refs : Vec< ObjectRef> ,
295
entries : Vec< PackEntry> ,
a17fc622 Andy McCall 1 month ago ↶
296
64818938 Andy McCall 1 month ago ↶
297
dict : Option< Arc< zstd:: dict:: DecoderDictionary< 'static > > > ,
d02e545d Andy McCall 1 month ago ↶
298
}
299
a17fc622 Andy McCall 1 month ago ↶
300
impl std:: fmt:: Debug for PackIndex {
301
fn fmt ( & self , f : & mut std:: fmt:: Formatter< '_ > ) -> std:: fmt:: Result {
302
f.debug_struct ( " PackIndex" )
64818938 Andy McCall 1 month ago ↶
303
.field ( " key" , & self .key)
a17fc622 Andy McCall 1 month ago ↶
304
.field ( " objects" , & self .refs.len ( ) )
305
.field ( " dict" , & self .dict.is_some ( ) )
306
.finish ( )
64818938 Andy McCall 1 month ago ↶
307
}
308
}
309
310
311
fn decode_entry (
312
r : & ObjectRef,
313
e : PackEntry,
314
stored : Vec< u8 > ,
315
dict : Option< & Arc< zstd:: dict:: DecoderDictionary< 'static > > > ,
316
) -> Result< Object> {
317
let body = if e.flags & FLAG_DICT ! = 0 {
767ac6cd Andy McCall 17 days ago
318
let dict = dict.ok_or_else ( | | StoreError:: Corrupt( * r, NO_DICT .into ( ) ) ) ? ;
64818938 Andy McCall 1 month ago ↶
319
zstd:: bulk:: Decompressor:: with_prepared_dictionary( dict)
320
.and_then ( | mut d | d.decompress ( & stored, e.raw_len as usize ) )
321
.map_err ( | err | StoreError:: Corrupt( * r, format! ( " zstd: {err} " ) ) ) ?
322
} else if e.flags & FLAG_ZSTD ! = 0 {
323
zstd:: bulk:: decompress( & stored, e.raw_len as usize )
324
.map_err ( | err | StoreError:: Corrupt( * r, format! ( " zstd: {err} " ) ) ) ?
325
} else {
326
stored
327
} ;
328
if body.len ( ) ! = e.raw_len as usize | | hash_object ( e.ty, & body) ! = * r {
329
return Err ( StoreError:: Corrupt(
330
* r,
331
" packed object does not match its reference" .into ( ) ,
332
) ) ;
a17fc622 Andy McCall 1 month ago ↶
333
}
64818938 Andy McCall 1 month ago ↶
334
Ok ( ( e.ty, body) )
a17fc622 Andy McCall 1 month ago ↶
335
}
336
d02e545d Andy McCall 1 month ago ↶
337
impl PackIndex {
7e0e8e5a Andy McCall 17 days ago ↶
338
767ac6cd Andy McCall 17 days ago
339
340
341
fn load (
342
backend : & Backend,
343
idx_key : & str ,
344
pack_key : & str ,
345
pack_len : u64 ,
346
) -> Result< Option< PackIndex> > {
347
let data = match backend.get ( idx_key) {
348
Ok ( d) => d,
349
Err ( BackendError:: NotFound( _ ) ) => return Ok ( None ) ,
350
Err ( e) => return Err ( backend_err ( e) ) ,
351
} ;
64818938 Andy McCall 1 month ago ↶
352
let bad = | m : & str | StoreError:: Corrupt( ObjectRef:: ZERO , format! ( " {idx_key} : {m} " ) ) ;
d02e545d Andy McCall 1 month ago ↶
353
if data.len ( ) < 12 | | & data[ .. 8 ] ! = IDX_MAGIC {
354
return Err ( bad ( " bad index header" ) ) ;
355
}
356
let count = u32 :: from_le_bytes( data[ 8 .. 12 ] .try_into ( ) .unwrap ( ) ) as usize ;
357
if data.len ( ) ! = 12 + count * IDX_ENTRY {
358
return Err ( bad ( " index length mismatch" ) ) ;
359
}
360
let mut refs = Vec :: with_capacity( count) ;
361
let mut entries = Vec :: with_capacity( count) ;
362
for i in 0 .. count {
363
let e = & data[ 12 + i * IDX_ENTRY .. 12 + ( i + 1 ) * IDX_ENTRY ] ;
364
let r = ObjectRef:: from_slice( & e[ .. 32 ] ) .unwrap ( ) ;
365
if let Some ( prev) = refs.last ( ) {
366
if r < = * prev {
367
return Err ( bad ( " index not sorted" ) ) ;
368
}
369
}
370
refs.push ( r) ;
2b872d07 Andy McCall 1 month ago ↶
371
let entry = PackEntry {
d02e545d Andy McCall 1 month ago ↶
372
offset: u64 :: from_le_bytes( e[ 32 .. 40 ] .try_into ( ) .unwrap ( ) ) ,
373
stored_len: u32 :: from_le_bytes( e[ 40 .. 44 ] .try_into ( ) .unwrap ( ) ) ,
374
raw_len: u32 :: from_le_bytes( e[ 44 .. 48 ] .try_into ( ) .unwrap ( ) ) ,
75167672 Andy McCall 1 month ago
375
ty: ObjectType:: ALL
376
.get ( e[ 48 ] as usize )
377
.copied ( )
378
.ok_or_else ( | | bad ( " unknown object type" ) ) ? ,
d02e545d Andy McCall 1 month ago ↶
379
flags: e[ 49 ] ,
2b872d07 Andy McCall 1 month ago ↶
380
} ;
a9f686ed Andy McCall 1 month ago ↶
381
if entry.stored_len > MAX_OBJECT_LEN | | entry.raw_len > MAX_OBJECT_LEN {
382
return Err ( StoreError:: Corrupt(
383
r,
384
format! (
64818938 Andy McCall 1 month ago ↶
385
" {idx_key} : the index claims an object of {} bytes ({} stored), more than the {MAX_OBJECT_LEN} an object may be" ,
386
entry.raw_len, entry.stored_len
a9f686ed Andy McCall 1 month ago ↶
387
) ,
388
) ) ;
389
}
390
if entry.offset.saturating_add ( entry.stored_len as u64 ) > pack_len {
2b872d07 Andy McCall 1 month ago ↶
391
return Err ( bad ( " index entry points outside the pack" ) ) ;
392
}
393
entries.push ( entry) ;
d02e545d Andy McCall 1 month ago ↶
394
}
64818938 Andy McCall 1 month ago ↶
395
let dict_key = format! ( " {} .dict" , pack_key.trim_end_matches ( " .pack" ) ) ;
396
let dict = get_optional ( backend, & dict_key) ?
397
.map ( | d | Arc:: new( zstd:: dict:: DecoderDictionary:: copy( & d) ) ) ;
767ac6cd Andy McCall 17 days ago
398
Ok ( Some ( PackIndex {
64818938 Andy McCall 1 month ago ↶
399
key: pack_key.to_string ( ) ,
75167672 Andy McCall 1 month ago
400
refs,
401
entries,
a17fc622 Andy McCall 1 month ago ↶
402
dict,
767ac6cd Andy McCall 17 days ago
403
} ) )
d02e545d Andy McCall 1 month ago ↶
404
}
405
406
fn find ( & self , r : & ObjectRef) -> Option< PackEntry> {
407
self .refs.binary_search ( r) .ok ( ) .map ( | i | self .entries[ i] )
408
}
409
64818938 Andy McCall 1 month ago ↶
410
411
fn fetch ( & self , backend : & Backend, r : & ObjectRef, e : PackEntry) -> Result< Object> {
412
let stored = backend
413
.get_range ( & self .key, e.offset.. e.offset + e.stored_len as u64 )
414
.map_err ( | err | match err {
415
BackendError:: NotFound( _ ) => StoreError:: NotFound( * r) ,
416
other => backend_err ( other) ,
a17fc622 Andy McCall 1 month ago ↶
417
} ) ? ;
64818938 Andy McCall 1 month ago ↶
418
decode_entry ( r, e, stored, self .dict.as_ref ( ) )
d02e545d Andy McCall 1 month ago ↶
419
}
420
}
421
422
fn type_index ( ty : ObjectType) -> u8 {
423
ObjectType:: ALL .iter ( ) .position ( | t | * t = = ty) .unwrap ( ) as u8
424
}
425
426
427
# [ derive ( Debug, Clone, Default, PartialEq, Eq ) ]
428
pub struct PackReport {
429
pub objects : usize ,
430
pub raw_bytes : u64 ,
431
pub packed_bytes : u64 ,
432
}
433
135212de Andy McCall 1 month ago ↶
434
435
pub const MAX_PACKS : usize = 16 ;
436
ba160f60 Andy McCall 1 month ago ↶
437
438
# [ derive ( Debug, Clone, Default, PartialEq, Eq ) ]
439
pub struct PruneReport {
440
pub objects : usize ,
441
pub bytes : u64 ,
442
443
pub deferred : usize ,
444
}
445
ff0af1ee unknown 1 month ago
446
# [ derive ( Debug, thiserror::Error ) ]
447
pub enum StoreError {
448
# [ error ( " io: {0}" ) ]
449
Io( # [ from ] io:: Error) ,
450
# [ error ( " corrupt object {0}: {1}" ) ]
451
Corrupt( ObjectRef, String ) ,
452
# [ error ( " object {0}" ) ]
453
Object( # [ from ] ObjectError) ,
454
# [ error ( " object {0} is a {1}, expected {2}" ) ]
455
WrongType( ObjectRef, & 'static str , & 'static str ) ,
456
# [ error ( " object {0} not found" ) ]
457
NotFound( ObjectRef) ,
6f8aeb99 Andy McCall 1 month ago ↶
458
# [ error ( " a {kind} object of {size} bytes is larger than the {limit} bytes an object may be; nothing was stored. A change this large adds or touches too many files at once: commit it in parts (a directory at a time)" ) ]
a9f686ed Andy McCall 1 month ago ↶
459
TooLarge {
460
kind: & 'static str ,
461
size: usize ,
462
limit: u32 ,
463
} ,
ff0af1ee unknown 1 month ago
464
}
465
466
pub type Result < T> = std:: result:: Result< T, StoreError> ;
467
468
pub struct Store {
64818938 Andy McCall 1 month ago ↶
469
backend : Arc< Backend> ,
470
471
472
scratch : PathBuf,
473
474
local : bool ,
767ac6cd Andy McCall 17 days ago
475
packs : RefCell< Vec< Arc< PackIndex> > > ,
476
477
478
packs_listed : RefCell< Listed> ,
479
480
481
has_relisted : std:: cell:: Cell< Option< std:: time:: Instant> > ,
8f10a07a Andy McCall 1 month ago ↶
482
483
484
pending : RefCell< Option< Pending> > ,
485
batch_depth : std:: cell:: Cell< u32 > ,
ce14f0d6 Andy McCall 1 month ago ↶
486
487
metas : RefCell< Option< crate:: fasthash:: FastMap< ObjectRef, crate:: state:: ChangeMeta> > > ,
ef7d4ea6 Andy McCall 16 days ago ↶
488
489
490
entry_lists : RefCell< Vec< String> > ,
491
492
folded : std:: cell:: Cell< bool > ,
493
494
legacy_folded : std:: cell:: Cell< bool > ,
495
496
497
entries : RefCell< Option< std:: collections:: BTreeSet< String> > > ,
771661aa Andy McCall 16 days ago ↶
498
499
entries_read : std:: cell:: Cell< Option< std:: time:: Instant> > ,
500
501
502
lists_read : RefCell< std:: collections:: HashMap< String, Arc< Vec< String> > > > ,
ef7d4ea6 Andy McCall 16 days ago ↶
503
504
505
journal_queue : RefCell< Vec< String> > ,
8f10a07a Andy McCall 1 month ago ↶
506
}
507
64818938 Andy McCall 1 month ago ↶
508
impl std:: fmt:: Debug for Store {
509
fn fmt ( & self , f : & mut std:: fmt:: Formatter< '_ > ) -> std:: fmt:: Result {
510
f.debug_struct ( " Store" )
511
.field ( " backend" , & self .backend.describe ( ) )
512
.field ( " scratch" , & self .scratch)
513
.field ( " packs" , & self .packs.borrow ( ) .len ( ) )
514
.finish ( )
515
}
516
}
517
8f10a07a Andy McCall 1 month ago ↶
518
519
520
521
# [ derive ( Debug ) ]
522
struct Pending {
523
buffered : Vec< ( ObjectType, Vec< u8 > ) > ,
524
buffered_bytes : usize ,
525
lookup : std:: collections:: HashMap< ObjectRef, PendingEntry> ,
526
pack : Option< PendingPack> ,
527
report : PackReport,
ff0af1ee unknown 1 month ago
528
}
529
8f10a07a Andy McCall 1 month ago ↶
530
# [ derive ( Debug, Clone, Copy ) ]
531
enum PendingEntry {
532
Buffered( usize ) ,
533
Packed( PackEntry) ,
534
}
535
536
# [ derive ( Debug ) ]
537
struct PendingPack {
538
id : String,
539
tmp_path : PathBuf,
540
writer : io:: BufWriter< fs:: File> ,
541
offset : u64 ,
542
index : Vec< ( ObjectRef, PackEntry) > ,
543
}
544
545
const SPILL_OBJECTS : usize = 256 ;
546
const SPILL_BYTES : usize = 8 < < 20 ;
547
548
549
pub struct Batch < 'a > {
550
store : & 'a Store,
551
done : bool ,
552
}
553
554
impl Batch < '_ > {
555
pub fn finish ( mut self ) -> Result< ( ) > {
556
self .done = true ;
557
self .store.finish_batch ( )
558
}
559
}
560
561
impl Drop for Batch < '_ > {
562
fn drop ( & mut self ) {
563
if ! self .done {
564
let _ = self .store.finish_batch ( ) ;
565
}
566
}
567
}
568
192fad1e Andy McCall 1 month ago ↶
569
570
fn write_pack_entry (
571
pack : & mut PendingPack,
572
r : & ObjectRef,
573
ty : ObjectType,
574
raw_len : usize ,
575
flags : u8 ,
576
stored : & [u8 ],
577
report : & mut PackReport,
578
) -> Result< PackEntry> {
579
let header_len = 1 + 1 + 4 + 4 + 32 ;
580
pack.writer.write_all ( & [ type_index ( ty) , flags] ) ? ;
581
pack.writer
582
.write_all ( & ( stored.len ( ) as u32 ) .to_le_bytes ( ) ) ? ;
583
pack.writer.write_all ( & ( raw_len as u32 ) .to_le_bytes ( ) ) ? ;
584
pack.writer.write_all ( & r.0 ) ? ;
585
pack.writer.write_all ( stored) ? ;
586
let entry = PackEntry {
587
offset: pack.offset + header_len,
588
stored_len: stored.len ( ) as u32 ,
589
raw_len: raw_len as u32 ,
590
ty,
591
flags,
592
} ;
593
pack.offset + = header_len + stored.len ( ) as u64 ;
594
pack.index.push ( ( * r, entry) ) ;
595
report.objects + = 1 ;
596
report.raw_bytes + = raw_len as u64 ;
597
report.packed_bytes + = stored.len ( ) as u64 ;
598
Ok ( entry)
64818938 Andy McCall 1 month ago ↶
599
}
600
601
602
fn fresh_id ( ) -> String {
603
let mut nonce = [ 0 u8 ; 8 ] ;
604
rand:: rngs:: OsRng.fill_bytes ( & mut nonce) ;
605
hex:: encode( nonce)
606
}
607
608
609
610
611
612
fn memory_backend_for ( dir : & Path) -> Option< Arc< Backend> > {
613
use std:: collections:: HashMap;
614
use std:: sync:: { Mutex, OnceLock} ;
615
if std:: env:: var_os( " OXY_STORE_BACKEND" ) .as_deref ( ) ! = Some ( std:: ffi:: OsStr:: new( " memory" ) ) {
616
return None ;
617
}
618
static STORES : OnceLock< Mutex< HashMap< PathBuf, Arc< MemoryStore> > > > = OnceLock:: new( ) ;
544d0541 Andy McCall 10 days ago ↶
619
let key = crate :: portable:: canonical( dir) .unwrap_or_else ( | _| dir.to_path_buf ( ) ) ;
64818938 Andy McCall 1 month ago ↶
620
let mut map = STORES
621
.get_or_init ( Default :: default)
622
.lock ( )
623
.unwrap_or_else ( | p | p.into_inner ( ) ) ;
624
let store = map
625
.entry ( key)
626
.or_insert_with ( | | Arc:: new( MemoryStore:: new( ) ) )
627
.clone ( ) ;
628
Some ( store)
192fad1e Andy McCall 1 month ago ↶
629
}
630
dd21fd82 Andy McCall 1 month ago ↶
631
# [ cfg ( test ) ]
632
thread_local! {
633
634
635
636
pub ( crate ) static TEST_BACKEND : RefCell< Option< Arc< Backend> > > = const { RefCell:: new( None ) } ;
637
}
638
ff0af1ee unknown 1 month ago
639
impl Store {
64818938 Andy McCall 1 month ago ↶
640
ff0af1ee unknown 1 month ago
641
pub fn open ( dir : impl Into< PathBuf> ) -> io:: Result< Store> {
642
let dir = dir.into ( ) ;
643
fs:: create_dir_all( dir.join ( " loose" ) ) ? ;
dd21fd82 Andy McCall 1 month ago ↶
644
# [ cfg ( test ) ]
645
if let Some ( backend) = TEST_BACKEND .with ( | t | t.borrow ( ) .clone ( ) ) {
646
return Store:: open_on( backend, dir) ;
647
}
64818938 Andy McCall 1 month ago ↶
648
if let Some ( backend) = memory_backend_for ( & dir) {
649
return Store:: open_on( backend, dir) ;
650
}
651
let backend: Arc< Backend> =
652
Arc:: new( LocalStore:: open( & dir) .map_err ( | e | io:: Error:: other( e.to_string ( ) ) ) ? ) ;
653
let mut store = Store:: open_on( backend, dir) ? ;
654
store.local = true ;
655
Ok ( store)
656
}
657
658
659
660
pub fn open_on ( backend : Arc< Backend> , scratch : impl Into< PathBuf> ) -> io:: Result< Store> {
661
let scratch = scratch.into ( ) ;
662
fs:: create_dir_all( & scratch) ? ;
75167672 Andy McCall 1 month ago
663
let store = Store {
64818938 Andy McCall 1 month ago ↶
664
backend,
665
scratch,
666
local: false ,
75167672 Andy McCall 1 month ago
667
packs: RefCell:: new( Vec :: new( ) ) ,
767ac6cd Andy McCall 17 days ago
668
packs_listed: RefCell:: new( Vec :: new( ) ) ,
669
has_relisted: std:: cell:: Cell:: new( None ) ,
75167672 Andy McCall 1 month ago
670
pending: RefCell:: new( None ) ,
671
batch_depth: std:: cell:: Cell:: new( 0 ) ,
ce14f0d6 Andy McCall 1 month ago ↶
672
metas: RefCell:: new( None ) ,
ef7d4ea6 Andy McCall 16 days ago ↶
673
entry_lists: RefCell:: new( Vec :: new( ) ) ,
674
folded: std:: cell:: Cell:: new( false ) ,
675
legacy_folded: std:: cell:: Cell:: new( false ) ,
676
entries: RefCell:: new( None ) ,
771661aa Andy McCall 16 days ago ↶
677
entries_read: std:: cell:: Cell:: new( None ) ,
678
lists_read: RefCell:: new( Default :: default( ) ) ,
ef7d4ea6 Andy McCall 16 days ago ↶
679
journal_queue: RefCell:: new( Vec :: new( ) ) ,
75167672 Andy McCall 1 month ago
680
} ;
681
store
682
.reload_packs ( )
683
.map_err ( | e | io:: Error:: other( e.to_string ( ) ) ) ? ;
d02e545d Andy McCall 1 month ago ↶
684
Ok ( store)
64818938 Andy McCall 1 month ago ↶
685
}
686
687
688
pub fn backend ( & self ) -> & Arc< Backend> {
689
& self .backend
d02e545d Andy McCall 1 month ago ↶
690
}
691
64818938 Andy McCall 1 month ago ↶
692
693
694
pub fn is_local ( & self ) -> bool {
695
self .local
767ac6cd Andy McCall 17 days ago
696
}
697
698
699
700
701
pub fn refresh ( & self ) -> Result< ( ) > {
ef7d4ea6 Andy McCall 16 days ago ↶
702
* self .entries.borrow_mut ( ) = None ;
767ac6cd Andy McCall 17 days ago
703
self .reload_packs ( )
704
}
705
706
707
fn list_packs ( & self ) -> Result< ( Vec< strata:: Entry> , Listed) > {
708
let listed = self .backend.list ( " pack-" ) .map_err ( backend_err) ? ;
709
let sig = listed
710
.iter ( )
711
.map ( | e | ( e.key.clone ( ) , e.size, e.modified_ms) )
712
.collect ( ) ;
713
Ok ( ( listed, sig) )
64818938 Andy McCall 1 month ago ↶
714
}
715
767ac6cd Andy McCall 17 days ago
716
717
718
719
d02e545d Andy McCall 1 month ago ↶
720
fn reload_packs ( & self ) -> Result< ( ) > {
767ac6cd Andy McCall 17 days ago
721
let ( listed, sig) = self .list_packs ( ) ? ;
722
self .load_listed ( listed, sig)
723
}
724
725
fn load_listed ( & self , mut listed : Vec< strata:: Entry> , mut sig : Listed) -> Result< ( ) > {
726
let had: std:: collections:: HashMap< String, Arc< PackIndex> > = self
727
.packs
728
.borrow ( )
729
.iter ( )
730
.map ( | p | ( p.key.clone ( ) , p.clone ( ) ) )
731
.collect ( ) ;
732
let mut loaded: std:: collections:: HashMap< String, Arc< PackIndex> > = Default :: default( ) ;
733
for attempt in 0 .. 2 {
734
let retired = self .retired_in ( & listed) ? ;
735
let mut idx_keys: Vec< & str > = listed
736
.iter ( )
737
.map ( | e | e.key.as_str ( ) )
738
.filter ( | k | k.ends_with ( " .idx" ) )
739
.collect ( ) ;
740
idx_keys.sort ( ) ;
741
let mut packs = Vec :: new( ) ;
742
let mut vanished = false ;
743
for idx in idx_keys {
744
let pack = format! ( " {} .pack" , idx.trim_end_matches ( " .idx" ) ) ;
745
if retired.contains_key ( & pack) {
746
continue ;
747
}
748
let Some ( p) = listed.iter ( ) .find ( | e | e.key = = pack ) else {
749
continue ;
750
} ;
751
if let Some ( kept) = had.get ( & pack) .or_else ( | | loaded.get ( & pack) ) {
752
packs.push ( kept.clone ( ) ) ;
753
continue ;
754
}
755
match PackIndex:: load( self .backend.as_ref ( ) , idx, & pack, p.size) ? {
756
Some ( index) => {
757
let index = Arc:: new( index) ;
758
loaded.insert ( pack, index.clone ( ) ) ;
759
packs.push ( index) ;
760
}
761
None => vanished = true ,
762
}
763
}
764
if vanished & & attempt = = 0 {
765
( listed, sig) = self .list_packs ( ) ? ;
766
continue ;
767
}
ef7d4ea6 Andy McCall 16 days ago ↶
768
let lists: Vec< String> = listed
769
.iter ( )
770
.map ( | e | e.key.as_str ( ) )
771
.filter ( | k | is_entry_list ( k) & & ! retired.contains_key ( * k) )
772
.map ( str :: to_string)
773
.collect ( ) ;
774
self .folded
775
.set ( lists.iter ( ) .any ( | k | k.ends_with ( LIST_SUFFIX ) ) ) ;
776
self .legacy_folded
777
.set ( listed.iter ( ) .any ( | e | e.key = = LEGACY_FOLDED_KEY ) ) ;
778
* self .entry_lists.borrow_mut ( ) = lists;
779
if * self .packs_listed.borrow ( ) ! = sig {
780
* self .entries.borrow_mut ( ) = None ;
781
}
767ac6cd Andy McCall 17 days ago
782
* self .packs.borrow_mut ( ) = packs;
783
* self .packs_listed.borrow_mut ( ) = sig;
784
return Ok ( ( ) ) ;
785
}
786
unreachable! ( " the second round always returns" )
787
}
788
789
790
791
792
793
fn reload_if_changed ( & self ) -> Result< bool > {
794
let ( listed, sig) = self .list_packs ( ) ? ;
795
if * self .packs_listed.borrow ( ) = = sig {
796
return Ok ( false ) ;
797
}
798
self .load_listed ( listed, sig) ? ;
799
Ok ( true )
800
}
801
802
803
804
fn retired_in (
805
& self ,
806
listed : & [strata:: Entry],
807
) -> Result< std:: collections:: BTreeMap< String, u64 > > {
808
match listed.iter ( ) .find ( | e | e.key = = RETIRED_KEY ) {
809
Some ( e) if e.size > 0 => Ok ( self .read_retired ( ) ? .0 ) ,
810
_ => Ok ( Default :: default( ) ) ,
811
}
812
}
813
814
815
fn read_retired (
ef7d4ea6 Andy McCall 16 days ago ↶
816
& self ,
817
) -> Result< (
818
std:: collections:: BTreeMap< String, u64 > ,
819
Option< strata:: Version> ,
820
) > {
821
self .read_record ( RETIRED_KEY )
822
}
823
824
825
826
fn read_record (
767ac6cd Andy McCall 17 days ago
827
& self ,
ef7d4ea6 Andy McCall 16 days ago ↶
828
key : & str ,
767ac6cd Andy McCall 17 days ago
829
) -> Result< (
830
std:: collections:: BTreeMap< String, u64 > ,
831
Option< strata:: Version> ,
832
) > {
ef7d4ea6 Andy McCall 16 days ago ↶
833
match self .backend.read_ref ( key) .map_err ( backend_err) ? {
767ac6cd Andy McCall 17 days ago
834
Some ( ( bytes, v) ) => Ok ( ( parse_retired ( & bytes) , Some ( v) ) ) ,
835
None => Ok ( ( Default :: default( ) , None ) ) ,
836
}
837
}
838
839
840
841
842
fn update_retired (
ef7d4ea6 Andy McCall 16 days ago ↶
843
& self ,
844
edit : impl FnMut( &mut std ::collections ::BTreeMap <String , u64 > ) ,
845
) -> Result< ( ) > {
846
self .update_record ( RETIRED_KEY , " the record of retired packs" , edit)
847
}
848
849
850
851
852
fn update_record (
767ac6cd Andy McCall 17 days ago
853
& self ,
ef7d4ea6 Andy McCall 16 days ago ↶
854
key : & str ,
855
what : & str ,
767ac6cd Andy McCall 17 days ago
856
mut edit : impl FnMut( &mut std ::collections ::BTreeMap <String , u64 > ) ,
857
) -> Result< ( ) > {
858
for _ in 0 .. RETIRED_TRIES {
ef7d4ea6 Andy McCall 16 days ago ↶
859
let ( mut m, version) = self .read_record ( key) ? ;
767ac6cd Andy McCall 17 days ago
860
let before = m.clone ( ) ;
861
edit ( & mut m) ;
ef7d4ea6 Andy McCall 16 days ago ↶
862
863
if m = = before & & ( version.is_some ( ) | | m.is_empty ( ) ) {
767ac6cd Andy McCall 17 days ago
864
return Ok ( ( ) ) ;
865
}
866
match self
867
.backend
ef7d4ea6 Andy McCall 16 days ago ↶
868
.write_ref ( key, & encode_retired ( & m) , version.as_ref ( ) )
767ac6cd Andy McCall 17 days ago
869
{
870
Ok ( _ ) => return Ok ( ( ) ) ,
871
Err ( BackendError:: Conflict( _ ) ) => continue ,
872
Err ( e) => return Err ( backend_err ( e) ) ,
873
}
874
}
ef7d4ea6 Andy McCall 16 days ago ↶
875
Err ( StoreError:: Io( io:: Error:: other( format! (
876
" other writers kept changing {what} ; run the command again"
877
) ) ) )
767ac6cd Andy McCall 17 days ago
878
}
879
880
881
882
883
884
fn retire ( & self , keys : & [String]) -> Result< ( ) > {
885
if keys.is_empty ( ) {
886
return Ok ( ( ) ) ;
887
}
888
let now = now_ms ( ) ;
889
self .update_retired ( | m | {
890
for k in keys {
891
m.entry ( k.clone ( ) ) .or_insert ( now) ;
892
}
893
} )
894
}
895
896
897
898
899
900
901
pub fn expire_retired (
902
& self ,
903
older_than : std:: time:: Duration,
904
progress : Progress,
905
) -> Result< RetiredReport> {
906
let ( listed, _ ) = self .list_packs ( ) ? ;
907
let retired = self .retired_in ( & listed) ? ;
908
let mut report = RetiredReport:: default( ) ;
909
if retired.is_empty ( ) {
910
return Ok ( report) ;
911
}
912
let now = now_ms ( ) ;
913
let older_than = older_than.as_millis ( ) as u64 ;
914
let due: Vec< ( String, u64 ) > = retired
64818938 Andy McCall 1 month ago ↶
915
.iter ( )
767ac6cd Andy McCall 17 days ago
916
.filter ( | ( _, t ) | now.saturating_sub ( * * t) > = older_than )
917
.map ( | ( k , t ) | ( k.clone ( ) , * t) )
d02e545d Andy McCall 1 month ago ↶
918
.collect ( ) ;
767ac6cd Andy McCall 17 days ago
919
report.kept = retired.len ( ) - due.len ( ) ;
920
if due.is_empty ( ) {
921
return Ok ( report) ;
922
}
923
progress ( & format! ( " deleting {} retired files" , due.len ( ) ) ) ;
924
925
self .reload_packs ( ) ? ;
926
for ( key, _ ) in & due {
927
if key.ends_with ( " .pack" ) {
928
self .remove_pack ( key) ? ;
929
report.packs + = 1 ;
ef7d4ea6 Andy McCall 16 days ago ↶
930
} else if is_entry_list ( key) {
931
932
delete ( self .backend.as_ref ( ) , key) ? ;
767ac6cd Andy McCall 17 days ago
933
} else if let Some ( r) = loose_ref ( key) {
934
if self .find_packed ( & r) .is_some ( ) {
935
delete ( self .backend.as_ref ( ) , key) ? ;
936
report.loose + = 1 ;
937
}
d02e545d Andy McCall 1 month ago ↶
938
}
939
}
767ac6cd Andy McCall 17 days ago
940
self .update_retired ( | m | {
941
for ( k, t) in & due {
942
if m.get ( k) = = Some ( t) {
943
m.remove ( k) ;
944
}
945
}
946
} ) ? ;
947
self .reload_packs ( ) ? ;
948
Ok ( report)
d02e545d Andy McCall 1 month ago ↶
949
}
950
767ac6cd Andy McCall 17 days ago
951
952
fn retired_loose ( & self ) -> Result< std:: collections:: HashSet< String> > {
953
let ( listed, _ ) = self .list_packs ( ) ? ;
954
Ok ( self
955
.retired_in ( & listed) ?
956
.into_keys ( )
957
.filter ( | k | k.starts_with ( " loose/" ) )
958
.collect ( ) )
959
}
960
d02e545d Andy McCall 1 month ago ↶
961
pub fn pack_count ( & self ) -> usize {
962
self .packs.borrow ( ) .len ( )
963
}
964
965
fn find_packed ( & self , r : & ObjectRef) -> Option< ( usize , PackEntry) > {
75167672 Andy McCall 1 month ago
966
self .packs
967
.borrow ( )
968
.iter ( )
969
.enumerate ( )
970
.find_map ( | ( i , p ) | p.find ( r) .map ( | e | ( i, e) ) )
d02e545d Andy McCall 1 month ago ↶
971
}
972
767ac6cd Andy McCall 17 days ago
973
974
64818938 Andy McCall 1 month ago ↶
975
fn loose_objects ( & self ) -> Result< Vec< ( ObjectRef, strata:: Entry) > > {
767ac6cd Andy McCall 17 days ago
976
let retired = self .retired_loose ( ) ? ;
977
let mut out = self .loose_objects_all ( ) ? ;
978
out.retain ( | ( _, e ) | ! retired.contains ( & e.key) ) ;
979
Ok ( out)
980
}
981
982
983
fn loose_objects_all ( & self ) -> Result< Vec< ( ObjectRef, strata:: Entry) > > {
64818938 Andy McCall 1 month ago ↶
984
let mut out: Vec< ( ObjectRef, strata:: Entry) > = self
985
.backend
986
.list ( " loose/" )
987
.map_err ( backend_err) ?
988
.into_iter ( )
989
.filter_map ( | e | loose_ref ( & e.key) .map ( | r | ( r, e) ) )
990
.collect ( ) ;
991
out.sort_by_key ( | ( r , _) | * r) ;
d02e545d Andy McCall 1 month ago ↶
992
Ok ( out)
993
}
994
995
pub fn loose_count ( & self ) -> io:: Result< usize > {
64818938 Andy McCall 1 month ago ↶
996
self .loose_objects ( )
997
.map ( | l | l.len ( ) )
998
.map_err ( | e | io:: Error:: other( e.to_string ( ) ) )
d02e545d Andy McCall 1 month ago ↶
999
}
1000
64818938 Andy McCall 1 month ago ↶
1001
1002
d02e545d Andy McCall 1 month ago ↶
1003
pub fn pack_loose ( & self ) -> Result< PackReport> {
1004
let loose = self .loose_objects ( ) ? ;
75167672 Andy McCall 1 month ago
1005
self .write_pack (
1006
& loose.iter ( ) .map ( | ( r , _) | * r) .collect:: < Vec< _ > > ( ) ,
64818938 Andy McCall 1 month ago ↶
1007
& loose.iter ( ) .map ( | ( _, e ) | e.key.clone ( ) ) .collect:: < Vec< _ > > ( ) ,
75167672 Andy McCall 1 month ago
1008
& [ ] ,
da652d63 Andy McCall 1 month ago ↶
1009
ZSTD_LEVEL ,
9ae4d15a Andy McCall 1 month ago ↶
1010
& mut silent ( ) ,
75167672 Andy McCall 1 month ago
1011
)
da652d63 Andy McCall 1 month ago ↶
1012
}
1013
1014
1015
1016
1017
pub fn repack_all ( & self , level : i32 ) -> Result< ( PackReport, usize ) > {
9ae4d15a Andy McCall 1 month ago ↶
1018
self .repack_all_with ( level, & mut silent ( ) )
1019
}
1020
1021
1022
pub fn repack_all_with ( & self , level : i32 , progress : Progress) -> Result< ( PackReport, usize ) > {
da652d63 Andy McCall 1 month ago ↶
1023
let loose = self .loose_objects ( ) ? ;
a67914bc Andy McCall 18 days ago ↶
1024
let ( mut refs, keys, trained) : ( Vec< ObjectRef> , Vec< String> , bool ) = {
da652d63 Andy McCall 1 month ago ↶
1025
let packs = self .packs.borrow ( ) ;
1026
(
1027
packs.iter ( ) .flat_map ( | p | p.refs.iter ( ) .copied ( ) ) .collect ( ) ,
64818938 Andy McCall 1 month ago ↶
1028
packs.iter ( ) .map ( | p | p.key.clone ( ) ) .collect ( ) ,
a67914bc Andy McCall 18 days ago ↶
1029
packs.first ( ) .is_some_and ( | p | p.dict.is_some ( ) ) ,
da652d63 Andy McCall 1 month ago ↶
1030
)
1031
} ;
a67914bc Andy McCall 18 days ago ↶
1032
1033
1034
1035
1036
if loose.is_empty ( ) & & keys.len ( ) < = 1 & & ( level = = ZSTD_LEVEL | | trained) {
da652d63 Andy McCall 1 month ago ↶
1037
return Ok ( ( PackReport:: default( ) , 0 ) ) ;
1038
}
1039
refs.extend ( loose.iter ( ) .map ( | ( r , _) | * r) ) ;
64818938 Andy McCall 1 month ago ↶
1040
let loose_keys: Vec< String> = loose.into_iter ( ) .map ( | ( _, e ) | e.key ) .collect ( ) ;
1041
let n = keys.len ( ) ;
1042
let report = self .write_pack ( & refs, & loose_keys, & keys, level, progress) ? ;
da652d63 Andy McCall 1 month ago ↶
1043
Ok ( ( report, n) )
ba160f60 Andy McCall 1 month ago ↶
1044
}
1045
135212de Andy McCall 1 month ago ↶
1046
1047
1048
pub fn merge_packs ( & self ) -> Result< usize > {
64818938 Andy McCall 1 month ago ↶
1049
let ( refs, keys) : ( Vec< ObjectRef> , Vec< String> ) = {
135212de Andy McCall 1 month ago ↶
1050
let packs = self .packs.borrow ( ) ;
1051
if packs.len ( ) < = MAX_PACKS {
1052
return Ok ( 0 ) ;
1053
}
75167672 Andy McCall 1 month ago
1054
(
1055
packs.iter ( ) .flat_map ( | p | p.refs.iter ( ) .copied ( ) ) .collect ( ) ,
64818938 Andy McCall 1 month ago ↶
1056
packs.iter ( ) .map ( | p | p.key.clone ( ) ) .collect ( ) ,
75167672 Andy McCall 1 month ago
1057
)
135212de Andy McCall 1 month ago ↶
1058
} ;
64818938 Andy McCall 1 month ago ↶
1059
let n = keys.len ( ) ;
1060
self .write_pack ( & refs, & [ ] , & keys, ZSTD_LEVEL , & mut silent ( ) ) ? ;
135212de Andy McCall 1 month ago ↶
1061
Ok ( n)
1062
}
1063
ba160f60 Andy McCall 1 month ago ↶
1064
1065
1066
64818938 Andy McCall 1 month ago ↶
1067
1068
75167672 Andy McCall 1 month ago
1069
pub fn prune (
9ae4d15a Andy McCall 1 month ago ↶
1070
& self ,
1071
keep : & std:: collections:: HashSet< ObjectRef> ,
1072
grace : std:: time:: Duration,
1073
) -> Result< PruneReport> {
1074
self .prune_with ( keep, grace, & mut silent ( ) )
1075
}
1076
1077
1078
pub fn prune_with (
75167672 Andy McCall 1 month ago
1079
& self ,
1080
keep : & std:: collections:: HashSet< ObjectRef> ,
1081
grace : std:: time:: Duration,
9ae4d15a Andy McCall 1 month ago ↶
1082
progress : Progress,
75167672 Andy McCall 1 month ago
1083
) -> Result< PruneReport> {
9ae4d15a Andy McCall 1 month ago ↶
1084
progress ( " pruning loose objects" ) ;
64818938 Andy McCall 1 month ago ↶
1085
let now = now_ms ( ) ;
1086
let grace_ms = grace.as_millis ( ) as u64 ;
1087
1088
let old_enough =
1089
| modified : Option< u64 > | modified.is_none_or ( | t | now.saturating_sub ( t) > = grace_ms ) ;
ba160f60 Andy McCall 1 month ago ↶
1090
let mut report = PruneReport:: default( ) ;
ef7d4ea6 Andy McCall 16 days ago ↶
1091
let mut gone: Vec< String> = Vec :: new( ) ;
767ac6cd Andy McCall 17 days ago
1092
1093
1094
let retired = self .retired_loose ( ) ? ;
1095
let mut unretired: Vec< String> = Vec :: new( ) ;
1096
for ( r, entry) in self .loose_objects_all ( ) ? {
ba160f60 Andy McCall 1 month ago ↶
1097
if keep.contains ( & r) {
1098
continue ;
1099
}
767ac6cd Andy McCall 17 days ago
1100
if retired.contains ( & entry.key) {
1101
if old_enough ( entry.modified_ms) {
1102
delete ( self .backend.as_ref ( ) , & entry.key) ? ;
1103
unretired.push ( entry.key) ;
1104
}
1105
continue ;
1106
}
64818938 Andy McCall 1 month ago ↶
1107
if ! old_enough ( entry.modified_ms) {
ba160f60 Andy McCall 1 month ago ↶
1108
report.deferred + = 1 ;
1109
continue ;
1110
}
64818938 Andy McCall 1 month ago ↶
1111
report.bytes + = entry.size;
ba160f60 Andy McCall 1 month ago ↶
1112
report.objects + = 1 ;
64818938 Andy McCall 1 month ago ↶
1113
delete ( self .backend.as_ref ( ) , & entry.key) ? ;
ef7d4ea6 Andy McCall 16 days ago ↶
1114
self .remove_sidecars ( & r, & mut gone) ? ;
767ac6cd Andy McCall 17 days ago
1115
}
1116
if ! unretired.is_empty ( ) {
1117
self .update_retired ( | m | {
1118
for k in & unretired {
1119
m.remove ( k) ;
1120
}
1121
} ) ? ;
ba160f60 Andy McCall 1 month ago ↶
1122
}
1123
1124
let mut keep_refs: Vec< ObjectRef> = Vec :: new( ) ;
64818938 Andy McCall 1 month ago ↶
1125
let mut old_keys: Vec< String> = Vec :: new( ) ;
ba160f60 Andy McCall 1 month ago ↶
1126
let mut rewrite = false ;
9ae4d15a Andy McCall 1 month ago ↶
1127
progress ( " pruning packs" ) ;
ba160f60 Andy McCall 1 month ago ↶
1128
for p in self .packs.borrow ( ) .iter ( ) {
1129
let unreachable = p.refs.iter ( ) .filter ( | r | ! keep.contains ( r) ) .count ( ) ;
1130
if unreachable = = 0 {
1131
continue ;
1132
}
64818938 Andy McCall 1 month ago ↶
1133
let modified = self
1134
.backend
1135
.stat ( & p.key)
1136
.map_err ( backend_err) ?
1137
.and_then ( | e | e.modified_ms ) ;
1138
if ! old_enough ( modified) {
ba160f60 Andy McCall 1 month ago ↶
1139
report.deferred + = unreachable;
1140
continue ;
1141
}
1142
rewrite = true ;
64818938 Andy McCall 1 month ago ↶
1143
old_keys.push ( p.key.clone ( ) ) ;
ba160f60 Andy McCall 1 month ago ↶
1144
for ( r, e) in p.refs.iter ( ) .zip ( p.entries.iter ( ) ) {
1145
if keep.contains ( r) {
1146
keep_refs.push ( * r) ;
1147
} else {
1148
report.objects + = 1 ;
1149
report.bytes + = e.stored_len as u64 ;
1150
}
1151
}
1152
}
1153
if rewrite {
1154
let dropped: Vec< ObjectRef> = {
1155
let packs = self .packs.borrow ( ) ;
75167672 Andy McCall 1 month ago
1156
packs
1157
.iter ( )
64818938 Andy McCall 1 month ago ↶
1158
.filter ( | p | old_keys.contains ( & p.key) )
75167672 Andy McCall 1 month ago
1159
.flat_map ( | p | p.refs.iter ( ) .copied ( ) )
1160
.filter ( | r | ! keep.contains ( r) )
1161
.collect ( )
ba160f60 Andy McCall 1 month ago ↶
1162
} ;
64818938 Andy McCall 1 month ago ↶
1163
self .write_pack ( & keep_refs, & [ ] , & old_keys, ZSTD_LEVEL , progress) ? ;
ba160f60 Andy McCall 1 month ago ↶
1164
for r in dropped {
ef7d4ea6 Andy McCall 16 days ago ↶
1165
self .remove_sidecars ( & r, & mut gone) ? ;
ba160f60 Andy McCall 1 month ago ↶
1166
}
1167
}
ef7d4ea6 Andy McCall 16 days ago ↶
1168
self .journal_forget ( & gone) ? ;
ba160f60 Andy McCall 1 month ago ↶
1169
Ok ( report)
1170
}
1171
64818938 Andy McCall 1 month ago ↶
1172
1173
ef7d4ea6 Andy McCall 16 days ago ↶
1174
1175
fn remove_sidecars ( & self , r : & ObjectRef, gone : & mut Vec< String> ) -> Result< ( ) > {
64818938 Andy McCall 1 month ago ↶
1176
for index in [ " sigs" , " seals" , " receipts" ] {
1177
delete ( self .backend.as_ref ( ) , & legacy_key ( index, r) ) ? ;
1178
}
1179
for index in [ " sig" , " seal" , " receipt" ] {
1180
for e in self
1181
.backend
1182
.list ( & fanout_prefix ( index, r) )
1183
.map_err ( backend_err) ?
1184
{
1185
delete ( self .backend.as_ref ( ) , & e.key) ? ;
ef7d4ea6 Andy McCall 16 days ago ↶
1186
gone.push ( e.key) ;
ba160f60 Andy McCall 1 month ago ↶
1187
}
1188
}
1189
Ok ( ( ) )
1190
}
1191
ef7d4ea6 Andy McCall 16 days ago ↶
1192
1193
1194
fn journal_forget ( & self , gone : & [String]) -> Result< ( ) > {
1195
if self .local | | gone.is_empty ( ) {
1196
return Ok ( ( ) ) ;
1197
}
1198
if let Some ( set) = self .entries.borrow_mut ( ) .as_mut ( ) {
1199
for k in gone {
1200
set.remove ( k) ;
1201
}
1202
}
1203
self .update_record ( JOURNAL_KEY , " the journal of entries" , | m | {
1204
for k in gone {
1205
m.remove ( k) ;
1206
}
1207
} )
1208
}
1209
64818938 Andy McCall 1 month ago ↶
1210
1211
1212
75167672 Andy McCall 1 month ago
1213
fn finish_pack_files (
1214
& self ,
1215
id : & str ,
1216
tmp_pack : & Path,
1217
index : & [( ObjectRef , PackEntry ) ],
1218
) -> Result< ( ) > {
64818938 Andy McCall 1 month ago ↶
1219
let mut idx = Vec :: with_capacity( 12 + index.len ( ) * IDX_ENTRY ) ;
1220
idx.extend_from_slice ( IDX_MAGIC ) ;
1221
idx.extend_from_slice ( & ( index.len ( ) as u32 ) .to_le_bytes ( ) ) ;
1222
for ( r, e) in index {
1223
idx.extend_from_slice ( & r.0 ) ;
1224
idx.extend_from_slice ( & e.offset.to_le_bytes ( ) ) ;
1225
idx.extend_from_slice ( & e.stored_len.to_le_bytes ( ) ) ;
1226
idx.extend_from_slice ( & e.raw_len.to_le_bytes ( ) ) ;
1227
idx.extend_from_slice ( & [ type_index ( e.ty) , e.flags] ) ;
8f10a07a Andy McCall 1 month ago ↶
1228
}
64818938 Andy McCall 1 month ago ↶
1229
let put = self
1230
.backend
1231
.put_new_file ( & format! ( " pack-{id} .pack" ) , tmp_pack)
1232
.map_err ( backend_err) ;
1233
1234
1235
let _ = fs:: remove_file( tmp_pack) ;
1236
put? ;
1237
self .backend
1238
.put_new ( & format! ( " pack-{id} .idx" ) , & idx)
1239
.map_err ( backend_err) ? ;
8f10a07a Andy McCall 1 month ago ↶
1240
self .reload_packs ( )
64818938 Andy McCall 1 month ago ↶
1241
}
1242
767ac6cd Andy McCall 17 days ago
1243
1244
64818938 Andy McCall 1 month ago ↶
1245
fn remove_pack ( & self , pack_key : & str ) -> Result< ( ) > {
1246
let stem = pack_key.trim_end_matches ( " .pack" ) ;
767ac6cd Andy McCall 17 days ago
1247
delete ( self .backend.as_ref ( ) , & format! ( " {stem} .idx" ) ) ? ;
64818938 Andy McCall 1 month ago ↶
1248
delete ( self .backend.as_ref ( ) , pack_key) ? ;
1249
delete ( self .backend.as_ref ( ) , & format! ( " {stem} .dict" ) )
8f10a07a Andy McCall 1 month ago ↶
1250
}
1251
767ac6cd Andy McCall 17 days ago
1252
1253
1254
1255
75167672 Andy McCall 1 month ago
1256
fn write_pack (
1257
& self ,
1258
refs : & [ObjectRef],
64818938 Andy McCall 1 month ago ↶
1259
loose_keys : & [String],
1260
old_packs : & [String],
da652d63 Andy McCall 1 month ago ↶
1261
level : i32 ,
9ae4d15a Andy McCall 1 month ago ↶
1262
progress : Progress,
75167672 Andy McCall 1 month ago
1263
) -> Result< PackReport> {
ba160f60 Andy McCall 1 month ago ↶
1264
if refs.is_empty ( ) {
767ac6cd Andy McCall 17 days ago
1265
self .retire ( old_packs) ? ;
ba160f60 Andy McCall 1 month ago ↶
1266
self .reload_packs ( ) ? ;
d02e545d Andy McCall 1 month ago ↶
1267
return Ok ( PackReport:: default( ) ) ;
1268
}
ba160f60 Andy McCall 1 month ago ↶
1269
let mut refs: Vec< ObjectRef> = refs.to_vec ( ) ;
1270
refs.sort ( ) ;
1271
refs.dedup ( ) ;
64818938 Andy McCall 1 month ago ↶
1272
let id = fresh_id ( ) ;
1273
let tmp_pack = self .scratch.join ( format! ( " .tmp-{id} .pack" ) ) ;
d02e545d Andy McCall 1 month ago ↶
1274
let mut report = PackReport:: default( ) ;
ba160f60 Andy McCall 1 month ago ↶
1275
let mut index: Vec< ( ObjectRef, PackEntry) > = Vec :: with_capacity( refs.len ( ) ) ;
a17fc622 Andy McCall 1 month ago ↶
1276
1277
let dict = if level = = REPACK_ZSTD_LEVEL & & refs.len ( ) > = DICT_MIN_OBJECTS {
1278
let _t = crate :: timing:: phase( " pack: train a dictionary" ) ;
9ae4d15a Andy McCall 1 month ago ↶
1279
progress ( & format! ( " training a dictionary on {} objects" , refs.len ( ) ) ) ;
a17fc622 Andy McCall 1 month ago ↶
1280
self .train_dictionary ( & refs)
1281
} else {
1282
None
1283
} ;
1284
let encoder = dict
1285
.as_ref ( )
1286
.map ( | d | zstd:: dict:: EncoderDictionary:: copy( d, level) ) ;
64818938 Andy McCall 1 month ago ↶
1287
let nocompress = self .nocompress_set ( ) ? ;
d02e545d Andy McCall 1 month ago ↶
1288
{
1289
let mut f = io:: BufWriter:: new( fs:: File:: create( & tmp_pack) ? ) ;
1290
f.write_all ( PACK_MAGIC ) ? ;
1291
let mut offset = PACK_MAGIC .len ( ) as u64 ;
da652d63 Andy McCall 1 month ago ↶
1292
1293
1294
let mut batches = refs.chunks ( 1024 ) ;
1295
let mut compressed_batch = Vec :: new( ) ;
9ae4d15a Andy McCall 1 month ago ↶
1296
let total = refs.len ( ) ;
1297
let mut reported = usize :: MAX ;
da652d63 Andy McCall 1 month ago ↶
1298
loop {
1299
if compressed_batch.is_empty ( ) {
1300
let Some ( batch) = batches.next ( ) else { break } ;
9ae4d15a Andy McCall 1 month ago ↶
1301
1302
let tenth = report.objects * 10 / total.max ( 1 ) ;
1303
if tenth ! = reported {
1304
reported = tenth;
1305
progress ( & format! ( " packing {} of {total} objects" , report.objects) ) ;
1306
}
da652d63 Andy McCall 1 month ago ↶
1307
let mut read: Vec< ( ObjectRef, ObjectType, Vec< u8 > , bool ) > =
1308
Vec :: with_capacity( batch.len ( ) ) ;
1309
for r in batch {
1310
let ( ty, body) = self .get ( r) ? ;
64818938 Andy McCall 1 month ago ↶
1311
read.push ( ( * r, ty, body, nocompress.contains ( r) ) ) ;
75167672 Andy McCall 1 month ago
1312
}
da652d63 Andy McCall 1 month ago ↶
1313
use rayon:: prelude:: * ;
1314
compressed_batch = read
1315
.into_par_iter ( )
a17fc622 Andy McCall 1 month ago ↶
1316
.map_init (
1317
| | {
1318
encoder.as_ref ( ) .and_then ( | e | {
1319
zstd:: bulk:: Compressor:: with_prepared_dictionary( e) .ok ( )
1320
} )
1321
} ,
1322
| with_dict , ( r , ty , body , raw ) | {
1323
let packed = if raw {
1324
None
1325
} else {
1326
1327
1328
let dicted = with_dict
1329
.as_mut ( )
1330
.filter ( | _| body.len ( ) < = DICT_OBJECT_MAX )
1331
.and_then ( | c | c.compress ( & body) .ok ( ) )
1332
.map ( | c | ( FLAG_ZSTD | FLAG_DICT , c) ) ;
1333
dicted
1334
.or_else ( | | {
1335
zstd:: bulk:: compress( & body, level)
1336
.ok ( )
1337
.map ( | c | ( FLAG_ZSTD , c) )
1338
} )
1339
.filter ( | ( _, c ) | c.len ( ) < body.len ( ) )
1340
} ;
1341
( r, ty, body, packed)
1342
} ,
1343
)
da652d63 Andy McCall 1 month ago ↶
1344
.collect:: < Vec< _ > > ( ) ;
1345
compressed_batch.reverse ( ) ;
1346
}
1347
let Some ( ( r, ty, body, packed) ) = compressed_batch.pop ( ) else {
1348
continue ;
1349
} ;
1350
let r = & r;
1351
let ( flags, stored) = match packed {
a17fc622 Andy McCall 1 month ago ↶
1352
Some ( c) => c,
da652d63 Andy McCall 1 month ago ↶
1353
None => ( 0 , body.clone ( ) ) ,
cdb5beba Andy McCall 1 month ago ↶
1354
} ;
d02e545d Andy McCall 1 month ago ↶
1355
let header_len = 1 + 1 + 4 + 4 + 32 ;
1356
f.write_all ( & [ type_index ( ty) , flags] ) ? ;
1357
f.write_all ( & ( stored.len ( ) as u32 ) .to_le_bytes ( ) ) ? ;
1358
f.write_all ( & ( body.len ( ) as u32 ) .to_le_bytes ( ) ) ? ;
1359
f.write_all ( & r.0 ) ? ;
1360
f.write_all ( & stored) ? ;
75167672 Andy McCall 1 month ago
1361
index.push ( (
1362
* r,
1363
PackEntry {
1364
offset: offset + header_len,
1365
stored_len: stored.len ( ) as u32 ,
1366
raw_len: body.len ( ) as u32 ,
1367
ty,
1368
flags,
1369
} ,
1370
) ) ;
d02e545d Andy McCall 1 month ago ↶
1371
offset + = header_len + stored.len ( ) as u64 ;
1372
report.objects + = 1 ;
1373
report.raw_bytes + = body.len ( ) as u64 ;
1374
report.packed_bytes + = stored.len ( ) as u64 ;
1375
}
1376
f.flush ( ) ? ;
1377
f.get_ref ( ) .sync_all ( ) ? ;
a17fc622 Andy McCall 1 month ago ↶
1378
}
1379
1380
if let Some ( d) = & dict {
64818938 Andy McCall 1 month ago ↶
1381
self .backend
1382
.put_new ( & format! ( " pack-{id} .dict" ) , d)
1383
.map_err ( backend_err) ? ;
d02e545d Andy McCall 1 month ago ↶
1384
}
9ae4d15a Andy McCall 1 month ago ↶
1385
progress ( & format! ( " writing the index of {} objects" , index.len ( ) ) ) ;
8f10a07a Andy McCall 1 month ago ↶
1386
self .finish_pack_files ( & id, & tmp_pack, & index) ? ;
767ac6cd Andy McCall 17 days ago
1387
let mut retired = old_packs.to_vec ( ) ;
1388
retired.extend_from_slice ( loose_keys) ;
1389
self .retire ( & retired) ? ;
d02e545d Andy McCall 1 month ago ↶
1390
self .reload_packs ( ) ? ;
1391
Ok ( report)
ff0af1ee unknown 1 month ago
1392
}
1393
a17fc622 Andy McCall 1 month ago ↶
1394
1395
1396
fn train_dictionary ( & self , refs : & [ObjectRef]) -> Option< Vec< u8 > > {
1397
let step = ( refs.len ( ) / 10_000 ) .max ( 1 ) ;
1398
let mut samples: Vec< Vec< u8 > > = Vec :: new( ) ;
1399
let mut total = 0 usize ;
1400
for r in refs.iter ( ) .step_by ( step) {
1401
if total > 32 < < 20 {
1402
break ;
1403
}
1404
let Ok ( ( ty, body) ) = self .get ( r) else {
1405
continue ;
1406
} ;
1407
if matches! (
1408
ty,
1409
ObjectType:: Chunk | ObjectType:: Blob | ObjectType:: Change
1410
) & & ! body.is_empty ( )
1411
& & body.len ( ) < 1 < < 20
1412
{
1413
total + = body.len ( ) ;
1414
samples.push ( body) ;
1415
}
1416
}
1417
zstd:: dict:: from_samples( & samples, DICT_BYTES ) .ok ( )
1418
}
1419
64818938 Andy McCall 1 month ago ↶
1420
ff0af1ee unknown 1 month ago
1421
pub fn dir ( & self ) -> & Path {
64818938 Andy McCall 1 month ago ↶
1422
& self .scratch
ff0af1ee unknown 1 month ago
1423
}
1424
64818938 Andy McCall 1 month ago ↶
1425
1426
1427
# [ cfg ( test ) ]
387335ff Andy McCall 1 month ago ↶
1428
pub ( crate ) fn path_for ( & self , r : & ObjectRef) -> PathBuf {
64818938 Andy McCall 1 month ago ↶
1429
self .scratch.join ( loose_key ( r) )
1430
}
1431
1432
1433
1434
# [ cfg ( test ) ]
1435
pub ( crate ) fn remove_loose_for_test ( & self , r : & ObjectRef) -> Result< ( ) > {
1436
delete ( self .backend.as_ref ( ) , & loose_key ( r) )
ff0af1ee unknown 1 month ago
1437
}
1438
767ac6cd Andy McCall 17 days ago
1439
1440
1441
1442
ff0af1ee unknown 1 month ago
1443
pub fn has ( & self , r : & ObjectRef) -> bool {
767ac6cd Andy McCall 17 days ago
1444
if self .has_now ( r) {
1445
return true ;
1446
}
1447
let recent = self
1448
.has_relisted
1449
.get ( )
1450
.is_some_and ( | t | t.elapsed ( ) < HAS_RELIST_AFTER ) ;
1451
if recent {
1452
return false ;
1453
}
1454
match self .reload_if_changed ( ) {
1455
Ok ( true ) => {
1456
self .has_relisted.set ( None ) ;
1457
self .has_now ( r)
1458
}
1459
_ => {
1460
self .has_relisted.set ( Some ( std:: time:: Instant:: now( ) ) ) ;
1461
false
1462
}
1463
}
1464
}
1465
1466
1467
fn has_now ( & self , r : & ObjectRef) -> bool {
1468
self .find_packed ( r) .is_some ( )
75167672 Andy McCall 1 month ago
1469
| | self
1470
.pending
1471
.borrow ( )
1472
.as_ref ( )
1473
.is_some_and ( | p | p.lookup.contains_key ( r) )
767ac6cd Andy McCall 17 days ago
1474
| | self .backend.has ( & loose_key ( r) ) .unwrap_or ( false )
ff0af1ee unknown 1 month ago
1475
}
1476
8f10a07a Andy McCall 1 month ago ↶
1477
1478
1479
1480
1481
pub fn begin_batch ( & self ) -> Result< Batch< '_ > > {
1482
let depth = self .batch_depth.get ( ) ;
1483
if depth = = 0 {
75167672 Andy McCall 1 month ago
1484
* self .pending.borrow_mut ( ) = Some ( Pending {
1485
buffered: vec! [ ] ,
1486
buffered_bytes: 0 ,
1487
lookup: Default :: default( ) ,
1488
pack: None ,
1489
report: PackReport:: default( ) ,
1490
} ) ;
8f10a07a Andy McCall 1 month ago ↶
1491
}
1492
self .batch_depth.set ( depth + 1 ) ;
75167672 Andy McCall 1 month ago
1493
Ok ( Batch {
1494
store: self ,
1495
done: false ,
1496
} )
8f10a07a Andy McCall 1 month ago ↶
1497
}
1498
1499
fn finish_batch ( & self ) -> Result< ( ) > {
1500
let depth = self .batch_depth.get ( ) ;
1501
if depth = = 0 {
1502
return Ok ( ( ) ) ;
1503
}
1504
self .batch_depth.set ( depth - 1 ) ;
1505
if depth > 1 {
1506
return Ok ( ( ) ) ;
1507
}
75167672 Andy McCall 1 month ago
1508
let Some ( pending) = self .pending.borrow_mut ( ) .take ( ) else {
ef7d4ea6 Andy McCall 16 days ago ↶
1509
return self .flush_journal ( ) ;
75167672 Andy McCall 1 month ago
1510
} ;
ef7d4ea6 Andy McCall 16 days ago ↶
1511
self .write_pending ( pending) ? ;
1512
self .flush_journal ( )
4dcb8c09 Andy McCall 1 month ago ↶
1513
}
1514
1515
1516
1517
1518
pub fn flush ( & self ) -> Result< ( ) > {
1519
let Some ( pending) = self .pending.borrow_mut ( ) .take ( ) else {
ef7d4ea6 Andy McCall 16 days ago ↶
1520
return self .flush_journal ( ) ;
4dcb8c09 Andy McCall 1 month ago ↶
1521
} ;
1522
self .write_pending ( pending) ? ;
ef7d4ea6 Andy McCall 16 days ago ↶
1523
self .flush_journal ( ) ? ;
4dcb8c09 Andy McCall 1 month ago ↶
1524
* self .pending.borrow_mut ( ) = Some ( Pending {
1525
buffered: vec! [ ] ,
1526
buffered_bytes: 0 ,
1527
lookup: Default :: default( ) ,
1528
pack: None ,
1529
report: PackReport:: default( ) ,
1530
} ) ;
1531
Ok ( ( ) )
1532
}
1533
1534
fn write_pending ( & self , pending : Pending) -> Result< ( ) > {
8f10a07a Andy McCall 1 month ago ↶
1535
match pending.pack {
1536
None => {
b30a880f Andy McCall 1 month ago ↶
1537
let objects: Vec< ( ObjectType, & [u8 ]) > = pending
1538
.buffered
1539
.iter ( )
1540
.map ( | ( ty , body ) | ( * ty, body.as_slice ( ) ) )
1541
.collect ( ) ;
1542
self .put_loose_many ( & objects)
8f10a07a Andy McCall 1 month ago ↶
1543
}
1544
Some ( mut pack) => {
1545
pack.writer.flush ( ) ? ;
1546
pack.writer.get_ref ( ) .sync_all ( ) ? ;
1547
drop ( pack.writer) ;
1548
pack.index.sort_by_key ( | ( r , _) | * r) ;
1549
self .finish_pack_files ( & pack.id, & pack.tmp_path, & pack.index) ? ;
1550
Ok ( ( ) )
1551
}
1552
}
1553
}
1554
1555
64818938 Andy McCall 1 month ago ↶
1556
fn pending_get ( & self , r : & ObjectRef) -> Option< Result< Object> > {
8f10a07a Andy McCall 1 month ago ↶
1557
let mut guard = self .pending.borrow_mut ( ) ;
1558
let pending = guard.as_mut ( ) ? ;
1559
let entry = * pending.lookup.get ( r) ? ;
1560
Some ( match entry {
1561
PendingEntry:: Buffered( i) => {
1562
let ( ty, body) = & pending.buffered[ i] ;
1563
Ok ( ( * ty, body.clone ( ) ) )
1564
}
1565
PendingEntry:: Packed( e) => {
75167672 Andy McCall 1 month ago
1566
let Some ( pack) = pending.pack.as_mut ( ) else {
1567
return Some ( Err ( StoreError:: NotFound( * r) ) ) ;
1568
} ;
64818938 Andy McCall 1 month ago ↶
1569
1570
( | | {
1571
pack.writer.flush ( ) ? ;
1572
use std:: io:: { Read, Seek, SeekFrom} ;
1573
let mut f = fs:: File:: open( & pack.tmp_path) ? ;
1574
f.seek ( SeekFrom:: Start( e.offset) ) ? ;
1575
let mut stored = vec! [ 0 u8 ; e.stored_len as usize ] ;
1576
f.read_exact ( & mut stored) ? ;
1577
decode_entry ( r, e, stored, None )
1578
} ) ( )
8f10a07a Andy McCall 1 month ago ↶
1579
}
1580
} )
1581
}
1582
1583
75167672 Andy McCall 1 month ago
1584
fn pending_pack_write (
1585
& self ,
1586
pack : & mut PendingPack,
1587
r : & ObjectRef,
1588
ty : ObjectType,
1589
body : & [u8 ],
1590
report : & mut PackReport,
1591
) -> Result< PackEntry> {
8f10a07a Andy McCall 1 month ago ↶
1592
let ( flags, stored) = if self .is_nocompress ( r) {
1593
( 0 , body.to_vec ( ) )
1594
} else {
75167672 Andy McCall 1 month ago
1595
let compressed = zstd:: bulk:: compress( body, ZSTD_LEVEL )
1596
.map_err ( | e | StoreError:: Corrupt( * r, format! ( " zstd: {e} " ) ) ) ? ;
1597
if compressed.len ( ) < body.len ( ) {
1598
( FLAG_ZSTD , compressed)
1599
} else {
1600
( 0 , body.to_vec ( ) )
1601
}
8f10a07a Andy McCall 1 month ago ↶
1602
} ;
192fad1e Andy McCall 1 month ago ↶
1603
write_pack_entry ( pack, r, ty, body.len ( ) , flags, & stored, report)
8f10a07a Andy McCall 1 month ago ↶
1604
}
1605
1606
fn put_pending ( & self , r : ObjectRef, ty : ObjectType, body : & [u8 ]) -> Result< ( ) > {
1607
let mut guard = self .pending.borrow_mut ( ) ;
1608
let pending = guard.as_mut ( ) .expect ( " batch open" ) ;
1609
if pending.lookup.contains_key ( & r) {
1610
return Ok ( ( ) ) ;
1611
}
75167672 Andy McCall 1 month ago
1612
if pending.pack.is_none ( )
1613
& & pending.buffered.len ( ) < SPILL_OBJECTS
1614
& & pending.buffered_bytes + body.len ( ) < = SPILL_BYTES
1615
{
8f10a07a Andy McCall 1 month ago ↶
1616
pending.buffered.push ( ( ty, body.to_vec ( ) ) ) ;
1617
pending.buffered_bytes + = body.len ( ) ;
75167672 Andy McCall 1 month ago
1618
pending
1619
.lookup
1620
.insert ( r, PendingEntry:: Buffered( pending.buffered.len ( ) - 1 ) ) ;
8f10a07a Andy McCall 1 month ago ↶
1621
return Ok ( ( ) ) ;
1622
}
192fad1e Andy McCall 1 month ago ↶
1623
self .ensure_pack ( pending) ? ;
1624
let mut report = std:: mem:: take( & mut pending.report) ;
1625
let pack = pending.pack.as_mut ( ) .unwrap ( ) ;
1626
let e = self .pending_pack_write ( pack, & r, ty, body, & mut report) ? ;
1627
pending.report = report;
1628
pending.lookup.insert ( r, PendingEntry:: Packed( e) ) ;
1629
Ok ( ( ) )
1630
}
1631
1632
1633
1634
fn ensure_pack ( & self , pending : & mut Pending) -> Result< ( ) > {
8f10a07a Andy McCall 1 month ago ↶
1635
if pending.pack.is_none ( ) {
1636
64818938 Andy McCall 1 month ago ↶
1637
let id = fresh_id ( ) ;
1638
let tmp_path = self .scratch.join ( format! ( " .tmp-{id} .pack" ) ) ;
8f10a07a Andy McCall 1 month ago ↶
1639
let mut writer = io:: BufWriter:: new( fs:: File:: create( & tmp_path) ? ) ;
1640
writer.write_all ( PACK_MAGIC ) ? ;
75167672 Andy McCall 1 month ago
1641
let mut pack = PendingPack {
1642
id,
1643
tmp_path,
1644
writer,
1645
offset: PACK_MAGIC .len ( ) as u64 ,
1646
index: vec! [ ] ,
1647
} ;
8f10a07a Andy McCall 1 month ago ↶
1648
let buffered = std:: mem:: take( & mut pending.buffered) ;
1649
pending.buffered_bytes = 0 ;
1650
let mut report = std:: mem:: take( & mut pending.report) ;
1651
for ( bty, bbody) in & buffered {
1652
let br = hash_object ( * bty, bbody) ;
1653
let e = self .pending_pack_write ( & mut pack, & br, * bty, bbody, & mut report) ? ;
1654
pending.lookup.insert ( br, PendingEntry:: Packed( e) ) ;
1655
}
1656
pending.report = report;
1657
pending.pack = Some ( pack) ;
192fad1e Andy McCall 1 month ago ↶
1658
}
1659
Ok ( ( ) )
1660
}
1661
1662
1663
1664
1665
pub fn put_many ( & self , objects : & [( ObjectType , &[u8 ]) ]) -> Result< Vec< ObjectRef> > {
1666
use rayon:: prelude:: * ;
1667
let total: usize = objects.iter ( ) .map ( | ( _, b ) | b.len ( ) ) .sum ( ) ;
1668
if self .pending.borrow ( ) .is_none ( ) | | total < SPILL_BYTES {
1669
return objects.iter ( ) .map ( | ( ty , b ) | self .put ( * ty, b) ) .collect ( ) ;
1670
}
1671
for ( ty, b) in objects {
1672
if b.len ( ) > MAX_OBJECT_LEN as usize {
1673
return Err ( StoreError:: TooLarge {
1674
kind: ty.name ( ) ,
1675
size: b.len ( ) ,
1676
limit: MAX_OBJECT_LEN ,
1677
} ) ;
1678
}
8f10a07a Andy McCall 1 month ago ↶
1679
}
192fad1e Andy McCall 1 month ago ↶
1680
let refs: Vec< ObjectRef> = objects
1681
.par_iter ( )
1682
.map ( | ( ty , b ) | hash_object ( * ty, b) )
1683
.collect ( ) ;
1684
1685
let mut seen: std:: collections:: HashSet< ObjectRef> = std:: collections:: HashSet:: new( ) ;
1686
let mut todo: Vec< usize > = Vec :: new( ) ;
1687
for ( i, r) in refs.iter ( ) .enumerate ( ) {
1688
let known = self
1689
.pending
1690
.borrow ( )
1691
.as_ref ( )
1692
.is_some_and ( | p | p.lookup.contains_key ( r) ) ;
1693
if seen.insert ( * r)
1694
& & ! known
1695
& & self .find_packed ( r) .is_none ( )
64818938 Andy McCall 1 month ago ↶
1696
& & ! self .backend.has ( & loose_key ( r) ) .unwrap_or ( false )
192fad1e Andy McCall 1 month ago ↶
1697
{
1698
todo.push ( i) ;
1699
}
1700
}
64818938 Andy McCall 1 month ago ↶
1701
let raw = self .nocompress_set ( ) ? ;
192fad1e Andy McCall 1 month ago ↶
1702
let packed: Vec< ( u8 , Vec< u8 > ) > = todo
1703
.par_iter ( )
1704
.map ( | i | {
1705
let body = objects[ * i] .1 ;
1706
if raw.contains ( & refs[ * i] ) {
1707
return ( 0 , body.to_vec ( ) ) ;
1708
}
1709
match zstd:: bulk:: compress( body, ZSTD_LEVEL ) {
1710
Ok ( c) if c.len ( ) < body.len ( ) => ( FLAG_ZSTD , c) ,
1711
_ => ( 0 , body.to_vec ( ) ) ,
1712
}
1713
} )
1714
.collect ( ) ;
1715
let mut guard = self .pending.borrow_mut ( ) ;
1716
let pending = guard.as_mut ( ) .expect ( " batch open" ) ;
1717
self .ensure_pack ( pending) ? ;
8f10a07a Andy McCall 1 month ago ↶
1718
let mut report = std:: mem:: take( & mut pending.report) ;
1719
let pack = pending.pack.as_mut ( ) .unwrap ( ) ;
192fad1e Andy McCall 1 month ago ↶
1720
for ( i, ( flags, stored) ) in todo.iter ( ) .zip ( packed) {
1721
let ( ty, body) = objects[ * i] ;
1722
let e = write_pack_entry ( pack, & refs[ * i] , ty, body.len ( ) , flags, & stored, & mut report) ? ;
1723
pending.lookup.insert ( refs[ * i] , PendingEntry:: Packed( e) ) ;
1724
}
8f10a07a Andy McCall 1 month ago ↶
1725
pending.report = report;
192fad1e Andy McCall 1 month ago ↶
1726
Ok ( refs)
8f10a07a Andy McCall 1 month ago ↶
1727
}
1728
ff0af1ee unknown 1 month ago
1729
1730
pub fn put ( & self , ty : ObjectType, body : & [u8 ]) -> Result< ObjectRef> {
a9f686ed Andy McCall 1 month ago ↶
1731
1732
1733
1734
if body.len ( ) > MAX_OBJECT_LEN as usize {
1735
return Err ( StoreError:: TooLarge {
1736
kind: ty.name ( ) ,
1737
size: body.len ( ) ,
1738
limit: MAX_OBJECT_LEN ,
1739
} ) ;
1740
}
ff0af1ee unknown 1 month ago
1741
let r = hash_object ( ty, body) ;
64818938 Andy McCall 1 month ago ↶
1742
if self .find_packed ( & r) .is_some ( ) | | self .backend.has ( & loose_key ( & r) ) .unwrap_or ( false ) {
ff0af1ee unknown 1 month ago
1743
return Ok ( r) ;
1744
}
8f10a07a Andy McCall 1 month ago ↶
1745
if self .pending.borrow ( ) .is_some ( ) {
1746
self .put_pending ( r, ty, body) ? ;
1747
return Ok ( r) ;
1748
}
1749
self .put_loose ( ty, body) ? ;
1750
Ok ( r)
1751
}
1752
64818938 Andy McCall 1 month ago ↶
1753
8f10a07a Andy McCall 1 month ago ↶
1754
fn put_loose ( & self , ty : ObjectType, body : & [u8 ]) -> Result< ObjectRef> {
1755
let r = hash_object ( ty, body) ;
64818938 Andy McCall 1 month ago ↶
1756
let mut bytes = header ( ty, body.len ( ) ) ;
1757
bytes.extend_from_slice ( body) ;
4dcb8c09 Andy McCall 1 month ago ↶
1758
crate :: durable:: step( " write loose object" ) ? ;
64818938 Andy McCall 1 month ago ↶
1759
put_new_or_existing ( self .backend.as_ref ( ) , & loose_key ( & r) , & bytes) ? ;
ff0af1ee unknown 1 month ago
1760
Ok ( r)
b30a880f Andy McCall 1 month ago ↶
1761
}
1762
1763
64818938 Andy McCall 1 month ago ↶
1764
1765
1766
b30a880f Andy McCall 1 month ago ↶
1767
fn put_loose_many ( & self , objects : & [( ObjectType , &[u8 ]) ]) -> Result< ( ) > {
1768
if objects.len ( ) < = 1 {
1769
for ( ty, body) in objects {
1770
self .put_loose ( * ty, body) ? ;
1771
}
1772
return Ok ( ( ) ) ;
1773
}
1774
let mut seen = std:: collections:: HashSet:: new( ) ;
64818938 Andy McCall 1 month ago ↶
1775
let items: Vec< ( String, Vec< u8 > ) > = objects
b30a880f Andy McCall 1 month ago ↶
1776
.iter ( )
1777
.filter_map ( | ( ty , body ) | {
1778
let r = hash_object ( * ty, body) ;
64818938 Andy McCall 1 month ago ↶
1779
seen.insert ( r) .then ( | | {
1780
let mut bytes = header ( * ty, body.len ( ) ) ;
1781
bytes.extend_from_slice ( body) ;
1782
( loose_key ( & r) , bytes)
b30a880f Andy McCall 1 month ago ↶
1783
} )
1784
} )
1785
.collect ( ) ;
64818938 Andy McCall 1 month ago ↶
1786
self .backend.put_many_new ( & items) .map_err ( backend_err)
ff0af1ee unknown 1 month ago
1787
}
1788
09245702 Andy McCall 18 days ago ↶
1789
1790
1791
1792
767ac6cd Andy McCall 17 days ago
1793
1794
1795
1796
1797
1798
64818938 Andy McCall 1 month ago ↶
1799
pub fn get ( & self , r : & ObjectRef) -> Result< Object> {
767ac6cd Andy McCall 17 days ago
1800
match self .get_now ( r) {
1801
Err ( e) if is_miss ( & e) => {
1802
1803
let packed_miss =
1804
! matches! ( e, StoreError:: NotFound( _ ) ) | | self .find_packed ( r) .is_some ( ) ;
1805
let changed = if packed_miss {
1806
self .reload_packs ( ) ? ;
1807
true
1808
} else {
1809
self .reload_if_changed ( ) ?
1810
} ;
1811
if changed {
1812
self .get_now ( r)
1813
} else {
1814
Err ( e)
1815
}
1816
}
1817
other => other,
1818
}
1819
}
1820
1821
1822
fn get_now ( & self , r : & ObjectRef) -> Result< Object> {
09245702 Andy McCall 18 days ago ↶
1823
if let Some ( ( i, e) ) = self .find_packed ( r) {
767ac6cd Andy McCall 17 days ago
1824
let pack = self .packs.borrow ( ) [ i] .clone ( ) ;
1825
return pack.fetch ( self .backend.as_ref ( ) , r, e) ;
09245702 Andy McCall 18 days ago ↶
1826
}
64818938 Andy McCall 1 month ago ↶
1827
match get_optional ( self .backend.as_ref ( ) , & loose_key ( r) ) ? {
1828
Some ( bytes) => parse_loose ( & bytes, r) ,
09245702 Andy McCall 18 days ago ↶
1829
None => self .pending_get ( r) .unwrap_or ( Err ( StoreError:: NotFound( * r) ) ) ,
afaba5f1 Andy McCall 1 month ago ↶
1830
}
1831
}
1832
09245702 Andy McCall 18 days ago ↶
1833
1834
1835
767ac6cd Andy McCall 17 days ago
1836
1837
afaba5f1 Andy McCall 1 month ago ↶
1838
pub fn get_many ( & self , refs : & [ObjectRef]) -> Vec< Result< Object> > {
767ac6cd Andy McCall 17 days ago
1839
let mut out = self .get_many_now ( refs) ;
1840
let missed: Vec< usize > = out
1841
.iter ( )
1842
.enumerate ( )
1843
.filter ( | ( _, g ) | g.as_ref ( ) .is_err_and ( is_miss) )
1844
.map ( | ( i , _) | i )
1845
.collect ( ) ;
1846
if missed.is_empty ( ) {
1847
return out;
1848
}
1849
let packed_miss = missed.iter ( ) .any ( | i | {
1850
! matches! ( out[ * i] , Err ( StoreError:: NotFound( _ ) ) )
1851
| | self .find_packed ( & refs[ * i] ) .is_some ( )
1852
} ) ;
1853
let changed = if packed_miss {
1854
self .reload_packs ( ) .map ( | _| true )
1855
} else {
1856
self .reload_if_changed ( )
1857
} ;
1858
match changed {
1859
Ok ( true ) => {
1860
let again: Vec< ObjectRef> = missed.iter ( ) .map ( | i | refs[ * i] ) .collect ( ) ;
1861
for ( i, g) in missed.into_iter ( ) .zip ( self .get_many_now ( & again) ) {
1862
out[ i] = g;
1863
}
1864
}
1865
Ok ( false ) => { }
1866
Err ( e) => {
1867
let why = e.to_string ( ) ;
1868
for i in missed {
1869
out[ i] = Err ( StoreError:: Io( io:: Error:: other( why.clone ( ) ) ) ) ;
1870
}
1871
}
1872
}
1873
out
1874
}
1875
1876
1877
fn get_many_now ( & self , refs : & [ObjectRef]) -> Vec< Result< Object> > {
afaba5f1 Andy McCall 1 month ago ↶
1878
use rayon:: prelude:: * ;
64818938 Andy McCall 1 month ago ↶
1879
let backend = self .backend.as_ref ( ) ;
afaba5f1 Andy McCall 1 month ago ↶
1880
let packs = self .packs.borrow ( ) ;
09245702 Andy McCall 18 days ago ↶
1881
let place: Vec< Option< ( usize , PackEntry) > > = refs
75167672 Andy McCall 1 month ago
1882
.iter ( )
09245702 Andy McCall 18 days ago ↶
1883
.map ( | r | {
afaba5f1 Andy McCall 1 month ago ↶
1884
packs
1885
.iter ( )
1886
.enumerate ( )
09245702 Andy McCall 18 days ago ↶
1887
.find_map ( | ( p , pack ) | pack.find ( r) .map ( | e | ( p, e) ) )
afaba5f1 Andy McCall 1 month ago ↶
1888
} )
1889
.collect ( ) ;
767ac6cd Andy McCall 17 days ago
1890
let indexes: & [ Arc< PackIndex> ] = & packs;
795b8b50 Andy McCall 9 days ago ↶
1891
1892
1893
1894
let order = read_order ( & place) ;
1895
let fetched: Vec< ( usize , Option< Result< Object> > ) > = order
afaba5f1 Andy McCall 1 month ago ↶
1896
.par_iter ( )
795b8b50 Andy McCall 9 days ago ↶
1897
.map ( | & i | {
1898
let r = & refs[ i] ;
1899
let got = match & place[ i] {
1900
Some ( ( p, e) ) => Some ( indexes[ * p] .fetch ( backend, r, * e) ) ,
1901
None => match get_optional ( backend, & loose_key ( r) ) {
1902
Ok ( Some ( bytes) ) => Some ( parse_loose ( & bytes, r) ) ,
1903
Ok ( None ) => None ,
1904
Err ( e) => Some ( Err ( e) ) ,
1905
} ,
1906
} ;
1907
( i, got)
09245702 Andy McCall 18 days ago ↶
1908
} )
afaba5f1 Andy McCall 1 month ago ↶
1909
.collect ( ) ;
1910
drop ( packs) ;
795b8b50 Andy McCall 9 days ago ↶
1911
let mut found: Vec< Option< Result< Object> > > = ( 0 .. refs.len ( ) ) .map ( | _| None ) .collect ( ) ;
1912
for ( i, got) in fetched {
1913
found[ i] = got;
1914
}
afaba5f1 Andy McCall 1 month ago ↶
1915
found
1916
.into_iter ( )
1917
.zip ( refs)
09245702 Andy McCall 18 days ago ↶
1918
.map ( | ( f , r ) | {
1919
f.unwrap_or_else ( | | self .pending_get ( r) .unwrap_or ( Err ( StoreError:: NotFound( * r) ) ) )
afaba5f1 Andy McCall 1 month ago ↶
1920
} )
1921
.collect ( )
ff0af1ee unknown 1 month ago
1922
}
1923
1924
pub fn get_typed ( & self , r : & ObjectRef, want : ObjectType) -> Result< Vec< u8 > > {
1925
let ( ty, body) = self .get ( r) ? ;
1926
if ty ! = want {
1927
return Err ( StoreError:: WrongType( * r, ty.name ( ) , want.name ( ) ) ) ;
1928
}
1929
Ok ( body)
1930
}
1931
6f8aeb99 Andy McCall 1 month ago ↶
1932
1933
ff0af1ee unknown 1 month ago
1934
pub fn put_change ( & self , c : & Change) -> Result< ObjectRef> {
5b4c5130 Andy McCall 1 month ago ↶
1935
let encoding = crate :: timing:: phase( " store: encode the change and its texts" ) ;
17f94b13 Andy McCall 1 month ago ↶
1936
let bytes = c.encode_streaming ( | batch | self .put_texts ( & batch) ) ? ;
5b4c5130 Andy McCall 1 month ago ↶
1937
drop ( encoding) ;
1938
let _t = crate :: timing:: phase( " store: put the encoding" ) ;
17f94b13 Andy McCall 1 month ago ↶
1939
self .put ( ObjectType:: Change, & bytes)
6f8aeb99 Andy McCall 1 month ago ↶
1940
}
1941
17f94b13 Andy McCall 1 month ago ↶
1942
1943
1944
pub fn put_texts ( & self , texts : & [crate:: objects:: StoredText]) -> Result< ( ) > {
192fad1e Andy McCall 1 month ago ↶
1945
let mut chunks: Vec< ( ObjectType, & [u8 ]) > = Vec :: new( ) ;
1946
for t in texts {
1947
if ! self .has ( & t.blob.hash ( ) ) {
1948
chunks.extend (
1949
crate :: chunk:: chunks( & t.content)
1950
.into_iter ( )
1951
.map ( | c | ( ObjectType:: Chunk, c) ) ,
1952
) ;
1953
}
6f8aeb99 Andy McCall 1 month ago ↶
1954
}
192fad1e Andy McCall 1 month ago ↶
1955
self .put_many ( & chunks) ? ;
1956
for t in texts {
1957
self .put ( ObjectType:: Blob, & t.blob.encode ( ) ) ? ;
6f8aeb99 Andy McCall 1 month ago ↶
1958
}
17f94b13 Andy McCall 1 month ago ↶
1959
Ok ( ( ) )
ff0af1ee unknown 1 month ago
1960
}
1961
1962
pub fn get_change ( & self , r : & ObjectRef) -> Result< Change> {
6f8aeb99 Andy McCall 1 month ago ↶
1963
Ok ( self .get_change_parts ( r) ? .0 )
1964
}
1965
1966
1967
1968
pub fn get_change_parts ( & self , r : & ObjectRef) -> Result< ( Change, Vec< ObjectRef> ) > {
1969
let bytes = self .get_typed ( r, ObjectType:: Change) ? ;
1970
let mut parts = Vec :: new( ) ;
1971
let mut failed = None ;
0f93f49c Andy McCall 1 month ago ↶
1972
let decoded = Change:: decode_with( & bytes, & mut | text, count, form| match self
1973
.read_text ( text)
1974
{
6f8aeb99 Andy McCall 1 month ago ↶
1975
Ok ( ( blob, content) ) => {
1976
parts.push ( * text) ;
1977
parts.extend ( blob.chunks) ;
0f93f49c Andy McCall 1 month ago ↶
1978
crate :: objects:: text_lines( & content, count, form)
6f8aeb99 Andy McCall 1 month ago ↶
1979
}
1980
Err ( e) => {
1981
let why = e.to_string ( ) ;
1982
failed = Some ( e) ;
1983
Err ( ObjectError:: Schema( format! (
1984
" text blob {} : {why} " ,
1985
text.short ( 12 )
1986
) ) )
1987
}
1988
} ) ;
1989
match ( decoded, failed) {
1990
( Ok ( c) , _ ) => Ok ( ( c, parts) ) ,
1991
( Err ( _ ) , Some ( StoreError:: NotFound( t) ) ) => Err ( StoreError:: Corrupt(
1992
* r,
1993
format! ( " its text blob {} is missing" , t.short ( 12 ) ) ,
1994
) ) ,
1995
( Err ( e) , _ ) => Err ( StoreError:: Corrupt( * r, e.to_string ( ) ) ) ,
1996
}
1997
}
1998
a4fad5b0 Andy McCall 1 month ago ↶
1999
2000
2001
2002
2003
pub fn get_change_lazy ( & self , r : & ObjectRef) -> Result< ( Change, Vec< crate:: objects:: TextRef> ) > {
86e1824f Andy McCall 1 month ago ↶
2004
let bytes = self .get_typed ( r, ObjectType:: Change) ? ;
a4fad5b0 Andy McCall 1 month ago ↶
2005
Change:: decode_skeleton( & bytes, * r) .map_err ( | e | StoreError:: Corrupt( * r, e.to_string ( ) ) )
86e1824f Andy McCall 1 month ago ↶
2006
}
2007
69816d68 Andy McCall 1 month ago ↶
2008
2009
2010
ce14f0d6 Andy McCall 1 month ago ↶
2011
2012
2013
2014
69816d68 Andy McCall 1 month ago ↶
2015
pub fn get_change_meta ( & self , r : & ObjectRef) -> Result< crate:: state:: ChangeMeta> {
ce14f0d6 Andy McCall 1 month ago ↶
2016
if self .metas.borrow ( ) .is_none ( ) {
2017
* self .metas.borrow_mut ( ) = Some ( self .read_metas_cache ( ) ) ;
2018
}
2019
let cached = self .metas.borrow ( ) .as_ref ( ) .and_then ( | m | m.get ( r) .cloned ( ) ) ;
2020
if let Some ( meta) = cached {
2021
if self .has ( r) {
2022
# [ cfg ( test ) ]
2023
assert_eq! (
2024
meta,
2025
Change:: decode_meta( & self .get_typed ( r, ObjectType:: Change) ? ) .unwrap ( ) ,
2026
" the metadata cache holds a change's metadata"
2027
) ;
2028
return Ok ( meta) ;
2029
}
2030
}
4d6c9c1b Andy McCall 1 month ago ↶
2031
let bytes = self .get_typed ( r, ObjectType:: Change) ? ;
ce14f0d6 Andy McCall 1 month ago ↶
2032
let meta =
2033
Change:: decode_meta( & bytes) .map_err ( | e | StoreError:: Corrupt( * r, e.to_string ( ) ) ) ? ;
2034
self .remember_meta ( r, & meta) ;
2035
Ok ( meta)
2036
}
2037
2038
2039
2040
64818938 Andy McCall 1 month ago ↶
2041
2042
ce14f0d6 Andy McCall 1 month ago ↶
2043
fn read_metas_cache ( & self ) -> crate:: fasthash:: FastMap< ObjectRef, crate:: state:: ChangeMeta> {
2044
let mut out = crate :: fasthash:: FastMap:: default( ) ;
64818938 Andy McCall 1 month ago ↶
2045
let Ok ( bytes) = fs:: read( self .scratch.join ( " metas" ) ) else {
ce14f0d6 Andy McCall 1 month ago ↶
2046
return out;
2047
} ;
2048
let mut r = crate :: flat:: Reader:: new( & bytes) ;
2049
let mut good = 0 ;
2050
while ! r.at_end ( ) {
2051
let mut record = | | -> std:: result:: Result< _ , ObjectError> {
2052
let h = r.object ( ) ? ;
2053
let body = r.bytes ( ) ? ;
2054
let check = r.fixed:: < 8> ( ) ? ;
2055
Ok ( ( h, body, check) )
2056
} ;
2057
let Ok ( ( h, body, check) ) = record ( ) else {
2058
break ;
2059
} ;
2060
let mut sum = blake3:: Hasher:: new( ) ;
2061
sum.update ( & h.0 ) .update ( body) ;
2062
if sum.finalize ( ) .as_bytes ( ) [ .. 8 ] ! = check {
2063
break ;
2064
}
2065
match crate :: state:: ChangeMeta:: read_flat( & mut crate :: flat:: Reader:: new( body) ) {
2066
Ok ( m) => out.insert ( h, m) ,
2067
Err ( _ ) => break ,
2068
} ;
2069
good + = 32 + 4 + body.len ( ) + 8 ;
2070
}
2071
2072
if good < bytes.len ( ) {
2073
let _ = fs:: OpenOptions:: new( )
2074
.write ( true )
64818938 Andy McCall 1 month ago ↶
2075
.open ( self .scratch.join ( " metas" ) )
ce14f0d6 Andy McCall 1 month ago ↶
2076
.and_then ( | f | f.set_len ( good as u64 ) ) ;
2077
}
2078
out
2079
}
2080
2081
2082
2083
fn remember_meta ( & self , r : & ObjectRef, meta : & crate:: state:: ChangeMeta) {
2084
let mut body = crate :: flat:: Writer:: default( ) ;
2085
meta.write_flat ( & mut body) ;
2086
let mut w = crate :: flat:: Writer:: default( ) ;
2087
w.object ( r) ;
2088
w.bytes ( & body.0 ) ;
2089
let mut sum = blake3:: Hasher:: new( ) ;
2090
sum.update ( & r.0 ) .update ( & body.0 ) ;
2091
w.fixed ( & sum.finalize ( ) .as_bytes ( ) [ .. 8 ] ) ;
2092
let _ = fs:: OpenOptions:: new( )
2093
.create ( true )
2094
.append ( true )
64818938 Andy McCall 1 month ago ↶
2095
.open ( self .scratch.join ( " metas" ) )
ce14f0d6 Andy McCall 1 month ago ↶
2096
.and_then ( | mut f | f.write_all ( & w.0 ) ) ;
2097
if let Some ( m) = self .metas.borrow_mut ( ) .as_mut ( ) {
2098
m.insert ( * r, meta.clone ( ) ) ;
2099
}
69816d68 Andy McCall 1 month ago ↶
2100
}
2101
6f8aeb99 Andy McCall 1 month ago ↶
2102
fn read_text ( & self , r : & ObjectRef) -> Result< ( Blob, Vec< u8 > ) > {
2103
let blob = Blob:: decode( & self .get_typed ( r, ObjectType:: Blob) ? ) ? ;
2104
let mut content = Vec :: with_capacity( blob.length as usize ) ;
2105
for c in & blob.chunks {
2106
content.extend ( self .get_typed ( c, ObjectType:: Chunk) ? ) ;
2107
}
2108
Ok ( ( blob, content) )
ff0af1ee unknown 1 month ago
2109
}
2110
2111
pub fn put_chunk ( & self , bytes : & [u8 ]) -> Result< ObjectRef> {
2112
self .put ( ObjectType:: Chunk, bytes)
2113
}
2114
cdb5beba Andy McCall 1 month ago ↶
2115
2116
pub fn put_chunk_nocompress ( & self , bytes : & [u8 ]) -> Result< ObjectRef> {
8f10a07a Andy McCall 1 month ago ↶
2117
let r = hash_object ( ObjectType:: Chunk, bytes) ;
64818938 Andy McCall 1 month ago ↶
2118
put_new_or_existing (
2119
self .backend.as_ref ( ) ,
2120
& format! ( " nocompress/{} " , r.to_hex ( ) ) ,
2121
b " " ,
2122
) ? ;
8f10a07a Andy McCall 1 month ago ↶
2123
self .put ( ObjectType:: Chunk, bytes)
cdb5beba Andy McCall 1 month ago ↶
2124
}
2125
2126
fn is_nocompress ( & self , r : & ObjectRef) -> bool {
64818938 Andy McCall 1 month ago ↶
2127
self .backend
2128
.has ( & format! ( " nocompress/{} " , r.to_hex ( ) ) )
2129
.unwrap_or ( false )
2130
}
2131
2132
2133
fn nocompress_set ( & self ) -> Result< std:: collections:: HashSet< ObjectRef> > {
2134
Ok ( self
2135
.backend
2136
.list ( " nocompress/" )
2137
.map_err ( backend_err) ?
2138
.into_iter ( )
2139
.filter_map ( | e | ObjectRef:: from_hex( e.key.trim_start_matches ( " nocompress/" ) ) )
2140
.collect ( ) )
cdb5beba Andy McCall 1 month ago ↶
2141
}
2142
86e682c2 Andy McCall 1 month ago ↶
2143
pub fn put_state ( & self , st : & StateObject) -> Result< ObjectRef> {
2144
self .put ( ObjectType:: State, & st.encode ( ) )
2145
}
2146
2147
pub fn get_state ( & self , r : & ObjectRef) -> Result< StateObject> {
2148
Ok ( StateObject:: decode( & self .get_typed ( r, ObjectType:: State) ? ) ? )
2149
}
2150
5b9c6ad4 Andy McCall 1 month ago ↶
2151
pub fn put_attest ( & self , a : & Attest) -> Result< ObjectRef> {
2152
self .put ( ObjectType:: Attest, & a.encode ( ) )
2153
}
2154
2155
pub fn get_attest ( & self , r : & ObjectRef) -> Result< Attest> {
2156
Ok ( Attest:: decode( & self .get_typed ( r, ObjectType:: Attest) ? ) ? )
fc1a466a Andy McCall 1 month ago ↶
2157
}
2158
2159
pub fn put_receipt ( & self , r : & Receipt) -> Result< ObjectRef> {
2160
self .put ( ObjectType:: Receipt, & r.encode ( ) )
5b9c6ad4 Andy McCall 1 month ago ↶
2161
}
2162
fc1a466a Andy McCall 1 month ago ↶
2163
pub fn get_receipt ( & self , r : & ObjectRef) -> Result< Receipt> {
2164
Ok ( Receipt:: decode( & self .get_typed ( r, ObjectType:: Receipt) ? ) ? )
5b9c6ad4 Andy McCall 1 month ago ↶
2165
}
2166
64818938 Andy McCall 1 month ago ↶
2167
2168
2169
fn index_add ( & self , index : & str , key : & ObjectRef, value : & ObjectRef) -> Result< ( ) > {
ef7d4ea6 Andy McCall 16 days ago ↶
2170
debug_assert! ( ENTRY_INDEXES .iter ( ) .any ( | ( i , _) | * i = = index) ) ;
64818938 Andy McCall 1 month ago ↶
2171
let entry = format! ( " {} {} " , fanout_prefix ( index, key) , value.to_hex ( ) ) ;
ef7d4ea6 Andy McCall 16 days ago ↶
2172
put_new_or_existing ( self .backend.as_ref ( ) , & entry, b " " ) ? ;
2173
self .journal_add ( entry)
fc1a466a Andy McCall 1 month ago ↶
2174
}
2175
64818938 Andy McCall 1 month ago ↶
2176
2177
2178
fn index_get ( & self , index : & str , legacy : & str , key : & ObjectRef) -> Result< Vec< ObjectRef> > {
2179
let mut values: Vec< ObjectRef> = self
ef7d4ea6 Andy McCall 16 days ago ↶
2180
.entry_keys ( & fanout_prefix ( index, key) ) ?
64818938 Andy McCall 1 month ago ↶
2181
.into_iter ( )
ef7d4ea6 Andy McCall 16 days ago ↶
2182
.filter_map ( | k | k.rsplit ( ' /' ) .next ( ) .and_then ( ObjectRef:: from_hex) )
64818938 Andy McCall 1 month ago ↶
2183
.collect ( ) ;
ef7d4ea6 Andy McCall 16 days ago ↶
2184
if ! self .reads_legacy ( ) {
2185
2186
} else if let Some ( bytes) = get_optional ( self .backend.as_ref ( ) , & legacy_key ( legacy, key) ) ? {
64818938 Andy McCall 1 month ago ↶
2187
values.extend (
2188
String :: from_utf8_lossy( & bytes)
2189
.lines ( )
2190
.filter_map ( ObjectRef:: from_hex) ,
2191
) ;
5b9c6ad4 Andy McCall 1 month ago ↶
2192
}
fc1a466a Andy McCall 1 month ago ↶
2193
values.sort ( ) ;
64818938 Andy McCall 1 month ago ↶
2194
values.dedup ( ) ;
2195
Ok ( values)
5b9c6ad4 Andy McCall 1 month ago ↶
2196
}
2197
fc1a466a Andy McCall 1 month ago ↶
2198
2199
pub fn index_seal ( & self , change : & ObjectRef, attest : & ObjectRef) -> Result< ( ) > {
64818938 Andy McCall 1 month ago ↶
2200
self .index_add ( " seal" , change, attest)
fc1a466a Andy McCall 1 month ago ↶
2201
}
2202
2203
2204
pub fn seals_for ( & self , change : & ObjectRef) -> Result< Vec< ObjectRef> > {
64818938 Andy McCall 1 month ago ↶
2205
self .index_get ( " seal" , " seals" , change)
fc1a466a Andy McCall 1 month ago ↶
2206
}
2207
2208
c63fed2e Andy McCall 18 days ago ↶
2209
2210
2211
pub fn put_certificate ( & self , c : & crate:: cert:: SignedCertificate) -> Result< ObjectRef> {
2212
let r = self .put ( ObjectType:: Certificate, & c.cert.encode ( ) ) ? ;
2213
self .put_signature ( & r, & c.sig) ? ;
2214
self .index_add ( " cert" , & c.cert.key_id ( ) , & r) ? ;
2215
Ok ( r)
2216
}
2217
2218
2219
pub fn get_certificate ( & self , r : & ObjectRef) -> Result< crate:: cert:: SignedCertificate> {
2220
let cert = crate :: cert:: Certificate:: decode( & self .get_typed ( r, ObjectType:: Certificate) ? ) ? ;
2221
let sig = self
2222
.signatures ( r) ?
2223
.into_iter ( )
2224
.find ( | s | s.key = = cert.issuer )
2225
.ok_or ( StoreError:: NotFound( * r) ) ? ;
2226
Ok ( crate :: cert:: SignedCertificate { cert, sig } )
2227
}
2228
2229
2230
pub fn certificate_of ( & self , key : & ObjectRef) -> Result< Option< ObjectRef> > {
2231
Ok ( self
2232
.index_get ( " cert" , " certs" , key) ?
2233
.into_iter ( )
2234
.find ( | r | self .has ( r) ) )
2235
}
2236
f53cbe7a Andy McCall 18 days ago ↶
2237
2238
pub ( crate ) fn index_add_pub (
2239
& self ,
2240
index : & str ,
2241
key : & ObjectRef,
2242
value : & ObjectRef,
2243
) -> Result< ( ) > {
2244
self .index_add ( index, key, value)
2245
}
2246
2247
2248
2249
pub ( crate ) fn index_get_pub ( & self , index : & str , key : & ObjectRef) -> Result< Vec< ObjectRef> > {
2250
self .index_get ( index, & format! ( " {index} -lists" ) , key)
2251
}
2252
fc1a466a Andy McCall 1 month ago ↶
2253
pub fn index_receipt ( & self , state : & ObjectRef, attest : & ObjectRef) -> Result< ( ) > {
64818938 Andy McCall 1 month ago ↶
2254
self .index_add ( " receipt" , state, attest)
fc1a466a Andy McCall 1 month ago ↶
2255
}
2256
2257
2258
pub fn receipts_for ( & self , state : & ObjectRef) -> Result< Vec< ObjectRef> > {
64818938 Andy McCall 1 month ago ↶
2259
self .index_get ( " receipt" , " receipts" , state)
fc1a466a Andy McCall 1 month ago ↶
2260
}
2261
3a4b3e01 Andy McCall 19 days ago ↶
2262
pub fn index_approval ( & self , state : & ObjectRef, attest : & ObjectRef) -> Result< ( ) > {
2263
self .index_add ( " approval" , state, attest)
2264
}
2265
2266
2267
2268
pub fn approvals_for ( & self , state : & ObjectRef) -> Result< Vec< ObjectRef> > {
2269
self .index_get ( " approval" , " approvals" , state)
2270
}
2271
2272
2273
pub fn approval_states ( & self ) -> Result< Vec< ObjectRef> > {
2274
self .indexed_states ( " approval" , " approvals" )
2275
}
2276
fc1a466a Andy McCall 1 month ago ↶
2277
2278
pub fn receipt_states ( & self ) -> Result< Vec< ObjectRef> > {
3a4b3e01 Andy McCall 19 days ago ↶
2279
self .indexed_states ( " receipt" , " receipts" )
2280
}
2281
2282
2283
2284
fn indexed_states ( & self , one : & str , many : & str ) -> Result< Vec< ObjectRef> > {
64818938 Andy McCall 1 month ago ↶
2285
let mut out: Vec< ObjectRef> = Vec :: new( ) ;
ef7d4ea6 Andy McCall 16 days ago ↶
2286
for key in self .entry_keys ( & format! ( " {one} /" ) ) ? {
64818938 Andy McCall 1 month ago ↶
2287
ef7d4ea6 Andy McCall 16 days ago ↶
2288
let mut parts = key.splitn ( 4 , ' /' ) ;
64818938 Andy McCall 1 month ago ↶
2289
let ( Some ( _ ) , Some ( hh) , Some ( rest) , Some ( _ ) ) =
2290
( parts.next ( ) , parts.next ( ) , parts.next ( ) , parts.next ( ) )
2291
else {
2292
continue ;
2293
} ;
2294
out.extend ( ObjectRef:: from_hex( & format! ( " {hh} {rest} " ) ) ) ;
fc1a466a Andy McCall 1 month ago ↶
2295
}
ef7d4ea6 Andy McCall 16 days ago ↶
2296
let legacy = if self .reads_legacy ( ) {
2297
self .backend
2298
.list ( & format! ( " {many} /" ) )
2299
.map_err ( backend_err) ?
2300
} else {
2301
Vec :: new( )
2302
} ;
2303
for e in legacy {
64818938 Andy McCall 1 month ago ↶
2304
let mut parts = e.key.splitn ( 3 , ' /' ) ;
2305
let ( Some ( _ ) , Some ( hh) , Some ( rest) ) = ( parts.next ( ) , parts.next ( ) , parts.next ( ) ) else {
2306
continue ;
2307
} ;
2308
out.extend ( ObjectRef:: from_hex( & format! ( " {hh} {rest} " ) ) ) ;
2309
}
fc1a466a Andy McCall 1 month ago ↶
2310
out.sort ( ) ;
64818938 Andy McCall 1 month ago ↶
2311
out.dedup ( ) ;
fc1a466a Andy McCall 1 month ago ↶
2312
Ok ( out)
5b9c6ad4 Andy McCall 1 month ago ↶
2313
}
2314
1983686f Andy McCall 1 month ago ↶
2315
pub fn put_identity ( & self , id : & Identity) -> Result< ObjectRef> {
2316
self .put ( ObjectType:: Identity, & id.encode ( ) )
2317
}
2318
2319
pub fn get_identity ( & self , r : & ObjectRef) -> Result< Identity> {
2320
Ok ( Identity:: decode( & self .get_typed ( r, ObjectType:: Identity) ? ) ? )
2321
}
2322
64818938 Andy McCall 1 month ago ↶
2323
2324
1983686f Andy McCall 1 month ago ↶
2325
pub fn signatures ( & self , r : & ObjectRef) -> Result< Vec< Signature> > {
64818938 Andy McCall 1 month ago ↶
2326
let mut sigs: Vec< Signature> = Vec :: new( ) ;
ef7d4ea6 Andy McCall 16 days ago ↶
2327
for key in self .entry_keys ( & fanout_prefix ( " sig" , r) ) ? {
2328
2329
2330
2331
let Some ( bytes) = get_optional ( self .backend.as_ref ( ) , & key) ? else {
2332
continue ;
2333
} ;
64818938 Andy McCall 1 month ago ↶
2334
sigs.extend ( Signature:: decode_list( & bytes) ? ) ;
1983686f Andy McCall 1 month ago ↶
2335
}
64818938 Andy McCall 1 month ago ↶
2336
2337
ef7d4ea6 Andy McCall 16 days ago ↶
2338
let legacy = if self .reads_legacy ( ) {
2339
get_optional ( self .backend.as_ref ( ) , & legacy_key ( " sigs" , r) ) ?
2340
} else {
2341
None
2342
} ;
2343
if let Some ( bytes) = legacy {
64818938 Andy McCall 1 month ago ↶
2344
for sig in Signature:: decode_list( & bytes) ? {
2345
if ! sigs.iter ( ) .any ( | s | s.key = = sig.key ) {
2346
sigs.push ( sig) ;
2347
}
2348
}
2349
}
2350
sigs.sort_by_key ( | s | s.key ) ;
2351
sigs.dedup_by_key ( | s | s.key ) ;
2352
Ok ( sigs)
1983686f Andy McCall 1 month ago ↶
2353
}
2354
64818938 Andy McCall 1 month ago ↶
2355
2356
2357
1983686f Andy McCall 1 month ago ↶
2358
pub fn put_signature ( & self , r : & ObjectRef, sig : & Signature) -> Result< ( ) > {
64818938 Andy McCall 1 month ago ↶
2359
let key = format! ( " {} {} " , fanout_prefix ( " sig" , r) , sig.key.to_hex ( ) ) ;
2360
let bytes = Signature:: encode_list( std:: slice:: from_ref( sig) ) ;
ef7d4ea6 Andy McCall 16 days ago ↶
2361
if get_optional ( self .backend.as_ref ( ) , & key) ? .as_deref ( ) ! = Some ( bytes.as_slice ( ) ) {
2362
delete ( self .backend.as_ref ( ) , & key) ? ;
2363
put_new_or_existing ( self .backend.as_ref ( ) , & key, & bytes) ? ;
2364
}
2365
2366
2367
self .journal_add ( key)
2368
}
2369
2370
2371
8156967b Andy McCall 16 days ago ↶
2372
2373
2374
ef7d4ea6 Andy McCall 16 days ago ↶
2375
fn reads_legacy ( & self ) -> bool {
8156967b Andy McCall 16 days ago ↶
2376
! self .legacy_folded.get ( )
ef7d4ea6 Andy McCall 16 days ago ↶
2377
}
2378
8156967b Andy McCall 16 days ago ↶
2379
2380
2381
2382
2383
2384
fn fold_legacy ( & self , progress : Progress) -> Result< ( ) > {
2385
let mut copied = 0 usize ;
2386
for ( index, legacy) in ENTRY_INDEXES {
2387
for e in self
2388
.backend
2389
.list ( & format! ( " {legacy} /" ) )
2390
.map_err ( backend_err) ?
2391
{
2392
2393
let mut parts = e.key.splitn ( 3 , ' /' ) ;
2394
let ( Some ( _ ) , Some ( hh) , Some ( rest) ) = ( parts.next ( ) , parts.next ( ) , parts.next ( ) )
2395
else {
2396
continue ;
2397
} ;
2398
let Some ( r) = ObjectRef:: from_hex( & format! ( " {hh} {rest} " ) ) else {
2399
continue ;
2400
} ;
2401
let Some ( bytes) = get_optional ( self .backend.as_ref ( ) , & e.key) ? else {
2402
continue ;
2403
} ;
2404
if index = = " sig" {
2405
let Ok ( sigs) = Signature:: decode_list( & bytes) else {
2406
progress ( & format! ( " {} : not a list of signatures, passed over" , e.key) ) ;
2407
continue ;
2408
} ;
2409
for sig in sigs {
2410
let key = format! ( " {} {} " , fanout_prefix ( " sig" , & r) , sig.key.to_hex ( ) ) ;
2411
let one = Signature:: encode_list( std:: slice:: from_ref( & sig) ) ;
2412
put_new_or_existing ( self .backend.as_ref ( ) , & key, & one) ? ;
2413
copied + = 1 ;
2414
}
2415
} else {
2416
for v in String :: from_utf8_lossy( & bytes)
2417
.lines ( )
2418
.filter_map ( ObjectRef:: from_hex)
2419
{
2420
let key = format! ( " {} {} " , fanout_prefix ( index, & r) , v.to_hex ( ) ) ;
2421
put_new_or_existing ( self .backend.as_ref ( ) , & key, b " " ) ? ;
2422
copied + = 1 ;
2423
}
2424
}
2425
}
2426
}
2427
if copied > 0 {
2428
progress ( & format! ( " {copied} entries copied out of older lists" ) ) ;
2429
}
2430
put_new_or_existing (
2431
self .backend.as_ref ( ) ,
2432
LEGACY_FOLDED_KEY ,
2433
format! ( " {} \n " , now_ms ( ) ) .as_bytes ( ) ,
2434
) ? ;
2435
self .legacy_folded.set ( true ) ;
2436
Ok ( ( ) )
2437
}
2438
ef7d4ea6 Andy McCall 16 days ago ↶
2439
2440
2441
2442
fn entry_keys ( & self , prefix : & str ) -> Result< Vec< String> > {
2443
if let Some ( keys) = self .with_entries ( | set | {
2444
set.range ( prefix.to_string ( ) .. )
2445
.take_while ( | k | k.starts_with ( prefix) )
2446
.cloned ( )
2447
.collect:: < Vec< String> > ( )
2448
} ) ? {
2449
return Ok ( keys) ;
2450
}
2451
Ok ( self
2452
.backend
2453
.list ( prefix)
2454
.map_err ( backend_err) ?
2455
.into_iter ( )
2456
.map ( | e | e.key )
2457
.collect ( ) )
2458
}
2459
2460
2461
2462
2463
fn with_entries < T> (
2464
& self ,
2465
f : impl FnOnce( &std ::collections ::BTreeSet <String > ) -> T,
2466
) -> Result< Option< T> > {
2467
if self .local | | ! self .folded.get ( ) {
2468
return Ok ( None ) ;
2469
}
771661aa Andy McCall 16 days ago ↶
2470
2471
2472
2473
2474
if self
2475
.entries_read
2476
.get ( )
2477
.is_some_and ( | t | t.elapsed ( ) > = ENTRIES_FRESH )
2478
{
2479
* self .entries.borrow_mut ( ) = None ;
2480
self .reload_if_changed ( ) ? ;
2481
if ! self .folded.get ( ) {
2482
return Ok ( None ) ;
2483
}
2484
}
ef7d4ea6 Andy McCall 16 days ago ↶
2485
if self .entries.borrow ( ) .is_none ( ) {
2486
let Some ( set) = self .read_entries ( ) ? else {
2487
return Ok ( None ) ;
2488
} ;
2489
* self .entries.borrow_mut ( ) = Some ( set) ;
2490
}
2491
Ok ( self .entries.borrow ( ) .as_ref ( ) .map ( f) )
2492
}
2493
2494
2495
2496
2497
2498
2499
fn read_entries ( & self ) -> Result< Option< std:: collections:: BTreeSet< String> > > {
2500
'attempt : for attempt in 0 .. 2 {
2501
if ! self .folded.get ( ) {
2502
return Ok ( None ) ;
2503
}
2504
let mut set = std:: collections:: BTreeSet:: new( ) ;
2505
let lists = self .entry_lists.borrow ( ) .clone ( ) ;
771661aa Andy McCall 16 days ago ↶
2506
self .lists_read
2507
.borrow_mut ( )
2508
.retain ( | k , _| lists.contains ( k) ) ;
ef7d4ea6 Andy McCall 16 days ago ↶
2509
for key in & lists {
771661aa Andy McCall 16 days ago ↶
2510
let known = self .lists_read.borrow ( ) .get ( key) .cloned ( ) ;
2511
let keys = match known {
2512
Some ( keys) => keys,
2513
None => match get_optional ( self .backend.as_ref ( ) , key) ? {
2514
Some ( bytes) => {
2515
let keys = Arc:: new( decode_entry_list ( key, & bytes) ? ) ;
2516
self .lists_read
2517
.borrow_mut ( )
2518
.insert ( key.clone ( ) , keys.clone ( ) ) ;
2519
keys
2520
}
2521
None if attempt = = 0 => {
2522
self .reload_packs ( ) ? ;
2523
continue 'attempt ;
2524
}
2525
None => return Ok ( None ) ,
2526
} ,
2527
} ;
2528
set.extend ( keys.iter ( ) .cloned ( ) ) ;
ef7d4ea6 Andy McCall 16 days ago ↶
2529
}
2530
set.extend ( self .read_record ( JOURNAL_KEY ) ? .0. into_keys ( ) ) ;
2531
set.extend ( self .journal_queue.borrow ( ) .iter ( ) .cloned ( ) ) ;
771661aa Andy McCall 16 days ago ↶
2532
self .entries_read.set ( Some ( std:: time:: Instant:: now( ) ) ) ;
ef7d4ea6 Andy McCall 16 days ago ↶
2533
return Ok ( Some ( set) ) ;
2534
}
2535
Ok ( None )
2536
}
2537
2538
2539
2540
2541
2542
fn journal_add ( & self , key : String) -> Result< ( ) > {
2543
if self .local {
2544
return Ok ( ( ) ) ;
2545
}
2546
if let Some ( set) = self .entries.borrow_mut ( ) .as_mut ( ) {
2547
if ! set.insert ( key.clone ( ) ) {
2548
return Ok ( ( ) ) ;
2549
}
2550
}
2551
if self .batch_depth.get ( ) > 0 {
2552
self .journal_queue.borrow_mut ( ) .push ( key) ;
2553
return Ok ( ( ) ) ;
2554
}
2555
self .journal_append ( std:: slice:: from_ref( & key) )
2556
}
2557
2558
2559
fn flush_journal ( & self ) -> Result< ( ) > {
2560
let queued = std:: mem:: take( & mut * self .journal_queue.borrow_mut ( ) ) ;
2561
self .journal_append ( & queued)
2562
}
2563
2564
2565
2566
2567
fn journal_append ( & self , keys : & [String]) -> Result< ( ) > {
2568
if keys.is_empty ( ) {
64818938 Andy McCall 1 month ago ↶
2569
return Ok ( ( ) ) ;
ef7d4ea6 Andy McCall 16 days ago ↶
2570
}
2571
let now = now_ms ( ) ;
2572
let mut unfolded = 0 ;
2573
self .update_record ( JOURNAL_KEY , " the journal of entries" , | m | {
2574
expire_folded ( m, now) ;
2575
for k in keys {
2576
m.entry ( k.clone ( ) ) .or_insert ( 0 ) ;
2577
}
2578
unfolded = m.values ( ) .filter ( | t | * * t = = 0 ) .count ( ) ;
2579
} ) ? ;
2580
if unfolded > JOURNAL_FOLD_AT {
2581
self .fold_journal ( ) ? ;
2582
}
2583
Ok ( ( ) )
2584
}
2585
2586
2587
2588
fn fold_journal ( & self ) -> Result< ( ) > {
2589
let ( journal, _ ) = self .read_record ( JOURNAL_KEY ) ? ;
2590
let keys: Vec< String> = journal
2591
.into_iter ( )
2592
.filter ( | ( _, t ) | * t = = 0 )
2593
.map ( | ( k , _) | k )
2594
.collect ( ) ;
2595
if keys.is_empty ( ) {
2596
return Ok ( ( ) ) ;
2597
}
2598
self .put_entry_list ( PART_SUFFIX , keys.iter ( ) ) ? ;
2599
self .mark_folded ( & keys)
2600
}
2601
2602
2603
2604
fn mark_folded ( & self , keys : & [String]) -> Result< ( ) > {
2605
let now = now_ms ( ) ;
2606
self .update_record ( JOURNAL_KEY , " the journal of entries" , | m | {
2607
expire_folded ( m, now) ;
2608
for k in keys {
2609
if let Some ( t) = m.get_mut ( k) {
2610
if * t = = 0 {
2611
* t = now;
2612
}
2613
}
2614
}
2615
} )
2616
}
2617
2618
2619
2620
fn put_entry_list < 'a > (
2621
& self ,
2622
suffix : & str ,
2623
keys : impl Iterator< Item = & 'a String> ,
2624
) -> Result< String> {
2625
let key = format! ( " pack-{} {suffix} " , fresh_id ( ) ) ;
2626
let bytes = encode_entry_list ( keys) ? ;
2627
self .backend.put_new ( & key, & bytes) .map_err ( backend_err) ? ;
2628
Ok ( key)
2629
}
2630
2631
8156967b Andy McCall 16 days ago ↶
2632
2633
ef7d4ea6 Andy McCall 16 days ago ↶
2634
2635
2636
2637
2638
2639
pub fn fold_entries ( & self , progress : Progress) -> Result< bool > {
2640
if self .local {
2641
return Ok ( false ) ;
1983686f Andy McCall 1 month ago ↶
2642
}
ef7d4ea6 Andy McCall 16 days ago ↶
2643
self .reload_packs ( ) ? ;
8156967b Andy McCall 16 days ago ↶
2644
if ! self .legacy_folded.get ( ) {
2645
progress ( " folding older lists of signatures and index entries" ) ;
2646
self .fold_legacy ( progress) ? ;
2647
}
ef7d4ea6 Andy McCall 16 days ago ↶
2648
let ( journal, _ ) = self .read_record ( JOURNAL_KEY ) ? ;
2649
progress ( " folding signatures and index entries" ) ;
2650
let mut all: std:: collections:: BTreeSet< String> = journal.keys ( ) .cloned ( ) .collect ( ) ;
2651
for ( index, _ ) in ENTRY_INDEXES {
2652
for e in self
2653
.backend
2654
.list ( & format! ( " {index} /" ) )
2655
.map_err ( backend_err) ?
2656
{
2657
all.insert ( e.key) ;
2658
}
2659
}
2660
let lists = self .entry_lists.borrow ( ) .clone ( ) ;
2661
let unfolded: Vec< String> = journal
2662
.iter ( )
2663
.filter ( | ( _, t ) | * * t = = 0 )
2664
.map ( | ( k , _) | k.clone ( ) )
2665
.collect ( ) ;
2666
if unfolded.is_empty ( ) & & lists.len ( ) = = 1 & & lists[ 0 ] .ends_with ( LIST_SUFFIX ) {
2667
if let Some ( bytes) = get_optional ( self .backend.as_ref ( ) , & lists[ 0 ] ) ? {
2668
let held: std:: collections:: BTreeSet< String> =
2669
decode_entry_list ( & lists[ 0 ] , & bytes) ? .into_iter ( ) .collect ( ) ;
2670
if held = = all {
2671
return Ok ( false ) ;
2672
}
2673
}
2674
}
2675
self .put_entry_list ( LIST_SUFFIX , all.iter ( ) ) ? ;
2676
self .retire ( & lists) ? ;
2677
if ! unfolded.is_empty ( ) {
2678
self .mark_folded ( & unfolded) ? ;
2679
}
2680
* self .entries.borrow_mut ( ) = None ;
2681
self .reload_packs ( ) ? ;
2682
Ok ( true )
1983686f Andy McCall 1 month ago ↶
2683
}
2684
54e2e630 Andy McCall 29 days ago ↶
2685
2686
2687
2688
2689
2690
2691
2692
pub fn adopt_unfinished_packs (
2693
& self ,
2694
min_age : std:: time:: Duration,
2695
progress : & dyn Fn( &str ) ,
2696
) -> Result< ( usize , usize ) > {
2697
use std:: io:: { BufReader, Read} ;
2698
let mut packs = 0 ;
2699
let mut objects = 0 ;
2700
let Ok ( dir) = fs:: read_dir( & self .scratch) else {
2701
return Ok ( ( 0 , 0 ) ) ;
2702
} ;
2703
for entry in dir.flatten ( ) {
2704
let name = entry.file_name ( ) .to_string_lossy ( ) .to_string ( ) ;
2705
let Some ( id) = name
2706
.strip_prefix ( " .tmp-" )
2707
.and_then ( | n | n.strip_suffix ( " .pack" ) )
2708
else {
2709
continue ;
2710
} ;
2711
let path = entry.path ( ) ;
2712
let meta = fs:: metadata( & path) ? ;
2713
let age = meta
2714
.modified ( )
2715
.ok ( )
2716
.and_then ( | m | m.elapsed ( ) .ok ( ) )
2717
.unwrap_or_default ( ) ;
2718
if age < min_age {
2719
progress ( & format! (
2720
" {name} : written to {} s ago, left alone" ,
2721
age.as_secs ( )
2722
) ) ;
2723
continue ;
2724
}
2725
let mut f = BufReader:: with_capacity( 1 < < 20 , fs:: File:: open( & path) ? ) ;
2726
let mut magic = [ 0 u8 ; 8 ] ;
2727
if f.read_exact ( & mut magic) .is_err ( ) | | & magic ! = PACK_MAGIC {
2728
progress ( & format! ( " {name} : not a pack, left alone" ) ) ;
2729
continue ;
2730
}
2731
let mut offset = PACK_MAGIC .len ( ) as u64 ;
2732
let mut index: Vec< ( ObjectRef, PackEntry) > = Vec :: new( ) ;
2733
loop {
2734
let mut head = [ 0 u8 ; 42 ] ;
2735
if f.read_exact ( & mut head) .is_err ( ) {
2736
break ;
2737
}
2738
let Some ( ty) = ObjectType:: ALL .get ( head[ 0 ] as usize ) .copied ( ) else {
2739
break ;
2740
} ;
2741
let flags = head[ 1 ] ;
2742
let stored_len = u32 :: from_le_bytes( head[ 2 .. 6 ] .try_into ( ) .unwrap ( ) ) ;
2743
let raw_len = u32 :: from_le_bytes( head[ 6 .. 10 ] .try_into ( ) .unwrap ( ) ) ;
2744
let r = ObjectRef( head[ 10 .. 42 ] .try_into ( ) .unwrap ( ) ) ;
2745
let mut stored = vec! [ 0 u8 ; stored_len as usize ] ;
2746
if flags & FLAG_DICT ! = 0 | | f.read_exact ( & mut stored) .is_err ( ) {
2747
break ;
2748
}
2749
let e = PackEntry {
2750
offset: offset + 42 ,
2751
stored_len,
2752
raw_len,
2753
ty,
2754
flags,
2755
} ;
2756
if decode_entry ( & r, e, stored, None ) .is_err ( ) {
2757
break ;
2758
}
2759
index.push ( ( r, e) ) ;
2760
offset + = 42 + stored_len as u64 ;
2761
if index.len ( ) .is_multiple_of ( 200_000 ) {
2762
progress ( & format! (
2763
" {name} : {} objects checked, {} MiB" ,
2764
index.len ( ) ,
2765
offset > > 20
2766
) ) ;
2767
}
2768
}
2769
if index.is_empty ( ) {
2770
progress ( & format! ( " {name} : no whole object, left alone" ) ) ;
2771
continue ;
2772
}
2773
if offset < meta.len ( ) {
2774
progress ( & format! (
2775
" {name} : {} torn bytes at the end cut off" ,
2776
meta.len ( ) - offset
2777
) ) ;
2778
fs:: OpenOptions:: new( )
2779
.write ( true )
2780
.open ( & path) ?
2781
.set_len ( offset) ? ;
2782
}
2783
index.sort_by_key ( | ( r , _) | * r) ;
2784
index.dedup_by_key ( | ( r , _) | * r) ;
2785
objects + = index.len ( ) ;
2786
packs + = 1 ;
2787
progress ( & format! ( " {name} : {} objects indexed" , index.len ( ) ) ) ;
2788
self .finish_pack_files ( id, & path, & index) ? ;
2789
}
2790
Ok ( ( packs, objects) )
2791
}
2792
d02e545d Andy McCall 1 month ago ↶
2793
ff0af1ee unknown 1 month ago
2794
pub fn list ( & self ) -> io:: Result< Vec< ObjectRef> > {
64818938 Andy McCall 1 month ago ↶
2795
let mut out: Vec< ObjectRef> = self
2796
.loose_objects ( )
2797
.map_err ( | e | io:: Error:: other( e.to_string ( ) ) ) ?
2798
.into_iter ( )
2799
.map ( | ( r , _) | r )
2800
.collect ( ) ;
d02e545d Andy McCall 1 month ago ↶
2801
for p in self .packs.borrow ( ) .iter ( ) {
2802
out.extend ( p.refs.iter ( ) .copied ( ) ) ;
ff0af1ee unknown 1 month ago
2803
}
2804
out.sort ( ) ;
d02e545d Andy McCall 1 month ago ↶
2805
out.dedup ( ) ;
ff0af1ee unknown 1 month ago
2806
Ok ( out)
767ac6cd Andy McCall 17 days ago
2807
}
2808
}
2809
2810
2811
2812
fn is_miss ( e : & StoreError) -> bool {
2813
match e {
2814
StoreError:: NotFound( _ ) => true ,
2815
StoreError:: Corrupt( _ , why) => why = = NO_DICT ,
2816
_ => false ,
ff0af1ee unknown 1 month ago
2817
}
afaba5f1 Andy McCall 1 month ago ↶
2818
}
2819
64818938 Andy McCall 1 month ago ↶
2820
795b8b50 Andy McCall 9 days ago ↶
2821
2822
2823
fn read_order ( place : & [Option< ( usize , PackEntry) > ]) -> Vec< usize > {
2824
let mut order: Vec< usize > = ( 0 .. place.len ( ) ) .collect ( ) ;
2825
order.sort_by_key ( | & i | match & place[ i] {
2826
Some ( ( p, e) ) => ( 0 , * p, e.offset) ,
2827
None => ( 1 , 0 , 0 ) ,
2828
} ) ;
2829
order
2830
}
2831
64818938 Andy McCall 1 month ago ↶
2832
fn parse_loose ( data : & [u8 ], r : & ObjectRef) -> Result< Object> {
2833
let nul = data
2834
.iter ( )
2835
.position ( | & b | b = = 0 )
2836
.ok_or_else ( | | StoreError:: Corrupt( * r, " no header" .into ( ) ) ) ? ;
2837
let head = std:: str:: from_utf8( & data[ .. nul] )
2838
.map_err ( | _| StoreError:: Corrupt( * r, " header not UTF-8" .into ( ) ) ) ? ;
2839
let mut parts = head.split ( ' ' ) ;
2840
let ( Some ( " oxy1" ) , Some ( ty) , Some ( len) , None ) =
2841
( parts.next ( ) , parts.next ( ) , parts.next ( ) , parts.next ( ) )
2842
else {
2843
return Err ( StoreError:: Corrupt( * r, format! ( " bad header {head:?} " ) ) ) ;
afaba5f1 Andy McCall 1 month ago ↶
2844
} ;
64818938 Andy McCall 1 month ago ↶
2845
let ty = ObjectType:: from_name( ty)
2846
.ok_or_else ( | | StoreError:: Corrupt( * r, format! ( " unknown type {ty} " ) ) ) ? ;
2847
let len: usize = len
2848
.parse ( )
2849
.map_err ( | _| StoreError:: Corrupt( * r, " bad length" .into ( ) ) ) ? ;
2850
let body = & data[ nul + 1 .. ] ;
2851
if body.len ( ) ! = len {
2852
return Err ( StoreError:: Corrupt(
2853
* r,
2854
format! ( " length {} != header {len} " , body.len ( ) ) ,
2855
) ) ;
2856
}
2857
if hash_object ( ty, body) ! = * r {
2858
return Err ( StoreError:: Corrupt( * r, " hash mismatch" .into ( ) ) ) ;
2859
}
2860
Ok ( ( ty, body.to_vec ( ) ) )
2861
}
2862
2863
2864
2865
# [ cfg ( test ) ]
2866
fn files_on_disk ( ) -> bool {
2867
std:: env:: var_os( " OXY_STORE_BACKEND" ) .is_none ( )
ff0af1ee unknown 1 month ago
2868
}
2869
2870
# [ cfg ( test ) ]
2871
mod tests {
2872
use super :: * ;
64818938 Andy McCall 1 month ago ↶
2873
2874
2875
2876
fn each_store ( dir : & Path, f : impl Fn( &str , Store ) ) {
2877
f ( " directory" , Store:: open( dir.join ( " objects" ) ) .unwrap ( ) ) ;
2878
f (
2879
" memory" ,
2880
Store:: open_on( Arc:: new( MemoryStore:: new( ) ) , dir.join ( " scratch" ) ) .unwrap ( ) ,
2881
) ;
2882
}
ff0af1ee unknown 1 month ago
2883
2884
# [ test ]
2885
fn put_get_verify ( ) {
64818938 Andy McCall 1 month ago ↶
2886
if ! super :: files_on_disk( ) {
2887
return ;
2888
}
ff0af1ee unknown 1 month ago
2889
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2890
let s = Store:: open( dir.path ( ) .join ( " objects" ) ) .unwrap ( ) ;
2891
let r = s.put ( ObjectType:: Chunk, b " hello" ) .unwrap ( ) ;
2892
assert! ( s.has ( & r) ) ;
2893
assert_eq! ( s.get ( & r) .unwrap ( ) , ( ObjectType:: Chunk, b " hello" .to_vec ( ) ) ) ;
2894
assert_eq! ( s.put ( ObjectType:: Chunk, b " hello" ) .unwrap ( ) , r) ;
2895
assert_eq! ( s.list ( ) .unwrap ( ) , vec! [ r] ) ;
75167672 Andy McCall 1 month ago
2896
assert! ( matches! (
2897
s.get_typed ( & r, ObjectType:: Blob) ,
2898
Err ( StoreError:: WrongType( .. ) )
2899
) ) ;
2900
assert! ( matches! (
2901
s.get ( & ObjectRef( [ 9 ; 32 ] ) ) ,
2902
Err ( StoreError:: NotFound( _ ) )
2903
) ) ;
ff0af1ee unknown 1 month ago
2904
2905
2906
let path = s.path_for ( & r) ;
2907
let mut data = fs:: read( & path) .unwrap ( ) ;
2908
let last = data.len ( ) - 1 ;
2909
data[ last] ^ = 1 ;
2910
fs:: write( & path, data) .unwrap ( ) ;
2911
assert! ( matches! ( s.get ( & r) , Err ( StoreError:: Corrupt( .. ) ) ) ) ;
2912
}
2913
2914
# [ test ]
d02e545d Andy McCall 1 month ago ↶
2915
fn packing_keeps_every_object_readable ( ) {
64818938 Andy McCall 1 month ago ↶
2916
if ! super :: files_on_disk( ) {
2917
return ;
2918
}
d02e545d Andy McCall 1 month ago ↶
2919
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2920
let s = Store:: open( dir.path ( ) ) .unwrap ( ) ;
2921
let mut refs = Vec :: new( ) ;
2922
for i in 0 .. 300 u32 {
2923
let body = format! ( " object {i} " ) .repeat ( 1 + ( i % 7 ) as usize ) ;
2924
refs.push ( ( s.put ( ObjectType:: Chunk, body.as_bytes ( ) ) .unwrap ( ) , body) ) ;
2925
}
2926
let c = crate :: objects:: tests:: sample_change( ) ;
2927
let cr = s.put_change ( & c) .unwrap ( ) ;
2928
assert_eq! ( s.loose_count ( ) .unwrap ( ) , 301 ) ;
2929
let report = s.pack_loose ( ) .unwrap ( ) ;
2930
assert_eq! ( report.objects, 301 ) ;
2931
assert! ( report.packed_bytes < report.raw_bytes, " {report:?}" ) ;
2932
assert_eq! ( s.loose_count ( ) .unwrap ( ) , 0 ) ;
2933
assert_eq! ( s.pack_count ( ) , 1 ) ;
2934
for ( r, body) in & refs {
2935
assert! ( s.has ( r) ) ;
75167672 Andy McCall 1 month ago
2936
assert_eq! (
2937
s.get ( r) .unwrap ( ) ,
2938
( ObjectType:: Chunk, body.as_bytes ( ) .to_vec ( ) )
2939
) ;
d02e545d Andy McCall 1 month ago ↶
2940
}
2941
assert_eq! ( s.get_change ( & cr) .unwrap ( ) , c) ;
2942
assert_eq! ( s.list ( ) .unwrap ( ) .len ( ) , 301 ) ;
2943
2944
75167672 Andy McCall 1 month ago
2945
assert_eq! (
2946
s.put ( ObjectType:: Chunk, refs[ 0 ] .1. as_bytes ( ) ) .unwrap ( ) ,
2947
refs[ 0 ] .0
2948
) ;
d02e545d Andy McCall 1 month ago ↶
2949
assert_eq! ( s.loose_count ( ) .unwrap ( ) , 0 ) ;
2950
let extra = s.put ( ObjectType:: Chunk, b " after packing" ) .unwrap ( ) ;
2951
s.pack_loose ( ) .unwrap ( ) ;
2952
assert_eq! ( s.pack_count ( ) , 2 ) ;
2953
assert_eq! ( s.get ( & extra) .unwrap ( ) .1 , b " after packing" ) ;
2954
2955
let s2 = Store:: open( dir.path ( ) ) .unwrap ( ) ;
2956
assert_eq! ( s2.pack_count ( ) , 2 ) ;
2957
assert_eq! ( s2.get ( & refs[ 5 ] .0 ) .unwrap ( ) .1 , refs[ 5 ] .1. as_bytes ( ) ) ;
75167672 Andy McCall 1 month ago
2958
assert! ( matches! (
2959
s2.get ( & ObjectRef( [ 9 ; 32 ] ) ) ,
2960
Err ( StoreError:: NotFound( _ ) )
2961
) ) ;
d02e545d Andy McCall 1 month ago ↶
2962
2963
let target = refs[ 5 ] .0 ;
2964
let ( pack_path, offset) = {
2965
let packs = s2.packs.borrow ( ) ;
2966
let ( i, e) = s2.find_packed ( & target) .unwrap ( ) ;
64818938 Andy McCall 1 month ago ↶
2967
( dir.path ( ) .join ( & packs[ i] .key) , e.offset as usize )
d02e545d Andy McCall 1 month ago ↶
2968
} ;
2969
let mut data = fs:: read( & pack_path) .unwrap ( ) ;
2970
data[ offset] ^ = 0xff ;
2971
fs:: write( & pack_path, data) .unwrap ( ) ;
2972
let s3 = Store:: open( dir.path ( ) ) .unwrap ( ) ;
2973
assert! ( matches! ( s3.get ( & target) , Err ( StoreError:: Corrupt( .. ) ) ) ) ;
75167672 Andy McCall 1 month ago
2974
assert_eq! (
2975
s3.get ( & refs[ 6 ] .0 ) .unwrap ( ) .1 ,
2976
refs[ 6 ] .1. as_bytes ( ) ,
2977
" other objects are unaffected"
2978
) ;
a17fc622 Andy McCall 1 month ago ↶
2979
}
2980
2981
# [ test ]
2982
fn a_full_repack_trains_a_dictionary_and_reads_back ( ) {
64818938 Andy McCall 1 month ago ↶
2983
if ! super :: files_on_disk( ) {
2984
return ;
2985
}
a17fc622 Andy McCall 1 month ago ↶
2986
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2987
let s = Store:: open( dir.path ( ) ) .unwrap ( ) ;
2988
2989
let bodies: Vec< Vec< u8 > > = ( 0 .. 1500 )
2990
.map ( | i | {
2991
format! (
2992
" // SPDX-License-Identifier: GPL-2.0\n #include <linux/module.h>\n \
2993
static int probe_{i} (struct device *dev)\n {{ \n \t return {i} ;\n }} \n "
2994
)
2995
.into_bytes ( )
2996
} )
2997
.collect ( ) ;
2998
let refs: Vec< ObjectRef> = bodies
2999
.iter ( )
3000
.map ( | b | s.put ( ObjectType:: Chunk, b) .unwrap ( ) )
3001
.collect ( ) ;
3002
s.repack_all ( REPACK_ZSTD_LEVEL ) .unwrap ( ) ;
3003
let dicts = fs:: read_dir( dir.path ( ) )
3004
.unwrap ( )
3005
.flatten ( )
3006
.filter ( | e | e.path ( ) .extension ( ) .is_some_and ( | x | x = = " dict" ) )
3007
.count ( ) ;
3008
assert_eq! ( dicts, 1 , " the pack has a dictionary" ) ;
3009
let with_dict = refs
3010
.iter ( )
3011
.filter ( | r | s.find_packed ( r) .unwrap ( ) .1. flags & FLAG_DICT ! = 0 )
3012
.count ( ) ;
3013
assert! ( with_dict > 1000 , " most use it: {with_dict}" ) ;
3014
let again = Store:: open( dir.path ( ) ) .unwrap ( ) ;
3015
for ( r, b) in refs.iter ( ) .zip ( & bodies) {
3016
assert_eq! ( & again.get ( r) .unwrap ( ) .1 , b) ;
3017
}
a67914bc Andy McCall 18 days ago ↶
3018
let packs = | dir : & Path| {
3019
let mut v: Vec< String> = fs:: read_dir( dir)
3020
.unwrap ( )
3021
.flatten ( )
3022
.map ( | e | e.file_name ( ) .to_string_lossy ( ) .into_owned ( ) )
3023
.filter ( | n | n.starts_with ( " pack-" ) )
3024
.collect ( ) ;
3025
v.sort ( ) ;
3026
v
3027
} ;
3028
3029
3030
let before = packs ( dir.path ( ) ) ;
3031
let ( report, merged) = again.repack_all ( REPACK_ZSTD_LEVEL ) .unwrap ( ) ;
3032
assert_eq! ( ( report.objects, merged) , ( 0 , 0 ) ) ;
3033
assert_eq! ( packs ( dir.path ( ) ) , before) ;
767ac6cd Andy McCall 17 days ago
3034
3035
a67914bc Andy McCall 18 days ago ↶
3036
let extra = again.put ( ObjectType:: Chunk, b " one more\n " ) .unwrap ( ) ;
a17fc622 Andy McCall 1 month ago ↶
3037
again.repack_all ( REPACK_ZSTD_LEVEL ) .unwrap ( ) ;
a67914bc Andy McCall 18 days ago ↶
3038
let files = packs ( dir.path ( ) ) ;
767ac6cd Andy McCall 17 days ago
3039
assert_eq! (
3040
files.len ( ) ,
3041
7 ,
3042
" two of pack, index and dictionary, and the retired record: {files:?}"
3043
) ;
3044
assert_eq! ( again.pack_count ( ) , 1 , " the old pack is not read" ) ;
a17fc622 Andy McCall 1 month ago ↶
3045
assert_eq! ( & again.get ( & refs[ 7 ] ) .unwrap ( ) .1 , & bodies[ 7 ] ) ;
a67914bc Andy McCall 18 days ago ↶
3046
assert_eq! ( again.get ( & extra) .unwrap ( ) .1 , b " one more\n " ) ;
767ac6cd Andy McCall 17 days ago
3047
again
3048
.expire_retired ( std:: time:: Duration:: ZERO , & mut silent ( ) )
3049
.unwrap ( ) ;
3050
let files = packs ( dir.path ( ) ) ;
3051
assert_eq! ( files.len ( ) , 4 , " one pack, index, dictionary: {files:?}" ) ;
3052
assert! ( before
3053
.iter ( )
3054
.filter ( | f | * f ! = RETIRED_KEY )
3055
.all ( | f | ! files.contains ( f) ) ) ;
3056
assert_eq! ( & again.get ( & refs[ 8 ] ) .unwrap ( ) .1 , & bodies[ 8 ] ) ;
d02e545d Andy McCall 1 month ago ↶
3057
}
3058
3059
# [ test ]
cdb5beba Andy McCall 1 month ago ↶
3060
fn nocompress_chunks_pack_raw ( ) {
3061
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
3062
let s = Store:: open( dir.path ( ) ) .unwrap ( ) ;
3063
let body = vec! [ b ' a' ; 10_000 ] ;
3064
let raw = s.put_chunk_nocompress ( & body) .unwrap ( ) ;
3065
let compressible = s.put ( ObjectType:: Chunk, & vec! [ b ' b' ; 10_000 ] ) .unwrap ( ) ;
3066
s.pack_loose ( ) .unwrap ( ) ;
3067
let packs = s.packs.borrow ( ) ;
3068
let ( i, e) = s.find_packed ( & raw) .unwrap ( ) ;
3069
assert_eq! ( ( e.flags, e.stored_len as usize ) , ( 0 , 10_000 ) ) ;
3070
let ( _ , c) = s.find_packed ( & compressible) .unwrap ( ) ;
3071
assert_eq! ( c.flags, FLAG_ZSTD ) ;
3072
assert! ( c.stored_len < 10_000 ) ;
64818938 Andy McCall 1 month ago ↶
3073
assert_eq! ( packs[ i] .fetch ( s.backend.as_ref ( ) , & raw, e) .unwrap ( ) .1 , body) ;
cdb5beba Andy McCall 1 month ago ↶
3074
}
3075
3076
# [ test ]
135212de Andy McCall 1 month ago ↶
3077
fn many_packs_merge_into_one ( ) {
3078
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
64818938 Andy McCall 1 month ago ↶
3079
each_store ( dir.path ( ) , | which , s | {
3080
let mut refs = Vec :: new( ) ;
3081
for i in 0 .. ( MAX_PACKS + 2 ) {
3082
refs.push (
3083
s.put ( ObjectType:: Chunk, format! ( " pack {i} " ) .as_bytes ( ) )
3084
.unwrap ( ) ,
3085
) ;
3086
s.pack_loose ( ) .unwrap ( ) ;
3087
}
3088
assert_eq! ( s.pack_count ( ) , MAX_PACKS + 2 , " {which}" ) ;
3089
assert_eq! ( s.merge_packs ( ) .unwrap ( ) , MAX_PACKS + 2 ) ;
3090
assert_eq! ( s.pack_count ( ) , 1 ) ;
3091
assert_eq! ( s.merge_packs ( ) .unwrap ( ) , 0 ) ;
3092
for ( i, r) in refs.iter ( ) .enumerate ( ) {
3093
assert_eq! ( s.get ( r) .unwrap ( ) .1 , format! ( " pack {i} " ) .as_bytes ( ) ) ;
3094
}
3095
let again = Store:: open_on( s.backend ( ) .clone ( ) , s.dir ( ) ) .unwrap ( ) ;
3096
assert_eq! ( again.pack_count ( ) , 1 , " {which}: reopened" ) ;
3097
} ) ;
135212de Andy McCall 1 month ago ↶
3098
}
3099
3100
# [ test ]
ba160f60 Andy McCall 1 month ago ↶
3101
fn prune_removes_unreachable_loose_and_packed_objects ( ) {
3102
use std:: collections:: HashSet;
3103
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
64818938 Andy McCall 1 month ago ↶
3104
each_store ( dir.path ( ) , | which , s | {
3105
let keep_a = s.put ( ObjectType:: Chunk, b " keep a" ) .unwrap ( ) ;
3106
let drop_a = s.put ( ObjectType:: Chunk, b " drop a" ) .unwrap ( ) ;
3107
s.pack_loose ( ) .unwrap ( ) ;
3108
let keep_b = s.put ( ObjectType:: Chunk, b " keep b" ) .unwrap ( ) ;
3109
let drop_b = s.put ( ObjectType:: Chunk, b " drop b" ) .unwrap ( ) ;
3110
let sk = crate :: identity:: SecretKey {
3111
ed_seed: [ 1 ; 32 ] ,
3112
ml_seed: [ 2 ; 32 ] ,
3113
} ;
3114
let id = sk.identity ( " a" , " b" , 0 ) ;
3115
let key = s.put_identity ( & id) .unwrap ( ) ;
3116
s.put_signature (
3117
& drop_b,
3118
& sk.sign ( crate :: identity:: Domain:: Change, & key, & drop_b) ,
3119
)
75167672 Andy McCall 1 month ago
3120
.unwrap ( ) ;
64818938 Andy McCall 1 month ago ↶
3121
s.index_seal ( & drop_a, & ObjectRef( [ 3 ; 32 ] ) ) .unwrap ( ) ;
3122
let keep: HashSet< ObjectRef> = [ keep_a, keep_b, key] .into_iter ( ) .collect ( ) ;
3123
3124
let r = s
3125
.prune ( & keep, std:: time:: Duration:: from_secs( 3600 ) )
3126
.unwrap ( ) ;
3127
assert_eq! ( ( r.objects, r.deferred) , ( 0 , 2 ) , " {which}" ) ;
3128
let r = s.prune ( & keep, std:: time:: Duration:: ZERO ) .unwrap ( ) ;
3129
assert_eq! ( r.objects, 2 , " {which}: {r:?}" ) ;
3130
assert! ( s.has ( & keep_a) & & s.has ( & keep_b) & & s.has ( & key) ) ;
3131
assert! ( ! s.has ( & drop_a) & & ! s.has ( & drop_b) ) ;
3132
assert! ( s.signatures ( & drop_b) .unwrap ( ) .is_empty ( ) ) ;
3133
assert! ( s.seals_for ( & drop_a) .unwrap ( ) .is_empty ( ) ) ;
3134
assert_eq! ( s.pack_count ( ) , 1 ) ;
3135
assert_eq! ( s.get ( & keep_a) .unwrap ( ) .1 , b " keep a" ) ;
3136
assert_eq! ( s.list ( ) .unwrap ( ) .len ( ) , 3 ) ;
3137
} ) ;
ba160f60 Andy McCall 1 month ago ↶
3138
}
3139
3140
# [ test ]
1983686f Andy McCall 1 month ago ↶
3141
fn signatures_are_stored_beside_objects ( ) {
64818938 Andy McCall 1 month ago ↶
3142
use crate :: identity:: { Domain, SecretKey} ;
3143
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
3144
each_store ( dir.path ( ) , | which , s | {
3145
let r = s.put ( ObjectType:: Chunk, b " x" ) .unwrap ( ) ;
3146
assert! ( s.signatures ( & r) .unwrap ( ) .is_empty ( ) ) ;
3147
let sk = SecretKey {
3148
ed_seed: [ 1 ; 32 ] ,
3149
ml_seed: [ 2 ; 32 ] ,
3150
} ;
3151
let id = sk.identity ( " a" , " b" , 0 ) ;
3152
let key = s.put_identity ( & id) .unwrap ( ) ;
3153
assert_eq! ( s.get_identity ( & key) .unwrap ( ) , id) ;
3154
let sig = sk.sign ( Domain:: Change, & key, & r) ;
3155
s.put_signature ( & r, & sig) .unwrap ( ) ;
3156
s.put_signature ( & r, & sig) .unwrap ( ) ;
3157
assert_eq! ( s.signatures ( & r) .unwrap ( ) , vec! [ sig.clone ( ) ] , " {which}" ) ;
3158
3159
let sk2 = SecretKey {
3160
ed_seed: [ 3 ; 32 ] ,
3161
ml_seed: [ 4 ; 32 ] ,
3162
} ;
3163
let key2 = s.put_identity ( & sk2.identity ( " c" , " d" , 0 ) ) .unwrap ( ) ;
3164
let sig2 = sk2.sign ( Domain:: Change, & key2, & r) ;
3165
s.put_signature ( & r, & sig2) .unwrap ( ) ;
3166
let mut both = vec! [ sig.clone ( ) , sig2.clone ( ) ] ;
3167
both.sort_by_key ( | s | s.key ) ;
3168
assert_eq! ( s.signatures ( & r) .unwrap ( ) , both) ;
3169
3170
assert_eq! ( s.get ( & r) .unwrap ( ) .1 , b " x" ) ;
3171
3172
s.index_seal ( & r, & ObjectRef( [ 5 ; 32 ] ) ) .unwrap ( ) ;
3173
s.index_seal ( & r, & ObjectRef( [ 4 ; 32 ] ) ) .unwrap ( ) ;
3174
s.index_seal ( & r, & ObjectRef( [ 5 ; 32 ] ) ) .unwrap ( ) ;
3175
assert_eq! (
3176
s.seals_for ( & r) .unwrap ( ) ,
3177
vec! [ ObjectRef( [ 4 ; 32 ] ) , ObjectRef( [ 5 ; 32 ] ) ]
3178
) ;
3179
let st = ObjectRef( [ 6 ; 32 ] ) ;
3180
s.index_receipt ( & st, & ObjectRef( [ 7 ; 32 ] ) ) .unwrap ( ) ;
3181
assert_eq! ( s.receipts_for ( & st) .unwrap ( ) , vec! [ ObjectRef( [ 7 ; 32 ] ) ] ) ;
3182
assert_eq! ( s.receipt_states ( ) .unwrap ( ) , vec! [ st] ) ;
3183
} ) ;
3184
}
3185
3186
3187
3188
3189
# [ test ]
3190
fn an_older_store_s_sidecar_lists_are_still_read ( ) {
3191
if ! super :: files_on_disk( ) {
3192
return ;
3193
}
1983686f Andy McCall 1 month ago ↶
3194
use crate :: identity:: { Domain, SecretKey} ;
3195
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
3196
let s = Store:: open( dir.path ( ) ) .unwrap ( ) ;
64818938 Andy McCall 1 month ago ↶
3197
let r = s.put ( ObjectType:: Chunk, b " old" ) .unwrap ( ) ;
3198
let h = r.to_hex ( ) ;
75167672 Andy McCall 1 month ago
3199
let sk = SecretKey {
3200
ed_seed: [ 1 ; 32 ] ,
3201
ml_seed: [ 2 ; 32 ] ,
3202
} ;
64818938 Andy McCall 1 month ago ↶
3203
let key = s.put_identity ( & sk.identity ( " a" , " b" , 0 ) ) .unwrap ( ) ;
1983686f Andy McCall 1 month ago ↶
3204
let sig = sk.sign ( Domain:: Change, & key, & r) ;
64818938 Andy McCall 1 month ago ↶
3205
let legacy = dir.path ( ) .join ( " sigs" ) .join ( & h[ .. 2 ] ) ;
3206
fs:: create_dir_all( & legacy) .unwrap ( ) ;
3207
fs:: write(
3208
legacy.join ( & h[ 2 .. ] ) ,
3209
Signature:: encode_list( std:: slice:: from_ref( & sig) ) ,
3210
)
3211
.unwrap ( ) ;
3212
let seals = dir.path ( ) .join ( " seals" ) .join ( & h[ .. 2 ] ) ;
3213
fs:: create_dir_all( & seals) .unwrap ( ) ;
3214
fs:: write(
3215
seals.join ( & h[ 2 .. ] ) ,
3216
format! ( " {} \n " , ObjectRef( [ 9 ; 32 ] ) .to_hex ( ) ) ,
3217
)
3218
.unwrap ( ) ;
3219
let receipts = dir.path ( ) .join ( " receipts" ) .join ( & h[ .. 2 ] ) ;
3220
fs:: create_dir_all( & receipts) .unwrap ( ) ;
3221
fs:: write(
3222
receipts.join ( & h[ 2 .. ] ) ,
3223
format! ( " {} \n " , ObjectRef( [ 8 ; 32 ] ) .to_hex ( ) ) ,
3224
)
3225
.unwrap ( ) ;
3226
3227
assert_eq! ( s.signatures ( & r) .unwrap ( ) , vec! [ sig.clone ( ) ] ) ;
3228
assert_eq! ( s.seals_for ( & r) .unwrap ( ) , vec! [ ObjectRef( [ 9 ; 32 ] ) ] ) ;
3229
assert_eq! ( s.receipts_for ( & r) .unwrap ( ) , vec! [ ObjectRef( [ 8 ; 32 ] ) ] ) ;
3230
assert_eq! ( s.receipt_states ( ) .unwrap ( ) , vec! [ r] ) ;
3231
1983686f Andy McCall 1 month ago ↶
3232
s.put_signature ( & r, & sig) .unwrap ( ) ;
64818938 Andy McCall 1 month ago ↶
3233
s.index_seal ( & r, & ObjectRef( [ 1 ; 32 ] ) ) .unwrap ( ) ;
3234
assert_eq! ( s.signatures ( & r) .unwrap ( ) .len ( ) , 1 ) ;
3235
assert_eq! (
3236
s.seals_for ( & r) .unwrap ( ) ,
3237
vec! [ ObjectRef( [ 1 ; 32 ] ) , ObjectRef( [ 9 ; 32 ] ) ]
3238
) ;
3239
3240
let r2 = s.put ( ObjectType:: Chunk, b " kept" ) .unwrap ( ) ;
3241
let keep = [ r2, key] .into_iter ( ) .collect ( ) ;
3242
s.prune ( & keep, std:: time:: Duration:: ZERO ) .unwrap ( ) ;
3243
assert! ( s.signatures ( & r) .unwrap ( ) .is_empty ( ) ) ;
3244
assert! ( s.seals_for ( & r) .unwrap ( ) .is_empty ( ) ) ;
3245
assert! ( s.receipt_states ( ) .unwrap ( ) .is_empty ( ) ) ;
1983686f Andy McCall 1 month ago ↶
3246
}
3247
3248
# [ test ]
ff0af1ee unknown 1 month ago
3249
fn change_round_trip_through_store ( ) {
3250
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
64818938 Andy McCall 1 month ago ↶
3251
each_store ( dir.path ( ) , | which , s | {
3252
let c = crate :: objects:: tests:: sample_change( ) ;
3253
let r = s.put_change ( & c) .unwrap ( ) ;
3254
assert_eq! ( r, c.hash ( ) , " {which}" ) ;
3255
assert_eq! ( s.get_change ( & r) .unwrap ( ) , c) ;
3256
assert_eq! ( s.get_change_meta ( & r) .unwrap ( ) .message, c.message) ;
3257
} ) ;
ff0af1ee unknown 1 month ago
3258
}
2b872d07 Andy McCall 1 month ago ↶
3259
3260
fn xorshift ( state : & mut u64 ) -> u64 {
3261
* state ^ = * state < < 13 ;
3262
* state ^ = * state > > 7 ;
3263
* state ^ = * state < < 17 ;
3264
* state
3265
}
3266
3267
3268
3269
3270
# [ test ]
3271
fn corrupted_packs_and_indexes_fail_cleanly ( ) {
64818938 Andy McCall 1 month ago ↶
3272
if ! super :: files_on_disk( ) {
3273
return ;
3274
}
2b872d07 Andy McCall 1 month ago ↶
3275
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
3276
let objects = dir.path ( ) .join ( " objects" ) ;
3277
let s = Store:: open( & objects) .unwrap ( ) ;
3278
let mut refs = Vec :: new( ) ;
3279
let mut seed = 0x9e3779b97f4a7c15 u64 ;
3280
for i in 0 .. 40 u32 {
3281
let len = ( xorshift ( & mut seed) % 5000 ) as usize ;
3282
let mut body = vec! [ 0 u8 ; len] ;
3283
for b in body.iter_mut ( ) {
75167672 Andy McCall 1 month ago
3284
* b = if i % 2 = = 0 {
3285
( xorshift ( & mut seed) & 0xff ) as u8
3286
} else {
3287
b ' a' + ( i % 26 ) as u8
3288
} ;
2b872d07 Andy McCall 1 month ago ↶
3289
}
3290
refs.push ( s.put ( ObjectType:: Chunk, & body) .unwrap ( ) ) ;
3291
}
3292
s.pack_loose ( ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
3293
let pack = fs:: read_dir( & objects)
3294
.unwrap ( )
3295
.filter_map ( | e | e.ok ( ) .map ( | e | e.path ( ) ) )
3296
.find ( | p | p.extension ( ) .is_some_and ( | x | x = = " pack" ) )
3297
.unwrap ( ) ;
2b872d07 Andy McCall 1 month ago ↶
3298
let idx = pack.with_extension ( " idx" ) ;
3299
let pristine_pack = fs:: read( & pack) .unwrap ( ) ;
3300
let pristine_idx = fs:: read( & idx) .unwrap ( ) ;
3301
3302
for round in 0 .. 300 u32 {
75167672 Andy McCall 1 month ago
3303
let ( path, pristine) = if round % 2 = = 0 {
3304
( & pack, & pristine_pack)
3305
} else {
3306
( & idx, & pristine_idx)
3307
} ;
2b872d07 Andy McCall 1 month ago ↶
3308
let mut data = pristine.clone ( ) ;
3309
match xorshift ( & mut seed) % 4 {
3310
0 => {
3311
let at = ( xorshift ( & mut seed) as usize ) % data.len ( ) ;
3312
data[ at] ^ = 1 < < ( xorshift ( & mut seed) % 8 ) ;
3313
}
3314
1 => {
3315
let at = ( xorshift ( & mut seed) as usize ) % data.len ( ) ;
3316
data[ at] = ( xorshift ( & mut seed) & 0xff ) as u8 ;
3317
}
3318
2 => {
3319
let keep = ( xorshift ( & mut seed) as usize ) % data.len ( ) ;
3320
data.truncate ( keep) ;
3321
}
3322
_ => {
3323
let at = ( xorshift ( & mut seed) as usize ) % data.len ( ) ;
3324
let n = ( ( xorshift ( & mut seed) % 8 ) + 1 ) as usize ;
3325
for k in 0 .. n.min ( data.len ( ) - at) {
3326
data[ at + k] = 0xff ;
3327
}
3328
}
3329
}
3330
fs:: write( path, & data) .unwrap ( ) ;
3331
3332
3333
if let Ok ( s2) = Store:: open( & objects) {
3334
for r in & refs {
3335
match s2.get ( r) {
75167672 Andy McCall 1 month ago
3336
Ok ( ( ty, body) ) => assert_eq! (
3337
hash_object ( ty, & body) ,
3338
* r,
3339
" a returned object always matches its reference"
3340
) ,
3341
Err ( StoreError:: Corrupt( .. ) )
3342
| Err ( StoreError:: NotFound( _ ) )
3343
| Err ( StoreError:: Io( _ ) ) => { }
2b872d07 Andy McCall 1 month ago ↶
3344
Err ( e) => panic! ( " unexpected error {e}" ) ,
3345
}
3346
}
3347
}
3348
fs:: write( path, pristine) .unwrap ( ) ;
3349
}
3350
3351
let s3 = Store:: open( & objects) .unwrap ( ) ;
3352
for r in & refs {
3353
let ( ty, body) = s3.get ( r) .unwrap ( ) ;
3354
assert_eq! ( hash_object ( ty, & body) , * r) ;
3355
}
3356
}
8f10a07a Andy McCall 1 month ago ↶
3357
3358
3359
3360
# [ test ]
3361
fn batches_write_loose_or_one_pack ( ) {
3362
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
64818938 Andy McCall 1 month ago ↶
3363
each_store ( dir.path ( ) , | which , s | {
3364
let outer = s.begin_batch ( ) .unwrap ( ) ;
3365
let a = s.put ( ObjectType:: Chunk, b " small a" ) .unwrap ( ) ;
3366
{
3367
let inner = s.begin_batch ( ) .unwrap ( ) ;
3368
let b = s.put ( ObjectType:: Chunk, b " small b" ) .unwrap ( ) ;
3369
assert! ( s.has ( & b) ) ;
3370
assert_eq! ( s.get ( & b) .unwrap ( ) .1 , b " small b" ) ;
3371
inner.finish ( ) .unwrap ( ) ;
3372
assert_eq! (
3373
s.loose_count ( ) .unwrap ( ) ,
3374
0 ,
3375
" {which}: the inner batch defers to the outer one"
3376
) ;
3377
}
3378
assert_eq! ( s.get ( & a) .unwrap ( ) .1 , b " small a" ) ;
75167672 Andy McCall 1 month ago
3379
assert_eq! (
64818938 Andy McCall 1 month ago ↶
3380
s.put ( ObjectType:: Chunk, b " small a" ) .unwrap ( ) ,
3381
a,
3382
" idempotent while pending"
75167672 Andy McCall 1 month ago
3383
) ;
64818938 Andy McCall 1 month ago ↶
3384
outer.finish ( ) .unwrap ( ) ;
3385
assert_eq! ( s.loose_count ( ) .unwrap ( ) , 2 ) ;
3386
assert_eq! ( s.pack_count ( ) , 0 ) ;
3387
assert_eq! ( s.get ( & a) .unwrap ( ) .1 , b " small a" ) ;
8f10a07a Andy McCall 1 month ago ↶
3388
64818938 Andy McCall 1 month ago ↶
3389
3390
let big = s.begin_batch ( ) .unwrap ( ) ;
3391
let mut refs = Vec :: new( ) ;
3392
let mut seed = 7 u64 ;
3393
for i in 0 .. 300 u32 {
3394
let mut body = vec! [ b ' z' ; 40_000 ] ;
3395
if i % 3 = = 0 {
3396
for b in body.iter_mut ( ) {
3397
* b = ( xorshift ( & mut seed) & 0xff ) as u8 ;
3398
}
8f10a07a Andy McCall 1 month ago ↶
3399
}
64818938 Andy McCall 1 month ago ↶
3400
body[ .. 4 ] .copy_from_slice ( & i.to_le_bytes ( ) ) ;
3401
let r = if i % 5 = = 0 {
3402
s.put_chunk_nocompress ( & body) .unwrap ( )
3403
} else {
3404
s.put ( ObjectType:: Chunk, & body) .unwrap ( )
3405
} ;
3406
assert_eq! (
3407
s.get ( & r) .unwrap ( ) .1 ,
3408
body,
3409
" readable while pending (object {i})"
3410
) ;
3411
refs.push ( ( r, body) ) ;
8f10a07a Andy McCall 1 month ago ↶
3412
}
64818938 Andy McCall 1 month ago ↶
3413
assert! ( s.has ( & refs[ 0 ] .0 ) ) ;
3414
big.finish ( ) .unwrap ( ) ;
3415
assert_eq! ( s.pack_count ( ) , 1 ) ;
75167672 Andy McCall 1 month ago
3416
assert_eq! (
64818938 Andy McCall 1 month ago ↶
3417
s.loose_count ( ) .unwrap ( ) ,
3418
2 ,
3419
" nothing from the big batch is loose"
75167672 Andy McCall 1 month ago
3420
) ;
64818938 Andy McCall 1 month ago ↶
3421
assert! ( fs:: read_dir( s.dir ( ) )
75167672 Andy McCall 1 month ago
3422
.unwrap ( )
64818938 Andy McCall 1 month ago ↶
3423
.flatten ( )
3424
.all ( | e | ! e.file_name ( ) .to_string_lossy ( ) .contains ( " tmp-" ) ) ) ;
3425
let s2 = Store:: open_on( s.backend ( ) .clone ( ) , s.dir ( ) ) .unwrap ( ) ;
3426
for ( r, body) in & refs {
3427
assert_eq! ( s2.get ( r) .unwrap ( ) .1 , * body) ;
3428
}
3429
3430
{
3431
let _b = s2.begin_batch ( ) .unwrap ( ) ;
3432
s2.put ( ObjectType:: Chunk, b " dropped" ) .unwrap ( ) ;
3433
}
3434
assert_eq! (
3435
s2.get ( & hash_object ( ObjectType:: Chunk, b " dropped" ) )
3436
.unwrap ( )
3437
.1 ,
3438
b " dropped"
3439
) ;
3440
} ) ;
795b8b50 Andy McCall 9 days ago ↶
3441
}
3442
3443
# [ test ]
3444
fn packed_objects_are_read_by_pack_then_offset ( ) {
3445
let at = | p : usize , offset : u64 | {
3446
Some ( (
3447
p,
3448
PackEntry {
3449
offset,
3450
stored_len: 1 ,
3451
raw_len: 1 ,
3452
ty: ObjectType:: Chunk,
3453
flags: 0 ,
3454
} ,
3455
) )
3456
} ;
3457
let place = vec! [ at ( 1 , 50 ) , None , at ( 0 , 900 ) , at ( 1 , 10 ) , None , at ( 0 , 5 ) ] ;
3458
assert_eq! ( read_order ( & place) , [ 5 , 2 , 3 , 0 , 1 , 4 ] ) ;
afaba5f1 Andy McCall 1 month ago ↶
3459
}
3460
3461
# [ test ]
3462
fn get_many_reads_loose_and_packed_objects_in_order ( ) {
3463
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
64818938 Andy McCall 1 month ago ↶
3464
each_store ( dir.path ( ) , | which , s | {
3465
let packed: Vec< ObjectRef> = ( 0 .. 20 )
3466
.map ( | i | {
3467
s.put ( ObjectType:: Chunk, format! ( " packed {i} " ) .as_bytes ( ) )
3468
.unwrap ( )
3469
} )
3470
.collect ( ) ;
3471
s.pack_loose ( ) .unwrap ( ) ;
3472
let loose: Vec< ObjectRef> = ( 0 .. 20 )
3473
.map ( | i | {
3474
s.put ( ObjectType:: Chunk, format! ( " loose {i} " ) .as_bytes ( ) )
3475
.unwrap ( )
3476
} )
3477
.collect ( ) ;
3478
let missing = hash_object ( ObjectType:: Chunk, b " never stored" ) ;
3479
let mut refs: Vec< ObjectRef> = Vec :: new( ) ;
3480
for i in 0 .. 20 {
3481
refs.push ( loose[ i] ) ;
3482
refs.push ( packed[ i] ) ;
3483
}
3484
refs.push ( missing) ;
3485
let got = s.get_many ( & refs) ;
3486
assert_eq! ( got.len ( ) , refs.len ( ) ) ;
3487
for ( i, r) in refs.iter ( ) .enumerate ( ) .take ( 40 ) {
3488
let want = if i % 2 = = 0 {
3489
format! ( " loose {} " , i / 2 )
3490
} else {
3491
format! ( " packed {} " , i / 2 )
3492
} ;
3493
let ( ty, body) = got[ i] .as_ref ( ) .unwrap ( ) ;
3494
assert_eq! (
3495
( * ty, body.as_slice ( ) ) ,
3496
( ObjectType:: Chunk, want.as_bytes ( ) ) ,
3497
" {which}: {r:?}"
3498
) ;
3499
}
3500
assert! ( matches! ( got[ 40 ] , Err ( StoreError:: NotFound( _ ) ) ) ) ;
3501
} ) ;
09245702 Andy McCall 18 days ago ↶
3502
}
3503
3504
3505
3506
3507
# [ test ]
3508
fn packed_objects_cost_no_loose_lookups ( ) {
3509
use std:: sync:: atomic:: { AtomicUsize, Ordering} ;
3510
struct Counting ( MemoryStore, AtomicUsize) ;
3511
impl crate:: strata:: Store for Counting {
3512
fn describe ( & self ) -> String {
3513
" counting" .into ( )
3514
}
3515
fn has ( & self , k : & str ) -> crate:: strata:: Result< bool > {
3516
self .0. has ( k)
3517
}
3518
fn get ( & self , k : & str ) -> crate:: strata:: Result< Vec< u8 > > {
3519
if k.starts_with ( " loose/" ) {
3520
self .1. fetch_add ( 1 , Ordering:: Relaxed) ;
3521
}
3522
self .0. get ( k)
3523
}
3524
fn get_range (
3525
& self ,
3526
k : & str ,
3527
r : std:: ops:: Range< u64 > ,
3528
) -> crate:: strata:: Result< Vec< u8 > > {
3529
self .0. get_range ( k, r)
3530
}
3531
fn put_new ( & self , k : & str , b : & [u8 ]) -> crate:: strata:: Result< ( ) > {
3532
self .0. put_new ( k, b)
3533
}
3534
fn list ( & self , p : & str ) -> crate:: strata:: Result< Vec< crate:: strata:: Entry> > {
3535
self .0. list ( p)
3536
}
3537
fn delete ( & self , k : & str ) -> crate:: strata:: Result< ( ) > {
3538
self .0. delete ( k)
3539
}
3540
fn read_ref (
3541
& self ,
3542
k : & str ,
3543
) -> crate:: strata:: Result< Option< ( Vec< u8 > , crate:: strata:: Version) > > {
3544
self .0. read_ref ( k)
3545
}
3546
fn write_ref (
3547
& self ,
3548
k : & str ,
3549
b : & [u8 ],
3550
e : Option< & crate:: strata:: Version> ,
3551
) -> crate:: strata:: Result< crate:: strata:: Version> {
3552
self .0. write_ref ( k, b, e)
3553
}
3554
}
3555
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
3556
let backend = Arc:: new( Counting( MemoryStore:: new( ) , AtomicUsize:: new( 0 ) ) ) ;
3557
let s = Store:: open_on( backend.clone ( ) , dir.path ( ) .join ( " scratch" ) ) .unwrap ( ) ;
3558
let refs: Vec< ObjectRef> = ( 0 .. 10 )
3559
.map ( | i | {
3560
s.put ( ObjectType:: Chunk, format! ( " p {i} " ) .as_bytes ( ) )
3561
.unwrap ( )
3562
} )
3563
.collect ( ) ;
3564
s.pack_loose ( ) .unwrap ( ) ;
3565
let s = Store:: open_on( backend.clone ( ) , dir.path ( ) .join ( " scratch" ) ) .unwrap ( ) ;
3566
backend.1. store ( 0 , Ordering:: Relaxed) ;
3567
for r in & refs {
3568
assert! ( s.get ( r) .is_ok ( ) ) ;
3569
}
3570
assert! ( s.get_many ( & refs) .iter ( ) .all ( | g | g.is_ok ( ) ) ) ;
3571
assert_eq! ( backend.1. load ( Ordering:: Relaxed) , 0 , " no loose lookups" ) ;
3572
3573
let missing = hash_object ( ObjectType:: Chunk, b " never stored" ) ;
3574
assert! ( matches! ( s.get ( & missing) , Err ( StoreError:: NotFound( _ ) ) ) ) ;
3575
assert! ( backend.1. load ( Ordering:: Relaxed) > 0 ) ;
767ac6cd Andy McCall 17 days ago
3576
}
3577
}
3578
3579
3580
3581
# [ cfg ( test ) ]
3582
pub ( crate ) mod reader_tests {
3583
use super :: * ;
3584
3585
fn chunk ( i : usize ) -> Vec< u8 > {
3586
format! ( " object {i} " ) .repeat ( 1 + i % 5 ) .into_bytes ( )
3587
}
3588
3589
3590
fn each_pair ( f : impl Fn( &str , Store , Store ) ) {
3591
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
3592
let s = Store:: open( dir.path ( ) .join ( " objects" ) ) .unwrap ( ) ;
3593
let r = Store:: open( dir.path ( ) .join ( " objects" ) ) .unwrap ( ) ;
3594
f ( " directory" , s, r) ;
3595
let m: Arc< Backend> = Arc:: new( MemoryStore:: new( ) ) ;
3596
let s = Store:: open_on( m.clone ( ) , dir.path ( ) .join ( " scratch" ) ) .unwrap ( ) ;
3597
let r = Store:: open_on( m, dir.path ( ) .join ( " scratch-r" ) ) .unwrap ( ) ;
3598
f ( " memory" , s, r) ;
3599
}
3600
3601
3602
pub ( crate ) fn age_retired ( s : & Store, by : std:: time:: Duration) {
3603
s.update_retired ( | m | {
3604
for t in m.values_mut ( ) {
3605
* t = t.saturating_sub ( by.as_millis ( ) as u64 ) ;
3606
}
3607
} )
3608
.unwrap ( ) ;
3609
}
3610
3611
fn retired_keys ( s : & Store) -> Vec< String> {
3612
s.read_retired ( ) .unwrap ( ) .0. into_keys ( ) .collect ( )
3613
}
3614
3615
# [ test ]
3616
fn a_reader_opened_before_a_repack_reads_every_object_after_it ( ) {
3617
each_pair ( | which , writer , _| {
3618
let mut refs = Vec :: new( ) ;
3619
for i in 0 .. 40 {
3620
refs.push ( writer.put ( ObjectType:: Chunk, & chunk ( i) ) .unwrap ( ) ) ;
3621
}
3622
writer.pack_loose ( ) .unwrap ( ) ;
3623
for i in 40 .. 80 {
3624
refs.push ( writer.put ( ObjectType:: Chunk, & chunk ( i) ) .unwrap ( ) ) ;
3625
}
3626
3627
let reader = Store:: open_on( writer.backend ( ) .clone ( ) , writer.dir ( ) ) .unwrap ( ) ;
3628
assert_eq! ( reader.pack_count ( ) , 1 , " {which}" ) ;
3629
let ( _ , merged) = writer.repack_all ( REPACK_ZSTD_LEVEL ) .unwrap ( ) ;
3630
assert_eq! ( merged, 1 ) ;
3631
3632
let retired = retired_keys ( & writer) ;
3633
assert_eq! ( retired.len ( ) , 81 , " {which}: {retired:?}" ) ;
3634
assert_eq! ( writer.loose_count ( ) .unwrap ( ) , 0 , " retired is not loose" ) ;
3635
3636
for ( i, r) in refs.iter ( ) .enumerate ( ) {
3637
assert_eq! ( reader.get ( r) .unwrap ( ) .1 , chunk ( i) , " {which}: {i}" ) ;
3638
}
3639
assert_eq! ( reader.pack_count ( ) , 1 ) ;
3640
3641
let fresh = Store:: open_on( writer.backend ( ) .clone ( ) , writer.dir ( ) ) .unwrap ( ) ;
3642
assert_eq! ( fresh.pack_count ( ) , 1 ) ;
3643
assert_ne! ( fresh.packs.borrow ( ) [ 0 ] .key, reader.packs.borrow ( ) [ 0 ] .key) ;
3644
3645
3646
let report = writer
3647
.expire_retired ( std:: time:: Duration:: ZERO , & mut silent ( ) )
3648
.unwrap ( ) ;
3649
assert_eq! ( ( report.packs, report.loose) , ( 1 , 80 ) , " {which}" ) ;
3650
assert! ( retired_keys ( & writer) .is_empty ( ) ) ;
3651
for ( i, r) in refs.iter ( ) .enumerate ( ) {
3652
assert_eq! ( reader.get ( r) .unwrap ( ) .1 , chunk ( i) , " {which}: {i}" ) ;
3653
}
3654
let other = Store:: open_on( writer.backend ( ) .clone ( ) , writer.dir ( ) ) .unwrap ( ) ;
3655
let before = other.packs.borrow ( ) [ 0 ] .key.clone ( ) ;
3656
writer.put ( ObjectType:: Chunk, b " new" ) .unwrap ( ) ;
3657
writer.repack_all ( REPACK_ZSTD_LEVEL ) .unwrap ( ) ;
3658
writer
3659
.expire_retired ( std:: time:: Duration:: ZERO , & mut silent ( ) )
3660
.unwrap ( ) ;
3661
assert! ( other.get_many ( & refs) .iter ( ) .all ( | g | g.is_ok ( ) ) , " {which}" ) ;
3662
assert_ne! ( other.packs.borrow ( ) [ 0 ] .key, before) ;
3663
let third = Store:: open_on( writer.backend ( ) .clone ( ) , writer.dir ( ) ) .unwrap ( ) ;
3664
writer.put ( ObjectType:: Chunk, b " newer" ) .unwrap ( ) ;
3665
writer.repack_all ( REPACK_ZSTD_LEVEL ) .unwrap ( ) ;
3666
writer
3667
.expire_retired ( std:: time:: Duration:: ZERO , & mut silent ( ) )
3668
.unwrap ( ) ;
3669
assert! ( refs.iter ( ) .all ( | r | third.has ( r) ) , " {which}" ) ;
3670
assert! ( ! third.has ( & hash_object ( ObjectType:: Chunk, b " never" ) ) ) ;
3671
} ) ;
3672
}
3673
3674
# [ test ]
3675
fn a_reader_opened_before_a_spilled_batch_reads_its_objects ( ) {
3676
each_pair ( | which , writer , reader | {
3677
assert_eq! ( reader.pack_count ( ) , 0 ) ;
3678
let mut refs = Vec :: new( ) ;
3679
{
3680
let batch = writer.begin_batch ( ) .unwrap ( ) ;
3681
for i in 0 .. ( SPILL_OBJECTS + 50 ) {
3682
refs.push ( writer.put ( ObjectType:: Chunk, & chunk ( i) ) .unwrap ( ) ) ;
3683
}
3684
batch.finish ( ) .unwrap ( ) ;
3685
}
3686
assert_eq! ( writer.pack_count ( ) , 1 , " {which}: the batch spilled" ) ;
3687
assert_eq! ( reader.get ( & refs[ 0 ] ) .unwrap ( ) .1 , chunk ( 0 ) , " {which}" ) ;
3688
assert_eq! ( reader.pack_count ( ) , 1 , " the miss loaded the new pack" ) ;
3689
let got = reader.get_many ( & refs) ;
3690
assert! ( got.iter ( ) .all ( | g | g.is_ok ( ) ) , " {which}" ) ;
3691
3692
let late = Store:: open_on( writer.backend ( ) .clone ( ) , writer.dir ( ) ) .unwrap ( ) ;
3693
{
3694
let batch = writer.begin_batch ( ) .unwrap ( ) ;
3695
for i in 1000 .. ( 1000 + SPILL_OBJECTS + 1 ) {
3696
writer.put ( ObjectType:: Chunk, & chunk ( i) ) .unwrap ( ) ;
3697
}
3698
batch.finish ( ) .unwrap ( ) ;
3699
}
3700
assert! ( late.has ( & hash_object ( ObjectType:: Chunk, & chunk ( 1000 ) ) ) ) ;
3701
} ) ;
3702
}
3703
3704
# [ test ]
3705
fn retired_entries_expire_after_the_hour_and_are_never_loaded ( ) {
3706
each_pair ( | which , s , _| {
3707
for i in 0 .. 10 {
3708
s.put ( ObjectType:: Chunk, & chunk ( i) ) .unwrap ( ) ;
3709
}
3710
s.pack_loose ( ) .unwrap ( ) ;
3711
s.put ( ObjectType:: Chunk, & chunk ( 10 ) ) .unwrap ( ) ;
3712
s.repack_all ( ZSTD_LEVEL ) .unwrap ( ) ;
3713
3714
age_retired ( & s, RETIRED_FOR + std:: time:: Duration:: from_secs( 60 ) ) ;
3715
let old = retired_keys ( & s) ;
3716
assert_eq! ( old.len ( ) , 12 , " {which}: {old:?}" ) ;
3717
3718
s.put ( ObjectType:: Chunk, & chunk ( 11 ) ) .unwrap ( ) ;
3719
s.repack_all ( ZSTD_LEVEL ) .unwrap ( ) ;
3720
assert_eq! ( retired_keys ( & s) .len ( ) , 14 ) ;
3721
let listed = s
3722
.backend ( )
3723
.list ( " pack-" )
3724
.unwrap ( )
3725
.into_iter ( )
3726
.filter ( | e | e.key.ends_with ( " .idx" ) )
3727
.count ( ) ;
3728
assert_eq! ( listed, 3 , " {which}" ) ;
3729
let fresh = Store:: open_on( s.backend ( ) .clone ( ) , s.dir ( ) ) .unwrap ( ) ;
3730
assert_eq! (
3731
fresh.pack_count ( ) ,
3732
1 ,
3733
" {which}: retired packs are not loaded"
3734
) ;
3735
let report = s.expire_retired ( RETIRED_FOR , & mut silent ( ) ) .unwrap ( ) ;
3736
assert_eq! (
3737
report,
3738
RetiredReport {
3739
packs: 1 ,
3740
loose: 11 ,
3741
kept: 2
3742
} ,
3743
" {which}"
3744
) ;
3745
let young = retired_keys ( & s) ;
3746
assert_eq! ( young.len ( ) , 2 , " {which}: {young:?}" ) ;
3747
assert! ( young.iter ( ) .all ( | k | ! old.contains ( k) ) ) ;
3748
for k in old.iter ( ) .filter ( | k | k.ends_with ( " .pack" ) ) {
3749
assert! ( ! s.backend ( ) .has ( k) .unwrap ( ) , " {which}: {k}" ) ;
3750
}
3751
for k in & young {
3752
assert! ( s.backend ( ) .has ( k) .unwrap ( ) , " {which}: {k} kept" ) ;
3753
}
3754
for i in 0 .. 12 {
3755
let r = hash_object ( ObjectType:: Chunk, & chunk ( i) ) ;
3756
assert_eq! ( fresh.get ( & r) .unwrap ( ) .1 , chunk ( i) , " {which}" ) ;
3757
}
3758
} ) ;
3759
}
3760
3761
3762
3763
# [ test ]
3764
fn a_retired_loose_object_no_pack_holds_is_kept ( ) {
3765
each_pair ( | which , s , _| {
3766
let a = s.put ( ObjectType:: Chunk, b " a" ) .unwrap ( ) ;
3767
let b = s.put ( ObjectType:: Chunk, b " b" ) .unwrap ( ) ;
3768
s.pack_loose ( ) .unwrap ( ) ;
3769
assert_eq! ( s.loose_count ( ) .unwrap ( ) , 0 ) ;
3770
3771
let first = s.packs.borrow ( ) [ 0 ] .key.clone ( ) ;
3772
s.write_pack ( & [ a] , & [ ] , & [ first] , ZSTD_LEVEL , & mut silent ( ) )
3773
.unwrap ( ) ;
3774
let report = s
3775
.expire_retired ( std:: time:: Duration:: ZERO , & mut silent ( ) )
3776
.unwrap ( ) ;
3777
assert_eq! ( ( report.packs, report.loose) , ( 1 , 1 ) , " {which}: a's copy" ) ;
3778
assert! ( retired_keys ( & s) .is_empty ( ) ) ;
3779
assert_eq! ( s.get ( & b) .unwrap ( ) .1 , b " b" , " {which}" ) ;
3780
assert_eq! ( s.get ( & a) .unwrap ( ) .1 , b " a" ) ;
3781
assert_eq! ( s.loose_count ( ) .unwrap ( ) , 1 , " b is loose again" ) ;
3782
3783
let keep = [ a] .into_iter ( ) .collect ( ) ;
3784
let r = s.prune ( & keep, std:: time:: Duration:: ZERO ) .unwrap ( ) ;
3785
assert_eq! ( r.objects, 1 ) ;
3786
assert! ( ! s.has ( & b) ) ;
3787
} ) ;
3788
}
3789
3790
3791
3792
pub ( crate ) struct Racing {
3793
inner : MemoryStore,
3794
lists : std:: sync:: atomic:: AtomicUsize,
3795
3796
pub (crate) doomed : std:: sync:: Mutex< Option< String> > ,
3797
3798
ghost : bool ,
3799
}
3800
3801
impl crate:: strata:: Store for Racing {
3802
fn describe ( & self ) -> String {
3803
" racing" .into ( )
3804
}
3805
fn has ( & self , k : & str ) -> crate:: strata:: Result< bool > {
3806
self .inner.has ( k)
3807
}
3808
fn get ( & self , k : & str ) -> crate:: strata:: Result< Vec< u8 > > {
3809
self .inner.get ( k)
3810
}
3811
fn get_range ( & self , k : & str , r : std:: ops:: Range< u64 > ) -> crate:: strata:: Result< Vec< u8 > > {
3812
self .inner.get_range ( k, r)
3813
}
3814
fn put_new ( & self , k : & str , b : & [u8 ]) -> crate:: strata:: Result< ( ) > {
3815
self .inner.put_new ( k, b)
3816
}
3817
fn list ( & self , p : & str ) -> crate:: strata:: Result< Vec< crate:: strata:: Entry> > {
3818
let mut out = self .inner.list ( p) ? ;
3819
if p = = " pack-" {
3820
self .lists
3821
.fetch_add ( 1 , std:: sync:: atomic:: Ordering:: Relaxed) ;
3822
if let Some ( k) = self .doomed.lock ( ) .unwrap ( ) .take ( ) {
3823
self .inner.delete ( & k) ? ;
3824
}
3825
if self .ghost {
3826
for k in [ " pack-0000.idx" , " pack-0000.pack" ] {
3827
out.push ( crate :: strata:: Entry {
3828
key: k.into ( ) ,
3829
size: 100 ,
3830
modified_ms: None ,
3831
} ) ;
3832
}
3833
out.sort_by ( | a , b | a.key.cmp ( & b.key) ) ;
3834
}
3835
}
3836
Ok ( out)
3837
}
3838
fn delete ( & self , k : & str ) -> crate:: strata:: Result< ( ) > {
3839
self .inner.delete ( k)
3840
}
3841
fn read_ref (
3842
& self ,
3843
k : & str ,
3844
) -> crate:: strata:: Result< Option< ( Vec< u8 > , crate:: strata:: Version) > > {
3845
self .inner.read_ref ( k)
3846
}
3847
fn write_ref (
3848
& self ,
3849
k : & str ,
3850
b : & [u8 ],
3851
e : Option< & crate:: strata:: Version> ,
3852
) -> crate:: strata:: Result< crate:: strata:: Version> {
3853
self .inner.write_ref ( k, b, e)
3854
}
3855
}
3856
3857
pub ( crate ) fn racing ( ghost : bool ) -> Arc< Racing> {
3858
Arc:: new( Racing {
3859
inner: MemoryStore:: new( ) ,
3860
lists: Default :: default( ) ,
3861
doomed: Default :: default( ) ,
3862
ghost,
3863
} )
3864
}
3865
3866
# [ test ]
3867
fn an_index_deleted_between_listing_and_loading_is_passed_over ( ) {
3868
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
3869
let backend = racing ( false ) ;
3870
let s = Store:: open_on( backend.clone ( ) , dir.path ( ) ) .unwrap ( ) ;
3871
let a = s.put ( ObjectType:: Chunk, b " a" ) .unwrap ( ) ;
3872
s.pack_loose ( ) .unwrap ( ) ;
3873
let b = s.put ( ObjectType:: Chunk, b " b" ) .unwrap ( ) ;
3874
s.pack_loose ( ) .unwrap ( ) ;
3875
let idx = {
3876
let packs = s.packs.borrow ( ) ;
3877
let ( i, _ ) = s.find_packed ( & a) .unwrap ( ) ;
3878
format! ( " {} .idx" , packs[ i] .key.trim_end_matches ( " .pack" ) )
3879
} ;
3880
* backend.doomed.lock ( ) .unwrap ( ) = Some ( idx) ;
3881
let before = backend.lists.load ( std:: sync:: atomic:: Ordering:: Relaxed) ;
3882
let again = Store:: open_on( backend.clone ( ) , dir.path ( ) ) .unwrap ( ) ;
3883
assert_eq! ( again.pack_count ( ) , 1 , " the deleted one is passed over" ) ;
3884
assert_eq! (
3885
backend.lists.load ( std:: sync:: atomic:: Ordering:: Relaxed) - before,
3886
2 ,
3887
" and the store listed again"
3888
) ;
3889
assert_eq! ( again.get ( & b) .unwrap ( ) .1 , b " b" ) ;
3890
3891
let ghosted = racing ( true ) ;
3892
let s = Store:: open_on( ghosted.clone ( ) , dir.path ( ) .join ( " g" ) ) .unwrap ( ) ;
3893
let c = s.put ( ObjectType:: Chunk, b " c" ) .unwrap ( ) ;
3894
s.pack_loose ( ) .unwrap ( ) ;
3895
let again = Store:: open_on( ghosted, dir.path ( ) .join ( " g" ) ) .unwrap ( ) ;
3896
assert_eq! ( again.pack_count ( ) , 1 ) ;
3897
assert_eq! ( again.get ( & c) .unwrap ( ) .1 , b " c" ) ;
a9f686ed Andy McCall 1 month ago ↶
3898
}
3899
}
3900
3901
# [ cfg ( test ) ]
3902
mod batch_tests {
3903
use super :: * ;
3904
3905
# [ test ]
3906
fn a_large_batch_packs_every_object_and_indexes_them_all ( ) {
3907
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
3908
let store = Store:: open( dir.path ( ) ) .unwrap ( ) ;
3909
let mut refs = Vec :: new( ) ;
3910
{
3911
let batch = store.begin_batch ( ) .unwrap ( ) ;
3912
for i in 0 .. 1000 u32 {
3913
let body = format! ( " object {i} {} " , " x" .repeat ( 20_000 + i as usize ) ) .into_bytes ( ) ;
3914
refs.push ( store.put ( ObjectType:: Blob, & body) .unwrap ( ) ) ;
3915
}
3916
batch.finish ( ) .unwrap ( ) ;
3917
}
3918
let again = Store:: open( dir.path ( ) ) .unwrap ( ) ;
3919
for r in & refs {
3920
again
54e2e630 Andy McCall 29 days ago ↶
3921
.get ( r)
3922
.unwrap_or_else ( | e | panic! ( " {}: {e}" , r.short ( 12 ) ) ) ;
3923
}
3924
}
3925
3926
# [ test ]
3927
fn a_pack_a_stopped_batch_left_is_adopted_and_its_torn_end_cut ( ) {
3928
3929
3930
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
3931
let mut refs = Vec :: new( ) ;
3932
{
3933
let store = Store:: open( dir.path ( ) ) .unwrap ( ) ;
3934
let batch = store.begin_batch ( ) .unwrap ( ) ;
3935
for i in 0 .. 300 u32 {
3936
let body = format! ( " object {i} {} " , " y" .repeat ( 20_000 + i as usize ) ) .into_bytes ( ) ;
3937
refs.push ( store.put ( ObjectType:: Blob, & body) .unwrap ( ) ) ;
3938
}
3939
std:: mem:: forget( batch) ;
3940
}
3941
let tmp: Vec< _ > = fs:: read_dir( dir.path ( ) )
3942
.unwrap ( )
3943
.flatten ( )
3944
.filter ( | e | e.file_name ( ) .to_string_lossy ( ) .starts_with ( " .tmp-" ) )
3945
.map ( | e | e.path ( ) )
3946
.collect ( ) ;
3947
assert_eq! ( tmp.len ( ) , 1 , " one unfinished pack" ) ;
3948
3949
let mut f = fs:: OpenOptions:: new( ) .append ( true ) .open ( & tmp[ 0 ] ) .unwrap ( ) ;
3950
f.write_all ( & [ 0 , 1 , 0xff , 0 , 0 , 0 , 9 , 9 , 0 , 0 ] ) .unwrap ( ) ;
3951
f.write_all ( & [ 7 ; 40 ] ) .unwrap ( ) ;
3952
drop ( f) ;
3953
let store = Store:: open( dir.path ( ) ) .unwrap ( ) ;
3954
assert! ( store.get ( & refs[ 0 ] ) .is_err ( ) , " unreadable before" ) ;
3955
let young = store
3956
.adopt_unfinished_packs ( std:: time:: Duration:: from_secs( 3600 ) , & | _ | { } )
3957
.unwrap ( ) ;
3958
assert_eq! ( young, ( 0 , 0 ) , " a pack written to lately is left alone" ) ;
3959
let ( packs, objects) = store
3960
.adopt_unfinished_packs ( std:: time:: Duration:: ZERO , & | _ | { } )
3961
.unwrap ( ) ;
3962
assert_eq! ( ( packs, objects) , ( 1 , 300 ) ) ;
3963
for r in & refs {
3964
store
a9f686ed Andy McCall 1 month ago ↶
3965
.get ( r)
3966
.unwrap_or_else ( | e | panic! ( " {}: {e}" , r.short ( 12 ) ) ) ;
3967
}
54e2e630 Andy McCall 29 days ago ↶
3968
let again = Store:: open( dir.path ( ) ) .unwrap ( ) ;
3969
assert! ( again.get ( & refs[ 299 ] ) .is_ok ( ) , " indexed for good" ) ;
a9f686ed Andy McCall 1 month ago ↶
3970
}
3971
}
3972
3973
# [ cfg ( test ) ]
3974
mod size_tests {
3975
use super :: * ;
3976
3977
# [ test ]
3978
fn an_object_over_the_limit_is_refused_and_nothing_is_written ( ) {
3979
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
3980
let store = Store:: open( dir.path ( ) ) .unwrap ( ) ;
3981
let big = vec! [ b ' x' ; MAX_OBJECT_LEN as usize + 1 ] ;
3982
{
3983
let batch = store.begin_batch ( ) .unwrap ( ) ;
3984
store.put ( ObjectType:: Blob, b " small" ) .unwrap ( ) ;
3985
let e = store.put ( ObjectType:: Change, & big) .unwrap_err ( ) ;
3986
assert! (
3987
matches! ( e, StoreError:: TooLarge { kind: " change" , .. } ) ,
3988
" {e}"
3989
) ;
3990
assert! ( e.to_string ( ) .contains ( " commit it in parts" ) , " {e}" ) ;
3991
batch.finish ( ) .unwrap ( ) ;
3992
}
3993
3994
let again = Store:: open( dir.path ( ) ) .unwrap ( ) ;
3995
again.get ( & hash_object ( ObjectType:: Blob, b " small" ) ) .unwrap ( ) ;
6f8aeb99 Andy McCall 1 month ago ↶
3996
}
3997
}
3998
3999
# [ cfg ( test ) ]
4000
mod text_tests {
4001
use super :: * ;
4002
use crate :: objects:: { AtomRef, Edit, CHANGE_VERSION } ;
4003
4004
fn large_change ( ) -> Change {
4005
let mut c = crate :: objects:: tests:: sample_change( ) ;
4006
assert_eq! ( c.version, CHANGE_VERSION ) ;
4007
c.edits[ 0 ] = Edit:: Insert {
4008
node: crate :: ids:: NodeId( [ 7 ; 16 ] ) ,
4009
after: AtomRef:: Start,
4010
before: AtomRef:: End,
4011
lines: ( 0 .. 2000 )
4012
.map ( | i | format! ( " line {i} \n " ) .into_bytes ( ) )
4013
.collect ( ) ,
4014
} ;
4015
c
4016
}
4017
4018
# [ test ]
4019
fn a_large_insert_is_stored_beside_its_change_and_read_back ( ) {
4020
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
4021
let s = Store:: open( dir.path ( ) ) .unwrap ( ) ;
4022
let c = large_change ( ) ;
4023
let r = s.put_change ( & c) .unwrap ( ) ;
4024
assert_eq! ( r, c.hash ( ) ) ;
4025
let ( _ , body) = s.get ( & r) .unwrap ( ) ;
4026
assert! (
4027
body.len ( ) < 1024 ,
4028
" the change holds a reference, not {} bytes of lines" ,
4029
body.len ( )
4030
) ;
4031
let ( back, parts) = s.get_change_parts ( & r) .unwrap ( ) ;
4032
assert_eq! ( back, c) ;
4033
4034
assert_eq! ( parts.len ( ) , 2 ) ;
4035
for p in & parts {
4036
assert! ( s.has ( p) ) ;
4037
}
4038
4039
let n = s.list ( ) .unwrap ( ) .len ( ) ;
4040
s.put_change ( & c) .unwrap ( ) ;
4041
assert_eq! ( s.list ( ) .unwrap ( ) .len ( ) , n) ;
4042
}
4043
4044
# [ test ]
4045
fn a_change_whose_text_blob_is_missing_is_reported_not_misread ( ) {
64818938 Andy McCall 1 month ago ↶
4046
if ! super :: files_on_disk( ) {
4047
return ;
4048
}
6f8aeb99 Andy McCall 1 month ago ↶
4049
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
4050
let s = Store:: open( dir.path ( ) ) .unwrap ( ) ;
4051
let c = large_change ( ) ;
4052
let r = s.put_change ( & c) .unwrap ( ) ;
4053
let ( _ , parts) = s.get_change_parts ( & r) .unwrap ( ) ;
4054
fs:: remove_file( s.path_for ( & parts[ 0 ] ) ) .unwrap ( ) ;
4055
let e = s.get_change ( & r) .unwrap_err ( ) ;
4056
assert! (
4057
matches! ( & e, StoreError:: Corrupt( h, why) if * h = = r & & why.contains ( " missing" ) ) ,
4058
" {e}"
4059
) ;
192fad1e Andy McCall 1 month ago ↶
4060
}
4061
}
4062
4063
# [ cfg ( test ) ]
4064
mod put_many_tests {
4065
use super :: * ;
4066
4067
# [ test ]
4068
fn many_objects_in_a_batch_are_compressed_together_and_all_read_back ( ) {
4069
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
4070
let s = Store:: open( dir.path ( ) ) .unwrap ( ) ;
4071
let before = s.put ( ObjectType:: Chunk, b " already here" ) .unwrap ( ) ;
4072
let bodies: Vec< Vec< u8 > > = ( 0 .. 200 u32 )
4073
.map ( | i | format! ( " chunk {i} " ) .repeat ( 6000 ) .into_bytes ( ) )
4074
.collect ( ) ;
4075
let mut objects: Vec< ( ObjectType, & [u8 ]) > = bodies
4076
.iter ( )
4077
.map ( | b | ( ObjectType:: Chunk, b.as_slice ( ) ) )
4078
.collect ( ) ;
4079
objects.push ( ( ObjectType:: Chunk, bodies[ 0 ] .as_slice ( ) ) ) ;
4080
objects.push ( ( ObjectType:: Chunk, b " already here" ) ) ;
4081
let refs = {
4082
let batch = s.begin_batch ( ) .unwrap ( ) ;
4083
let refs = s.put_many ( & objects) .unwrap ( ) ;
4084
batch.finish ( ) .unwrap ( ) ;
4085
refs
4086
} ;
4087
assert_eq! ( refs.len ( ) , objects.len ( ) ) ;
4088
assert_eq! ( refs[ 200 ] , refs[ 0 ] , " a repeat is the same object" ) ;
4089
assert_eq! ( refs[ 201 ] , before) ;
4090
let again = Store:: open( dir.path ( ) ) .unwrap ( ) ;
4091
for ( r, b) in refs.iter ( ) .zip ( & objects) {
4092
assert_eq! ( again.get ( r) .unwrap ( ) .1 , b.1 ) ;
ef7d4ea6 Andy McCall 16 days ago ↶
4093
}
4094
}
4095
}
4096
4097
4098
4099
# [ cfg ( test ) ]
4100
mod entry_tests {
4101
use super :: * ;
4102
use crate :: identity:: { Domain, SecretKey} ;
4103
use crate :: strata:: Store as _ ;
4104
use std:: collections:: HashMap;
4105
use std:: sync:: atomic:: { AtomicBool, Ordering} ;
4106
use std:: sync:: Mutex;
4107
4108
4109
4110
4111
4112
# [ derive ( Default ) ]
4113
struct Counted {
4114
inner : MemoryStore,
4115
counts : Mutex< HashMap< & 'static str , usize > > ,
4116
interfere : AtomicBool,
4117
}
4118
4119
impl Counted {
4120
fn count ( & self , kind : & 'static str ) {
4121
* self .counts.lock ( ) .unwrap ( ) .entry ( kind) .or_default ( ) + = 1 ;
4122
}
4123
fn n ( & self , kind : & str ) -> usize {
4124
self .counts.lock ( ) .unwrap ( ) .get ( kind) .copied ( ) .unwrap_or ( 0 )
192fad1e Andy McCall 1 month ago ↶
4125
}
ef7d4ea6 Andy McCall 16 days ago ↶
4126
fn reset ( & self ) {
4127
self .counts.lock ( ) .unwrap ( ) .clear ( ) ;
4128
}
4129
fn total ( & self ) -> usize {
4130
self .counts.lock ( ) .unwrap ( ) .values ( ) .sum ( )
4131
}
4132
fn summary ( & self ) -> String {
4133
let c = self .counts.lock ( ) .unwrap ( ) ;
4134
let mut v: Vec< String> = c.iter ( ) .map ( | ( k , n ) | format! ( " {k} {n} " ) ) .collect ( ) ;
4135
v.sort ( ) ;
4136
v.join ( " , " )
4137
}
4138
}
4139
4140
impl crate:: strata:: Store for Counted {
4141
fn describe ( & self ) -> String {
4142
" counted" .into ( )
4143
}
4144
fn has ( & self , k : & str ) -> crate:: strata:: Result< bool > {
4145
self .count ( " has" ) ;
4146
self .inner.has ( k)
4147
}
4148
fn get ( & self , k : & str ) -> crate:: strata:: Result< Vec< u8 > > {
4149
self .count ( if is_legacy ( k) { " get_legacy" } else { " get" } ) ;
4150
self .inner.get ( k)
4151
}
4152
fn get_range ( & self , k : & str , r : std:: ops:: Range< u64 > ) -> crate:: strata:: Result< Vec< u8 > > {
4153
self .count ( " range" ) ;
4154
self .inner.get_range ( k, r)
4155
}
4156
fn put_new ( & self , k : & str , b : & [u8 ]) -> crate:: strata:: Result< ( ) > {
4157
self .count ( " put" ) ;
4158
self .inner.put_new ( k, b)
4159
}
4160
fn list ( & self , p : & str ) -> crate:: strata:: Result< Vec< crate:: strata:: Entry> > {
4161
self .count ( if p = = " pack-" {
4162
" list_packs"
4163
} else if is_legacy ( p) {
4164
" list_legacy"
4165
} else {
4166
" list"
4167
} ) ;
4168
self .inner.list ( p)
4169
}
4170
fn delete ( & self , k : & str ) -> crate:: strata:: Result< ( ) > {
4171
self .count ( " delete" ) ;
4172
self .inner.delete ( k)
4173
}
4174
fn read_ref (
4175
& self ,
4176
k : & str ,
4177
) -> crate:: strata:: Result< Option< ( Vec< u8 > , crate:: strata:: Version) > > {
4178
self .count ( if k = = JOURNAL_KEY {
4179
" read_journal"
4180
} else {
4181
" read_ref"
4182
} ) ;
4183
self .inner.read_ref ( k)
4184
}
4185
fn write_ref (
4186
& self ,
4187
k : & str ,
4188
b : & [u8 ],
4189
e : Option< & crate:: strata:: Version> ,
4190
) -> crate:: strata:: Result< crate:: strata:: Version> {
4191
self .count ( " write_ref" ) ;
4192
if k = = JOURNAL_KEY & & self .interfere.swap ( false , Ordering:: SeqCst) {
4193
let ( mut m, v) = match self .inner.read_ref ( k) ? {
4194
Some ( ( bytes, v) ) => ( parse_retired ( & bytes) , Some ( v) ) ,
4195
None => ( Default :: default( ) , None ) ,
4196
} ;
4197
m.insert (
4198
format! ( " seal/{} " , " c" .repeat ( 64 ) ) + " /" + & " d" .repeat ( 64 ) ,
4199
0 ,
4200
) ;
4201
self .inner.write_ref ( k, & encode_retired ( & m) , v.as_ref ( ) ) ? ;
4202
}
4203
self .inner.write_ref ( k, b, e)
4204
}
4205
}
4206
4207
4208
fn is_legacy ( k : & str ) -> bool {
4209
ENTRY_INDEXES
4210
.iter ( )
4211
.any ( | ( _, legacy ) | k.starts_with ( & format! ( " {legacy} /" ) ) )
4212
}
4213
4214
struct Fixture {
4215
backend : Arc< Counted> ,
4216
dir : tempfile:: TempDir,
4217
handles : std:: cell:: Cell< usize > ,
4218
}
4219
4220
impl Fixture {
4221
fn new ( ) -> Fixture {
4222
Fixture {
4223
backend: Arc:: new( Counted:: default( ) ) ,
4224
dir: tempfile:: tempdir( ) .unwrap ( ) ,
4225
handles: Default :: default( ) ,
4226
}
4227
}
4228
4229
fn open ( & self ) -> Store {
4230
let n = self .handles.get ( ) + 1 ;
4231
self .handles.set ( n) ;
4232
Store:: open_on( self .backend.clone ( ) , self .dir.path ( ) .join ( format! ( " s{n} " ) ) ) .unwrap ( )
4233
}
4234
}
4235
4236
fn signer ( n : u8 ) -> SecretKey {
4237
SecretKey {
4238
ed_seed: [ n; 32 ] ,
4239
ml_seed: [ n + 100 ; 32 ] ,
4240
}
4241
}
4242
4243
fn sign ( s : & Store, r : & ObjectRef, n : u8 ) -> Signature {
4244
let sk = signer ( n) ;
4245
let key = s.put_identity ( & sk.identity ( " a" , " b" , 0 ) ) .unwrap ( ) ;
4246
let sig = sk.sign ( Domain:: Change, & key, r) ;
4247
s.put_signature ( r, & sig) .unwrap ( ) ;
4248
sig
4249
}
4250
4251
fn journal ( s : & Store) -> std:: collections:: BTreeMap< String, u64 > {
4252
s.read_record ( JOURNAL_KEY ) .unwrap ( ) .0
4253
}
4254
4255
fn fold ( s : & Store) -> bool {
4256
s.fold_entries ( & mut silent ( ) ) .unwrap ( )
4257
}
4258
4259
4260
fn old_writer_signs ( f : & Fixture, r : & ObjectRef, n : u8 ) -> Signature {
4261
let sk = signer ( n) ;
4262
let sig = sk.sign ( Domain:: Change, & ObjectRef( [ n; 32 ] ) , r) ;
4263
f.backend
4264
.put_new (
4265
& format! ( " {} {} " , fanout_prefix ( " sig" , r) , sig.key.to_hex ( ) ) ,
4266
& Signature:: encode_list( std:: slice:: from_ref( & sig) ) ,
4267
)
4268
.unwrap ( ) ;
4269
sig
4270
}
4271
4272
4273
4274
4275
# [ test ]
4276
fn an_entry_written_after_a_fold_is_found_through_the_journal ( ) {
4277
let f = Fixture:: new( ) ;
4278
let w = f.open ( ) ;
4279
let r1 = w.put ( ObjectType:: Chunk, b " one" ) .unwrap ( ) ;
4280
let s1 = sign ( & w, & r1, 1 ) ;
4281
assert! ( fold ( & w) ) ;
4282
assert! ( ! fold ( & w) , " nothing new: no second list" ) ;
4283
4284
let early = f.open ( ) ;
4285
assert_eq! ( early.signatures ( & r1) .unwrap ( ) , vec! [ s1.clone ( ) ] ) ;
4286
4287
let w2 = f.open ( ) ;
4288
let r2 = w2.put ( ObjectType:: Chunk, b " two" ) .unwrap ( ) ;
4289
let s2 = sign ( & w2, & r2, 2 ) ;
4290
w2.index_seal ( & r2, & ObjectRef( [ 9 ; 32 ] ) ) .unwrap ( ) ;
4291
w2.index_receipt ( & r2, & ObjectRef( [ 8 ; 32 ] ) ) .unwrap ( ) ;
4292
4293
assert_eq! ( w2.signatures ( & r2) .unwrap ( ) , vec! [ s2.clone ( ) ] ) ;
4294
4295
let later = f.open ( ) ;
4296
f.backend.reset ( ) ;
4297
assert_eq! ( later.signatures ( & r1) .unwrap ( ) , vec! [ s1] ) ;
4298
assert_eq! ( later.signatures ( & r2) .unwrap ( ) , vec! [ s2.clone ( ) ] ) ;
4299
assert_eq! ( later.seals_for ( & r2) .unwrap ( ) , vec! [ ObjectRef( [ 9 ; 32 ] ) ] ) ;
4300
assert_eq! ( later.receipts_for ( & r2) .unwrap ( ) , vec! [ ObjectRef( [ 8 ; 32 ] ) ] ) ;
4301
assert_eq! ( later.receipt_states ( ) .unwrap ( ) , vec! [ r2] ) ;
4302
assert! ( later.seals_for ( & r1) .unwrap ( ) .is_empty ( ) ) ;
4303
assert_eq! ( f.backend.n ( " list" ) , 0 , " {}" , f.backend.summary ( ) ) ;
4304
assert_eq! ( f.backend.n ( " read_journal" ) , 1 , " {}" , f.backend.summary ( ) ) ;
4305
4306
4307
4308
assert! ( early.signatures ( & r2) .unwrap ( ) .is_empty ( ) ) ;
4309
early.refresh ( ) .unwrap ( ) ;
4310
assert_eq! ( early.signatures ( & r2) .unwrap ( ) , vec! [ s2] ) ;
4311
}
4312
4313
4314
4315
4316
4317
# [ test ]
4318
fn a_fold_keeps_the_journal_for_the_hour_readers_may_need_it ( ) {
4319
let f = Fixture:: new( ) ;
4320
let w = f.open ( ) ;
4321
let r1 = w.put ( ObjectType:: Chunk, b " one" ) .unwrap ( ) ;
4322
let s1 = sign ( & w, & r1, 1 ) ;
4323
assert! ( fold ( & w) ) ;
4324
let r2 = w.put ( ObjectType:: Chunk, b " two" ) .unwrap ( ) ;
4325
let s2 = sign ( & w, & r2, 2 ) ;
4326
assert! ( journal ( & w) .values ( ) .any ( | t | * t = = 0 ) ) ;
4327
4328
4329
let stale = f.open ( ) ;
4330
let stale_after_expiry = f.open ( ) ;
4331
assert! ( fold ( & w) ) ;
4332
assert! (
4333
journal ( & w) .values ( ) .all ( | t | * t ! = 0 ) ,
4334
" every line is marked folded"
4335
) ;
4336
assert! ( journal ( & w) .len ( ) > = 2 , " and kept" ) ;
4337
assert_eq! ( stale.signatures ( & r1) .unwrap ( ) , vec! [ s1.clone ( ) ] ) ;
4338
assert_eq! ( stale.signatures ( & r2) .unwrap ( ) , vec! [ s2.clone ( ) ] ) ;
4339
4340
4341
4342
reader_tests:: age_retired( & w, RETIRED_FOR * 2 ) ;
4343
w.update_record ( JOURNAL_KEY , " test" , | m | {
4344
for t in m.values_mut ( ) {
4345
* t = t.saturating_sub ( 2 * RETIRED_FOR .as_millis ( ) as u64 ) ;
4346
}
4347
} )
4348
.unwrap ( ) ;
4349
w.expire_retired ( RETIRED_FOR , & mut silent ( ) ) .unwrap ( ) ;
4350
let lists: Vec< String> = f
4351
.backend
4352
.inner
4353
.list ( " pack-" )
4354
.unwrap ( )
4355
.into_iter ( )
4356
.map ( | e | e.key )
4357
.filter ( | k | is_entry_list ( k) )
4358
.collect ( ) ;
4359
assert_eq! ( lists.len ( ) , 1 , " the replaced list is deleted: {lists:?}" ) ;
4360
let r3 = w.put ( ObjectType:: Chunk, b " three" ) .unwrap ( ) ;
4361
let s3 = sign ( & w, & r3, 3 ) ;
4362
let j = journal ( & w) ;
4363
assert_eq! ( j.len ( ) , 1 , " only the new line is left: {j:?}" ) ;
4364
assert! ( j.keys ( ) .all ( | k | k.starts_with ( & fanout_prefix ( " sig" , & r3) ) ) ) ;
4365
4366
4367
assert_eq! (
4368
stale_after_expiry.signatures ( & r1) .unwrap ( ) ,
4369
vec! [ s1.clone ( ) ]
4370
) ;
4371
assert_eq! (
4372
stale_after_expiry.signatures ( & r3) .unwrap ( ) ,
4373
vec! [ s3.clone ( ) ]
4374
) ;
4375
let fresh = f.open ( ) ;
4376
f.backend.reset ( ) ;
4377
assert_eq! ( fresh.signatures ( & r1) .unwrap ( ) , vec! [ s1] ) ;
4378
assert_eq! ( fresh.signatures ( & r2) .unwrap ( ) , vec! [ s2] ) ;
4379
assert_eq! ( fresh.signatures ( & r3) .unwrap ( ) , vec! [ s3] ) ;
4380
assert_eq! ( f.backend.n ( " list" ) , 0 ) ;
4381
}
4382
4383
4384
4385
4386
4387
# [ test ]
4388
fn an_old_layout_store_reads_before_and_after_its_first_fold ( ) {
4389
let f = Fixture:: new( ) ;
4390
let w = f.open ( ) ;
4391
let r1 = w.put ( ObjectType:: Chunk, b " one" ) .unwrap ( ) ;
4392
let r2 = w.put ( ObjectType:: Chunk, b " two" ) .unwrap ( ) ;
4393
let s1 = old_writer_signs ( & f, & r1, 1 ) ;
4394
let s2 = SecretKey {
4395
ed_seed: [ 2 ; 32 ] ,
4396
ml_seed: [ 3 ; 32 ] ,
4397
}
4398
.sign ( Domain:: Change, & ObjectRef( [ 2 ; 32 ] ) , & r2) ;
4399
f.backend
4400
.put_new (
4401
& legacy_key ( " sigs" , & r2) ,
4402
& Signature:: encode_list( std:: slice:: from_ref( & s2) ) ,
4403
)
4404
.unwrap ( ) ;
4405
f.backend
4406
.put_new (
4407
& format! ( " {} {} " , fanout_prefix ( " seal" , & r1) , " ab" .repeat ( 32 ) ) ,
4408
b " " ,
4409
)
4410
.unwrap ( ) ;
4411
f.backend
4412
.put_new (
4413
& legacy_key ( " receipts" , & r2) ,
4414
format! ( " {} \n " , ObjectRef( [ 7 ; 32 ] ) .to_hex ( ) ) .as_bytes ( ) ,
4415
)
4416
.unwrap ( ) ;
4417
let check = | s : & Store| {
4418
assert_eq! ( s.signatures ( & r1) .unwrap ( ) , vec! [ s1.clone ( ) ] ) ;
4419
assert_eq! ( s.signatures ( & r2) .unwrap ( ) , vec! [ s2.clone ( ) ] ) ;
4420
assert_eq! ( s.seals_for ( & r1) .unwrap ( ) , vec! [ ObjectRef( [ 0xab ; 32 ] ) ] ) ;
4421
assert_eq! ( s.receipts_for ( & r2) .unwrap ( ) , vec! [ ObjectRef( [ 7 ; 32 ] ) ] ) ;
4422
assert_eq! ( s.receipt_states ( ) .unwrap ( ) , vec! [ r2] ) ;
4423
} ;
4424
let reader = f.open ( ) ;
4425
assert! ( ! reader.folded.get ( ) ) ;
4426
check ( & reader) ;
4427
assert! ( f.backend.inner.read_ref ( JOURNAL_KEY ) .unwrap ( ) .is_none ( ) ) ;
4428
4429
assert! ( fold ( & w) ) ;
4430
let reader = f.open ( ) ;
4431
assert! ( reader.folded.get ( ) ) ;
4432
check ( & reader) ;
8156967b Andy McCall 16 days ago ↶
4433
4434
4435
4436
assert! ( f.backend.inner.has ( LEGACY_FOLDED_KEY ) .unwrap ( ) ) ;
4437
assert! ( f
4438
.backend
4439
.inner
4440
.has ( & format! ( " {} {} " , fanout_prefix ( " sig" , & r2) , s2.key.to_hex ( ) ) )
4441
.unwrap ( ) ) ;
4442
assert! ( f
4443
.backend
4444
.inner
4445
.has ( & format! (
4446
" {} {} " ,
4447
fanout_prefix ( " receipt" , & r2) ,
4448
ObjectRef( [ 7 ; 32 ] ) .to_hex ( )
4449
) )
4450
.unwrap ( ) ) ;
ef7d4ea6 Andy McCall 16 days ago ↶
4451
4452
4453
let r3 = w.put ( ObjectType:: Chunk, b " three" ) .unwrap ( ) ;
4454
let s3 = old_writer_signs ( & f, & r3, 3 ) ;
4455
assert! ( f.open ( ) .signatures ( & r3) .unwrap ( ) .is_empty ( ) ) ;
4456
assert! ( fold ( & w) ) ;
4457
assert_eq! ( f.open ( ) .signatures ( & r3) .unwrap ( ) , vec! [ s3] ) ;
4458
}
4459
4460
4461
4462
# [ test ]
4463
fn concurrent_journal_appends_lose_nothing ( ) {
4464
let f = Fixture:: new( ) ;
4465
4466
let w = f.open ( ) ;
4467
f.backend.interfere.store ( true , Ordering:: SeqCst) ;
4468
w.index_seal ( & ObjectRef( [ 1 ; 32 ] ) , & ObjectRef( [ 2 ; 32 ] ) )
4469
.unwrap ( ) ;
4470
let j = journal ( & w) ;
4471
assert_eq! ( j.len ( ) , 2 , " the competitor's line and ours: {j:?}" ) ;
4472
4473
let backend: Arc< Backend> = f.backend.clone ( ) ;
4474
std:: thread:: scope( | scope | {
4475
for t in 0 .. 8 u8 {
4476
let backend = backend.clone ( ) ;
4477
let dir = f.dir.path ( ) .join ( format! ( " t{t} " ) ) ;
4478
scope.spawn ( move | | {
4479
let s = Store:: open_on( backend, dir) .unwrap ( ) ;
4480
for i in 0 .. 25 u8 {
4481
s.index_seal ( & ObjectRef( [ t + 10 ; 32 ] ) , & ObjectRef( [ i; 32 ] ) )
4482
.unwrap ( ) ;
4483
}
4484
} ) ;
4485
}
4486
} ) ;
4487
assert_eq! ( journal ( & w) .len ( ) , 2 + 8 * 25 ) ;
4488
assert! ( fold ( & w) ) ;
4489
let r = f.open ( ) ;
4490
for t in 0 .. 8 u8 {
4491
assert_eq! ( r.seals_for ( & ObjectRef( [ t + 10 ; 32 ] ) ) .unwrap ( ) .len ( ) , 25 ) ;
4492
}
4493
}
4494
4495
4496
4497
# [ test ]
4498
fn a_long_journal_is_folded_into_a_partial_list ( ) {
4499
let f = Fixture:: new( ) ;
4500
let w = f.open ( ) ;
4501
assert! ( fold ( & w) , " folded, with nothing yet" ) ;
4502
let key = ObjectRef( [ 5 ; 32 ] ) ;
4503
f.backend.reset ( ) ;
4504
let batch = w.begin_batch ( ) .unwrap ( ) ;
4505
for i in 0 .. = JOURNAL_FOLD_AT as u32 {
4506
let mut v = [ 0 u8 ; 32 ] ;
4507
v[ .. 4 ] .copy_from_slice ( & i.to_le_bytes ( ) ) ;
4508
w.index_seal ( & key, & ObjectRef( v) ) .unwrap ( ) ;
4509
}
4510
assert_eq! ( f.backend.n ( " write_ref" ) , 0 , " nothing until the flush" ) ;
4511
batch.finish ( ) .unwrap ( ) ;
4512
let parts = f
4513
.backend
4514
.inner
4515
.list ( " pack-" )
4516
.unwrap ( )
4517
.into_iter ( )
4518
.filter ( | e | e.key.ends_with ( PART_SUFFIX ) )
4519
.count ( ) ;
4520
assert_eq! ( parts, 1 ) ;
4521
assert! ( journal ( & w) .values ( ) .all ( | t | * t ! = 0 ) , " all folded" ) ;
4522
let r = f.open ( ) ;
4523
assert_eq! ( r.seals_for ( & key) .unwrap ( ) .len ( ) , JOURNAL_FOLD_AT + 1 ) ;
4524
4525
assert! ( fold ( & w) ) ;
4526
let lists: Vec< String> = f.open ( ) .entry_lists.borrow ( ) .clone ( ) ;
4527
assert_eq! ( lists.len ( ) , 1 ) ;
4528
assert! ( lists[ 0 ] .ends_with ( LIST_SUFFIX ) ) ;
4529
assert_eq! ( f.open ( ) .seals_for ( & key) .unwrap ( ) .len ( ) , JOURNAL_FOLD_AT + 1 ) ;
4530
}
4531
4532
4533
4534
# [ test ]
4535
fn pruned_objects_leave_the_journal ( ) {
4536
let f = Fixture:: new( ) ;
4537
let w = f.open ( ) ;
4538
let gone = w.put ( ObjectType:: Chunk, b " gone" ) .unwrap ( ) ;
4539
let kept = w.put ( ObjectType:: Chunk, b " kept" ) .unwrap ( ) ;
4540
sign ( & w, & gone, 1 ) ;
4541
let s = sign ( & w, & kept, 2 ) ;
4542
assert! ( fold ( & w) ) ;
4543
let keep: std:: collections:: HashSet< ObjectRef> = w
4544
.list ( )
4545
.unwrap ( )
4546
.into_iter ( )
4547
.filter ( | r | * r ! = gone)
4548
.collect ( ) ;
4549
w.prune ( & keep, std:: time:: Duration:: ZERO ) .unwrap ( ) ;
4550
assert! ( journal ( & w) .keys ( ) .all ( | k | ! k.contains ( & gone.to_hex ( ) [ 2 .. ] ) ) ) ;
4551
fold ( & w) ;
4552
let r = f.open ( ) ;
4553
assert! ( r.signatures ( & gone) .unwrap ( ) .is_empty ( ) ) ;
4554
assert_eq! ( r.signatures ( & kept) .unwrap ( ) , vec! [ s] ) ;
4555
let all = r.with_entries ( | e | e.clone ( ) ) .unwrap ( ) .unwrap ( ) ;
4556
assert! (
4557
all.iter ( ) .all ( | k | ! k.contains ( & gone.to_hex ( ) [ 2 .. ] ) ) ,
4558
" {all:?}"
4559
) ;
4560
}
4561
4562
4563
4564
4565
# [ test ]
4566
fn a_folded_store_looks_up_entries_without_listing ( ) {
4567
let f = Fixture:: new( ) ;
4568
let w = f.open ( ) ;
4569
let refs: Vec< ObjectRef> = ( 0 .. 20 )
4570
.map ( | i | {
4571
w.put ( ObjectType:: Chunk, format! ( " o {i} " ) .as_bytes ( ) )
4572
.unwrap ( )
4573
} )
4574
.collect ( ) ;
4575
for r in & refs[ .. 5 ] {
4576
sign ( & w, r, 1 ) ;
4577
}
4578
let lookups = | s : & Store| {
4579
for r in & refs {
4580
s.signatures ( r) .unwrap ( ) ;
4581
s.seals_for ( r) .unwrap ( ) ;
4582
}
4583
} ;
4584
let before = f.open ( ) ;
4585
f.backend.reset ( ) ;
4586
lookups ( & before) ;
4587
let ( before_total, before_list) = ( f.backend.total ( ) , f.backend.n ( " list" ) ) ;
4588
eprintln! ( " 40 lookups before the fold: {}" , f.backend.summary ( ) ) ;
4589
4590
assert! ( fold ( & w) ) ;
4591
let after = f.open ( ) ;
4592
f.backend.reset ( ) ;
4593
lookups ( & after) ;
4594
eprintln! ( " 40 lookups after the fold: {}" , f.backend.summary ( ) ) ;
4595
assert_eq! ( before_list, 40 ) ;
4596
assert_eq! ( f.backend.n ( " list" ) , 0 ) ;
8156967b Andy McCall 16 days ago ↶
4597
assert_eq! ( f.backend.n ( " get_legacy" ) , 0 , " the older lists are folded" ) ;
ef7d4ea6 Andy McCall 16 days ago ↶
4598
assert_eq! ( f.backend.n ( " read_journal" ) , 1 ) ;
4599
assert! (
8156967b Andy McCall 16 days ago ↶
4600
f.backend.total ( ) < before_total / 10 ,
ef7d4ea6 Andy McCall 16 days ago ↶
4601
" {}" ,
4602
f.backend.summary ( )
4603
) ;
8156967b Andy McCall 16 days ago ↶
4604
}
4605
4606
4607
4608
4609
# [ test ]
4610
fn the_legacy_marker_stops_reads_of_the_older_lists ( ) {
4611
let f = Fixture:: new( ) ;
4612
let w = f.open ( ) ;
4613
let r = w.put ( ObjectType:: Chunk, b " old" ) .unwrap ( ) ;
4614
let sig = SecretKey {
4615
ed_seed: [ 4 ; 32 ] ,
4616
ml_seed: [ 5 ; 32 ] ,
4617
}
4618
.sign ( Domain:: Change, & ObjectRef( [ 4 ; 32 ] ) , & r) ;
4619
f.backend
4620
.put_new (
4621
& legacy_key ( " sigs" , & r) ,
4622
& Signature:: encode_list( std:: slice:: from_ref( & sig) ) ,
4623
)
4624
.unwrap ( ) ;
4625
for ( legacy, v) in [ ( " seals" , 9 u8 ) , ( " certs" , 8 ) , ( " accepted-lists" , 6 ) ] {
4626
f.backend
4627
.put_new (
4628
& legacy_key ( legacy, & r) ,
4629
format! ( " {} \n " , ObjectRef( [ v; 32 ] ) .to_hex ( ) ) .as_bytes ( ) ,
4630
)
4631
.unwrap ( ) ;
4632
}
4633
let check = | s : & Store| {
4634
assert_eq! ( s.signatures ( & r) .unwrap ( ) , vec! [ sig.clone ( ) ] ) ;
4635
assert_eq! ( s.seals_for ( & r) .unwrap ( ) , vec! [ ObjectRef( [ 9 ; 32 ] ) ] ) ;
4636
assert_eq! (
4637
s.index_get ( " cert" , " certs" , & r) .unwrap ( ) ,
4638
vec! [ ObjectRef( [ 8 ; 32 ] ) ]
4639
) ;
4640
assert_eq! (
4641
s.index_get_pub ( " accepted" , & r) .unwrap ( ) ,
4642
vec! [ ObjectRef( [ 6 ; 32 ] ) ]
4643
) ;
4644
} ;
4645
let before = f.open ( ) ;
4646
f.backend.reset ( ) ;
4647
check ( & before) ;
4648
assert_eq! ( f.backend.n ( " get_legacy" ) , 4 , " {}" , f.backend.summary ( ) ) ;
4649
4650
assert! ( fold ( & w) ) ;
4651
let folded = f.open ( ) ;
4652
f.backend.reset ( ) ;
4653
check ( & folded) ;
4654
assert_eq! ( f.backend.n ( " get_legacy" ) + f.backend.n ( " list" ) , 0 ) ;
4655
4656
4657
4658
for e in f.backend.inner.list ( " pack-" ) .unwrap ( ) {
4659
if is_entry_list ( & e.key) {
4660
f.backend.inner.delete ( & e.key) .unwrap ( ) ;
4661
}
4662
}
4663
let marked = f.open ( ) ;
4664
assert! ( ! marked.folded.get ( ) & & marked.legacy_folded.get ( ) ) ;
4665
f.backend.reset ( ) ;
4666
check ( & marked) ;
4667
assert_eq! ( f.backend.n ( " get_legacy" ) , 0 , " {}" , f.backend.summary ( ) ) ;
4668
assert_eq! ( f.backend.n ( " list" ) , 4 ) ;
771661aa Andy McCall 16 days ago ↶
4669
}
4670
4671
4672
4673
4674
# [ test ]
4675
fn a_long_lived_handle_sees_new_entries_within_the_interval ( ) {
4676
let f = Fixture:: new( ) ;
4677
let w = f.open ( ) ;
4678
let r1 = w.put ( ObjectType:: Chunk, b " one" ) .unwrap ( ) ;
4679
let s1 = sign ( & w, & r1, 1 ) ;
4680
assert! ( fold ( & w) ) ;
4681
let a = f.open ( ) ;
4682
assert_eq! ( a.signatures ( & r1) .unwrap ( ) , vec! [ s1] ) ;
4683
4684
let b = f.open ( ) ;
4685
let r2 = b.put ( ObjectType:: Chunk, b " two" ) .unwrap ( ) ;
4686
let s2 = sign ( & b, & r2, 2 ) ;
4687
b.index_approval ( & r2, & ObjectRef( [ 3 ; 32 ] ) ) .unwrap ( ) ;
4688
4689
assert! ( a.signatures ( & r2) .unwrap ( ) .is_empty ( ) ) ;
4690
4691
4692
let t = a.entries_read.get ( ) .unwrap ( ) ;
4693
a.entries_read.set ( t.checked_sub ( ENTRIES_FRESH ) ) ;
4694
f.backend.reset ( ) ;
4695
assert_eq! ( a.signatures ( & r2) .unwrap ( ) , vec! [ s2] ) ;
4696
assert_eq! ( a.approvals_for ( & r2) .unwrap ( ) , vec! [ ObjectRef( [ 3 ; 32 ] ) ] ) ;
4697
assert_eq! ( f.backend.n ( " read_journal" ) , 1 , " {}" , f.backend.summary ( ) ) ;
4698
assert_eq! ( f.backend.n ( " list_packs" ) , 1 , " {}" , f.backend.summary ( ) ) ;
4699
assert_eq! ( f.backend.n ( " list" ) , 0 , " {}" , f.backend.summary ( ) ) ;
4700
assert_eq! (
4701
f.backend.n ( " get" ) ,
4702
1 ,
4703
" the signature; the list already read is not read again: {}" ,
4704
f.backend.summary ( )
4705
) ;
8f10a07a Andy McCall 1 month ago ↶
4706
}
ff0af1ee unknown 1 month ago
4707
}