Skip to content

Commit 3175206

Browse files
committed
When persisting mappings, truncate file that is larger than we expect
1 parent 98ca0f4 commit 3175206

1 file changed

Lines changed: 33 additions & 16 deletions

File tree

lib/segment/src/id_tracker/mutable_id_tracker.rs

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::cmp::Ordering;
12
use std::collections::BTreeMap;
23
use std::io::{self, BufReader, BufWriter, Read, Seek, Write};
34
use std::path::{Path, PathBuf};
@@ -111,6 +112,9 @@ pub struct MutableIdTracker {
111112
is_alive_lock: IsAliveLock,
112113

113114
/// Size of correctly persisted mappings in bytes
115+
///
116+
/// We initialize this on load, and keep bumping it after reach succesful flush. Pending
117+
/// changes are wrtiten to the file after this offset.
114118
persisted_mappings_size: Arc<AtomicU64>,
115119
}
116120

@@ -409,9 +413,10 @@ fn store_mapping_changes(
409413
// Create or open file in append mode to write new changes to the end
410414
let mut file = File::options()
411415
.create(true)
412-
.write(true)
416+
.append(true)
413417
.open(mappings_path)?;
414418

419+
// Ensure correct file length to not corrupt mappings when appending
415420
let file_len = file
416421
.metadata()
417422
.map_err(|err| {
@@ -420,22 +425,34 @@ fn store_mapping_changes(
420425
))
421426
})?
422427
.len();
423-
424-
// Seek to the end of the persisted part of the file.
425-
// If previously call of `store_mapping_changes` failed,
426-
// `persisted_mappings_size` may be less than actual file size.
427428
let file_start_appending = persisted_mappings_size.load(std::sync::atomic::Ordering::Relaxed);
428-
if file_start_appending <= file_len {
429-
file.seek(io::SeekFrom::Start(file_start_appending))
430-
.map_err(|err| {
431-
OperationError::service_error(format!(
432-
"Failed to seek to the persisted position of ID tracker mappings: {err}"
433-
))
434-
})?;
435-
} else {
436-
return Err(OperationError::service_error(format!(
437-
"Mutable ID tracker mappings file size is less than persisted mappings size, cannot append new mappings (file size: {file_len}, persisted mappings size: {file_start_appending})",
438-
)));
429+
match file_len.cmp(&file_start_appending) {
430+
// File size is what we expect, continue normally
431+
Ordering::Equal => {}
432+
// File is larger than expected, previous flush might not have completed properly
433+
// May happen if system is out of disk space and the file cannot be grown
434+
// Try to truncate to what we expect to clean up but ignore if we fail, then seek to the
435+
// correct append point
436+
Ordering::Greater => {
437+
if let Err(err) = file.set_len(file_start_appending) {
438+
log::warn!(
439+
"Failed to truncate immutable ID tracker mappings file that is too large, ignoring: {err}"
440+
);
441+
}
442+
443+
file.seek(io::SeekFrom::Start(file_start_appending))
444+
.map_err(|err| {
445+
OperationError::service_error(format!(
446+
"Failed to seek to the persisted position of ID tracker mappings: {err}",
447+
))
448+
})?;
449+
}
450+
// File is smaller than expected, indicates a bug we cannot recover from
451+
Ordering::Less => {
452+
return Err(OperationError::service_error(format!(
453+
"Mutable ID tracker mappings file size is less than persisted mappings size, cannot append new mappings (file size: {file_len}, persisted mappings size: {file_start_appending})",
454+
)));
455+
}
439456
}
440457

441458
let mut writer = BufWriter::new(file);

0 commit comments

Comments
 (0)