-
Notifications
You must be signed in to change notification settings - Fork 88
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Return error instead of panicking if rewriting fails #343
Changes from 1 commit
3a75b78
77f8beb
34db5d0
452d57e
a20cd43
43b25ca
6fcb077
8cc474d
1fd5416
c606f51
61fbdb6
862fe0b
0d2924b
a59609a
0554cd1
2c81285
bd2c3b4
005418f
2c8d59a
395d530
cee2d8f
8c2eb45
445fd1e
3106c04
bb27b29
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -124,8 +124,7 @@ fn test_file_write_error() { | |
assert_eq!(engine.last_index(2).unwrap(), 1); | ||
} | ||
|
||
#[test] | ||
fn test_file_rotate_error() { | ||
fn test_file_rotate_error(restart_after_failure: bool) { | ||
let dir = tempfile::Builder::new() | ||
.prefix("test_file_rotate_error") | ||
.tempdir() | ||
|
@@ -138,88 +137,113 @@ fn test_file_rotate_error() { | |
let fs = Arc::new(ObfuscatedFileSystem::default()); | ||
let entry = vec![b'x'; 1024]; | ||
|
||
let engine = Engine::open_with_file_system(cfg.clone(), fs.clone()).unwrap(); | ||
engine | ||
let mut engine = Some(Engine::open_with_file_system(cfg.clone(), fs.clone()).unwrap()); | ||
let mut engine_ref = engine.as_ref().unwrap(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need, you can re-assign a variable after it's moved, e.g. |
||
engine_ref | ||
.write(&mut generate_batch(1, 1, 2, Some(&entry)), false) | ||
.unwrap(); | ||
engine | ||
engine_ref | ||
.write(&mut generate_batch(1, 2, 3, Some(&entry)), false) | ||
.unwrap(); | ||
engine | ||
engine_ref | ||
.write(&mut generate_batch(1, 3, 4, Some(&entry)), false) | ||
.unwrap(); | ||
engine | ||
engine_ref | ||
.write(&mut generate_batch(1, 4, 5, Some(&entry)), false) | ||
.unwrap(); | ||
assert_eq!(engine.file_span(LogQueue::Append).1, 1); | ||
assert_eq!(engine_ref.file_span(LogQueue::Append).1, 1); | ||
// The next write will be followed by a rotate. | ||
{ | ||
// Fail to sync old log file. | ||
let _f = FailGuard::new("log_fd::sync::err", "return"); | ||
assert!(catch_unwind_silent(|| { | ||
let _ = engine.write(&mut generate_batch(1, 4, 5, Some(&entry)), false); | ||
let _ = engine_ref.write(&mut generate_batch(1, 4, 5, Some(&entry)), false); | ||
}) | ||
.is_err()); | ||
assert_eq!(engine.file_span(LogQueue::Append).1, 1); | ||
} | ||
if restart_after_failure { | ||
engine = None; | ||
engine = Some(Engine::open_with_file_system(cfg.clone(), fs.clone()).unwrap()); | ||
engine_ref = engine.as_ref().unwrap(); | ||
} | ||
assert_eq!(engine_ref.file_span(LogQueue::Append).1, 1); | ||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make two versions of this test: // case 1
if restart {
let engine = Engine::open_with_file_system(cfg.clone(), fs.clone()).unwrap();
}
// case 2
// ... |
||
// Fail to create new log file. | ||
let _f = FailGuard::new("default_fs::create::err", "return"); | ||
assert!(engine | ||
assert!(engine_ref | ||
.write(&mut generate_batch(1, 4, 5, Some(&entry)), false) | ||
.is_err()); | ||
assert_eq!(engine.file_span(LogQueue::Append).1, 1); | ||
} | ||
if restart_after_failure { | ||
engine = None; | ||
engine = Some(Engine::open_with_file_system(cfg.clone(), fs.clone()).unwrap()); | ||
engine_ref = engine.as_ref().unwrap(); | ||
} | ||
let num_files_before = std::fs::read_dir(&dir).unwrap().count(); | ||
{ | ||
// Fail to write header of new log file. | ||
let _f = FailGuard::new("log_file::write::err", "1*off->return"); | ||
assert!(engine | ||
assert!(engine_ref | ||
.write(&mut generate_batch(1, 4, 5, Some(&entry)), false) | ||
.is_err()); | ||
assert_eq!(engine.file_span(LogQueue::Append).1, 1); | ||
// Although the header is not written, the file is still created. | ||
assert_eq!( | ||
std::fs::read_dir(&dir).unwrap().count() - num_files_before, | ||
1 | ||
); | ||
} | ||
{ | ||
if restart_after_failure { | ||
engine = None; | ||
engine = Some(Engine::open_with_file_system(cfg.clone(), fs.clone()).unwrap()); | ||
engine_ref = engine.as_ref().unwrap(); | ||
// The new log file is added during recovery phase of restart. | ||
assert_eq!(engine_ref.file_span(LogQueue::Append).1, 2); | ||
} else { | ||
assert_eq!(engine_ref.file_span(LogQueue::Append).1, 1); | ||
} | ||
// Although the header is not written, the file is still created. | ||
assert_eq!( | ||
std::fs::read_dir(&dir).unwrap().count() - num_files_before, | ||
1 | ||
); | ||
if !restart_after_failure { | ||
// If the engine restarted, the write does not require sync will succeed. | ||
// Fail to sync new log file. The old log file is already sync-ed at this point. | ||
let _f = FailGuard::new("log_fd::sync::err", "return"); | ||
assert!(catch_unwind_silent(|| { | ||
let _ = engine.write(&mut generate_batch(1, 4, 5, Some(&entry)), false); | ||
let _ = engine_ref.write(&mut generate_batch(1, 4, 5, Some(&entry)), false); | ||
}) | ||
.is_err()); | ||
assert_eq!(engine.file_span(LogQueue::Append).1, 1); | ||
// The file was created but without any content (header) should be | ||
// recycled. And a new file should be created. | ||
assert_eq!( | ||
std::fs::read_dir(&dir).unwrap().count() - num_files_before, | ||
1 | ||
); | ||
assert_eq!(engine_ref.file_span(LogQueue::Append).1, 1); | ||
} | ||
drop(engine); | ||
let engine = Engine::open_with_file_system(cfg.clone(), fs.clone()).unwrap(); | ||
|
||
// Only one log file should be created after all the incidents. | ||
assert_eq!( | ||
std::fs::read_dir(&dir).unwrap().count() - num_files_before, | ||
1 | ||
); | ||
// We can continue writing after the incidents. | ||
engine | ||
engine_ref | ||
.write(&mut generate_batch(2, 1, 2, Some(&entry)), true) | ||
.unwrap(); | ||
drop(engine); | ||
let engine = Engine::open_with_file_system(cfg, fs).unwrap(); | ||
if restart_after_failure { | ||
engine = None; | ||
engine = Some(Engine::open_with_file_system(cfg.clone(), fs.clone()).unwrap()); | ||
engine_ref = engine.as_ref().unwrap(); | ||
} | ||
assert_eq!( | ||
std::fs::read_dir(&dir).unwrap().count() - num_files_before, | ||
1 | ||
); | ||
assert_eq!(engine.first_index(1).unwrap(), 1); | ||
assert_eq!(engine.last_index(1).unwrap(), 4); | ||
assert_eq!(engine.first_index(2).unwrap(), 1); | ||
assert_eq!(engine.last_index(2).unwrap(), 1); | ||
assert_eq!(engine_ref.first_index(1).unwrap(), 1); | ||
assert_eq!(engine_ref.last_index(1).unwrap(), 4); | ||
assert_eq!(engine_ref.first_index(2).unwrap(), 1); | ||
assert_eq!(engine_ref.last_index(2).unwrap(), 1); | ||
} | ||
|
||
#[test] | ||
fn test_file_rotate_error_without_restart() { | ||
test_file_rotate_error(false); | ||
} | ||
|
||
#[test] | ||
fn test_file_rotate_error_with_restart() { | ||
test_file_rotate_error(true); | ||
} | ||
|
||
#[test] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This error needs to be handled carefully now. (e.g. remove the newly created file and make sure the old writer is okay to write again) Better just unwrap it as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
build_file_writer
above is the same.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made
sync_dir
panic if it fails.But
build_file_writer
should be fine, right? It is the type of panic this PR trying to avoid (this can be confirmed bytest_no_space_write_error
). If it fails, the new file won't be used for writing and will be recycled the next timerotate_impl
is called. So, it already meet your expectation?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably.. I suggest add a few restart in test_file_rotate_error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a few more verifications in test_file_rotate_error test, should be able to address your concern? PTAL