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
ff0af1ee unknown 1 month ago
27
5b9c6ad4 Andy McCall 1 month ago ↶
28
use crate :: attest:: Attest;
ff0af1ee unknown 1 month ago
29
use crate :: hash:: { hash_object, header, ObjectRef, ObjectType} ;
1983686f Andy McCall 1 month ago ↶
30
use crate :: identity:: { Identity, Signature} ;
6f8aeb99 Andy McCall 1 month ago ↶
31
use crate :: objects:: { Blob, Change, ObjectError, StateObject} ;
fc1a466a Andy McCall 1 month ago ↶
32
use crate :: receipt:: Receipt;
64818938 Andy McCall 1 month ago ↶
33
use crate :: strata:: { self , Error as BackendError, LocalStore, MemoryStore} ;
ff0af1ee unknown 1 month ago
34
use rand:: RngCore;
d02e545d Andy McCall 1 month ago ↶
35
use std:: cell:: RefCell;
ff0af1ee unknown 1 month ago
36
use std:: fs;
64818938 Andy McCall 1 month ago ↶
37
use std:: io:: { self , Write} ;
ff0af1ee unknown 1 month ago
38
use std:: path:: { Path, PathBuf} ;
64818938 Andy McCall 1 month ago ↶
39
use std:: sync:: Arc;
ff0af1ee unknown 1 month ago
40
d02e545d Andy McCall 1 month ago ↶
41
const PACK_MAGIC : & [ u8 ; 8 ] = b " OXYPACK1" ;
42
const IDX_MAGIC : & [ u8 ; 8 ] = b " OXYIDX01" ;
43
const IDX_ENTRY : usize = 32 + 8 + 4 + 4 + 1 + 1 ;
44
const FLAG_ZSTD : u8 = 1 ;
a17fc622 Andy McCall 1 month ago ↶
45
46
47
48
const FLAG_DICT : u8 = 2 ;
49
50
51
const DICT_BYTES : usize = 110 < < 10 ;
52
const DICT_MIN_OBJECTS : usize = 1000 ;
53
54
const DICT_OBJECT_MAX : usize = 64 < < 10 ;
d02e545d Andy McCall 1 month ago ↶
55
const ZSTD_LEVEL : i32 = 3 ;
da652d63 Andy McCall 1 month ago ↶
56
57
pub const REPACK_ZSTD_LEVEL : i32 = 9 ;
2b872d07 Andy McCall 1 month ago ↶
58
59
60
const MAX_OBJECT_LEN : u32 = 1 < < 30 ;
d02e545d Andy McCall 1 month ago ↶
61
afaba5f1 Andy McCall 1 month ago ↶
62
63
pub type Object = ( ObjectType, Vec< u8 > ) ;
64
9ae4d15a Andy McCall 1 month ago ↶
65
66
67
pub type Progress < 'a > = & 'a mut dyn FnMut ( & str ) ;
64818938 Andy McCall 1 month ago ↶
68
69
70
pub type Backend = dyn strata:: Store;
9ae4d15a Andy McCall 1 month ago ↶
71
72
fn silent ( ) -> impl FnMut( & str ) {
73
| _ | { }
64818938 Andy McCall 1 month ago ↶
74
}
75
76
77
78
fn backend_err ( e : BackendError) -> StoreError {
79
match e {
80
BackendError:: Io( e) => StoreError:: Io( e) ,
81
other => StoreError:: Io( io:: Error:: other( other.to_string ( ) ) ) ,
82
}
83
}
84
85
86
fn put_new_or_existing ( backend : & Backend, key : & str , bytes : & [u8 ]) -> Result< ( ) > {
87
match backend.put_new ( key, bytes) {
88
Ok ( ( ) ) | Err ( BackendError:: Exists( _ ) ) => Ok ( ( ) ) ,
89
Err ( e) => Err ( backend_err ( e) ) ,
90
}
91
}
92
93
94
fn get_optional ( backend : & Backend, key : & str ) -> Result< Option< Vec< u8 > > > {
95
match backend.get ( key) {
96
Ok ( b) => Ok ( Some ( b) ) ,
97
Err ( BackendError:: NotFound( _ ) ) => Ok ( None ) ,
98
Err ( e) => Err ( backend_err ( e) ) ,
99
}
100
}
101
102
fn delete ( backend : & Backend, key : & str ) -> Result< ( ) > {
103
backend.delete ( key) .map_err ( backend_err)
104
}
105
106
107
fn loose_key ( r : & ObjectRef) -> String {
108
let h = r.to_hex ( ) ;
109
format! ( " loose/{} /{} " , & h[ .. 2 ] , & h[ 2 .. ] )
110
}
111
112
113
fn loose_ref ( key : & str ) -> Option< ObjectRef> {
114
let rest = key.strip_prefix ( " loose/" ) ? ;
115
let ( hh, tail) = rest.split_once ( ' /' ) ? ;
116
if hh.len ( ) ! = 2 | | tail.contains ( ' /' ) {
117
return None ;
118
}
119
ObjectRef:: from_hex( & format! ( " {hh} {tail} " ) )
120
}
121
122
123
fn fanout_prefix ( index : & str , r : & ObjectRef) -> String {
124
let h = r.to_hex ( ) ;
125
format! ( " {index} /{} /{} /" , & h[ .. 2 ] , & h[ 2 .. ] )
126
}
127
128
129
fn legacy_key ( index : & str , r : & ObjectRef) -> String {
130
let h = r.to_hex ( ) ;
131
format! ( " {index} /{} /{} " , & h[ .. 2 ] , & h[ 2 .. ] )
9ae4d15a Andy McCall 1 month ago ↶
132
}
133
64818938 Andy McCall 1 month ago ↶
134
fn now_ms ( ) -> u64 {
135
std:: time:: SystemTime:: now( )
136
.duration_since ( std:: time:: UNIX_EPOCH )
137
.map ( | d | d.as_millis ( ) as u64 )
138
.unwrap_or ( 0 )
139
}
140
d02e545d Andy McCall 1 month ago ↶
141
# [ derive ( Clone, Copy, Debug ) ]
142
struct PackEntry {
143
offset : u64 ,
144
stored_len : u32 ,
145
raw_len : u32 ,
146
ty : ObjectType,
147
flags : u8 ,
148
}
149
150
151
struct PackIndex {
64818938 Andy McCall 1 month ago ↶
152
153
key : String,
d02e545d Andy McCall 1 month ago ↶
154
refs : Vec< ObjectRef> ,
155
entries : Vec< PackEntry> ,
a17fc622 Andy McCall 1 month ago ↶
156
64818938 Andy McCall 1 month ago ↶
157
dict : Option< Arc< zstd:: dict:: DecoderDictionary< 'static > > > ,
d02e545d Andy McCall 1 month ago ↶
158
}
159
a17fc622 Andy McCall 1 month ago ↶
160
impl std:: fmt:: Debug for PackIndex {
161
fn fmt ( & self , f : & mut std:: fmt:: Formatter< '_ > ) -> std:: fmt:: Result {
162
f.debug_struct ( " PackIndex" )
64818938 Andy McCall 1 month ago ↶
163
.field ( " key" , & self .key)
a17fc622 Andy McCall 1 month ago ↶
164
.field ( " objects" , & self .refs.len ( ) )
165
.field ( " dict" , & self .dict.is_some ( ) )
166
.finish ( )
64818938 Andy McCall 1 month ago ↶
167
}
168
}
169
170
171
fn decode_entry (
172
r : & ObjectRef,
173
e : PackEntry,
174
stored : Vec< u8 > ,
175
dict : Option< & Arc< zstd:: dict:: DecoderDictionary< 'static > > > ,
176
) -> Result< Object> {
177
let body = if e.flags & FLAG_DICT ! = 0 {
178
let dict = dict.ok_or_else ( | | {
179
StoreError:: Corrupt( * r, " packed with a dictionary the pack lacks" .into ( ) )
180
} ) ? ;
181
zstd:: bulk:: Decompressor:: with_prepared_dictionary( dict)
182
.and_then ( | mut d | d.decompress ( & stored, e.raw_len as usize ) )
183
.map_err ( | err | StoreError:: Corrupt( * r, format! ( " zstd: {err} " ) ) ) ?
184
} else if e.flags & FLAG_ZSTD ! = 0 {
185
zstd:: bulk:: decompress( & stored, e.raw_len as usize )
186
.map_err ( | err | StoreError:: Corrupt( * r, format! ( " zstd: {err} " ) ) ) ?
187
} else {
188
stored
189
} ;
190
if body.len ( ) ! = e.raw_len as usize | | hash_object ( e.ty, & body) ! = * r {
191
return Err ( StoreError:: Corrupt(
192
* r,
193
" packed object does not match its reference" .into ( ) ,
194
) ) ;
a17fc622 Andy McCall 1 month ago ↶
195
}
64818938 Andy McCall 1 month ago ↶
196
Ok ( ( e.ty, body) )
a17fc622 Andy McCall 1 month ago ↶
197
}
198
d02e545d Andy McCall 1 month ago ↶
199
impl PackIndex {
64818938 Andy McCall 1 month ago ↶
200
fn load ( backend : & Backend, idx_key : & str , pack_key : & str ) -> Result< PackIndex> {
201
let data = backend.get ( idx_key) .map_err ( backend_err) ? ;
202
let bad = | m : & str | StoreError:: Corrupt( ObjectRef:: ZERO , format! ( " {idx_key} : {m} " ) ) ;
d02e545d Andy McCall 1 month ago ↶
203
if data.len ( ) < 12 | | & data[ .. 8 ] ! = IDX_MAGIC {
204
return Err ( bad ( " bad index header" ) ) ;
205
}
206
let count = u32 :: from_le_bytes( data[ 8 .. 12 ] .try_into ( ) .unwrap ( ) ) as usize ;
207
if data.len ( ) ! = 12 + count * IDX_ENTRY {
208
return Err ( bad ( " index length mismatch" ) ) ;
209
}
64818938 Andy McCall 1 month ago ↶
210
let pack_len = backend
211
.stat ( pack_key)
212
.map_err ( backend_err) ?
213
.ok_or_else ( | | bad ( " the index names a pack that is not there" ) ) ?
214
.size;
d02e545d Andy McCall 1 month ago ↶
215
let mut refs = Vec :: with_capacity( count) ;
216
let mut entries = Vec :: with_capacity( count) ;
217
for i in 0 .. count {
218
let e = & data[ 12 + i * IDX_ENTRY .. 12 + ( i + 1 ) * IDX_ENTRY ] ;
219
let r = ObjectRef:: from_slice( & e[ .. 32 ] ) .unwrap ( ) ;
220
if let Some ( prev) = refs.last ( ) {
221
if r < = * prev {
222
return Err ( bad ( " index not sorted" ) ) ;
223
}
224
}
225
refs.push ( r) ;
2b872d07 Andy McCall 1 month ago ↶
226
let entry = PackEntry {
d02e545d Andy McCall 1 month ago ↶
227
offset: u64 :: from_le_bytes( e[ 32 .. 40 ] .try_into ( ) .unwrap ( ) ) ,
228
stored_len: u32 :: from_le_bytes( e[ 40 .. 44 ] .try_into ( ) .unwrap ( ) ) ,
229
raw_len: u32 :: from_le_bytes( e[ 44 .. 48 ] .try_into ( ) .unwrap ( ) ) ,
75167672 Andy McCall 1 month ago
230
ty: ObjectType:: ALL
231
.get ( e[ 48 ] as usize )
232
.copied ( )
233
.ok_or_else ( | | bad ( " unknown object type" ) ) ? ,
d02e545d Andy McCall 1 month ago ↶
234
flags: e[ 49 ] ,
2b872d07 Andy McCall 1 month ago ↶
235
} ;
a9f686ed Andy McCall 1 month ago ↶
236
if entry.stored_len > MAX_OBJECT_LEN | | entry.raw_len > MAX_OBJECT_LEN {
237
return Err ( StoreError:: Corrupt(
238
r,
239
format! (
64818938 Andy McCall 1 month ago ↶
240
" {idx_key} : the index claims an object of {} bytes ({} stored), more than the {MAX_OBJECT_LEN} an object may be" ,
241
entry.raw_len, entry.stored_len
a9f686ed Andy McCall 1 month ago ↶
242
) ,
243
) ) ;
244
}
245
if entry.offset.saturating_add ( entry.stored_len as u64 ) > pack_len {
2b872d07 Andy McCall 1 month ago ↶
246
return Err ( bad ( " index entry points outside the pack" ) ) ;
247
}
248
entries.push ( entry) ;
d02e545d Andy McCall 1 month ago ↶
249
}
64818938 Andy McCall 1 month ago ↶
250
let dict_key = format! ( " {} .dict" , pack_key.trim_end_matches ( " .pack" ) ) ;
251
let dict = get_optional ( backend, & dict_key) ?
252
.map ( | d | Arc:: new( zstd:: dict:: DecoderDictionary:: copy( & d) ) ) ;
75167672 Andy McCall 1 month ago
253
Ok ( PackIndex {
64818938 Andy McCall 1 month ago ↶
254
key: pack_key.to_string ( ) ,
75167672 Andy McCall 1 month ago
255
refs,
256
entries,
a17fc622 Andy McCall 1 month ago ↶
257
dict,
75167672 Andy McCall 1 month ago
258
} )
d02e545d Andy McCall 1 month ago ↶
259
}
260
261
fn find ( & self , r : & ObjectRef) -> Option< PackEntry> {
262
self .refs.binary_search ( r) .ok ( ) .map ( | i | self .entries[ i] )
263
}
264
64818938 Andy McCall 1 month ago ↶
265
266
fn fetch ( & self , backend : & Backend, r : & ObjectRef, e : PackEntry) -> Result< Object> {
267
let stored = backend
268
.get_range ( & self .key, e.offset.. e.offset + e.stored_len as u64 )
269
.map_err ( | err | match err {
270
BackendError:: NotFound( _ ) => StoreError:: NotFound( * r) ,
271
other => backend_err ( other) ,
a17fc622 Andy McCall 1 month ago ↶
272
} ) ? ;
64818938 Andy McCall 1 month ago ↶
273
decode_entry ( r, e, stored, self .dict.as_ref ( ) )
d02e545d Andy McCall 1 month ago ↶
274
}
275
}
276
277
fn type_index ( ty : ObjectType) -> u8 {
278
ObjectType:: ALL .iter ( ) .position ( | t | * t = = ty) .unwrap ( ) as u8
279
}
280
281
282
# [ derive ( Debug, Clone, Default, PartialEq, Eq ) ]
283
pub struct PackReport {
284
pub objects : usize ,
285
pub raw_bytes : u64 ,
286
pub packed_bytes : u64 ,
287
}
288
135212de Andy McCall 1 month ago ↶
289
290
pub const MAX_PACKS : usize = 16 ;
291
ba160f60 Andy McCall 1 month ago ↶
292
293
# [ derive ( Debug, Clone, Default, PartialEq, Eq ) ]
294
pub struct PruneReport {
295
pub objects : usize ,
296
pub bytes : u64 ,
297
298
pub deferred : usize ,
299
}
300
ff0af1ee unknown 1 month ago
301
# [ derive ( Debug, thiserror::Error ) ]
302
pub enum StoreError {
303
# [ error ( " io: {0}" ) ]
304
Io( # [ from ] io:: Error) ,
305
# [ error ( " corrupt object {0}: {1}" ) ]
306
Corrupt( ObjectRef, String ) ,
307
# [ error ( " object {0}" ) ]
308
Object( # [ from ] ObjectError) ,
309
# [ error ( " object {0} is a {1}, expected {2}" ) ]
310
WrongType( ObjectRef, & 'static str , & 'static str ) ,
311
# [ error ( " object {0} not found" ) ]
312
NotFound( ObjectRef) ,
6f8aeb99 Andy McCall 1 month ago ↶
313
# [ 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 ↶
314
TooLarge {
315
kind: & 'static str ,
316
size: usize ,
317
limit: u32 ,
318
} ,
ff0af1ee unknown 1 month ago
319
}
320
321
pub type Result < T> = std:: result:: Result< T, StoreError> ;
322
323
pub struct Store {
64818938 Andy McCall 1 month ago ↶
324
backend : Arc< Backend> ,
325
326
327
scratch : PathBuf,
328
329
local : bool ,
d02e545d Andy McCall 1 month ago ↶
330
packs : RefCell< Vec< PackIndex> > ,
8f10a07a Andy McCall 1 month ago ↶
331
332
333
pending : RefCell< Option< Pending> > ,
334
batch_depth : std:: cell:: Cell< u32 > ,
ce14f0d6 Andy McCall 1 month ago ↶
335
336
metas : RefCell< Option< crate:: fasthash:: FastMap< ObjectRef, crate:: state:: ChangeMeta> > > ,
8f10a07a Andy McCall 1 month ago ↶
337
}
338
64818938 Andy McCall 1 month ago ↶
339
impl std:: fmt:: Debug for Store {
340
fn fmt ( & self , f : & mut std:: fmt:: Formatter< '_ > ) -> std:: fmt:: Result {
341
f.debug_struct ( " Store" )
342
.field ( " backend" , & self .backend.describe ( ) )
343
.field ( " scratch" , & self .scratch)
344
.field ( " packs" , & self .packs.borrow ( ) .len ( ) )
345
.finish ( )
346
}
347
}
348
8f10a07a Andy McCall 1 month ago ↶
349
350
351
352
# [ derive ( Debug ) ]
353
struct Pending {
354
buffered : Vec< ( ObjectType, Vec< u8 > ) > ,
355
buffered_bytes : usize ,
356
lookup : std:: collections:: HashMap< ObjectRef, PendingEntry> ,
357
pack : Option< PendingPack> ,
358
report : PackReport,
ff0af1ee unknown 1 month ago
359
}
360
8f10a07a Andy McCall 1 month ago ↶
361
# [ derive ( Debug, Clone, Copy ) ]
362
enum PendingEntry {
363
Buffered( usize ) ,
364
Packed( PackEntry) ,
365
}
366
367
# [ derive ( Debug ) ]
368
struct PendingPack {
369
id : String,
370
tmp_path : PathBuf,
371
writer : io:: BufWriter< fs:: File> ,
372
offset : u64 ,
373
index : Vec< ( ObjectRef, PackEntry) > ,
374
}
375
376
const SPILL_OBJECTS : usize = 256 ;
377
const SPILL_BYTES : usize = 8 < < 20 ;
378
379
380
pub struct Batch < 'a > {
381
store : & 'a Store,
382
done : bool ,
383
}
384
385
impl Batch < '_ > {
386
pub fn finish ( mut self ) -> Result< ( ) > {
387
self .done = true ;
388
self .store.finish_batch ( )
389
}
390
}
391
392
impl Drop for Batch < '_ > {
393
fn drop ( & mut self ) {
394
if ! self .done {
395
let _ = self .store.finish_batch ( ) ;
396
}
397
}
398
}
399
192fad1e Andy McCall 1 month ago ↶
400
401
fn write_pack_entry (
402
pack : & mut PendingPack,
403
r : & ObjectRef,
404
ty : ObjectType,
405
raw_len : usize ,
406
flags : u8 ,
407
stored : & [u8 ],
408
report : & mut PackReport,
409
) -> Result< PackEntry> {
410
let header_len = 1 + 1 + 4 + 4 + 32 ;
411
pack.writer.write_all ( & [ type_index ( ty) , flags] ) ? ;
412
pack.writer
413
.write_all ( & ( stored.len ( ) as u32 ) .to_le_bytes ( ) ) ? ;
414
pack.writer.write_all ( & ( raw_len as u32 ) .to_le_bytes ( ) ) ? ;
415
pack.writer.write_all ( & r.0 ) ? ;
416
pack.writer.write_all ( stored) ? ;
417
let entry = PackEntry {
418
offset: pack.offset + header_len,
419
stored_len: stored.len ( ) as u32 ,
420
raw_len: raw_len as u32 ,
421
ty,
422
flags,
423
} ;
424
pack.offset + = header_len + stored.len ( ) as u64 ;
425
pack.index.push ( ( * r, entry) ) ;
426
report.objects + = 1 ;
427
report.raw_bytes + = raw_len as u64 ;
428
report.packed_bytes + = stored.len ( ) as u64 ;
429
Ok ( entry)
64818938 Andy McCall 1 month ago ↶
430
}
431
432
433
fn fresh_id ( ) -> String {
434
let mut nonce = [ 0 u8 ; 8 ] ;
435
rand:: rngs:: OsRng.fill_bytes ( & mut nonce) ;
436
hex:: encode( nonce)
437
}
438
439
440
441
442
443
fn memory_backend_for ( dir : & Path) -> Option< Arc< Backend> > {
444
use std:: collections:: HashMap;
445
use std:: sync:: { Mutex, OnceLock} ;
446
if std:: env:: var_os( " OXY_STORE_BACKEND" ) .as_deref ( ) ! = Some ( std:: ffi:: OsStr:: new( " memory" ) ) {
447
return None ;
448
}
449
static STORES : OnceLock< Mutex< HashMap< PathBuf, Arc< MemoryStore> > > > = OnceLock:: new( ) ;
450
let key = dir.canonicalize ( ) .unwrap_or_else ( | _| dir.to_path_buf ( ) ) ;
451
let mut map = STORES
452
.get_or_init ( Default :: default)
453
.lock ( )
454
.unwrap_or_else ( | p | p.into_inner ( ) ) ;
455
let store = map
456
.entry ( key)
457
.or_insert_with ( | | Arc:: new( MemoryStore:: new( ) ) )
458
.clone ( ) ;
459
Some ( store)
192fad1e Andy McCall 1 month ago ↶
460
}
461
dd21fd82 Andy McCall 1 month ago ↶
462
# [ cfg ( test ) ]
463
thread_local! {
464
465
466
467
pub ( crate ) static TEST_BACKEND : RefCell< Option< Arc< Backend> > > = const { RefCell:: new( None ) } ;
468
}
469
ff0af1ee unknown 1 month ago
470
impl Store {
64818938 Andy McCall 1 month ago ↶
471
ff0af1ee unknown 1 month ago
472
pub fn open ( dir : impl Into< PathBuf> ) -> io:: Result< Store> {
473
let dir = dir.into ( ) ;
474
fs:: create_dir_all( dir.join ( " loose" ) ) ? ;
dd21fd82 Andy McCall 1 month ago ↶
475
# [ cfg ( test ) ]
476
if let Some ( backend) = TEST_BACKEND .with ( | t | t.borrow ( ) .clone ( ) ) {
477
return Store:: open_on( backend, dir) ;
478
}
64818938 Andy McCall 1 month ago ↶
479
if let Some ( backend) = memory_backend_for ( & dir) {
480
return Store:: open_on( backend, dir) ;
481
}
482
let backend: Arc< Backend> =
483
Arc:: new( LocalStore:: open( & dir) .map_err ( | e | io:: Error:: other( e.to_string ( ) ) ) ? ) ;
484
let mut store = Store:: open_on( backend, dir) ? ;
485
store.local = true ;
486
Ok ( store)
487
}
488
489
490
491
pub fn open_on ( backend : Arc< Backend> , scratch : impl Into< PathBuf> ) -> io:: Result< Store> {
492
let scratch = scratch.into ( ) ;
493
fs:: create_dir_all( & scratch) ? ;
75167672 Andy McCall 1 month ago
494
let store = Store {
64818938 Andy McCall 1 month ago ↶
495
backend,
496
scratch,
497
local: false ,
75167672 Andy McCall 1 month ago
498
packs: RefCell:: new( Vec :: new( ) ) ,
499
pending: RefCell:: new( None ) ,
500
batch_depth: std:: cell:: Cell:: new( 0 ) ,
ce14f0d6 Andy McCall 1 month ago ↶
501
metas: RefCell:: new( None ) ,
75167672 Andy McCall 1 month ago
502
} ;
503
store
504
.reload_packs ( )
505
.map_err ( | e | io:: Error:: other( e.to_string ( ) ) ) ? ;
d02e545d Andy McCall 1 month ago ↶
506
Ok ( store)
64818938 Andy McCall 1 month ago ↶
507
}
508
509
510
pub fn backend ( & self ) -> & Arc< Backend> {
511
& self .backend
d02e545d Andy McCall 1 month ago ↶
512
}
513
64818938 Andy McCall 1 month ago ↶
514
515
516
pub fn is_local ( & self ) -> bool {
517
self .local
518
}
519
d02e545d Andy McCall 1 month ago ↶
520
fn reload_packs ( & self ) -> Result< ( ) > {
64818938 Andy McCall 1 month ago ↶
521
let listed = self .backend.list ( " pack-" ) .map_err ( backend_err) ? ;
522
let mut idx_keys: Vec< & str > = listed
523
.iter ( )
524
.map ( | e | e.key.as_str ( ) )
525
.filter ( | k | k.ends_with ( " .idx" ) )
d02e545d Andy McCall 1 month ago ↶
526
.collect ( ) ;
64818938 Andy McCall 1 month ago ↶
527
idx_keys.sort ( ) ;
528
let mut packs = Vec :: new( ) ;
529
for idx in idx_keys {
530
let pack = format! ( " {} .pack" , idx.trim_end_matches ( " .idx" ) ) ;
531
if listed.iter ( ) .any ( | e | e.key = = pack ) {
532
packs.push ( PackIndex:: load( self .backend.as_ref ( ) , idx, & pack) ? ) ;
d02e545d Andy McCall 1 month ago ↶
533
}
534
}
535
* self .packs.borrow_mut ( ) = packs;
536
Ok ( ( ) )
537
}
538
539
pub fn pack_count ( & self ) -> usize {
540
self .packs.borrow ( ) .len ( )
541
}
542
543
fn find_packed ( & self , r : & ObjectRef) -> Option< ( usize , PackEntry) > {
75167672 Andy McCall 1 month ago
544
self .packs
545
.borrow ( )
546
.iter ( )
547
.enumerate ( )
548
.find_map ( | ( i , p ) | p.find ( r) .map ( | e | ( i, e) ) )
d02e545d Andy McCall 1 month ago ↶
549
}
550
64818938 Andy McCall 1 month ago ↶
551
552
fn loose_objects ( & self ) -> Result< Vec< ( ObjectRef, strata:: Entry) > > {
553
let mut out: Vec< ( ObjectRef, strata:: Entry) > = self
554
.backend
555
.list ( " loose/" )
556
.map_err ( backend_err) ?
557
.into_iter ( )
558
.filter_map ( | e | loose_ref ( & e.key) .map ( | r | ( r, e) ) )
559
.collect ( ) ;
560
out.sort_by_key ( | ( r , _) | * r) ;
d02e545d Andy McCall 1 month ago ↶
561
Ok ( out)
562
}
563
564
pub fn loose_count ( & self ) -> io:: Result< usize > {
64818938 Andy McCall 1 month ago ↶
565
self .loose_objects ( )
566
.map ( | l | l.len ( ) )
567
.map_err ( | e | io:: Error:: other( e.to_string ( ) ) )
d02e545d Andy McCall 1 month ago ↶
568
}
569
64818938 Andy McCall 1 month ago ↶
570
571
d02e545d Andy McCall 1 month ago ↶
572
pub fn pack_loose ( & self ) -> Result< PackReport> {
573
let loose = self .loose_objects ( ) ? ;
75167672 Andy McCall 1 month ago
574
self .write_pack (
575
& loose.iter ( ) .map ( | ( r , _) | * r) .collect:: < Vec< _ > > ( ) ,
64818938 Andy McCall 1 month ago ↶
576
& loose.iter ( ) .map ( | ( _, e ) | e.key.clone ( ) ) .collect:: < Vec< _ > > ( ) ,
75167672 Andy McCall 1 month ago
577
& [ ] ,
da652d63 Andy McCall 1 month ago ↶
578
ZSTD_LEVEL ,
9ae4d15a Andy McCall 1 month ago ↶
579
& mut silent ( ) ,
75167672 Andy McCall 1 month ago
580
)
da652d63 Andy McCall 1 month ago ↶
581
}
582
583
584
585
586
pub fn repack_all ( & self , level : i32 ) -> Result< ( PackReport, usize ) > {
9ae4d15a Andy McCall 1 month ago ↶
587
self .repack_all_with ( level, & mut silent ( ) )
588
}
589
590
591
pub fn repack_all_with ( & self , level : i32 , progress : Progress) -> Result< ( PackReport, usize ) > {
da652d63 Andy McCall 1 month ago ↶
592
let loose = self .loose_objects ( ) ? ;
a67914bc Andy McCall 19 days ago ↶
593
let ( mut refs, keys, trained) : ( Vec< ObjectRef> , Vec< String> , bool ) = {
da652d63 Andy McCall 1 month ago ↶
594
let packs = self .packs.borrow ( ) ;
595
(
596
packs.iter ( ) .flat_map ( | p | p.refs.iter ( ) .copied ( ) ) .collect ( ) ,
64818938 Andy McCall 1 month ago ↶
597
packs.iter ( ) .map ( | p | p.key.clone ( ) ) .collect ( ) ,
a67914bc Andy McCall 19 days ago ↶
598
packs.first ( ) .is_some_and ( | p | p.dict.is_some ( ) ) ,
da652d63 Andy McCall 1 month ago ↶
599
)
600
} ;
a67914bc Andy McCall 19 days ago ↶
601
602
603
604
605
if loose.is_empty ( ) & & keys.len ( ) < = 1 & & ( level = = ZSTD_LEVEL | | trained) {
da652d63 Andy McCall 1 month ago ↶
606
return Ok ( ( PackReport:: default( ) , 0 ) ) ;
607
}
608
refs.extend ( loose.iter ( ) .map ( | ( r , _) | * r) ) ;
64818938 Andy McCall 1 month ago ↶
609
let loose_keys: Vec< String> = loose.into_iter ( ) .map ( | ( _, e ) | e.key ) .collect ( ) ;
610
let n = keys.len ( ) ;
611
let report = self .write_pack ( & refs, & loose_keys, & keys, level, progress) ? ;
da652d63 Andy McCall 1 month ago ↶
612
Ok ( ( report, n) )
ba160f60 Andy McCall 1 month ago ↶
613
}
614
135212de Andy McCall 1 month ago ↶
615
616
617
pub fn merge_packs ( & self ) -> Result< usize > {
64818938 Andy McCall 1 month ago ↶
618
let ( refs, keys) : ( Vec< ObjectRef> , Vec< String> ) = {
135212de Andy McCall 1 month ago ↶
619
let packs = self .packs.borrow ( ) ;
620
if packs.len ( ) < = MAX_PACKS {
621
return Ok ( 0 ) ;
622
}
75167672 Andy McCall 1 month ago
623
(
624
packs.iter ( ) .flat_map ( | p | p.refs.iter ( ) .copied ( ) ) .collect ( ) ,
64818938 Andy McCall 1 month ago ↶
625
packs.iter ( ) .map ( | p | p.key.clone ( ) ) .collect ( ) ,
75167672 Andy McCall 1 month ago
626
)
135212de Andy McCall 1 month ago ↶
627
} ;
64818938 Andy McCall 1 month ago ↶
628
let n = keys.len ( ) ;
629
self .write_pack ( & refs, & [ ] , & keys, ZSTD_LEVEL , & mut silent ( ) ) ? ;
135212de Andy McCall 1 month ago ↶
630
Ok ( n)
631
}
632
ba160f60 Andy McCall 1 month ago ↶
633
634
635
64818938 Andy McCall 1 month ago ↶
636
637
75167672 Andy McCall 1 month ago
638
pub fn prune (
9ae4d15a Andy McCall 1 month ago ↶
639
& self ,
640
keep : & std:: collections:: HashSet< ObjectRef> ,
641
grace : std:: time:: Duration,
642
) -> Result< PruneReport> {
643
self .prune_with ( keep, grace, & mut silent ( ) )
644
}
645
646
647
pub fn prune_with (
75167672 Andy McCall 1 month ago
648
& self ,
649
keep : & std:: collections:: HashSet< ObjectRef> ,
650
grace : std:: time:: Duration,
9ae4d15a Andy McCall 1 month ago ↶
651
progress : Progress,
75167672 Andy McCall 1 month ago
652
) -> Result< PruneReport> {
9ae4d15a Andy McCall 1 month ago ↶
653
progress ( " pruning loose objects" ) ;
64818938 Andy McCall 1 month ago ↶
654
let now = now_ms ( ) ;
655
let grace_ms = grace.as_millis ( ) as u64 ;
656
657
let old_enough =
658
| modified : Option< u64 > | modified.is_none_or ( | t | now.saturating_sub ( t) > = grace_ms ) ;
ba160f60 Andy McCall 1 month ago ↶
659
let mut report = PruneReport:: default( ) ;
660
64818938 Andy McCall 1 month ago ↶
661
for ( r, entry) in self .loose_objects ( ) ? {
ba160f60 Andy McCall 1 month ago ↶
662
if keep.contains ( & r) {
663
continue ;
664
}
64818938 Andy McCall 1 month ago ↶
665
if ! old_enough ( entry.modified_ms) {
ba160f60 Andy McCall 1 month ago ↶
666
report.deferred + = 1 ;
667
continue ;
668
}
64818938 Andy McCall 1 month ago ↶
669
report.bytes + = entry.size;
ba160f60 Andy McCall 1 month ago ↶
670
report.objects + = 1 ;
64818938 Andy McCall 1 month ago ↶
671
delete ( self .backend.as_ref ( ) , & entry.key) ? ;
ba160f60 Andy McCall 1 month ago ↶
672
self .remove_sidecars ( & r) ? ;
673
}
674
675
let mut keep_refs: Vec< ObjectRef> = Vec :: new( ) ;
64818938 Andy McCall 1 month ago ↶
676
let mut old_keys: Vec< String> = Vec :: new( ) ;
ba160f60 Andy McCall 1 month ago ↶
677
let mut rewrite = false ;
9ae4d15a Andy McCall 1 month ago ↶
678
progress ( " pruning packs" ) ;
ba160f60 Andy McCall 1 month ago ↶
679
for p in self .packs.borrow ( ) .iter ( ) {
680
let unreachable = p.refs.iter ( ) .filter ( | r | ! keep.contains ( r) ) .count ( ) ;
681
if unreachable = = 0 {
682
continue ;
683
}
64818938 Andy McCall 1 month ago ↶
684
let modified = self
685
.backend
686
.stat ( & p.key)
687
.map_err ( backend_err) ?
688
.and_then ( | e | e.modified_ms ) ;
689
if ! old_enough ( modified) {
ba160f60 Andy McCall 1 month ago ↶
690
report.deferred + = unreachable;
691
continue ;
692
}
693
rewrite = true ;
64818938 Andy McCall 1 month ago ↶
694
old_keys.push ( p.key.clone ( ) ) ;
ba160f60 Andy McCall 1 month ago ↶
695
for ( r, e) in p.refs.iter ( ) .zip ( p.entries.iter ( ) ) {
696
if keep.contains ( r) {
697
keep_refs.push ( * r) ;
698
} else {
699
report.objects + = 1 ;
700
report.bytes + = e.stored_len as u64 ;
701
}
702
}
703
}
704
if rewrite {
705
let dropped: Vec< ObjectRef> = {
706
let packs = self .packs.borrow ( ) ;
75167672 Andy McCall 1 month ago
707
packs
708
.iter ( )
64818938 Andy McCall 1 month ago ↶
709
.filter ( | p | old_keys.contains ( & p.key) )
75167672 Andy McCall 1 month ago
710
.flat_map ( | p | p.refs.iter ( ) .copied ( ) )
711
.filter ( | r | ! keep.contains ( r) )
712
.collect ( )
ba160f60 Andy McCall 1 month ago ↶
713
} ;
64818938 Andy McCall 1 month ago ↶
714
self .write_pack ( & keep_refs, & [ ] , & old_keys, ZSTD_LEVEL , progress) ? ;
ba160f60 Andy McCall 1 month ago ↶
715
for r in dropped {
716
self .remove_sidecars ( & r) ? ;
717
}
718
}
719
Ok ( report)
720
}
721
64818938 Andy McCall 1 month ago ↶
722
723
ba160f60 Andy McCall 1 month ago ↶
724
fn remove_sidecars ( & self , r : & ObjectRef) -> Result< ( ) > {
64818938 Andy McCall 1 month ago ↶
725
for index in [ " sigs" , " seals" , " receipts" ] {
726
delete ( self .backend.as_ref ( ) , & legacy_key ( index, r) ) ? ;
727
}
728
for index in [ " sig" , " seal" , " receipt" ] {
729
for e in self
730
.backend
731
.list ( & fanout_prefix ( index, r) )
732
.map_err ( backend_err) ?
733
{
734
delete ( self .backend.as_ref ( ) , & e.key) ? ;
ba160f60 Andy McCall 1 month ago ↶
735
}
736
}
737
Ok ( ( ) )
738
}
739
64818938 Andy McCall 1 month ago ↶
740
741
742
75167672 Andy McCall 1 month ago
743
fn finish_pack_files (
744
& self ,
745
id : & str ,
746
tmp_pack : & Path,
747
index : & [( ObjectRef , PackEntry ) ],
748
) -> Result< ( ) > {
64818938 Andy McCall 1 month ago ↶
749
let mut idx = Vec :: with_capacity( 12 + index.len ( ) * IDX_ENTRY ) ;
750
idx.extend_from_slice ( IDX_MAGIC ) ;
751
idx.extend_from_slice ( & ( index.len ( ) as u32 ) .to_le_bytes ( ) ) ;
752
for ( r, e) in index {
753
idx.extend_from_slice ( & r.0 ) ;
754
idx.extend_from_slice ( & e.offset.to_le_bytes ( ) ) ;
755
idx.extend_from_slice ( & e.stored_len.to_le_bytes ( ) ) ;
756
idx.extend_from_slice ( & e.raw_len.to_le_bytes ( ) ) ;
757
idx.extend_from_slice ( & [ type_index ( e.ty) , e.flags] ) ;
8f10a07a Andy McCall 1 month ago ↶
758
}
64818938 Andy McCall 1 month ago ↶
759
let put = self
760
.backend
761
.put_new_file ( & format! ( " pack-{id} .pack" ) , tmp_pack)
762
.map_err ( backend_err) ;
763
764
765
let _ = fs:: remove_file( tmp_pack) ;
766
put? ;
767
self .backend
768
.put_new ( & format! ( " pack-{id} .idx" ) , & idx)
769
.map_err ( backend_err) ? ;
8f10a07a Andy McCall 1 month ago ↶
770
self .reload_packs ( )
64818938 Andy McCall 1 month ago ↶
771
}
772
773
774
fn remove_pack ( & self , pack_key : & str ) -> Result< ( ) > {
775
let stem = pack_key.trim_end_matches ( " .pack" ) ;
776
delete ( self .backend.as_ref ( ) , pack_key) ? ;
777
delete ( self .backend.as_ref ( ) , & format! ( " {stem} .idx" ) ) ? ;
778
delete ( self .backend.as_ref ( ) , & format! ( " {stem} .dict" ) )
8f10a07a Andy McCall 1 month ago ↶
779
}
780
64818938 Andy McCall 1 month ago ↶
781
75167672 Andy McCall 1 month ago
782
fn write_pack (
783
& self ,
784
refs : & [ObjectRef],
64818938 Andy McCall 1 month ago ↶
785
loose_keys : & [String],
786
old_packs : & [String],
da652d63 Andy McCall 1 month ago ↶
787
level : i32 ,
9ae4d15a Andy McCall 1 month ago ↶
788
progress : Progress,
75167672 Andy McCall 1 month ago
789
) -> Result< PackReport> {
ba160f60 Andy McCall 1 month ago ↶
790
if refs.is_empty ( ) {
791
for p in old_packs {
64818938 Andy McCall 1 month ago ↶
792
self .remove_pack ( p) ? ;
ba160f60 Andy McCall 1 month ago ↶
793
}
794
self .reload_packs ( ) ? ;
d02e545d Andy McCall 1 month ago ↶
795
return Ok ( PackReport:: default( ) ) ;
796
}
ba160f60 Andy McCall 1 month ago ↶
797
let mut refs: Vec< ObjectRef> = refs.to_vec ( ) ;
798
refs.sort ( ) ;
799
refs.dedup ( ) ;
64818938 Andy McCall 1 month ago ↶
800
let id = fresh_id ( ) ;
801
let tmp_pack = self .scratch.join ( format! ( " .tmp-{id} .pack" ) ) ;
d02e545d Andy McCall 1 month ago ↶
802
let mut report = PackReport:: default( ) ;
ba160f60 Andy McCall 1 month ago ↶
803
let mut index: Vec< ( ObjectRef, PackEntry) > = Vec :: with_capacity( refs.len ( ) ) ;
a17fc622 Andy McCall 1 month ago ↶
804
805
let dict = if level = = REPACK_ZSTD_LEVEL & & refs.len ( ) > = DICT_MIN_OBJECTS {
806
let _t = crate :: timing:: phase( " pack: train a dictionary" ) ;
9ae4d15a Andy McCall 1 month ago ↶
807
progress ( & format! ( " training a dictionary on {} objects" , refs.len ( ) ) ) ;
a17fc622 Andy McCall 1 month ago ↶
808
self .train_dictionary ( & refs)
809
} else {
810
None
811
} ;
812
let encoder = dict
813
.as_ref ( )
814
.map ( | d | zstd:: dict:: EncoderDictionary:: copy( d, level) ) ;
64818938 Andy McCall 1 month ago ↶
815
let nocompress = self .nocompress_set ( ) ? ;
d02e545d Andy McCall 1 month ago ↶
816
{
817
let mut f = io:: BufWriter:: new( fs:: File:: create( & tmp_pack) ? ) ;
818
f.write_all ( PACK_MAGIC ) ? ;
819
let mut offset = PACK_MAGIC .len ( ) as u64 ;
da652d63 Andy McCall 1 month ago ↶
820
821
822
let mut batches = refs.chunks ( 1024 ) ;
823
let mut compressed_batch = Vec :: new( ) ;
9ae4d15a Andy McCall 1 month ago ↶
824
let total = refs.len ( ) ;
825
let mut reported = usize :: MAX ;
da652d63 Andy McCall 1 month ago ↶
826
loop {
827
if compressed_batch.is_empty ( ) {
828
let Some ( batch) = batches.next ( ) else { break } ;
9ae4d15a Andy McCall 1 month ago ↶
829
830
let tenth = report.objects * 10 / total.max ( 1 ) ;
831
if tenth ! = reported {
832
reported = tenth;
833
progress ( & format! ( " packing {} of {total} objects" , report.objects) ) ;
834
}
da652d63 Andy McCall 1 month ago ↶
835
let mut read: Vec< ( ObjectRef, ObjectType, Vec< u8 > , bool ) > =
836
Vec :: with_capacity( batch.len ( ) ) ;
837
for r in batch {
838
let ( ty, body) = self .get ( r) ? ;
64818938 Andy McCall 1 month ago ↶
839
read.push ( ( * r, ty, body, nocompress.contains ( r) ) ) ;
75167672 Andy McCall 1 month ago
840
}
da652d63 Andy McCall 1 month ago ↶
841
use rayon:: prelude:: * ;
842
compressed_batch = read
843
.into_par_iter ( )
a17fc622 Andy McCall 1 month ago ↶
844
.map_init (
845
| | {
846
encoder.as_ref ( ) .and_then ( | e | {
847
zstd:: bulk:: Compressor:: with_prepared_dictionary( e) .ok ( )
848
} )
849
} ,
850
| with_dict , ( r , ty , body , raw ) | {
851
let packed = if raw {
852
None
853
} else {
854
855
856
let dicted = with_dict
857
.as_mut ( )
858
.filter ( | _| body.len ( ) < = DICT_OBJECT_MAX )
859
.and_then ( | c | c.compress ( & body) .ok ( ) )
860
.map ( | c | ( FLAG_ZSTD | FLAG_DICT , c) ) ;
861
dicted
862
.or_else ( | | {
863
zstd:: bulk:: compress( & body, level)
864
.ok ( )
865
.map ( | c | ( FLAG_ZSTD , c) )
866
} )
867
.filter ( | ( _, c ) | c.len ( ) < body.len ( ) )
868
} ;
869
( r, ty, body, packed)
870
} ,
871
)
da652d63 Andy McCall 1 month ago ↶
872
.collect:: < Vec< _ > > ( ) ;
873
compressed_batch.reverse ( ) ;
874
}
875
let Some ( ( r, ty, body, packed) ) = compressed_batch.pop ( ) else {
876
continue ;
877
} ;
878
let r = & r;
879
let ( flags, stored) = match packed {
a17fc622 Andy McCall 1 month ago ↶
880
Some ( c) => c,
da652d63 Andy McCall 1 month ago ↶
881
None => ( 0 , body.clone ( ) ) ,
cdb5beba Andy McCall 1 month ago ↶
882
} ;
d02e545d Andy McCall 1 month ago ↶
883
let header_len = 1 + 1 + 4 + 4 + 32 ;
884
f.write_all ( & [ type_index ( ty) , flags] ) ? ;
885
f.write_all ( & ( stored.len ( ) as u32 ) .to_le_bytes ( ) ) ? ;
886
f.write_all ( & ( body.len ( ) as u32 ) .to_le_bytes ( ) ) ? ;
887
f.write_all ( & r.0 ) ? ;
888
f.write_all ( & stored) ? ;
75167672 Andy McCall 1 month ago
889
index.push ( (
890
* r,
891
PackEntry {
892
offset: offset + header_len,
893
stored_len: stored.len ( ) as u32 ,
894
raw_len: body.len ( ) as u32 ,
895
ty,
896
flags,
897
} ,
898
) ) ;
d02e545d Andy McCall 1 month ago ↶
899
offset + = header_len + stored.len ( ) as u64 ;
900
report.objects + = 1 ;
901
report.raw_bytes + = body.len ( ) as u64 ;
902
report.packed_bytes + = stored.len ( ) as u64 ;
903
}
904
f.flush ( ) ? ;
905
f.get_ref ( ) .sync_all ( ) ? ;
a17fc622 Andy McCall 1 month ago ↶
906
}
907
908
if let Some ( d) = & dict {
64818938 Andy McCall 1 month ago ↶
909
self .backend
910
.put_new ( & format! ( " pack-{id} .dict" ) , d)
911
.map_err ( backend_err) ? ;
d02e545d Andy McCall 1 month ago ↶
912
}
9ae4d15a Andy McCall 1 month ago ↶
913
progress ( & format! ( " writing the index of {} objects" , index.len ( ) ) ) ;
8f10a07a Andy McCall 1 month ago ↶
914
self .finish_pack_files ( & id, & tmp_pack, & index) ? ;
ba160f60 Andy McCall 1 month ago ↶
915
for p in old_packs {
64818938 Andy McCall 1 month ago ↶
916
self .remove_pack ( p) ? ;
ba160f60 Andy McCall 1 month ago ↶
917
}
d02e545d Andy McCall 1 month ago ↶
918
self .reload_packs ( ) ? ;
64818938 Andy McCall 1 month ago ↶
919
for key in loose_keys {
920
delete ( self .backend.as_ref ( ) , key) ? ;
d02e545d Andy McCall 1 month ago ↶
921
}
922
Ok ( report)
ff0af1ee unknown 1 month ago
923
}
924
a17fc622 Andy McCall 1 month ago ↶
925
926
927
fn train_dictionary ( & self , refs : & [ObjectRef]) -> Option< Vec< u8 > > {
928
let step = ( refs.len ( ) / 10_000 ) .max ( 1 ) ;
929
let mut samples: Vec< Vec< u8 > > = Vec :: new( ) ;
930
let mut total = 0 usize ;
931
for r in refs.iter ( ) .step_by ( step) {
932
if total > 32 < < 20 {
933
break ;
934
}
935
let Ok ( ( ty, body) ) = self .get ( r) else {
936
continue ;
937
} ;
938
if matches! (
939
ty,
940
ObjectType:: Chunk | ObjectType:: Blob | ObjectType:: Change
941
) & & ! body.is_empty ( )
942
& & body.len ( ) < 1 < < 20
943
{
944
total + = body.len ( ) ;
945
samples.push ( body) ;
946
}
947
}
948
zstd:: dict:: from_samples( & samples, DICT_BYTES ) .ok ( )
949
}
950
64818938 Andy McCall 1 month ago ↶
951
ff0af1ee unknown 1 month ago
952
pub fn dir ( & self ) -> & Path {
64818938 Andy McCall 1 month ago ↶
953
& self .scratch
ff0af1ee unknown 1 month ago
954
}
955
64818938 Andy McCall 1 month ago ↶
956
957
958
# [ cfg ( test ) ]
387335ff Andy McCall 1 month ago ↶
959
pub ( crate ) fn path_for ( & self , r : & ObjectRef) -> PathBuf {
64818938 Andy McCall 1 month ago ↶
960
self .scratch.join ( loose_key ( r) )
961
}
962
963
964
965
# [ cfg ( test ) ]
966
pub ( crate ) fn remove_loose_for_test ( & self , r : & ObjectRef) -> Result< ( ) > {
967
delete ( self .backend.as_ref ( ) , & loose_key ( r) )
ff0af1ee unknown 1 month ago
968
}
969
970
pub fn has ( & self , r : & ObjectRef) -> bool {
64818938 Andy McCall 1 month ago ↶
971
self .backend.has ( & loose_key ( r) ) .unwrap_or ( false )
75167672 Andy McCall 1 month ago
972
| | self .find_packed ( r) .is_some ( )
973
| | self
974
.pending
975
.borrow ( )
976
.as_ref ( )
977
.is_some_and ( | p | p.lookup.contains_key ( r) )
ff0af1ee unknown 1 month ago
978
}
979
8f10a07a Andy McCall 1 month ago ↶
980
981
982
983
984
pub fn begin_batch ( & self ) -> Result< Batch< '_ > > {
985
let depth = self .batch_depth.get ( ) ;
986
if depth = = 0 {
75167672 Andy McCall 1 month ago
987
* self .pending.borrow_mut ( ) = Some ( Pending {
988
buffered: vec! [ ] ,
989
buffered_bytes: 0 ,
990
lookup: Default :: default( ) ,
991
pack: None ,
992
report: PackReport:: default( ) ,
993
} ) ;
8f10a07a Andy McCall 1 month ago ↶
994
}
995
self .batch_depth.set ( depth + 1 ) ;
75167672 Andy McCall 1 month ago
996
Ok ( Batch {
997
store: self ,
998
done: false ,
999
} )
8f10a07a Andy McCall 1 month ago ↶
1000
}
1001
1002
fn finish_batch ( & self ) -> Result< ( ) > {
1003
let depth = self .batch_depth.get ( ) ;
1004
if depth = = 0 {
1005
return Ok ( ( ) ) ;
1006
}
1007
self .batch_depth.set ( depth - 1 ) ;
1008
if depth > 1 {
1009
return Ok ( ( ) ) ;
1010
}
75167672 Andy McCall 1 month ago
1011
let Some ( pending) = self .pending.borrow_mut ( ) .take ( ) else {
1012
return Ok ( ( ) ) ;
1013
} ;
4dcb8c09 Andy McCall 1 month ago ↶
1014
self .write_pending ( pending)
1015
}
1016
1017
1018
1019
1020
pub fn flush ( & self ) -> Result< ( ) > {
1021
let Some ( pending) = self .pending.borrow_mut ( ) .take ( ) else {
1022
return Ok ( ( ) ) ;
1023
} ;
1024
self .write_pending ( pending) ? ;
1025
* self .pending.borrow_mut ( ) = Some ( Pending {
1026
buffered: vec! [ ] ,
1027
buffered_bytes: 0 ,
1028
lookup: Default :: default( ) ,
1029
pack: None ,
1030
report: PackReport:: default( ) ,
1031
} ) ;
1032
Ok ( ( ) )
1033
}
1034
1035
fn write_pending ( & self , pending : Pending) -> Result< ( ) > {
8f10a07a Andy McCall 1 month ago ↶
1036
match pending.pack {
1037
None => {
b30a880f Andy McCall 1 month ago ↶
1038
let objects: Vec< ( ObjectType, & [u8 ]) > = pending
1039
.buffered
1040
.iter ( )
1041
.map ( | ( ty , body ) | ( * ty, body.as_slice ( ) ) )
1042
.collect ( ) ;
1043
self .put_loose_many ( & objects)
8f10a07a Andy McCall 1 month ago ↶
1044
}
1045
Some ( mut pack) => {
1046
pack.writer.flush ( ) ? ;
1047
pack.writer.get_ref ( ) .sync_all ( ) ? ;
1048
drop ( pack.writer) ;
1049
pack.index.sort_by_key ( | ( r , _) | * r) ;
1050
self .finish_pack_files ( & pack.id, & pack.tmp_path, & pack.index) ? ;
1051
Ok ( ( ) )
1052
}
1053
}
1054
}
1055
1056
64818938 Andy McCall 1 month ago ↶
1057
fn pending_get ( & self , r : & ObjectRef) -> Option< Result< Object> > {
8f10a07a Andy McCall 1 month ago ↶
1058
let mut guard = self .pending.borrow_mut ( ) ;
1059
let pending = guard.as_mut ( ) ? ;
1060
let entry = * pending.lookup.get ( r) ? ;
1061
Some ( match entry {
1062
PendingEntry:: Buffered( i) => {
1063
let ( ty, body) = & pending.buffered[ i] ;
1064
Ok ( ( * ty, body.clone ( ) ) )
1065
}
1066
PendingEntry:: Packed( e) => {
75167672 Andy McCall 1 month ago
1067
let Some ( pack) = pending.pack.as_mut ( ) else {
1068
return Some ( Err ( StoreError:: NotFound( * r) ) ) ;
1069
} ;
64818938 Andy McCall 1 month ago ↶
1070
1071
( | | {
1072
pack.writer.flush ( ) ? ;
1073
use std:: io:: { Read, Seek, SeekFrom} ;
1074
let mut f = fs:: File:: open( & pack.tmp_path) ? ;
1075
f.seek ( SeekFrom:: Start( e.offset) ) ? ;
1076
let mut stored = vec! [ 0 u8 ; e.stored_len as usize ] ;
1077
f.read_exact ( & mut stored) ? ;
1078
decode_entry ( r, e, stored, None )
1079
} ) ( )
8f10a07a Andy McCall 1 month ago ↶
1080
}
1081
} )
1082
}
1083
1084
75167672 Andy McCall 1 month ago
1085
fn pending_pack_write (
1086
& self ,
1087
pack : & mut PendingPack,
1088
r : & ObjectRef,
1089
ty : ObjectType,
1090
body : & [u8 ],
1091
report : & mut PackReport,
1092
) -> Result< PackEntry> {
8f10a07a Andy McCall 1 month ago ↶
1093
let ( flags, stored) = if self .is_nocompress ( r) {
1094
( 0 , body.to_vec ( ) )
1095
} else {
75167672 Andy McCall 1 month ago
1096
let compressed = zstd:: bulk:: compress( body, ZSTD_LEVEL )
1097
.map_err ( | e | StoreError:: Corrupt( * r, format! ( " zstd: {e} " ) ) ) ? ;
1098
if compressed.len ( ) < body.len ( ) {
1099
( FLAG_ZSTD , compressed)
1100
} else {
1101
( 0 , body.to_vec ( ) )
1102
}
8f10a07a Andy McCall 1 month ago ↶
1103
} ;
192fad1e Andy McCall 1 month ago ↶
1104
write_pack_entry ( pack, r, ty, body.len ( ) , flags, & stored, report)
8f10a07a Andy McCall 1 month ago ↶
1105
}
1106
1107
fn put_pending ( & self , r : ObjectRef, ty : ObjectType, body : & [u8 ]) -> Result< ( ) > {
1108
let mut guard = self .pending.borrow_mut ( ) ;
1109
let pending = guard.as_mut ( ) .expect ( " batch open" ) ;
1110
if pending.lookup.contains_key ( & r) {
1111
return Ok ( ( ) ) ;
1112
}
75167672 Andy McCall 1 month ago
1113
if pending.pack.is_none ( )
1114
& & pending.buffered.len ( ) < SPILL_OBJECTS
1115
& & pending.buffered_bytes + body.len ( ) < = SPILL_BYTES
1116
{
8f10a07a Andy McCall 1 month ago ↶
1117
pending.buffered.push ( ( ty, body.to_vec ( ) ) ) ;
1118
pending.buffered_bytes + = body.len ( ) ;
75167672 Andy McCall 1 month ago
1119
pending
1120
.lookup
1121
.insert ( r, PendingEntry:: Buffered( pending.buffered.len ( ) - 1 ) ) ;
8f10a07a Andy McCall 1 month ago ↶
1122
return Ok ( ( ) ) ;
1123
}
192fad1e Andy McCall 1 month ago ↶
1124
self .ensure_pack ( pending) ? ;
1125
let mut report = std:: mem:: take( & mut pending.report) ;
1126
let pack = pending.pack.as_mut ( ) .unwrap ( ) ;
1127
let e = self .pending_pack_write ( pack, & r, ty, body, & mut report) ? ;
1128
pending.report = report;
1129
pending.lookup.insert ( r, PendingEntry:: Packed( e) ) ;
1130
Ok ( ( ) )
1131
}
1132
1133
1134
1135
fn ensure_pack ( & self , pending : & mut Pending) -> Result< ( ) > {
8f10a07a Andy McCall 1 month ago ↶
1136
if pending.pack.is_none ( ) {
1137
64818938 Andy McCall 1 month ago ↶
1138
let id = fresh_id ( ) ;
1139
let tmp_path = self .scratch.join ( format! ( " .tmp-{id} .pack" ) ) ;
8f10a07a Andy McCall 1 month ago ↶
1140
let mut writer = io:: BufWriter:: new( fs:: File:: create( & tmp_path) ? ) ;
1141
writer.write_all ( PACK_MAGIC ) ? ;
75167672 Andy McCall 1 month ago
1142
let mut pack = PendingPack {
1143
id,
1144
tmp_path,
1145
writer,
1146
offset: PACK_MAGIC .len ( ) as u64 ,
1147
index: vec! [ ] ,
1148
} ;
8f10a07a Andy McCall 1 month ago ↶
1149
let buffered = std:: mem:: take( & mut pending.buffered) ;
1150
pending.buffered_bytes = 0 ;
1151
let mut report = std:: mem:: take( & mut pending.report) ;
1152
for ( bty, bbody) in & buffered {
1153
let br = hash_object ( * bty, bbody) ;
1154
let e = self .pending_pack_write ( & mut pack, & br, * bty, bbody, & mut report) ? ;
1155
pending.lookup.insert ( br, PendingEntry:: Packed( e) ) ;
1156
}
1157
pending.report = report;
1158
pending.pack = Some ( pack) ;
192fad1e Andy McCall 1 month ago ↶
1159
}
1160
Ok ( ( ) )
1161
}
1162
1163
1164
1165
1166
pub fn put_many ( & self , objects : & [( ObjectType , &[u8 ]) ]) -> Result< Vec< ObjectRef> > {
1167
use rayon:: prelude:: * ;
1168
let total: usize = objects.iter ( ) .map ( | ( _, b ) | b.len ( ) ) .sum ( ) ;
1169
if self .pending.borrow ( ) .is_none ( ) | | total < SPILL_BYTES {
1170
return objects.iter ( ) .map ( | ( ty , b ) | self .put ( * ty, b) ) .collect ( ) ;
1171
}
1172
for ( ty, b) in objects {
1173
if b.len ( ) > MAX_OBJECT_LEN as usize {
1174
return Err ( StoreError:: TooLarge {
1175
kind: ty.name ( ) ,
1176
size: b.len ( ) ,
1177
limit: MAX_OBJECT_LEN ,
1178
} ) ;
1179
}
8f10a07a Andy McCall 1 month ago ↶
1180
}
192fad1e Andy McCall 1 month ago ↶
1181
let refs: Vec< ObjectRef> = objects
1182
.par_iter ( )
1183
.map ( | ( ty , b ) | hash_object ( * ty, b) )
1184
.collect ( ) ;
1185
1186
let mut seen: std:: collections:: HashSet< ObjectRef> = std:: collections:: HashSet:: new( ) ;
1187
let mut todo: Vec< usize > = Vec :: new( ) ;
1188
for ( i, r) in refs.iter ( ) .enumerate ( ) {
1189
let known = self
1190
.pending
1191
.borrow ( )
1192
.as_ref ( )
1193
.is_some_and ( | p | p.lookup.contains_key ( r) ) ;
1194
if seen.insert ( * r)
1195
& & ! known
1196
& & self .find_packed ( r) .is_none ( )
64818938 Andy McCall 1 month ago ↶
1197
& & ! self .backend.has ( & loose_key ( r) ) .unwrap_or ( false )
192fad1e Andy McCall 1 month ago ↶
1198
{
1199
todo.push ( i) ;
1200
}
1201
}
64818938 Andy McCall 1 month ago ↶
1202
let raw = self .nocompress_set ( ) ? ;
192fad1e Andy McCall 1 month ago ↶
1203
let packed: Vec< ( u8 , Vec< u8 > ) > = todo
1204
.par_iter ( )
1205
.map ( | i | {
1206
let body = objects[ * i] .1 ;
1207
if raw.contains ( & refs[ * i] ) {
1208
return ( 0 , body.to_vec ( ) ) ;
1209
}
1210
match zstd:: bulk:: compress( body, ZSTD_LEVEL ) {
1211
Ok ( c) if c.len ( ) < body.len ( ) => ( FLAG_ZSTD , c) ,
1212
_ => ( 0 , body.to_vec ( ) ) ,
1213
}
1214
} )
1215
.collect ( ) ;
1216
let mut guard = self .pending.borrow_mut ( ) ;
1217
let pending = guard.as_mut ( ) .expect ( " batch open" ) ;
1218
self .ensure_pack ( pending) ? ;
8f10a07a Andy McCall 1 month ago ↶
1219
let mut report = std:: mem:: take( & mut pending.report) ;
1220
let pack = pending.pack.as_mut ( ) .unwrap ( ) ;
192fad1e Andy McCall 1 month ago ↶
1221
for ( i, ( flags, stored) ) in todo.iter ( ) .zip ( packed) {
1222
let ( ty, body) = objects[ * i] ;
1223
let e = write_pack_entry ( pack, & refs[ * i] , ty, body.len ( ) , flags, & stored, & mut report) ? ;
1224
pending.lookup.insert ( refs[ * i] , PendingEntry:: Packed( e) ) ;
1225
}
8f10a07a Andy McCall 1 month ago ↶
1226
pending.report = report;
192fad1e Andy McCall 1 month ago ↶
1227
Ok ( refs)
8f10a07a Andy McCall 1 month ago ↶
1228
}
1229
ff0af1ee unknown 1 month ago
1230
1231
pub fn put ( & self , ty : ObjectType, body : & [u8 ]) -> Result< ObjectRef> {
a9f686ed Andy McCall 1 month ago ↶
1232
1233
1234
1235
if body.len ( ) > MAX_OBJECT_LEN as usize {
1236
return Err ( StoreError:: TooLarge {
1237
kind: ty.name ( ) ,
1238
size: body.len ( ) ,
1239
limit: MAX_OBJECT_LEN ,
1240
} ) ;
1241
}
ff0af1ee unknown 1 month ago
1242
let r = hash_object ( ty, body) ;
64818938 Andy McCall 1 month ago ↶
1243
if self .find_packed ( & r) .is_some ( ) | | self .backend.has ( & loose_key ( & r) ) .unwrap_or ( false ) {
ff0af1ee unknown 1 month ago
1244
return Ok ( r) ;
1245
}
8f10a07a Andy McCall 1 month ago ↶
1246
if self .pending.borrow ( ) .is_some ( ) {
1247
self .put_pending ( r, ty, body) ? ;
1248
return Ok ( r) ;
1249
}
1250
self .put_loose ( ty, body) ? ;
1251
Ok ( r)
1252
}
1253
64818938 Andy McCall 1 month ago ↶
1254
8f10a07a Andy McCall 1 month ago ↶
1255
fn put_loose ( & self , ty : ObjectType, body : & [u8 ]) -> Result< ObjectRef> {
1256
let r = hash_object ( ty, body) ;
64818938 Andy McCall 1 month ago ↶
1257
let mut bytes = header ( ty, body.len ( ) ) ;
1258
bytes.extend_from_slice ( body) ;
4dcb8c09 Andy McCall 1 month ago ↶
1259
crate :: durable:: step( " write loose object" ) ? ;
64818938 Andy McCall 1 month ago ↶
1260
put_new_or_existing ( self .backend.as_ref ( ) , & loose_key ( & r) , & bytes) ? ;
ff0af1ee unknown 1 month ago
1261
Ok ( r)
b30a880f Andy McCall 1 month ago ↶
1262
}
1263
1264
64818938 Andy McCall 1 month ago ↶
1265
1266
1267
b30a880f Andy McCall 1 month ago ↶
1268
fn put_loose_many ( & self , objects : & [( ObjectType , &[u8 ]) ]) -> Result< ( ) > {
1269
if objects.len ( ) < = 1 {
1270
for ( ty, body) in objects {
1271
self .put_loose ( * ty, body) ? ;
1272
}
1273
return Ok ( ( ) ) ;
1274
}
1275
let mut seen = std:: collections:: HashSet:: new( ) ;
64818938 Andy McCall 1 month ago ↶
1276
let items: Vec< ( String, Vec< u8 > ) > = objects
b30a880f Andy McCall 1 month ago ↶
1277
.iter ( )
1278
.filter_map ( | ( ty , body ) | {
1279
let r = hash_object ( * ty, body) ;
64818938 Andy McCall 1 month ago ↶
1280
seen.insert ( r) .then ( | | {
1281
let mut bytes = header ( * ty, body.len ( ) ) ;
1282
bytes.extend_from_slice ( body) ;
1283
( loose_key ( & r) , bytes)
b30a880f Andy McCall 1 month ago ↶
1284
} )
1285
} )
1286
.collect ( ) ;
64818938 Andy McCall 1 month ago ↶
1287
self .backend.put_many_new ( & items) .map_err ( backend_err)
ff0af1ee unknown 1 month ago
1288
}
1289
09245702 Andy McCall 19 days ago ↶
1290
1291
1292
1293
64818938 Andy McCall 1 month ago ↶
1294
pub fn get ( & self , r : & ObjectRef) -> Result< Object> {
09245702 Andy McCall 19 days ago ↶
1295
if let Some ( ( i, e) ) = self .find_packed ( r) {
1296
return self .packs.borrow ( ) [ i] .fetch ( self .backend.as_ref ( ) , r, e) ;
1297
}
64818938 Andy McCall 1 month ago ↶
1298
match get_optional ( self .backend.as_ref ( ) , & loose_key ( r) ) ? {
1299
Some ( bytes) => parse_loose ( & bytes, r) ,
09245702 Andy McCall 19 days ago ↶
1300
None => self .pending_get ( r) .unwrap_or ( Err ( StoreError:: NotFound( * r) ) ) ,
afaba5f1 Andy McCall 1 month ago ↶
1301
}
1302
}
1303
09245702 Andy McCall 19 days ago ↶
1304
1305
1306
afaba5f1 Andy McCall 1 month ago ↶
1307
pub fn get_many ( & self , refs : & [ObjectRef]) -> Vec< Result< Object> > {
1308
use rayon:: prelude:: * ;
64818938 Andy McCall 1 month ago ↶
1309
let backend = self .backend.as_ref ( ) ;
afaba5f1 Andy McCall 1 month ago ↶
1310
let packs = self .packs.borrow ( ) ;
09245702 Andy McCall 19 days ago ↶
1311
let place: Vec< Option< ( usize , PackEntry) > > = refs
75167672 Andy McCall 1 month ago
1312
.iter ( )
09245702 Andy McCall 19 days ago ↶
1313
.map ( | r | {
afaba5f1 Andy McCall 1 month ago ↶
1314
packs
1315
.iter ( )
1316
.enumerate ( )
09245702 Andy McCall 19 days ago ↶
1317
.find_map ( | ( p , pack ) | pack.find ( r) .map ( | e | ( p, e) ) )
afaba5f1 Andy McCall 1 month ago ↶
1318
} )
1319
.collect ( ) ;
1320
let indexes: & [ PackIndex] = & packs;
09245702 Andy McCall 19 days ago ↶
1321
let found: Vec< Option< Result< Object> > > = refs
afaba5f1 Andy McCall 1 month ago ↶
1322
.par_iter ( )
09245702 Andy McCall 19 days ago ↶
1323
.zip ( place.par_iter ( ) )
1324
.map ( | ( r , at ) | match at {
1325
Some ( ( p, e) ) => Some ( indexes[ * p] .fetch ( backend, r, * e) ) ,
1326
None => match get_optional ( backend, & loose_key ( r) ) {
1327
Ok ( Some ( bytes) ) => Some ( parse_loose ( & bytes, r) ) ,
1328
Ok ( None ) => None ,
1329
Err ( e) => Some ( Err ( e) ) ,
1330
} ,
1331
} )
afaba5f1 Andy McCall 1 month ago ↶
1332
.collect ( ) ;
1333
drop ( packs) ;
1334
found
1335
.into_iter ( )
1336
.zip ( refs)
09245702 Andy McCall 19 days ago ↶
1337
.map ( | ( f , r ) | {
1338
f.unwrap_or_else ( | | self .pending_get ( r) .unwrap_or ( Err ( StoreError:: NotFound( * r) ) ) )
afaba5f1 Andy McCall 1 month ago ↶
1339
} )
1340
.collect ( )
ff0af1ee unknown 1 month ago
1341
}
1342
1343
pub fn get_typed ( & self , r : & ObjectRef, want : ObjectType) -> Result< Vec< u8 > > {
1344
let ( ty, body) = self .get ( r) ? ;
1345
if ty ! = want {
1346
return Err ( StoreError:: WrongType( * r, ty.name ( ) , want.name ( ) ) ) ;
1347
}
1348
Ok ( body)
1349
}
1350
6f8aeb99 Andy McCall 1 month ago ↶
1351
1352
ff0af1ee unknown 1 month ago
1353
pub fn put_change ( & self , c : & Change) -> Result< ObjectRef> {
5b4c5130 Andy McCall 1 month ago ↶
1354
let encoding = crate :: timing:: phase( " store: encode the change and its texts" ) ;
17f94b13 Andy McCall 1 month ago ↶
1355
let bytes = c.encode_streaming ( | batch | self .put_texts ( & batch) ) ? ;
5b4c5130 Andy McCall 1 month ago ↶
1356
drop ( encoding) ;
1357
let _t = crate :: timing:: phase( " store: put the encoding" ) ;
17f94b13 Andy McCall 1 month ago ↶
1358
self .put ( ObjectType:: Change, & bytes)
6f8aeb99 Andy McCall 1 month ago ↶
1359
}
1360
17f94b13 Andy McCall 1 month ago ↶
1361
1362
1363
pub fn put_texts ( & self , texts : & [crate:: objects:: StoredText]) -> Result< ( ) > {
192fad1e Andy McCall 1 month ago ↶
1364
let mut chunks: Vec< ( ObjectType, & [u8 ]) > = Vec :: new( ) ;
1365
for t in texts {
1366
if ! self .has ( & t.blob.hash ( ) ) {
1367
chunks.extend (
1368
crate :: chunk:: chunks( & t.content)
1369
.into_iter ( )
1370
.map ( | c | ( ObjectType:: Chunk, c) ) ,
1371
) ;
1372
}
6f8aeb99 Andy McCall 1 month ago ↶
1373
}
192fad1e Andy McCall 1 month ago ↶
1374
self .put_many ( & chunks) ? ;
1375
for t in texts {
1376
self .put ( ObjectType:: Blob, & t.blob.encode ( ) ) ? ;
6f8aeb99 Andy McCall 1 month ago ↶
1377
}
17f94b13 Andy McCall 1 month ago ↶
1378
Ok ( ( ) )
ff0af1ee unknown 1 month ago
1379
}
1380
1381
pub fn get_change ( & self , r : & ObjectRef) -> Result< Change> {
6f8aeb99 Andy McCall 1 month ago ↶
1382
Ok ( self .get_change_parts ( r) ? .0 )
1383
}
1384
1385
1386
1387
pub fn get_change_parts ( & self , r : & ObjectRef) -> Result< ( Change, Vec< ObjectRef> ) > {
1388
let bytes = self .get_typed ( r, ObjectType:: Change) ? ;
1389
let mut parts = Vec :: new( ) ;
1390
let mut failed = None ;
0f93f49c Andy McCall 1 month ago ↶
1391
let decoded = Change:: decode_with( & bytes, & mut | text, count, form| match self
1392
.read_text ( text)
1393
{
6f8aeb99 Andy McCall 1 month ago ↶
1394
Ok ( ( blob, content) ) => {
1395
parts.push ( * text) ;
1396
parts.extend ( blob.chunks) ;
0f93f49c Andy McCall 1 month ago ↶
1397
crate :: objects:: text_lines( & content, count, form)
6f8aeb99 Andy McCall 1 month ago ↶
1398
}
1399
Err ( e) => {
1400
let why = e.to_string ( ) ;
1401
failed = Some ( e) ;
1402
Err ( ObjectError:: Schema( format! (
1403
" text blob {} : {why} " ,
1404
text.short ( 12 )
1405
) ) )
1406
}
1407
} ) ;
1408
match ( decoded, failed) {
1409
( Ok ( c) , _ ) => Ok ( ( c, parts) ) ,
1410
( Err ( _ ) , Some ( StoreError:: NotFound( t) ) ) => Err ( StoreError:: Corrupt(
1411
* r,
1412
format! ( " its text blob {} is missing" , t.short ( 12 ) ) ,
1413
) ) ,
1414
( Err ( e) , _ ) => Err ( StoreError:: Corrupt( * r, e.to_string ( ) ) ) ,
1415
}
1416
}
1417
a4fad5b0 Andy McCall 1 month ago ↶
1418
1419
1420
1421
1422
pub fn get_change_lazy ( & self , r : & ObjectRef) -> Result< ( Change, Vec< crate:: objects:: TextRef> ) > {
86e1824f Andy McCall 1 month ago ↶
1423
let bytes = self .get_typed ( r, ObjectType:: Change) ? ;
a4fad5b0 Andy McCall 1 month ago ↶
1424
Change:: decode_skeleton( & bytes, * r) .map_err ( | e | StoreError:: Corrupt( * r, e.to_string ( ) ) )
86e1824f Andy McCall 1 month ago ↶
1425
}
1426
69816d68 Andy McCall 1 month ago ↶
1427
1428
1429
ce14f0d6 Andy McCall 1 month ago ↶
1430
1431
1432
1433
69816d68 Andy McCall 1 month ago ↶
1434
pub fn get_change_meta ( & self , r : & ObjectRef) -> Result< crate:: state:: ChangeMeta> {
ce14f0d6 Andy McCall 1 month ago ↶
1435
if self .metas.borrow ( ) .is_none ( ) {
1436
* self .metas.borrow_mut ( ) = Some ( self .read_metas_cache ( ) ) ;
1437
}
1438
let cached = self .metas.borrow ( ) .as_ref ( ) .and_then ( | m | m.get ( r) .cloned ( ) ) ;
1439
if let Some ( meta) = cached {
1440
if self .has ( r) {
1441
# [ cfg ( test ) ]
1442
assert_eq! (
1443
meta,
1444
Change:: decode_meta( & self .get_typed ( r, ObjectType:: Change) ? ) .unwrap ( ) ,
1445
" the metadata cache holds a change's metadata"
1446
) ;
1447
return Ok ( meta) ;
1448
}
1449
}
4d6c9c1b Andy McCall 1 month ago ↶
1450
let bytes = self .get_typed ( r, ObjectType:: Change) ? ;
ce14f0d6 Andy McCall 1 month ago ↶
1451
let meta =
1452
Change:: decode_meta( & bytes) .map_err ( | e | StoreError:: Corrupt( * r, e.to_string ( ) ) ) ? ;
1453
self .remember_meta ( r, & meta) ;
1454
Ok ( meta)
1455
}
1456
1457
1458
1459
64818938 Andy McCall 1 month ago ↶
1460
1461
ce14f0d6 Andy McCall 1 month ago ↶
1462
fn read_metas_cache ( & self ) -> crate:: fasthash:: FastMap< ObjectRef, crate:: state:: ChangeMeta> {
1463
let mut out = crate :: fasthash:: FastMap:: default( ) ;
64818938 Andy McCall 1 month ago ↶
1464
let Ok ( bytes) = fs:: read( self .scratch.join ( " metas" ) ) else {
ce14f0d6 Andy McCall 1 month ago ↶
1465
return out;
1466
} ;
1467
let mut r = crate :: flat:: Reader:: new( & bytes) ;
1468
let mut good = 0 ;
1469
while ! r.at_end ( ) {
1470
let mut record = | | -> std:: result:: Result< _ , ObjectError> {
1471
let h = r.object ( ) ? ;
1472
let body = r.bytes ( ) ? ;
1473
let check = r.fixed:: < 8> ( ) ? ;
1474
Ok ( ( h, body, check) )
1475
} ;
1476
let Ok ( ( h, body, check) ) = record ( ) else {
1477
break ;
1478
} ;
1479
let mut sum = blake3:: Hasher:: new( ) ;
1480
sum.update ( & h.0 ) .update ( body) ;
1481
if sum.finalize ( ) .as_bytes ( ) [ .. 8 ] ! = check {
1482
break ;
1483
}
1484
match crate :: state:: ChangeMeta:: read_flat( & mut crate :: flat:: Reader:: new( body) ) {
1485
Ok ( m) => out.insert ( h, m) ,
1486
Err ( _ ) => break ,
1487
} ;
1488
good + = 32 + 4 + body.len ( ) + 8 ;
1489
}
1490
1491
if good < bytes.len ( ) {
1492
let _ = fs:: OpenOptions:: new( )
1493
.write ( true )
64818938 Andy McCall 1 month ago ↶
1494
.open ( self .scratch.join ( " metas" ) )
ce14f0d6 Andy McCall 1 month ago ↶
1495
.and_then ( | f | f.set_len ( good as u64 ) ) ;
1496
}
1497
out
1498
}
1499
1500
1501
1502
fn remember_meta ( & self , r : & ObjectRef, meta : & crate:: state:: ChangeMeta) {
1503
let mut body = crate :: flat:: Writer:: default( ) ;
1504
meta.write_flat ( & mut body) ;
1505
let mut w = crate :: flat:: Writer:: default( ) ;
1506
w.object ( r) ;
1507
w.bytes ( & body.0 ) ;
1508
let mut sum = blake3:: Hasher:: new( ) ;
1509
sum.update ( & r.0 ) .update ( & body.0 ) ;
1510
w.fixed ( & sum.finalize ( ) .as_bytes ( ) [ .. 8 ] ) ;
1511
let _ = fs:: OpenOptions:: new( )
1512
.create ( true )
1513
.append ( true )
64818938 Andy McCall 1 month ago ↶
1514
.open ( self .scratch.join ( " metas" ) )
ce14f0d6 Andy McCall 1 month ago ↶
1515
.and_then ( | mut f | f.write_all ( & w.0 ) ) ;
1516
if let Some ( m) = self .metas.borrow_mut ( ) .as_mut ( ) {
1517
m.insert ( * r, meta.clone ( ) ) ;
1518
}
69816d68 Andy McCall 1 month ago ↶
1519
}
1520
6f8aeb99 Andy McCall 1 month ago ↶
1521
fn read_text ( & self , r : & ObjectRef) -> Result< ( Blob, Vec< u8 > ) > {
1522
let blob = Blob:: decode( & self .get_typed ( r, ObjectType:: Blob) ? ) ? ;
1523
let mut content = Vec :: with_capacity( blob.length as usize ) ;
1524
for c in & blob.chunks {
1525
content.extend ( self .get_typed ( c, ObjectType:: Chunk) ? ) ;
1526
}
1527
Ok ( ( blob, content) )
ff0af1ee unknown 1 month ago
1528
}
1529
1530
pub fn put_chunk ( & self , bytes : & [u8 ]) -> Result< ObjectRef> {
1531
self .put ( ObjectType:: Chunk, bytes)
1532
}
1533
cdb5beba Andy McCall 1 month ago ↶
1534
1535
pub fn put_chunk_nocompress ( & self , bytes : & [u8 ]) -> Result< ObjectRef> {
8f10a07a Andy McCall 1 month ago ↶
1536
let r = hash_object ( ObjectType:: Chunk, bytes) ;
64818938 Andy McCall 1 month ago ↶
1537
put_new_or_existing (
1538
self .backend.as_ref ( ) ,
1539
& format! ( " nocompress/{} " , r.to_hex ( ) ) ,
1540
b " " ,
1541
) ? ;
8f10a07a Andy McCall 1 month ago ↶
1542
self .put ( ObjectType:: Chunk, bytes)
cdb5beba Andy McCall 1 month ago ↶
1543
}
1544
1545
fn is_nocompress ( & self , r : & ObjectRef) -> bool {
64818938 Andy McCall 1 month ago ↶
1546
self .backend
1547
.has ( & format! ( " nocompress/{} " , r.to_hex ( ) ) )
1548
.unwrap_or ( false )
1549
}
1550
1551
1552
fn nocompress_set ( & self ) -> Result< std:: collections:: HashSet< ObjectRef> > {
1553
Ok ( self
1554
.backend
1555
.list ( " nocompress/" )
1556
.map_err ( backend_err) ?
1557
.into_iter ( )
1558
.filter_map ( | e | ObjectRef:: from_hex( e.key.trim_start_matches ( " nocompress/" ) ) )
1559
.collect ( ) )
cdb5beba Andy McCall 1 month ago ↶
1560
}
1561
86e682c2 Andy McCall 1 month ago ↶
1562
pub fn put_state ( & self , st : & StateObject) -> Result< ObjectRef> {
1563
self .put ( ObjectType:: State, & st.encode ( ) )
1564
}
1565
1566
pub fn get_state ( & self , r : & ObjectRef) -> Result< StateObject> {
1567
Ok ( StateObject:: decode( & self .get_typed ( r, ObjectType:: State) ? ) ? )
1568
}
1569
5b9c6ad4 Andy McCall 1 month ago ↶
1570
pub fn put_attest ( & self , a : & Attest) -> Result< ObjectRef> {
1571
self .put ( ObjectType:: Attest, & a.encode ( ) )
1572
}
1573
1574
pub fn get_attest ( & self , r : & ObjectRef) -> Result< Attest> {
1575
Ok ( Attest:: decode( & self .get_typed ( r, ObjectType:: Attest) ? ) ? )
fc1a466a Andy McCall 1 month ago ↶
1576
}
1577
1578
pub fn put_receipt ( & self , r : & Receipt) -> Result< ObjectRef> {
1579
self .put ( ObjectType:: Receipt, & r.encode ( ) )
5b9c6ad4 Andy McCall 1 month ago ↶
1580
}
1581
fc1a466a Andy McCall 1 month ago ↶
1582
pub fn get_receipt ( & self , r : & ObjectRef) -> Result< Receipt> {
1583
Ok ( Receipt:: decode( & self .get_typed ( r, ObjectType:: Receipt) ? ) ? )
5b9c6ad4 Andy McCall 1 month ago ↶
1584
}
1585
64818938 Andy McCall 1 month ago ↶
1586
1587
1588
fn index_add ( & self , index : & str , key : & ObjectRef, value : & ObjectRef) -> Result< ( ) > {
1589
let entry = format! ( " {} {} " , fanout_prefix ( index, key) , value.to_hex ( ) ) ;
1590
put_new_or_existing ( self .backend.as_ref ( ) , & entry, b " " )
fc1a466a Andy McCall 1 month ago ↶
1591
}
1592
64818938 Andy McCall 1 month ago ↶
1593
1594
1595
fn index_get ( & self , index : & str , legacy : & str , key : & ObjectRef) -> Result< Vec< ObjectRef> > {
1596
let mut values: Vec< ObjectRef> = self
1597
.backend
1598
.list ( & fanout_prefix ( index, key) )
1599
.map_err ( backend_err) ?
1600
.into_iter ( )
1601
.filter_map ( | e | e.key.rsplit ( ' /' ) .next ( ) .and_then ( ObjectRef:: from_hex) )
1602
.collect ( ) ;
1603
if let Some ( bytes) = get_optional ( self .backend.as_ref ( ) , & legacy_key ( legacy, key) ) ? {
1604
values.extend (
1605
String :: from_utf8_lossy( & bytes)
1606
.lines ( )
1607
.filter_map ( ObjectRef:: from_hex) ,
1608
) ;
5b9c6ad4 Andy McCall 1 month ago ↶
1609
}
fc1a466a Andy McCall 1 month ago ↶
1610
values.sort ( ) ;
64818938 Andy McCall 1 month ago ↶
1611
values.dedup ( ) ;
1612
Ok ( values)
5b9c6ad4 Andy McCall 1 month ago ↶
1613
}
1614
fc1a466a Andy McCall 1 month ago ↶
1615
1616
pub fn index_seal ( & self , change : & ObjectRef, attest : & ObjectRef) -> Result< ( ) > {
64818938 Andy McCall 1 month ago ↶
1617
self .index_add ( " seal" , change, attest)
fc1a466a Andy McCall 1 month ago ↶
1618
}
1619
1620
1621
pub fn seals_for ( & self , change : & ObjectRef) -> Result< Vec< ObjectRef> > {
64818938 Andy McCall 1 month ago ↶
1622
self .index_get ( " seal" , " seals" , change)
fc1a466a Andy McCall 1 month ago ↶
1623
}
1624
1625
1626
pub fn index_receipt ( & self , state : & ObjectRef, attest : & ObjectRef) -> Result< ( ) > {
64818938 Andy McCall 1 month ago ↶
1627
self .index_add ( " receipt" , state, attest)
fc1a466a Andy McCall 1 month ago ↶
1628
}
1629
1630
1631
pub fn receipts_for ( & self , state : & ObjectRef) -> Result< Vec< ObjectRef> > {
64818938 Andy McCall 1 month ago ↶
1632
self .index_get ( " receipt" , " receipts" , state)
fc1a466a Andy McCall 1 month ago ↶
1633
}
1634
3a4b3e01 Andy McCall 19 days ago ↶
1635
pub fn index_approval ( & self , state : & ObjectRef, attest : & ObjectRef) -> Result< ( ) > {
1636
self .index_add ( " approval" , state, attest)
1637
}
1638
1639
1640
1641
pub fn approvals_for ( & self , state : & ObjectRef) -> Result< Vec< ObjectRef> > {
1642
self .index_get ( " approval" , " approvals" , state)
1643
}
1644
1645
1646
pub fn approval_states ( & self ) -> Result< Vec< ObjectRef> > {
1647
self .indexed_states ( " approval" , " approvals" )
1648
}
1649
fc1a466a Andy McCall 1 month ago ↶
1650
1651
pub fn receipt_states ( & self ) -> Result< Vec< ObjectRef> > {
3a4b3e01 Andy McCall 19 days ago ↶
1652
self .indexed_states ( " receipt" , " receipts" )
1653
}
1654
1655
1656
1657
fn indexed_states ( & self , one : & str , many : & str ) -> Result< Vec< ObjectRef> > {
64818938 Andy McCall 1 month ago ↶
1658
let mut out: Vec< ObjectRef> = Vec :: new( ) ;
3a4b3e01 Andy McCall 19 days ago ↶
1659
for e in self .backend.list ( & format! ( " {one} /" ) ) .map_err ( backend_err) ? {
64818938 Andy McCall 1 month ago ↶
1660
1661
let mut parts = e.key.splitn ( 4 , ' /' ) ;
1662
let ( Some ( _ ) , Some ( hh) , Some ( rest) , Some ( _ ) ) =
1663
( parts.next ( ) , parts.next ( ) , parts.next ( ) , parts.next ( ) )
1664
else {
1665
continue ;
1666
} ;
1667
out.extend ( ObjectRef:: from_hex( & format! ( " {hh} {rest} " ) ) ) ;
fc1a466a Andy McCall 1 month ago ↶
1668
}
3a4b3e01 Andy McCall 19 days ago ↶
1669
for e in self
1670
.backend
1671
.list ( & format! ( " {many} /" ) )
1672
.map_err ( backend_err) ?
1673
{
64818938 Andy McCall 1 month ago ↶
1674
let mut parts = e.key.splitn ( 3 , ' /' ) ;
1675
let ( Some ( _ ) , Some ( hh) , Some ( rest) ) = ( parts.next ( ) , parts.next ( ) , parts.next ( ) ) else {
1676
continue ;
1677
} ;
1678
out.extend ( ObjectRef:: from_hex( & format! ( " {hh} {rest} " ) ) ) ;
1679
}
fc1a466a Andy McCall 1 month ago ↶
1680
out.sort ( ) ;
64818938 Andy McCall 1 month ago ↶
1681
out.dedup ( ) ;
fc1a466a Andy McCall 1 month ago ↶
1682
Ok ( out)
5b9c6ad4 Andy McCall 1 month ago ↶
1683
}
1684
1983686f Andy McCall 1 month ago ↶
1685
pub fn put_identity ( & self , id : & Identity) -> Result< ObjectRef> {
1686
self .put ( ObjectType:: Identity, & id.encode ( ) )
1687
}
1688
1689
pub fn get_identity ( & self , r : & ObjectRef) -> Result< Identity> {
1690
Ok ( Identity:: decode( & self .get_typed ( r, ObjectType:: Identity) ? ) ? )
1691
}
1692
64818938 Andy McCall 1 month ago ↶
1693
1694
1983686f Andy McCall 1 month ago ↶
1695
pub fn signatures ( & self , r : & ObjectRef) -> Result< Vec< Signature> > {
64818938 Andy McCall 1 month ago ↶
1696
let mut sigs: Vec< Signature> = Vec :: new( ) ;
1697
for e in self
1698
.backend
1699
.list ( & fanout_prefix ( " sig" , r) )
1700
.map_err ( backend_err) ?
1701
{
1702
let bytes = self .backend.get ( & e.key) .map_err ( backend_err) ? ;
1703
sigs.extend ( Signature:: decode_list( & bytes) ? ) ;
1983686f Andy McCall 1 month ago ↶
1704
}
64818938 Andy McCall 1 month ago ↶
1705
1706
1707
if let Some ( bytes) = get_optional ( self .backend.as_ref ( ) , & legacy_key ( " sigs" , r) ) ? {
1708
for sig in Signature:: decode_list( & bytes) ? {
1709
if ! sigs.iter ( ) .any ( | s | s.key = = sig.key ) {
1710
sigs.push ( sig) ;
1711
}
1712
}
1713
}
1714
sigs.sort_by_key ( | s | s.key ) ;
1715
sigs.dedup_by_key ( | s | s.key ) ;
1716
Ok ( sigs)
1983686f Andy McCall 1 month ago ↶
1717
}
1718
64818938 Andy McCall 1 month ago ↶
1719
1720
1721
1983686f Andy McCall 1 month ago ↶
1722
pub fn put_signature ( & self , r : & ObjectRef, sig : & Signature) -> Result< ( ) > {
64818938 Andy McCall 1 month ago ↶
1723
let key = format! ( " {} {} " , fanout_prefix ( " sig" , r) , sig.key.to_hex ( ) ) ;
1724
let bytes = Signature:: encode_list( std:: slice:: from_ref( sig) ) ;
1725
if get_optional ( self .backend.as_ref ( ) , & key) ? .as_deref ( ) = = Some ( bytes.as_slice ( ) ) {
1726
return Ok ( ( ) ) ;
1983686f Andy McCall 1 month ago ↶
1727
}
64818938 Andy McCall 1 month ago ↶
1728
delete ( self .backend.as_ref ( ) , & key) ? ;
1729
put_new_or_existing ( self .backend.as_ref ( ) , & key, & bytes)
1983686f Andy McCall 1 month ago ↶
1730
}
1731
54e2e630 Andy McCall 29 days ago ↶
1732
1733
1734
1735
1736
1737
1738
1739
pub fn adopt_unfinished_packs (
1740
& self ,
1741
min_age : std:: time:: Duration,
1742
progress : & dyn Fn( &str ) ,
1743
) -> Result< ( usize , usize ) > {
1744
use std:: io:: { BufReader, Read} ;
1745
let mut packs = 0 ;
1746
let mut objects = 0 ;
1747
let Ok ( dir) = fs:: read_dir( & self .scratch) else {
1748
return Ok ( ( 0 , 0 ) ) ;
1749
} ;
1750
for entry in dir.flatten ( ) {
1751
let name = entry.file_name ( ) .to_string_lossy ( ) .to_string ( ) ;
1752
let Some ( id) = name
1753
.strip_prefix ( " .tmp-" )
1754
.and_then ( | n | n.strip_suffix ( " .pack" ) )
1755
else {
1756
continue ;
1757
} ;
1758
let path = entry.path ( ) ;
1759
let meta = fs:: metadata( & path) ? ;
1760
let age = meta
1761
.modified ( )
1762
.ok ( )
1763
.and_then ( | m | m.elapsed ( ) .ok ( ) )
1764
.unwrap_or_default ( ) ;
1765
if age < min_age {
1766
progress ( & format! (
1767
" {name} : written to {} s ago, left alone" ,
1768
age.as_secs ( )
1769
) ) ;
1770
continue ;
1771
}
1772
let mut f = BufReader:: with_capacity( 1 < < 20 , fs:: File:: open( & path) ? ) ;
1773
let mut magic = [ 0 u8 ; 8 ] ;
1774
if f.read_exact ( & mut magic) .is_err ( ) | | & magic ! = PACK_MAGIC {
1775
progress ( & format! ( " {name} : not a pack, left alone" ) ) ;
1776
continue ;
1777
}
1778
let mut offset = PACK_MAGIC .len ( ) as u64 ;
1779
let mut index: Vec< ( ObjectRef, PackEntry) > = Vec :: new( ) ;
1780
loop {
1781
let mut head = [ 0 u8 ; 42 ] ;
1782
if f.read_exact ( & mut head) .is_err ( ) {
1783
break ;
1784
}
1785
let Some ( ty) = ObjectType:: ALL .get ( head[ 0 ] as usize ) .copied ( ) else {
1786
break ;
1787
} ;
1788
let flags = head[ 1 ] ;
1789
let stored_len = u32 :: from_le_bytes( head[ 2 .. 6 ] .try_into ( ) .unwrap ( ) ) ;
1790
let raw_len = u32 :: from_le_bytes( head[ 6 .. 10 ] .try_into ( ) .unwrap ( ) ) ;
1791
let r = ObjectRef( head[ 10 .. 42 ] .try_into ( ) .unwrap ( ) ) ;
1792
let mut stored = vec! [ 0 u8 ; stored_len as usize ] ;
1793
if flags & FLAG_DICT ! = 0 | | f.read_exact ( & mut stored) .is_err ( ) {
1794
break ;
1795
}
1796
let e = PackEntry {
1797
offset: offset + 42 ,
1798
stored_len,
1799
raw_len,
1800
ty,
1801
flags,
1802
} ;
1803
if decode_entry ( & r, e, stored, None ) .is_err ( ) {
1804
break ;
1805
}
1806
index.push ( ( r, e) ) ;
1807
offset + = 42 + stored_len as u64 ;
1808
if index.len ( ) .is_multiple_of ( 200_000 ) {
1809
progress ( & format! (
1810
" {name} : {} objects checked, {} MiB" ,
1811
index.len ( ) ,
1812
offset > > 20
1813
) ) ;
1814
}
1815
}
1816
if index.is_empty ( ) {
1817
progress ( & format! ( " {name} : no whole object, left alone" ) ) ;
1818
continue ;
1819
}
1820
if offset < meta.len ( ) {
1821
progress ( & format! (
1822
" {name} : {} torn bytes at the end cut off" ,
1823
meta.len ( ) - offset
1824
) ) ;
1825
fs:: OpenOptions:: new( )
1826
.write ( true )
1827
.open ( & path) ?
1828
.set_len ( offset) ? ;
1829
}
1830
index.sort_by_key ( | ( r , _) | * r) ;
1831
index.dedup_by_key ( | ( r , _) | * r) ;
1832
objects + = index.len ( ) ;
1833
packs + = 1 ;
1834
progress ( & format! ( " {name} : {} objects indexed" , index.len ( ) ) ) ;
1835
self .finish_pack_files ( id, & path, & index) ? ;
1836
}
1837
Ok ( ( packs, objects) )
1838
}
1839
d02e545d Andy McCall 1 month ago ↶
1840
ff0af1ee unknown 1 month ago
1841
pub fn list ( & self ) -> io:: Result< Vec< ObjectRef> > {
64818938 Andy McCall 1 month ago ↶
1842
let mut out: Vec< ObjectRef> = self
1843
.loose_objects ( )
1844
.map_err ( | e | io:: Error:: other( e.to_string ( ) ) ) ?
1845
.into_iter ( )
1846
.map ( | ( r , _) | r )
1847
.collect ( ) ;
d02e545d Andy McCall 1 month ago ↶
1848
for p in self .packs.borrow ( ) .iter ( ) {
1849
out.extend ( p.refs.iter ( ) .copied ( ) ) ;
ff0af1ee unknown 1 month ago
1850
}
1851
out.sort ( ) ;
d02e545d Andy McCall 1 month ago ↶
1852
out.dedup ( ) ;
ff0af1ee unknown 1 month ago
1853
Ok ( out)
1854
}
afaba5f1 Andy McCall 1 month ago ↶
1855
}
1856
64818938 Andy McCall 1 month ago ↶
1857
1858
fn parse_loose ( data : & [u8 ], r : & ObjectRef) -> Result< Object> {
1859
let nul = data
1860
.iter ( )
1861
.position ( | & b | b = = 0 )
1862
.ok_or_else ( | | StoreError:: Corrupt( * r, " no header" .into ( ) ) ) ? ;
1863
let head = std:: str:: from_utf8( & data[ .. nul] )
1864
.map_err ( | _| StoreError:: Corrupt( * r, " header not UTF-8" .into ( ) ) ) ? ;
1865
let mut parts = head.split ( ' ' ) ;
1866
let ( Some ( " oxy1" ) , Some ( ty) , Some ( len) , None ) =
1867
( parts.next ( ) , parts.next ( ) , parts.next ( ) , parts.next ( ) )
1868
else {
1869
return Err ( StoreError:: Corrupt( * r, format! ( " bad header {head:?} " ) ) ) ;
afaba5f1 Andy McCall 1 month ago ↶
1870
} ;
64818938 Andy McCall 1 month ago ↶
1871
let ty = ObjectType:: from_name( ty)
1872
.ok_or_else ( | | StoreError:: Corrupt( * r, format! ( " unknown type {ty} " ) ) ) ? ;
1873
let len: usize = len
1874
.parse ( )
1875
.map_err ( | _| StoreError:: Corrupt( * r, " bad length" .into ( ) ) ) ? ;
1876
let body = & data[ nul + 1 .. ] ;
1877
if body.len ( ) ! = len {
1878
return Err ( StoreError:: Corrupt(
1879
* r,
1880
format! ( " length {} != header {len} " , body.len ( ) ) ,
1881
) ) ;
1882
}
1883
if hash_object ( ty, body) ! = * r {
1884
return Err ( StoreError:: Corrupt( * r, " hash mismatch" .into ( ) ) ) ;
1885
}
1886
Ok ( ( ty, body.to_vec ( ) ) )
1887
}
1888
1889
1890
1891
# [ cfg ( test ) ]
1892
fn files_on_disk ( ) -> bool {
1893
std:: env:: var_os( " OXY_STORE_BACKEND" ) .is_none ( )
ff0af1ee unknown 1 month ago
1894
}
1895
1896
# [ cfg ( test ) ]
1897
mod tests {
1898
use super :: * ;
64818938 Andy McCall 1 month ago ↶
1899
1900
1901
1902
fn each_store ( dir : & Path, f : impl Fn( &str , Store ) ) {
1903
f ( " directory" , Store:: open( dir.join ( " objects" ) ) .unwrap ( ) ) ;
1904
f (
1905
" memory" ,
1906
Store:: open_on( Arc:: new( MemoryStore:: new( ) ) , dir.join ( " scratch" ) ) .unwrap ( ) ,
1907
) ;
1908
}
ff0af1ee unknown 1 month ago
1909
1910
# [ test ]
1911
fn put_get_verify ( ) {
64818938 Andy McCall 1 month ago ↶
1912
if ! super :: files_on_disk( ) {
1913
return ;
1914
}
ff0af1ee unknown 1 month ago
1915
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1916
let s = Store:: open( dir.path ( ) .join ( " objects" ) ) .unwrap ( ) ;
1917
let r = s.put ( ObjectType:: Chunk, b " hello" ) .unwrap ( ) ;
1918
assert! ( s.has ( & r) ) ;
1919
assert_eq! ( s.get ( & r) .unwrap ( ) , ( ObjectType:: Chunk, b " hello" .to_vec ( ) ) ) ;
1920
assert_eq! ( s.put ( ObjectType:: Chunk, b " hello" ) .unwrap ( ) , r) ;
1921
assert_eq! ( s.list ( ) .unwrap ( ) , vec! [ r] ) ;
75167672 Andy McCall 1 month ago
1922
assert! ( matches! (
1923
s.get_typed ( & r, ObjectType:: Blob) ,
1924
Err ( StoreError:: WrongType( .. ) )
1925
) ) ;
1926
assert! ( matches! (
1927
s.get ( & ObjectRef( [ 9 ; 32 ] ) ) ,
1928
Err ( StoreError:: NotFound( _ ) )
1929
) ) ;
ff0af1ee unknown 1 month ago
1930
1931
1932
let path = s.path_for ( & r) ;
1933
let mut data = fs:: read( & path) .unwrap ( ) ;
1934
let last = data.len ( ) - 1 ;
1935
data[ last] ^ = 1 ;
1936
fs:: write( & path, data) .unwrap ( ) ;
1937
assert! ( matches! ( s.get ( & r) , Err ( StoreError:: Corrupt( .. ) ) ) ) ;
1938
}
1939
1940
# [ test ]
d02e545d Andy McCall 1 month ago ↶
1941
fn packing_keeps_every_object_readable ( ) {
64818938 Andy McCall 1 month ago ↶
1942
if ! super :: files_on_disk( ) {
1943
return ;
1944
}
d02e545d Andy McCall 1 month ago ↶
1945
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
1946
let s = Store:: open( dir.path ( ) ) .unwrap ( ) ;
1947
let mut refs = Vec :: new( ) ;
1948
for i in 0 .. 300 u32 {
1949
let body = format! ( " object {i} " ) .repeat ( 1 + ( i % 7 ) as usize ) ;
1950
refs.push ( ( s.put ( ObjectType:: Chunk, body.as_bytes ( ) ) .unwrap ( ) , body) ) ;
1951
}
1952
let c = crate :: objects:: tests:: sample_change( ) ;
1953
let cr = s.put_change ( & c) .unwrap ( ) ;
1954
assert_eq! ( s.loose_count ( ) .unwrap ( ) , 301 ) ;
1955
let report = s.pack_loose ( ) .unwrap ( ) ;
1956
assert_eq! ( report.objects, 301 ) ;
1957
assert! ( report.packed_bytes < report.raw_bytes, " {report:?}" ) ;
1958
assert_eq! ( s.loose_count ( ) .unwrap ( ) , 0 ) ;
1959
assert_eq! ( s.pack_count ( ) , 1 ) ;
1960
for ( r, body) in & refs {
1961
assert! ( s.has ( r) ) ;
75167672 Andy McCall 1 month ago
1962
assert_eq! (
1963
s.get ( r) .unwrap ( ) ,
1964
( ObjectType:: Chunk, body.as_bytes ( ) .to_vec ( ) )
1965
) ;
d02e545d Andy McCall 1 month ago ↶
1966
}
1967
assert_eq! ( s.get_change ( & cr) .unwrap ( ) , c) ;
1968
assert_eq! ( s.list ( ) .unwrap ( ) .len ( ) , 301 ) ;
1969
1970
75167672 Andy McCall 1 month ago
1971
assert_eq! (
1972
s.put ( ObjectType:: Chunk, refs[ 0 ] .1. as_bytes ( ) ) .unwrap ( ) ,
1973
refs[ 0 ] .0
1974
) ;
d02e545d Andy McCall 1 month ago ↶
1975
assert_eq! ( s.loose_count ( ) .unwrap ( ) , 0 ) ;
1976
let extra = s.put ( ObjectType:: Chunk, b " after packing" ) .unwrap ( ) ;
1977
s.pack_loose ( ) .unwrap ( ) ;
1978
assert_eq! ( s.pack_count ( ) , 2 ) ;
1979
assert_eq! ( s.get ( & extra) .unwrap ( ) .1 , b " after packing" ) ;
1980
1981
let s2 = Store:: open( dir.path ( ) ) .unwrap ( ) ;
1982
assert_eq! ( s2.pack_count ( ) , 2 ) ;
1983
assert_eq! ( s2.get ( & refs[ 5 ] .0 ) .unwrap ( ) .1 , refs[ 5 ] .1. as_bytes ( ) ) ;
75167672 Andy McCall 1 month ago
1984
assert! ( matches! (
1985
s2.get ( & ObjectRef( [ 9 ; 32 ] ) ) ,
1986
Err ( StoreError:: NotFound( _ ) )
1987
) ) ;
d02e545d Andy McCall 1 month ago ↶
1988
1989
let target = refs[ 5 ] .0 ;
1990
let ( pack_path, offset) = {
1991
let packs = s2.packs.borrow ( ) ;
1992
let ( i, e) = s2.find_packed ( & target) .unwrap ( ) ;
64818938 Andy McCall 1 month ago ↶
1993
( dir.path ( ) .join ( & packs[ i] .key) , e.offset as usize )
d02e545d Andy McCall 1 month ago ↶
1994
} ;
1995
let mut data = fs:: read( & pack_path) .unwrap ( ) ;
1996
data[ offset] ^ = 0xff ;
1997
fs:: write( & pack_path, data) .unwrap ( ) ;
1998
let s3 = Store:: open( dir.path ( ) ) .unwrap ( ) ;
1999
assert! ( matches! ( s3.get ( & target) , Err ( StoreError:: Corrupt( .. ) ) ) ) ;
75167672 Andy McCall 1 month ago
2000
assert_eq! (
2001
s3.get ( & refs[ 6 ] .0 ) .unwrap ( ) .1 ,
2002
refs[ 6 ] .1. as_bytes ( ) ,
2003
" other objects are unaffected"
2004
) ;
a17fc622 Andy McCall 1 month ago ↶
2005
}
2006
2007
# [ test ]
2008
fn a_full_repack_trains_a_dictionary_and_reads_back ( ) {
64818938 Andy McCall 1 month ago ↶
2009
if ! super :: files_on_disk( ) {
2010
return ;
2011
}
a17fc622 Andy McCall 1 month ago ↶
2012
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2013
let s = Store:: open( dir.path ( ) ) .unwrap ( ) ;
2014
2015
let bodies: Vec< Vec< u8 > > = ( 0 .. 1500 )
2016
.map ( | i | {
2017
format! (
2018
" // SPDX-License-Identifier: GPL-2.0\n #include <linux/module.h>\n \
2019
static int probe_{i} (struct device *dev)\n {{ \n \t return {i} ;\n }} \n "
2020
)
2021
.into_bytes ( )
2022
} )
2023
.collect ( ) ;
2024
let refs: Vec< ObjectRef> = bodies
2025
.iter ( )
2026
.map ( | b | s.put ( ObjectType:: Chunk, b) .unwrap ( ) )
2027
.collect ( ) ;
2028
s.repack_all ( REPACK_ZSTD_LEVEL ) .unwrap ( ) ;
2029
let dicts = fs:: read_dir( dir.path ( ) )
2030
.unwrap ( )
2031
.flatten ( )
2032
.filter ( | e | e.path ( ) .extension ( ) .is_some_and ( | x | x = = " dict" ) )
2033
.count ( ) ;
2034
assert_eq! ( dicts, 1 , " the pack has a dictionary" ) ;
2035
let with_dict = refs
2036
.iter ( )
2037
.filter ( | r | s.find_packed ( r) .unwrap ( ) .1. flags & FLAG_DICT ! = 0 )
2038
.count ( ) ;
2039
assert! ( with_dict > 1000 , " most use it: {with_dict}" ) ;
2040
let again = Store:: open( dir.path ( ) ) .unwrap ( ) ;
2041
for ( r, b) in refs.iter ( ) .zip ( & bodies) {
2042
assert_eq! ( & again.get ( r) .unwrap ( ) .1 , b) ;
2043
}
a67914bc Andy McCall 19 days ago ↶
2044
let packs = | dir : & Path| {
2045
let mut v: Vec< String> = fs:: read_dir( dir)
2046
.unwrap ( )
2047
.flatten ( )
2048
.map ( | e | e.file_name ( ) .to_string_lossy ( ) .into_owned ( ) )
2049
.filter ( | n | n.starts_with ( " pack-" ) )
2050
.collect ( ) ;
2051
v.sort ( ) ;
2052
v
2053
} ;
2054
2055
2056
let before = packs ( dir.path ( ) ) ;
2057
let ( report, merged) = again.repack_all ( REPACK_ZSTD_LEVEL ) .unwrap ( ) ;
2058
assert_eq! ( ( report.objects, merged) , ( 0 , 0 ) ) ;
2059
assert_eq! ( packs ( dir.path ( ) ) , before) ;
2060
2061
let extra = again.put ( ObjectType:: Chunk, b " one more\n " ) .unwrap ( ) ;
a17fc622 Andy McCall 1 month ago ↶
2062
again.repack_all ( REPACK_ZSTD_LEVEL ) .unwrap ( ) ;
a67914bc Andy McCall 19 days ago ↶
2063
let files = packs ( dir.path ( ) ) ;
a17fc622 Andy McCall 1 month ago ↶
2064
assert_eq! ( files.len ( ) , 3 , " one pack, index and dictionary: {files:?}" ) ;
a67914bc Andy McCall 19 days ago ↶
2065
assert_ne! ( files, before) ;
a17fc622 Andy McCall 1 month ago ↶
2066
assert_eq! ( & again.get ( & refs[ 7 ] ) .unwrap ( ) .1 , & bodies[ 7 ] ) ;
a67914bc Andy McCall 19 days ago ↶
2067
assert_eq! ( again.get ( & extra) .unwrap ( ) .1 , b " one more\n " ) ;
d02e545d Andy McCall 1 month ago ↶
2068
}
2069
2070
# [ test ]
cdb5beba Andy McCall 1 month ago ↶
2071
fn nocompress_chunks_pack_raw ( ) {
2072
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2073
let s = Store:: open( dir.path ( ) ) .unwrap ( ) ;
2074
let body = vec! [ b ' a' ; 10_000 ] ;
2075
let raw = s.put_chunk_nocompress ( & body) .unwrap ( ) ;
2076
let compressible = s.put ( ObjectType:: Chunk, & vec! [ b ' b' ; 10_000 ] ) .unwrap ( ) ;
2077
s.pack_loose ( ) .unwrap ( ) ;
2078
let packs = s.packs.borrow ( ) ;
2079
let ( i, e) = s.find_packed ( & raw) .unwrap ( ) ;
2080
assert_eq! ( ( e.flags, e.stored_len as usize ) , ( 0 , 10_000 ) ) ;
2081
let ( _ , c) = s.find_packed ( & compressible) .unwrap ( ) ;
2082
assert_eq! ( c.flags, FLAG_ZSTD ) ;
2083
assert! ( c.stored_len < 10_000 ) ;
64818938 Andy McCall 1 month ago ↶
2084
assert_eq! ( packs[ i] .fetch ( s.backend.as_ref ( ) , & raw, e) .unwrap ( ) .1 , body) ;
cdb5beba Andy McCall 1 month ago ↶
2085
}
2086
2087
# [ test ]
135212de Andy McCall 1 month ago ↶
2088
fn many_packs_merge_into_one ( ) {
2089
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
64818938 Andy McCall 1 month ago ↶
2090
each_store ( dir.path ( ) , | which , s | {
2091
let mut refs = Vec :: new( ) ;
2092
for i in 0 .. ( MAX_PACKS + 2 ) {
2093
refs.push (
2094
s.put ( ObjectType:: Chunk, format! ( " pack {i} " ) .as_bytes ( ) )
2095
.unwrap ( ) ,
2096
) ;
2097
s.pack_loose ( ) .unwrap ( ) ;
2098
}
2099
assert_eq! ( s.pack_count ( ) , MAX_PACKS + 2 , " {which}" ) ;
2100
assert_eq! ( s.merge_packs ( ) .unwrap ( ) , MAX_PACKS + 2 ) ;
2101
assert_eq! ( s.pack_count ( ) , 1 ) ;
2102
assert_eq! ( s.merge_packs ( ) .unwrap ( ) , 0 ) ;
2103
for ( i, r) in refs.iter ( ) .enumerate ( ) {
2104
assert_eq! ( s.get ( r) .unwrap ( ) .1 , format! ( " pack {i} " ) .as_bytes ( ) ) ;
2105
}
2106
let again = Store:: open_on( s.backend ( ) .clone ( ) , s.dir ( ) ) .unwrap ( ) ;
2107
assert_eq! ( again.pack_count ( ) , 1 , " {which}: reopened" ) ;
2108
} ) ;
135212de Andy McCall 1 month ago ↶
2109
}
2110
2111
# [ test ]
ba160f60 Andy McCall 1 month ago ↶
2112
fn prune_removes_unreachable_loose_and_packed_objects ( ) {
2113
use std:: collections:: HashSet;
2114
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
64818938 Andy McCall 1 month ago ↶
2115
each_store ( dir.path ( ) , | which , s | {
2116
let keep_a = s.put ( ObjectType:: Chunk, b " keep a" ) .unwrap ( ) ;
2117
let drop_a = s.put ( ObjectType:: Chunk, b " drop a" ) .unwrap ( ) ;
2118
s.pack_loose ( ) .unwrap ( ) ;
2119
let keep_b = s.put ( ObjectType:: Chunk, b " keep b" ) .unwrap ( ) ;
2120
let drop_b = s.put ( ObjectType:: Chunk, b " drop b" ) .unwrap ( ) ;
2121
let sk = crate :: identity:: SecretKey {
2122
ed_seed: [ 1 ; 32 ] ,
2123
ml_seed: [ 2 ; 32 ] ,
2124
} ;
2125
let id = sk.identity ( " a" , " b" , 0 ) ;
2126
let key = s.put_identity ( & id) .unwrap ( ) ;
2127
s.put_signature (
2128
& drop_b,
2129
& sk.sign ( crate :: identity:: Domain:: Change, & key, & drop_b) ,
2130
)
75167672 Andy McCall 1 month ago
2131
.unwrap ( ) ;
64818938 Andy McCall 1 month ago ↶
2132
s.index_seal ( & drop_a, & ObjectRef( [ 3 ; 32 ] ) ) .unwrap ( ) ;
2133
let keep: HashSet< ObjectRef> = [ keep_a, keep_b, key] .into_iter ( ) .collect ( ) ;
2134
2135
let r = s
2136
.prune ( & keep, std:: time:: Duration:: from_secs( 3600 ) )
2137
.unwrap ( ) ;
2138
assert_eq! ( ( r.objects, r.deferred) , ( 0 , 2 ) , " {which}" ) ;
2139
let r = s.prune ( & keep, std:: time:: Duration:: ZERO ) .unwrap ( ) ;
2140
assert_eq! ( r.objects, 2 , " {which}: {r:?}" ) ;
2141
assert! ( s.has ( & keep_a) & & s.has ( & keep_b) & & s.has ( & key) ) ;
2142
assert! ( ! s.has ( & drop_a) & & ! s.has ( & drop_b) ) ;
2143
assert! ( s.signatures ( & drop_b) .unwrap ( ) .is_empty ( ) ) ;
2144
assert! ( s.seals_for ( & drop_a) .unwrap ( ) .is_empty ( ) ) ;
2145
assert_eq! ( s.pack_count ( ) , 1 ) ;
2146
assert_eq! ( s.get ( & keep_a) .unwrap ( ) .1 , b " keep a" ) ;
2147
assert_eq! ( s.list ( ) .unwrap ( ) .len ( ) , 3 ) ;
2148
} ) ;
ba160f60 Andy McCall 1 month ago ↶
2149
}
2150
2151
# [ test ]
1983686f Andy McCall 1 month ago ↶
2152
fn signatures_are_stored_beside_objects ( ) {
64818938 Andy McCall 1 month ago ↶
2153
use crate :: identity:: { Domain, SecretKey} ;
2154
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2155
each_store ( dir.path ( ) , | which , s | {
2156
let r = s.put ( ObjectType:: Chunk, b " x" ) .unwrap ( ) ;
2157
assert! ( s.signatures ( & r) .unwrap ( ) .is_empty ( ) ) ;
2158
let sk = SecretKey {
2159
ed_seed: [ 1 ; 32 ] ,
2160
ml_seed: [ 2 ; 32 ] ,
2161
} ;
2162
let id = sk.identity ( " a" , " b" , 0 ) ;
2163
let key = s.put_identity ( & id) .unwrap ( ) ;
2164
assert_eq! ( s.get_identity ( & key) .unwrap ( ) , id) ;
2165
let sig = sk.sign ( Domain:: Change, & key, & r) ;
2166
s.put_signature ( & r, & sig) .unwrap ( ) ;
2167
s.put_signature ( & r, & sig) .unwrap ( ) ;
2168
assert_eq! ( s.signatures ( & r) .unwrap ( ) , vec! [ sig.clone ( ) ] , " {which}" ) ;
2169
2170
let sk2 = SecretKey {
2171
ed_seed: [ 3 ; 32 ] ,
2172
ml_seed: [ 4 ; 32 ] ,
2173
} ;
2174
let key2 = s.put_identity ( & sk2.identity ( " c" , " d" , 0 ) ) .unwrap ( ) ;
2175
let sig2 = sk2.sign ( Domain:: Change, & key2, & r) ;
2176
s.put_signature ( & r, & sig2) .unwrap ( ) ;
2177
let mut both = vec! [ sig.clone ( ) , sig2.clone ( ) ] ;
2178
both.sort_by_key ( | s | s.key ) ;
2179
assert_eq! ( s.signatures ( & r) .unwrap ( ) , both) ;
2180
2181
assert_eq! ( s.get ( & r) .unwrap ( ) .1 , b " x" ) ;
2182
2183
s.index_seal ( & r, & ObjectRef( [ 5 ; 32 ] ) ) .unwrap ( ) ;
2184
s.index_seal ( & r, & ObjectRef( [ 4 ; 32 ] ) ) .unwrap ( ) ;
2185
s.index_seal ( & r, & ObjectRef( [ 5 ; 32 ] ) ) .unwrap ( ) ;
2186
assert_eq! (
2187
s.seals_for ( & r) .unwrap ( ) ,
2188
vec! [ ObjectRef( [ 4 ; 32 ] ) , ObjectRef( [ 5 ; 32 ] ) ]
2189
) ;
2190
let st = ObjectRef( [ 6 ; 32 ] ) ;
2191
s.index_receipt ( & st, & ObjectRef( [ 7 ; 32 ] ) ) .unwrap ( ) ;
2192
assert_eq! ( s.receipts_for ( & st) .unwrap ( ) , vec! [ ObjectRef( [ 7 ; 32 ] ) ] ) ;
2193
assert_eq! ( s.receipt_states ( ) .unwrap ( ) , vec! [ st] ) ;
2194
} ) ;
2195
}
2196
2197
2198
2199
2200
# [ test ]
2201
fn an_older_store_s_sidecar_lists_are_still_read ( ) {
2202
if ! super :: files_on_disk( ) {
2203
return ;
2204
}
1983686f Andy McCall 1 month ago ↶
2205
use crate :: identity:: { Domain, SecretKey} ;
2206
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2207
let s = Store:: open( dir.path ( ) ) .unwrap ( ) ;
64818938 Andy McCall 1 month ago ↶
2208
let r = s.put ( ObjectType:: Chunk, b " old" ) .unwrap ( ) ;
2209
let h = r.to_hex ( ) ;
75167672 Andy McCall 1 month ago
2210
let sk = SecretKey {
2211
ed_seed: [ 1 ; 32 ] ,
2212
ml_seed: [ 2 ; 32 ] ,
2213
} ;
64818938 Andy McCall 1 month ago ↶
2214
let key = s.put_identity ( & sk.identity ( " a" , " b" , 0 ) ) .unwrap ( ) ;
1983686f Andy McCall 1 month ago ↶
2215
let sig = sk.sign ( Domain:: Change, & key, & r) ;
64818938 Andy McCall 1 month ago ↶
2216
let legacy = dir.path ( ) .join ( " sigs" ) .join ( & h[ .. 2 ] ) ;
2217
fs:: create_dir_all( & legacy) .unwrap ( ) ;
2218
fs:: write(
2219
legacy.join ( & h[ 2 .. ] ) ,
2220
Signature:: encode_list( std:: slice:: from_ref( & sig) ) ,
2221
)
2222
.unwrap ( ) ;
2223
let seals = dir.path ( ) .join ( " seals" ) .join ( & h[ .. 2 ] ) ;
2224
fs:: create_dir_all( & seals) .unwrap ( ) ;
2225
fs:: write(
2226
seals.join ( & h[ 2 .. ] ) ,
2227
format! ( " {} \n " , ObjectRef( [ 9 ; 32 ] ) .to_hex ( ) ) ,
2228
)
2229
.unwrap ( ) ;
2230
let receipts = dir.path ( ) .join ( " receipts" ) .join ( & h[ .. 2 ] ) ;
2231
fs:: create_dir_all( & receipts) .unwrap ( ) ;
2232
fs:: write(
2233
receipts.join ( & h[ 2 .. ] ) ,
2234
format! ( " {} \n " , ObjectRef( [ 8 ; 32 ] ) .to_hex ( ) ) ,
2235
)
2236
.unwrap ( ) ;
2237
2238
assert_eq! ( s.signatures ( & r) .unwrap ( ) , vec! [ sig.clone ( ) ] ) ;
2239
assert_eq! ( s.seals_for ( & r) .unwrap ( ) , vec! [ ObjectRef( [ 9 ; 32 ] ) ] ) ;
2240
assert_eq! ( s.receipts_for ( & r) .unwrap ( ) , vec! [ ObjectRef( [ 8 ; 32 ] ) ] ) ;
2241
assert_eq! ( s.receipt_states ( ) .unwrap ( ) , vec! [ r] ) ;
2242
1983686f Andy McCall 1 month ago ↶
2243
s.put_signature ( & r, & sig) .unwrap ( ) ;
64818938 Andy McCall 1 month ago ↶
2244
s.index_seal ( & r, & ObjectRef( [ 1 ; 32 ] ) ) .unwrap ( ) ;
2245
assert_eq! ( s.signatures ( & r) .unwrap ( ) .len ( ) , 1 ) ;
2246
assert_eq! (
2247
s.seals_for ( & r) .unwrap ( ) ,
2248
vec! [ ObjectRef( [ 1 ; 32 ] ) , ObjectRef( [ 9 ; 32 ] ) ]
2249
) ;
2250
2251
let r2 = s.put ( ObjectType:: Chunk, b " kept" ) .unwrap ( ) ;
2252
let keep = [ r2, key] .into_iter ( ) .collect ( ) ;
2253
s.prune ( & keep, std:: time:: Duration:: ZERO ) .unwrap ( ) ;
2254
assert! ( s.signatures ( & r) .unwrap ( ) .is_empty ( ) ) ;
2255
assert! ( s.seals_for ( & r) .unwrap ( ) .is_empty ( ) ) ;
2256
assert! ( s.receipt_states ( ) .unwrap ( ) .is_empty ( ) ) ;
1983686f Andy McCall 1 month ago ↶
2257
}
2258
2259
# [ test ]
ff0af1ee unknown 1 month ago
2260
fn change_round_trip_through_store ( ) {
2261
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
64818938 Andy McCall 1 month ago ↶
2262
each_store ( dir.path ( ) , | which , s | {
2263
let c = crate :: objects:: tests:: sample_change( ) ;
2264
let r = s.put_change ( & c) .unwrap ( ) ;
2265
assert_eq! ( r, c.hash ( ) , " {which}" ) ;
2266
assert_eq! ( s.get_change ( & r) .unwrap ( ) , c) ;
2267
assert_eq! ( s.get_change_meta ( & r) .unwrap ( ) .message, c.message) ;
2268
} ) ;
ff0af1ee unknown 1 month ago
2269
}
2b872d07 Andy McCall 1 month ago ↶
2270
2271
fn xorshift ( state : & mut u64 ) -> u64 {
2272
* state ^ = * state < < 13 ;
2273
* state ^ = * state > > 7 ;
2274
* state ^ = * state < < 17 ;
2275
* state
2276
}
2277
2278
2279
2280
2281
# [ test ]
2282
fn corrupted_packs_and_indexes_fail_cleanly ( ) {
64818938 Andy McCall 1 month ago ↶
2283
if ! super :: files_on_disk( ) {
2284
return ;
2285
}
2b872d07 Andy McCall 1 month ago ↶
2286
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2287
let objects = dir.path ( ) .join ( " objects" ) ;
2288
let s = Store:: open( & objects) .unwrap ( ) ;
2289
let mut refs = Vec :: new( ) ;
2290
let mut seed = 0x9e3779b97f4a7c15 u64 ;
2291
for i in 0 .. 40 u32 {
2292
let len = ( xorshift ( & mut seed) % 5000 ) as usize ;
2293
let mut body = vec! [ 0 u8 ; len] ;
2294
for b in body.iter_mut ( ) {
75167672 Andy McCall 1 month ago
2295
* b = if i % 2 = = 0 {
2296
( xorshift ( & mut seed) & 0xff ) as u8
2297
} else {
2298
b ' a' + ( i % 26 ) as u8
2299
} ;
2b872d07 Andy McCall 1 month ago ↶
2300
}
2301
refs.push ( s.put ( ObjectType:: Chunk, & body) .unwrap ( ) ) ;
2302
}
2303
s.pack_loose ( ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
2304
let pack = fs:: read_dir( & objects)
2305
.unwrap ( )
2306
.filter_map ( | e | e.ok ( ) .map ( | e | e.path ( ) ) )
2307
.find ( | p | p.extension ( ) .is_some_and ( | x | x = = " pack" ) )
2308
.unwrap ( ) ;
2b872d07 Andy McCall 1 month ago ↶
2309
let idx = pack.with_extension ( " idx" ) ;
2310
let pristine_pack = fs:: read( & pack) .unwrap ( ) ;
2311
let pristine_idx = fs:: read( & idx) .unwrap ( ) ;
2312
2313
for round in 0 .. 300 u32 {
75167672 Andy McCall 1 month ago
2314
let ( path, pristine) = if round % 2 = = 0 {
2315
( & pack, & pristine_pack)
2316
} else {
2317
( & idx, & pristine_idx)
2318
} ;
2b872d07 Andy McCall 1 month ago ↶
2319
let mut data = pristine.clone ( ) ;
2320
match xorshift ( & mut seed) % 4 {
2321
0 => {
2322
let at = ( xorshift ( & mut seed) as usize ) % data.len ( ) ;
2323
data[ at] ^ = 1 < < ( xorshift ( & mut seed) % 8 ) ;
2324
}
2325
1 => {
2326
let at = ( xorshift ( & mut seed) as usize ) % data.len ( ) ;
2327
data[ at] = ( xorshift ( & mut seed) & 0xff ) as u8 ;
2328
}
2329
2 => {
2330
let keep = ( xorshift ( & mut seed) as usize ) % data.len ( ) ;
2331
data.truncate ( keep) ;
2332
}
2333
_ => {
2334
let at = ( xorshift ( & mut seed) as usize ) % data.len ( ) ;
2335
let n = ( ( xorshift ( & mut seed) % 8 ) + 1 ) as usize ;
2336
for k in 0 .. n.min ( data.len ( ) - at) {
2337
data[ at + k] = 0xff ;
2338
}
2339
}
2340
}
2341
fs:: write( path, & data) .unwrap ( ) ;
2342
2343
2344
if let Ok ( s2) = Store:: open( & objects) {
2345
for r in & refs {
2346
match s2.get ( r) {
75167672 Andy McCall 1 month ago
2347
Ok ( ( ty, body) ) => assert_eq! (
2348
hash_object ( ty, & body) ,
2349
* r,
2350
" a returned object always matches its reference"
2351
) ,
2352
Err ( StoreError:: Corrupt( .. ) )
2353
| Err ( StoreError:: NotFound( _ ) )
2354
| Err ( StoreError:: Io( _ ) ) => { }
2b872d07 Andy McCall 1 month ago ↶
2355
Err ( e) => panic! ( " unexpected error {e}" ) ,
2356
}
2357
}
2358
}
2359
fs:: write( path, pristine) .unwrap ( ) ;
2360
}
2361
2362
let s3 = Store:: open( & objects) .unwrap ( ) ;
2363
for r in & refs {
2364
let ( ty, body) = s3.get ( r) .unwrap ( ) ;
2365
assert_eq! ( hash_object ( ty, & body) , * r) ;
2366
}
2367
}
8f10a07a Andy McCall 1 month ago ↶
2368
2369
2370
2371
# [ test ]
2372
fn batches_write_loose_or_one_pack ( ) {
2373
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
64818938 Andy McCall 1 month ago ↶
2374
each_store ( dir.path ( ) , | which , s | {
2375
let outer = s.begin_batch ( ) .unwrap ( ) ;
2376
let a = s.put ( ObjectType:: Chunk, b " small a" ) .unwrap ( ) ;
2377
{
2378
let inner = s.begin_batch ( ) .unwrap ( ) ;
2379
let b = s.put ( ObjectType:: Chunk, b " small b" ) .unwrap ( ) ;
2380
assert! ( s.has ( & b) ) ;
2381
assert_eq! ( s.get ( & b) .unwrap ( ) .1 , b " small b" ) ;
2382
inner.finish ( ) .unwrap ( ) ;
2383
assert_eq! (
2384
s.loose_count ( ) .unwrap ( ) ,
2385
0 ,
2386
" {which}: the inner batch defers to the outer one"
2387
) ;
2388
}
2389
assert_eq! ( s.get ( & a) .unwrap ( ) .1 , b " small a" ) ;
75167672 Andy McCall 1 month ago
2390
assert_eq! (
64818938 Andy McCall 1 month ago ↶
2391
s.put ( ObjectType:: Chunk, b " small a" ) .unwrap ( ) ,
2392
a,
2393
" idempotent while pending"
75167672 Andy McCall 1 month ago
2394
) ;
64818938 Andy McCall 1 month ago ↶
2395
outer.finish ( ) .unwrap ( ) ;
2396
assert_eq! ( s.loose_count ( ) .unwrap ( ) , 2 ) ;
2397
assert_eq! ( s.pack_count ( ) , 0 ) ;
2398
assert_eq! ( s.get ( & a) .unwrap ( ) .1 , b " small a" ) ;
8f10a07a Andy McCall 1 month ago ↶
2399
64818938 Andy McCall 1 month ago ↶
2400
2401
let big = s.begin_batch ( ) .unwrap ( ) ;
2402
let mut refs = Vec :: new( ) ;
2403
let mut seed = 7 u64 ;
2404
for i in 0 .. 300 u32 {
2405
let mut body = vec! [ b ' z' ; 40_000 ] ;
2406
if i % 3 = = 0 {
2407
for b in body.iter_mut ( ) {
2408
* b = ( xorshift ( & mut seed) & 0xff ) as u8 ;
2409
}
8f10a07a Andy McCall 1 month ago ↶
2410
}
64818938 Andy McCall 1 month ago ↶
2411
body[ .. 4 ] .copy_from_slice ( & i.to_le_bytes ( ) ) ;
2412
let r = if i % 5 = = 0 {
2413
s.put_chunk_nocompress ( & body) .unwrap ( )
2414
} else {
2415
s.put ( ObjectType:: Chunk, & body) .unwrap ( )
2416
} ;
2417
assert_eq! (
2418
s.get ( & r) .unwrap ( ) .1 ,
2419
body,
2420
" readable while pending (object {i})"
2421
) ;
2422
refs.push ( ( r, body) ) ;
8f10a07a Andy McCall 1 month ago ↶
2423
}
64818938 Andy McCall 1 month ago ↶
2424
assert! ( s.has ( & refs[ 0 ] .0 ) ) ;
2425
big.finish ( ) .unwrap ( ) ;
2426
assert_eq! ( s.pack_count ( ) , 1 ) ;
75167672 Andy McCall 1 month ago
2427
assert_eq! (
64818938 Andy McCall 1 month ago ↶
2428
s.loose_count ( ) .unwrap ( ) ,
2429
2 ,
2430
" nothing from the big batch is loose"
75167672 Andy McCall 1 month ago
2431
) ;
64818938 Andy McCall 1 month ago ↶
2432
assert! ( fs:: read_dir( s.dir ( ) )
75167672 Andy McCall 1 month ago
2433
.unwrap ( )
64818938 Andy McCall 1 month ago ↶
2434
.flatten ( )
2435
.all ( | e | ! e.file_name ( ) .to_string_lossy ( ) .contains ( " tmp-" ) ) ) ;
2436
let s2 = Store:: open_on( s.backend ( ) .clone ( ) , s.dir ( ) ) .unwrap ( ) ;
2437
for ( r, body) in & refs {
2438
assert_eq! ( s2.get ( r) .unwrap ( ) .1 , * body) ;
2439
}
2440
2441
{
2442
let _b = s2.begin_batch ( ) .unwrap ( ) ;
2443
s2.put ( ObjectType:: Chunk, b " dropped" ) .unwrap ( ) ;
2444
}
2445
assert_eq! (
2446
s2.get ( & hash_object ( ObjectType:: Chunk, b " dropped" ) )
2447
.unwrap ( )
2448
.1 ,
2449
b " dropped"
2450
) ;
2451
} ) ;
afaba5f1 Andy McCall 1 month ago ↶
2452
}
2453
2454
# [ test ]
2455
fn get_many_reads_loose_and_packed_objects_in_order ( ) {
2456
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
64818938 Andy McCall 1 month ago ↶
2457
each_store ( dir.path ( ) , | which , s | {
2458
let packed: Vec< ObjectRef> = ( 0 .. 20 )
2459
.map ( | i | {
2460
s.put ( ObjectType:: Chunk, format! ( " packed {i} " ) .as_bytes ( ) )
2461
.unwrap ( )
2462
} )
2463
.collect ( ) ;
2464
s.pack_loose ( ) .unwrap ( ) ;
2465
let loose: Vec< ObjectRef> = ( 0 .. 20 )
2466
.map ( | i | {
2467
s.put ( ObjectType:: Chunk, format! ( " loose {i} " ) .as_bytes ( ) )
2468
.unwrap ( )
2469
} )
2470
.collect ( ) ;
2471
let missing = hash_object ( ObjectType:: Chunk, b " never stored" ) ;
2472
let mut refs: Vec< ObjectRef> = Vec :: new( ) ;
2473
for i in 0 .. 20 {
2474
refs.push ( loose[ i] ) ;
2475
refs.push ( packed[ i] ) ;
2476
}
2477
refs.push ( missing) ;
2478
let got = s.get_many ( & refs) ;
2479
assert_eq! ( got.len ( ) , refs.len ( ) ) ;
2480
for ( i, r) in refs.iter ( ) .enumerate ( ) .take ( 40 ) {
2481
let want = if i % 2 = = 0 {
2482
format! ( " loose {} " , i / 2 )
2483
} else {
2484
format! ( " packed {} " , i / 2 )
2485
} ;
2486
let ( ty, body) = got[ i] .as_ref ( ) .unwrap ( ) ;
2487
assert_eq! (
2488
( * ty, body.as_slice ( ) ) ,
2489
( ObjectType:: Chunk, want.as_bytes ( ) ) ,
2490
" {which}: {r:?}"
2491
) ;
2492
}
2493
assert! ( matches! ( got[ 40 ] , Err ( StoreError:: NotFound( _ ) ) ) ) ;
2494
} ) ;
09245702 Andy McCall 19 days ago ↶
2495
}
2496
2497
2498
2499
2500
# [ test ]
2501
fn packed_objects_cost_no_loose_lookups ( ) {
2502
use std:: sync:: atomic:: { AtomicUsize, Ordering} ;
2503
struct Counting ( MemoryStore, AtomicUsize) ;
2504
impl crate:: strata:: Store for Counting {
2505
fn describe ( & self ) -> String {
2506
" counting" .into ( )
2507
}
2508
fn has ( & self , k : & str ) -> crate:: strata:: Result< bool > {
2509
self .0. has ( k)
2510
}
2511
fn get ( & self , k : & str ) -> crate:: strata:: Result< Vec< u8 > > {
2512
if k.starts_with ( " loose/" ) {
2513
self .1. fetch_add ( 1 , Ordering:: Relaxed) ;
2514
}
2515
self .0. get ( k)
2516
}
2517
fn get_range (
2518
& self ,
2519
k : & str ,
2520
r : std:: ops:: Range< u64 > ,
2521
) -> crate:: strata:: Result< Vec< u8 > > {
2522
self .0. get_range ( k, r)
2523
}
2524
fn put_new ( & self , k : & str , b : & [u8 ]) -> crate:: strata:: Result< ( ) > {
2525
self .0. put_new ( k, b)
2526
}
2527
fn list ( & self , p : & str ) -> crate:: strata:: Result< Vec< crate:: strata:: Entry> > {
2528
self .0. list ( p)
2529
}
2530
fn delete ( & self , k : & str ) -> crate:: strata:: Result< ( ) > {
2531
self .0. delete ( k)
2532
}
2533
fn read_ref (
2534
& self ,
2535
k : & str ,
2536
) -> crate:: strata:: Result< Option< ( Vec< u8 > , crate:: strata:: Version) > > {
2537
self .0. read_ref ( k)
2538
}
2539
fn write_ref (
2540
& self ,
2541
k : & str ,
2542
b : & [u8 ],
2543
e : Option< & crate:: strata:: Version> ,
2544
) -> crate:: strata:: Result< crate:: strata:: Version> {
2545
self .0. write_ref ( k, b, e)
2546
}
2547
}
2548
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2549
let backend = Arc:: new( Counting( MemoryStore:: new( ) , AtomicUsize:: new( 0 ) ) ) ;
2550
let s = Store:: open_on( backend.clone ( ) , dir.path ( ) .join ( " scratch" ) ) .unwrap ( ) ;
2551
let refs: Vec< ObjectRef> = ( 0 .. 10 )
2552
.map ( | i | {
2553
s.put ( ObjectType:: Chunk, format! ( " p {i} " ) .as_bytes ( ) )
2554
.unwrap ( )
2555
} )
2556
.collect ( ) ;
2557
s.pack_loose ( ) .unwrap ( ) ;
2558
let s = Store:: open_on( backend.clone ( ) , dir.path ( ) .join ( " scratch" ) ) .unwrap ( ) ;
2559
backend.1. store ( 0 , Ordering:: Relaxed) ;
2560
for r in & refs {
2561
assert! ( s.get ( r) .is_ok ( ) ) ;
2562
}
2563
assert! ( s.get_many ( & refs) .iter ( ) .all ( | g | g.is_ok ( ) ) ) ;
2564
assert_eq! ( backend.1. load ( Ordering:: Relaxed) , 0 , " no loose lookups" ) ;
2565
2566
let missing = hash_object ( ObjectType:: Chunk, b " never stored" ) ;
2567
assert! ( matches! ( s.get ( & missing) , Err ( StoreError:: NotFound( _ ) ) ) ) ;
2568
assert! ( backend.1. load ( Ordering:: Relaxed) > 0 ) ;
a9f686ed Andy McCall 1 month ago ↶
2569
}
2570
}
2571
2572
# [ cfg ( test ) ]
2573
mod batch_tests {
2574
use super :: * ;
2575
2576
# [ test ]
2577
fn a_large_batch_packs_every_object_and_indexes_them_all ( ) {
2578
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2579
let store = Store:: open( dir.path ( ) ) .unwrap ( ) ;
2580
let mut refs = Vec :: new( ) ;
2581
{
2582
let batch = store.begin_batch ( ) .unwrap ( ) ;
2583
for i in 0 .. 1000 u32 {
2584
let body = format! ( " object {i} {} " , " x" .repeat ( 20_000 + i as usize ) ) .into_bytes ( ) ;
2585
refs.push ( store.put ( ObjectType:: Blob, & body) .unwrap ( ) ) ;
2586
}
2587
batch.finish ( ) .unwrap ( ) ;
2588
}
2589
let again = Store:: open( dir.path ( ) ) .unwrap ( ) ;
2590
for r in & refs {
2591
again
54e2e630 Andy McCall 29 days ago ↶
2592
.get ( r)
2593
.unwrap_or_else ( | e | panic! ( " {}: {e}" , r.short ( 12 ) ) ) ;
2594
}
2595
}
2596
2597
# [ test ]
2598
fn a_pack_a_stopped_batch_left_is_adopted_and_its_torn_end_cut ( ) {
2599
2600
2601
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2602
let mut refs = Vec :: new( ) ;
2603
{
2604
let store = Store:: open( dir.path ( ) ) .unwrap ( ) ;
2605
let batch = store.begin_batch ( ) .unwrap ( ) ;
2606
for i in 0 .. 300 u32 {
2607
let body = format! ( " object {i} {} " , " y" .repeat ( 20_000 + i as usize ) ) .into_bytes ( ) ;
2608
refs.push ( store.put ( ObjectType:: Blob, & body) .unwrap ( ) ) ;
2609
}
2610
std:: mem:: forget( batch) ;
2611
}
2612
let tmp: Vec< _ > = fs:: read_dir( dir.path ( ) )
2613
.unwrap ( )
2614
.flatten ( )
2615
.filter ( | e | e.file_name ( ) .to_string_lossy ( ) .starts_with ( " .tmp-" ) )
2616
.map ( | e | e.path ( ) )
2617
.collect ( ) ;
2618
assert_eq! ( tmp.len ( ) , 1 , " one unfinished pack" ) ;
2619
2620
let mut f = fs:: OpenOptions:: new( ) .append ( true ) .open ( & tmp[ 0 ] ) .unwrap ( ) ;
2621
f.write_all ( & [ 0 , 1 , 0xff , 0 , 0 , 0 , 9 , 9 , 0 , 0 ] ) .unwrap ( ) ;
2622
f.write_all ( & [ 7 ; 40 ] ) .unwrap ( ) ;
2623
drop ( f) ;
2624
let store = Store:: open( dir.path ( ) ) .unwrap ( ) ;
2625
assert! ( store.get ( & refs[ 0 ] ) .is_err ( ) , " unreadable before" ) ;
2626
let young = store
2627
.adopt_unfinished_packs ( std:: time:: Duration:: from_secs( 3600 ) , & | _ | { } )
2628
.unwrap ( ) ;
2629
assert_eq! ( young, ( 0 , 0 ) , " a pack written to lately is left alone" ) ;
2630
let ( packs, objects) = store
2631
.adopt_unfinished_packs ( std:: time:: Duration:: ZERO , & | _ | { } )
2632
.unwrap ( ) ;
2633
assert_eq! ( ( packs, objects) , ( 1 , 300 ) ) ;
2634
for r in & refs {
2635
store
a9f686ed Andy McCall 1 month ago ↶
2636
.get ( r)
2637
.unwrap_or_else ( | e | panic! ( " {}: {e}" , r.short ( 12 ) ) ) ;
2638
}
54e2e630 Andy McCall 29 days ago ↶
2639
let again = Store:: open( dir.path ( ) ) .unwrap ( ) ;
2640
assert! ( again.get ( & refs[ 299 ] ) .is_ok ( ) , " indexed for good" ) ;
a9f686ed Andy McCall 1 month ago ↶
2641
}
2642
}
2643
2644
# [ cfg ( test ) ]
2645
mod size_tests {
2646
use super :: * ;
2647
2648
# [ test ]
2649
fn an_object_over_the_limit_is_refused_and_nothing_is_written ( ) {
2650
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2651
let store = Store:: open( dir.path ( ) ) .unwrap ( ) ;
2652
let big = vec! [ b ' x' ; MAX_OBJECT_LEN as usize + 1 ] ;
2653
{
2654
let batch = store.begin_batch ( ) .unwrap ( ) ;
2655
store.put ( ObjectType:: Blob, b " small" ) .unwrap ( ) ;
2656
let e = store.put ( ObjectType:: Change, & big) .unwrap_err ( ) ;
2657
assert! (
2658
matches! ( e, StoreError:: TooLarge { kind: " change" , .. } ) ,
2659
" {e}"
2660
) ;
2661
assert! ( e.to_string ( ) .contains ( " commit it in parts" ) , " {e}" ) ;
2662
batch.finish ( ) .unwrap ( ) ;
2663
}
2664
2665
let again = Store:: open( dir.path ( ) ) .unwrap ( ) ;
2666
again.get ( & hash_object ( ObjectType:: Blob, b " small" ) ) .unwrap ( ) ;
6f8aeb99 Andy McCall 1 month ago ↶
2667
}
2668
}
2669
2670
# [ cfg ( test ) ]
2671
mod text_tests {
2672
use super :: * ;
2673
use crate :: objects:: { AtomRef, Edit, CHANGE_VERSION } ;
2674
2675
fn large_change ( ) -> Change {
2676
let mut c = crate :: objects:: tests:: sample_change( ) ;
2677
assert_eq! ( c.version, CHANGE_VERSION ) ;
2678
c.edits[ 0 ] = Edit:: Insert {
2679
node: crate :: ids:: NodeId( [ 7 ; 16 ] ) ,
2680
after: AtomRef:: Start,
2681
before: AtomRef:: End,
2682
lines: ( 0 .. 2000 )
2683
.map ( | i | format! ( " line {i} \n " ) .into_bytes ( ) )
2684
.collect ( ) ,
2685
} ;
2686
c
2687
}
2688
2689
# [ test ]
2690
fn a_large_insert_is_stored_beside_its_change_and_read_back ( ) {
2691
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2692
let s = Store:: open( dir.path ( ) ) .unwrap ( ) ;
2693
let c = large_change ( ) ;
2694
let r = s.put_change ( & c) .unwrap ( ) ;
2695
assert_eq! ( r, c.hash ( ) ) ;
2696
let ( _ , body) = s.get ( & r) .unwrap ( ) ;
2697
assert! (
2698
body.len ( ) < 1024 ,
2699
" the change holds a reference, not {} bytes of lines" ,
2700
body.len ( )
2701
) ;
2702
let ( back, parts) = s.get_change_parts ( & r) .unwrap ( ) ;
2703
assert_eq! ( back, c) ;
2704
2705
assert_eq! ( parts.len ( ) , 2 ) ;
2706
for p in & parts {
2707
assert! ( s.has ( p) ) ;
2708
}
2709
2710
let n = s.list ( ) .unwrap ( ) .len ( ) ;
2711
s.put_change ( & c) .unwrap ( ) ;
2712
assert_eq! ( s.list ( ) .unwrap ( ) .len ( ) , n) ;
2713
}
2714
2715
# [ test ]
2716
fn a_change_whose_text_blob_is_missing_is_reported_not_misread ( ) {
64818938 Andy McCall 1 month ago ↶
2717
if ! super :: files_on_disk( ) {
2718
return ;
2719
}
6f8aeb99 Andy McCall 1 month ago ↶
2720
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2721
let s = Store:: open( dir.path ( ) ) .unwrap ( ) ;
2722
let c = large_change ( ) ;
2723
let r = s.put_change ( & c) .unwrap ( ) ;
2724
let ( _ , parts) = s.get_change_parts ( & r) .unwrap ( ) ;
2725
fs:: remove_file( s.path_for ( & parts[ 0 ] ) ) .unwrap ( ) ;
2726
let e = s.get_change ( & r) .unwrap_err ( ) ;
2727
assert! (
2728
matches! ( & e, StoreError:: Corrupt( h, why) if * h = = r & & why.contains ( " missing" ) ) ,
2729
" {e}"
2730
) ;
192fad1e Andy McCall 1 month ago ↶
2731
}
2732
}
2733
2734
# [ cfg ( test ) ]
2735
mod put_many_tests {
2736
use super :: * ;
2737
2738
# [ test ]
2739
fn many_objects_in_a_batch_are_compressed_together_and_all_read_back ( ) {
2740
let dir = tempfile:: tempdir( ) .unwrap ( ) ;
2741
let s = Store:: open( dir.path ( ) ) .unwrap ( ) ;
2742
let before = s.put ( ObjectType:: Chunk, b " already here" ) .unwrap ( ) ;
2743
let bodies: Vec< Vec< u8 > > = ( 0 .. 200 u32 )
2744
.map ( | i | format! ( " chunk {i} " ) .repeat ( 6000 ) .into_bytes ( ) )
2745
.collect ( ) ;
2746
let mut objects: Vec< ( ObjectType, & [u8 ]) > = bodies
2747
.iter ( )
2748
.map ( | b | ( ObjectType:: Chunk, b.as_slice ( ) ) )
2749
.collect ( ) ;
2750
objects.push ( ( ObjectType:: Chunk, bodies[ 0 ] .as_slice ( ) ) ) ;
2751
objects.push ( ( ObjectType:: Chunk, b " already here" ) ) ;
2752
let refs = {
2753
let batch = s.begin_batch ( ) .unwrap ( ) ;
2754
let refs = s.put_many ( & objects) .unwrap ( ) ;
2755
batch.finish ( ) .unwrap ( ) ;
2756
refs
2757
} ;
2758
assert_eq! ( refs.len ( ) , objects.len ( ) ) ;
2759
assert_eq! ( refs[ 200 ] , refs[ 0 ] , " a repeat is the same object" ) ;
2760
assert_eq! ( refs[ 201 ] , before) ;
2761
let again = Store:: open( dir.path ( ) ) .unwrap ( ) ;
2762
for ( r, b) in refs.iter ( ) .zip ( & objects) {
2763
assert_eq! ( again.get ( r) .unwrap ( ) .1 , b.1 ) ;
2764
}
8f10a07a Andy McCall 1 month ago ↶
2765
}
ff0af1ee unknown 1 month ago
2766
}