Skip to content

Commit d4380b6

Browse files
committed
tracker/snapshot_file_content: avoid useless uploads after desync
When the replicating (writer) Verneuil VFS notices that there is spooled metadata, but that it doesn't match the sqlite file, we currently always copy everything from scratch. This makes sense for long-lived processes, but isn't great (still correct, just wasteful!) for short-lived ones that run between regular writes to the sqlite db. This commit notices when we have a valid spooled manifest, and it doesn't match the file on disk. We then force a full scan of the sqlite file, but still avoid uploading any chunk that happens to match what was in the manifest.
1 parent 8deba19 commit d4380b6

File tree

1 file changed

+41
-23
lines changed

1 file changed

+41
-23
lines changed

src/tracker/snapshot_file_contents.rs

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,10 @@ impl Tracker {
246246
enum CurrentManifest {
247247
// Don't generate a new snapshot at all.
248248
UpToDate,
249-
// Snapshot from scratch.
250-
Desync,
249+
FromScratch,
250+
// Snapshot from scratch, but the previous manifest
251+
// had some chunk fingerprints that could still match.
252+
Desync(Manifest, Option<Arc<Chunk>>),
251253
// Use the previous manifest as an initial snapshot.
252254
Initial(Manifest, Option<Arc<Chunk>>),
253255
}
@@ -262,12 +264,14 @@ enum CurrentManifest {
262264
impl Tracker {
263265
/// Loads the current manifest and figures out what to do with it.
264266
fn judge_current_manifest(&self, version_id: &[u8]) -> CurrentManifest {
265-
let mut current_manifest: Option<(Manifest, Option<Arc<Chunk>>)> = self
267+
let current_manifest: Option<(Manifest, Option<Arc<Chunk>>)> = self
266268
.read_current_manifest()
267269
.map_err(|e| chain_info!(e, "failed to read staged manifest file"))
268270
.ok()
269271
.flatten();
270272

273+
let desync: bool;
274+
271275
// If we're snapshotting after a write, we always want to go
272276
// through the whole process. We made some changes, let's
273277
// guarantee we try and publish them. That's important because,
@@ -326,10 +330,8 @@ impl Tracker {
326330
up_to_date = false;
327331
}
328332

329-
// If the manifest isn't up to date, we can't use it.
330-
if !up_to_date {
331-
current_manifest = None;
332-
}
333+
// If the manifest isn't up to date, remember that.
334+
desync = !up_to_date;
333335
} else {
334336
// If we're doing this opportunistically (not after a write)
335337
// and the staging manifest seems up to date, there's nothing
@@ -344,14 +346,20 @@ impl Tracker {
344346
}
345347

346348
// If we think there's work to do after a read
347-
// transaction, assume the worst, and rebuild
348-
// the snapshot from scratch.
349-
current_manifest = None;
349+
// transaction, assume the worst, and rescan
350+
// the whole file
351+
desync = true;
350352
}
351353

352354
match current_manifest {
353-
None => CurrentManifest::Desync,
354-
Some((manifest, base)) => CurrentManifest::Initial(manifest, base),
355+
None => CurrentManifest::FromScratch,
356+
Some((manifest, base)) => {
357+
if desync {
358+
CurrentManifest::Desync(manifest, base)
359+
} else {
360+
CurrentManifest::Initial(manifest, base)
361+
}
362+
}
355363
}
356364
}
357365

@@ -363,6 +371,7 @@ impl Tracker {
363371
fn snapshot_chunks(
364372
&self,
365373
base: Option<Vec<Fingerprint>>,
374+
force_refresh: bool,
366375
) -> Result<(u64, Vec<Fingerprint>, Vec<BundledChunk>, usize)> {
367376
use rand::Rng;
368377
let mut rng = rand::thread_rng();
@@ -402,12 +411,18 @@ impl Tracker {
402411
.is_some();
403412
let delta = (grown || wrote_past_end) as u64;
404413

405-
// We definitely don't know anything about what's at or
406-
// after chunk index `fprints.len()`. We also don't
407-
// want to go out of bounds if the new file shrunk.
408-
backfill_begin = (fprints.len() as u64)
409-
.clamp(0, num_chunks)
410-
.saturating_sub(delta);
414+
if force_refresh {
415+
// Assume all the chunks in the manifests exist, but confirm
416+
// that they match what we want.
417+
backfill_begin = 0;
418+
} else {
419+
// We definitely don't know anything about what's at or
420+
// after chunk index `fprints.len()`. We also don't
421+
// want to go out of bounds if the new file shrunk.
422+
backfill_begin = (fprints.len() as u64)
423+
.clamp(0, num_chunks)
424+
.saturating_sub(delta);
425+
}
411426
fprints
412427
} else {
413428
backfill_begin = 0;
@@ -551,6 +566,7 @@ impl Tracker {
551566
header_fprint: Fingerprint,
552567
version_id: Vec<u8>,
553568
current_manifest: Option<(Manifest, Option<Arc<Chunk>>)>,
569+
force_refresh: bool,
554570
) -> Result<(usize, Vec<Fingerprint>, Option<Fingerprint>)> {
555571
use std::os::unix::fs::MetadataExt;
556572

@@ -585,7 +601,8 @@ impl Tracker {
585601
// Try to get an initial list of chunks to work off.
586602
let base_fprints = Self::base_chunk_fprints(current_manifest.as_ref().map(|x| &x.0));
587603

588-
let (len, mut chunks, bundled_chunks, mut copied) = self.snapshot_chunks(base_fprints)?;
604+
let (len, mut chunks, bundled_chunks, mut copied) =
605+
self.snapshot_chunks(base_fprints, force_refresh)?;
589606

590607
let (ctime, ctime_ns) = match self.file.metadata() {
591608
Ok(meta) => (meta.ctime(), meta.ctime_nsec() as i32),
@@ -724,11 +741,12 @@ impl Tracker {
724741
e => chain_warn!(e, "failed to force populate version xattr", path=?self.path));
725742
}
726743

727-
let current_manifest: Option<(Manifest, Option<Arc<Chunk>>)> =
744+
let (current_manifest, force_refresh): (Option<(Manifest, Option<Arc<Chunk>>)>, bool) =
728745
match self.judge_current_manifest(&version_id) {
729746
CurrentManifest::UpToDate => return Ok(()),
730-
CurrentManifest::Desync => None,
731-
CurrentManifest::Initial(manifest, base) => Some((manifest, base)),
747+
CurrentManifest::FromScratch => (None, true),
748+
CurrentManifest::Desync(manifest, base) => (Some((manifest, base)), true),
749+
CurrentManifest::Initial(manifest, base) => (Some((manifest, base)), false),
732750
};
733751

734752
// We don't *have* to overwrite the .metadata file, but we
@@ -740,7 +758,7 @@ impl Tracker {
740758
// Publish an updated snapshot, and remember the chunks we
741759
// care about.
742760
let (copied, chunks, base_chunk) =
743-
self.stage_new_snapshot(header_fprint, version_id, current_manifest)?;
761+
self.stage_new_snapshot(header_fprint, version_id, current_manifest, force_refresh)?;
744762

745763
let published = self.maybe_update_ready_buffer(&chunks)?;
746764

0 commit comments

Comments
 (0)