|
|
… |
2345 unchanged lines |
| 2346 |
2346 |
|
|
| 2347 |
2347 |
|
// ---- the journal and entry lists (spec §13.7, #116) |
| 2348 |
2348 |
|
|
| 2349 |
|
- |
/// Whether lookups still read the one-list keys of older stores. |
|
2349 |
+ |
/// Whether lookups still read the one-list keys of older stores: until |
|
2350 |
+ |
/// a complete fold has copied them into per-object entries and the |
|
2351 |
+ |
/// pack listing shows its marker (spec §13.7). |
| 2350 |
2352 |
|
fn reads_legacy(&self) -> bool { |
| 2351 |
|
- |
true |
|
2353 |
+ |
!self.legacy_folded.get() |
| 2352 |
2354 |
|
} |
| 2353 |
2355 |
|
|
|
2356 |
+ |
/// Copy every older one-list key's contents into per-object entries, |
|
2357 |
+ |
/// keeping an entry already there, then write the marker that tells |
|
2358 |
+ |
/// readers to stop reading those keys. The keys themselves are left |
|
2359 |
+ |
/// for older readers. A list that cannot be read is passed over, and |
|
2360 |
+ |
/// said. |
|
2361 |
+ |
fn fold_legacy(&self, progress: Progress) -> Result<()> { |
|
2362 |
+ |
let mut copied = 0usize; |
|
2363 |
+ |
for (index, legacy) in ENTRY_INDEXES { |
|
2364 |
+ |
for e in self |
|
2365 |
+ |
.backend |
|
2366 |
+ |
.list(&format!("{legacy}/")) |
|
2367 |
+ |
.map_err(backend_err)? |
|
2368 |
+ |
{ |
|
2369 |
+ |
// <legacy>/<hh>/<rest> |
|
2370 |
+ |
let mut parts = e.key.splitn(3, '/'); |
|
2371 |
+ |
let (Some(_), Some(hh), Some(rest)) = (parts.next(), parts.next(), parts.next()) |
|
2372 |
+ |
else { |
|
2373 |
+ |
continue; |
|
2374 |
+ |
}; |
|
2375 |
+ |
let Some(r) = ObjectRef::from_hex(&format!("{hh}{rest}")) else { |
|
2376 |
+ |
continue; |
|
2377 |
+ |
}; |
|
2378 |
+ |
let Some(bytes) = get_optional(self.backend.as_ref(), &e.key)? else { |
|
2379 |
+ |
continue; |
|
2380 |
+ |
}; |
|
2381 |
+ |
if index == "sig" { |
|
2382 |
+ |
let Ok(sigs) = Signature::decode_list(&bytes) else { |
|
2383 |
+ |
progress(&format!("{}: not a list of signatures, passed over", e.key)); |
|
2384 |
+ |
continue; |
|
2385 |
+ |
}; |
|
2386 |
+ |
for sig in sigs { |
|
2387 |
+ |
let key = format!("{}{}", fanout_prefix("sig", &r), sig.key.to_hex()); |
|
2388 |
+ |
let one = Signature::encode_list(std::slice::from_ref(&sig)); |
|
2389 |
+ |
put_new_or_existing(self.backend.as_ref(), &key, &one)?; |
|
2390 |
+ |
copied += 1; |
|
2391 |
+ |
} |
|
2392 |
+ |
} else { |
|
2393 |
+ |
for v in String::from_utf8_lossy(&bytes) |
|
2394 |
+ |
.lines() |
|
2395 |
+ |
.filter_map(ObjectRef::from_hex) |
|
2396 |
+ |
{ |
|
2397 |
+ |
let key = format!("{}{}", fanout_prefix(index, &r), v.to_hex()); |
|
2398 |
+ |
put_new_or_existing(self.backend.as_ref(), &key, b"")?; |
|
2399 |
+ |
copied += 1; |
|
2400 |
+ |
} |
|
2401 |
+ |
} |
|
2402 |
+ |
} |
|
2403 |
+ |
} |
|
2404 |
+ |
if copied > 0 { |
|
2405 |
+ |
progress(&format!("{copied} entries copied out of older lists")); |
|
2406 |
+ |
} |
|
2407 |
+ |
put_new_or_existing( |
|
2408 |
+ |
self.backend.as_ref(), |
|
2409 |
+ |
LEGACY_FOLDED_KEY, |
|
2410 |
+ |
format!("{}\n", now_ms()).as_bytes(), |
|
2411 |
+ |
)?; |
|
2412 |
+ |
self.legacy_folded.set(true); |
|
2413 |
+ |
Ok(()) |
|
2414 |
+ |
} |
|
2415 |
+ |
|
| 2354 |
2416 |
|
/// The entry keys under `prefix` (an index's `<index>/` or one |
| 2355 |
2417 |
|
/// object's `<index>/<hh>/<rest>/`), sorted: on a folded store from |
| 2356 |
2418 |
|
/// its lists and journal, with no request; otherwise by listing. |
|
|
… |
157 unchanged lines |
| 2514 |
2576 |
|
} |
| 2515 |
2577 |
|
|
| 2516 |
2578 |
|
/// Fold every signature and index entry into one complete entry list |
| 2517 |
|
- |
/// (spec §13.7), what a collection does last: the journal's lines and |
|
2579 |
+ |
/// (spec §13.7), what a collection does last: first the older |
|
2580 |
+ |
/// one-list keys into per-object entries, once; then the journal's lines and |
| 2518 |
2581 |
|
/// a listing of every entry prefix, written as a new list, every list |
| 2519 |
2582 |
|
/// the packs' listing showed retired, and the journal lines read |
| 2520 |
2583 |
|
/// marked folded. Nothing when the store is this disk's directory, or |
|
|
… |
4 unchanged lines |
| 2525 |
2588 |
|
return Ok(false); |
| 2526 |
2589 |
|
} |
| 2527 |
2590 |
|
self.reload_packs()?; |
|
2591 |
+ |
if !self.legacy_folded.get() { |
|
2592 |
+ |
progress("folding older lists of signatures and index entries"); |
|
2593 |
+ |
self.fold_legacy(progress)?; |
|
2594 |
+ |
} |
| 2528 |
2595 |
|
let (journal, _) = self.read_record(JOURNAL_KEY)?; |
| 2529 |
2596 |
|
progress("folding signatures and index entries"); |
| 2530 |
2597 |
|
let mut all: std::collections::BTreeSet<String> = journal.keys().cloned().collect(); |
|
|
… |
1750 unchanged lines |
| 4281 |
4348 |
|
let reader = f.open(); |
| 4282 |
4349 |
|
assert!(reader.folded.get()); |
| 4283 |
4350 |
|
check(&reader); |
|
4351 |
+ |
|
|
4352 |
+ |
// The older lists' entries are per-object entries now, and the |
|
4353 |
+ |
// marker is there. |
|
4354 |
+ |
assert!(f.backend.inner.has(LEGACY_FOLDED_KEY).unwrap()); |
|
4355 |
+ |
assert!(f |
|
4356 |
+ |
.backend |
|
4357 |
+ |
.inner |
|
4358 |
+ |
.has(&format!("{}{}", fanout_prefix("sig", &r2), s2.key.to_hex())) |
|
4359 |
+ |
.unwrap()); |
|
4360 |
+ |
assert!(f |
|
4361 |
+ |
.backend |
|
4362 |
+ |
.inner |
|
4363 |
+ |
.has(&format!( |
|
4364 |
+ |
"{}{}", |
|
4365 |
+ |
fanout_prefix("receipt", &r2), |
|
4366 |
+ |
ObjectRef([7; 32]).to_hex() |
|
4367 |
+ |
)) |
|
4368 |
+ |
.unwrap()); |
| 4284 |
4369 |
|
|
| 4285 |
4370 |
|
// An older writer after the fold: missed until the next one. |
| 4286 |
4371 |
|
let r3 = w.put(ObjectType::Chunk, b"three").unwrap(); |
|
|
… |
140 unchanged lines |
| 4427 |
4512 |
|
eprintln!("40 lookups after the fold: {}", f.backend.summary()); |
| 4428 |
4513 |
|
assert_eq!(before_list, 40); |
| 4429 |
4514 |
|
assert_eq!(f.backend.n("list"), 0); |
|
4515 |
+ |
assert_eq!(f.backend.n("get_legacy"), 0, "the older lists are folded"); |
| 4430 |
4516 |
|
assert_eq!(f.backend.n("read_journal"), 1); |
| 4431 |
|
- |
let legacy = |b: &Counted| b.n("get_legacy") + b.n("list_legacy"); |
| 4432 |
4517 |
|
assert!( |
| 4433 |
|
- |
f.backend.total() - legacy(&f.backend) < (before_total - 40) / 4, |
|
4518 |
+ |
f.backend.total() < before_total / 10, |
| 4434 |
4519 |
|
"{}", |
| 4435 |
4520 |
|
f.backend.summary() |
| 4436 |
4521 |
|
); |
|
4522 |
+ |
} |
|
4523 |
+ |
|
|
4524 |
+ |
/// Once a fold has copied the older lists into per-object entries and |
|
4525 |
+ |
/// written its marker, readers stop reading them, folded or not; the |
|
4526 |
+ |
/// answers are the same. |
|
4527 |
+ |
#[test] |
|
4528 |
+ |
fn the_legacy_marker_stops_reads_of_the_older_lists() { |
|
4529 |
+ |
let f = Fixture::new(); |
|
4530 |
+ |
let w = f.open(); |
|
4531 |
+ |
let r = w.put(ObjectType::Chunk, b"old").unwrap(); |
|
4532 |
+ |
let sig = SecretKey { |
|
4533 |
+ |
ed_seed: [4; 32], |
|
4534 |
+ |
ml_seed: [5; 32], |
|
4535 |
+ |
} |
|
4536 |
+ |
.sign(Domain::Change, &ObjectRef([4; 32]), &r); |
|
4537 |
+ |
f.backend |
|
4538 |
+ |
.put_new( |
|
4539 |
+ |
&legacy_key("sigs", &r), |
|
4540 |
+ |
&Signature::encode_list(std::slice::from_ref(&sig)), |
|
4541 |
+ |
) |
|
4542 |
+ |
.unwrap(); |
|
4543 |
+ |
for (legacy, v) in [("seals", 9u8), ("certs", 8), ("accepted-lists", 6)] { |
|
4544 |
+ |
f.backend |
|
4545 |
+ |
.put_new( |
|
4546 |
+ |
&legacy_key(legacy, &r), |
|
4547 |
+ |
format!("{}\n", ObjectRef([v; 32]).to_hex()).as_bytes(), |
|
4548 |
+ |
) |
|
4549 |
+ |
.unwrap(); |
|
4550 |
+ |
} |
|
4551 |
+ |
let check = |s: &Store| { |
|
4552 |
+ |
assert_eq!(s.signatures(&r).unwrap(), vec![sig.clone()]); |
|
4553 |
+ |
assert_eq!(s.seals_for(&r).unwrap(), vec![ObjectRef([9; 32])]); |
|
4554 |
+ |
assert_eq!( |
|
4555 |
+ |
s.index_get("cert", "certs", &r).unwrap(), |
|
4556 |
+ |
vec![ObjectRef([8; 32])] |
|
4557 |
+ |
); |
|
4558 |
+ |
assert_eq!( |
|
4559 |
+ |
s.index_get_pub("accepted", &r).unwrap(), |
|
4560 |
+ |
vec![ObjectRef([6; 32])] |
|
4561 |
+ |
); |
|
4562 |
+ |
}; |
|
4563 |
+ |
let before = f.open(); |
|
4564 |
+ |
f.backend.reset(); |
|
4565 |
+ |
check(&before); |
|
4566 |
+ |
assert_eq!(f.backend.n("get_legacy"), 4, "{}", f.backend.summary()); |
|
4567 |
+ |
|
|
4568 |
+ |
assert!(fold(&w)); |
|
4569 |
+ |
let folded = f.open(); |
|
4570 |
+ |
f.backend.reset(); |
|
4571 |
+ |
check(&folded); |
|
4572 |
+ |
assert_eq!(f.backend.n("get_legacy") + f.backend.n("list"), 0); |
|
4573 |
+ |
|
|
4574 |
+ |
// Not folded (its lists gone), but marked: per-object listings, |
|
4575 |
+ |
// still no older lists. |
|
4576 |
+ |
for e in f.backend.inner.list("pack-").unwrap() { |
|
4577 |
+ |
if is_entry_list(&e.key) { |
|
4578 |
+ |
f.backend.inner.delete(&e.key).unwrap(); |
|
4579 |
+ |
} |
|
4580 |
+ |
} |
|
4581 |
+ |
let marked = f.open(); |
|
4582 |
+ |
assert!(!marked.folded.get() && marked.legacy_folded.get()); |
|
4583 |
+ |
f.backend.reset(); |
|
4584 |
+ |
check(&marked); |
|
4585 |
+ |
assert_eq!(f.backend.n("get_legacy"), 0, "{}", f.backend.summary()); |
|
4586 |
+ |
assert_eq!(f.backend.n("list"), 4); |
| 4437 |
4587 |
|
} |
| 4438 |
4588 |
|
} |