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