@ -8,6 +8,7 @@
# include "db_stress_tool/expected_state.h"
# include "db_stress_tool/db_stress_shared_state.h"
# include "rocksdb/trace_reader_writer.h"
namespace ROCKSDB_NAMESPACE {
@ -145,7 +146,11 @@ ExpectedStateManager::ExpectedStateManager(size_t max_key,
ExpectedStateManager : : ~ ExpectedStateManager ( ) { }
const std : : string FileExpectedStateManager : : kLatestFilename = " LATEST.state " ;
const std : : string FileExpectedStateManager : : kLatestBasename = " LATEST " ;
const std : : string FileExpectedStateManager : : kStateFilenameSuffix = " .state " ;
const std : : string FileExpectedStateManager : : kTraceFilenameSuffix = " .trace " ;
const std : : string FileExpectedStateManager : : kTempFilenamePrefix = " . " ;
const std : : string FileExpectedStateManager : : kTempFilenameSuffix = " .tmp " ;
FileExpectedStateManager : : FileExpectedStateManager (
size_t max_key , size_t num_column_families ,
@ -156,9 +161,58 @@ FileExpectedStateManager::FileExpectedStateManager(
}
Status FileExpectedStateManager : : Open ( ) {
Status s = Clean ( ) ;
// Before doing anything, sync directory state with ours. That is, determine
// `saved_seqno_`, and create any necessary missing files.
std : : vector < std : : string > expected_state_dir_children ;
Status s = Env : : Default ( ) - > GetChildren ( expected_state_dir_path_ ,
& expected_state_dir_children ) ;
bool found_trace = false ;
if ( s . ok ( ) ) {
for ( size_t i = 0 ; i < expected_state_dir_children . size ( ) ; + + i ) {
const auto & filename = expected_state_dir_children [ i ] ;
if ( filename . size ( ) > = kStateFilenameSuffix . size ( ) & &
filename . rfind ( kStateFilenameSuffix ) = =
filename . size ( ) - kStateFilenameSuffix . size ( ) & &
filename . rfind ( kLatestBasename , 0 ) = = std : : string : : npos ) {
SequenceNumber found_seqno = ParseUint64 (
filename . substr ( 0 , filename . size ( ) - kStateFilenameSuffix . size ( ) ) ) ;
if ( saved_seqno_ = = kMaxSequenceNumber | | found_seqno > saved_seqno_ ) {
saved_seqno_ = found_seqno ;
}
}
}
// Check if crash happened after creating state file but before creating
// trace file.
if ( saved_seqno_ ! = kMaxSequenceNumber ) {
std : : string saved_seqno_trace_path =
GetPathForFilename ( ToString ( saved_seqno_ ) + kTraceFilenameSuffix ) ;
Status exists_status = Env : : Default ( ) - > FileExists ( saved_seqno_trace_path ) ;
if ( exists_status . ok ( ) ) {
found_trace = true ;
} else if ( exists_status . IsNotFound ( ) ) {
found_trace = false ;
} else {
s = exists_status ;
}
}
}
if ( s . ok ( ) & & saved_seqno_ ! = kMaxSequenceNumber & & ! found_trace ) {
// Create an empty trace file so later logic does not need to distinguish
// missing vs. empty trace file.
std : : unique_ptr < WritableFile > wfile ;
const EnvOptions soptions ;
std : : string saved_seqno_trace_path =
GetPathForFilename ( ToString ( saved_seqno_ ) + kTraceFilenameSuffix ) ;
s = Env : : Default ( ) - > NewWritableFile ( saved_seqno_trace_path , & wfile ,
soptions ) ;
}
std : : string expected_state_file_path = GetPathForFilename ( kLatestFilename ) ;
if ( s . ok ( ) ) {
s = Clean ( ) ;
}
std : : string expected_state_file_path =
GetPathForFilename ( kLatestBasename + kStateFilenameSuffix ) ;
bool found = false ;
if ( s . ok ( ) ) {
Status exists_status = Env : : Default ( ) - > FileExists ( expected_state_file_path ) ;
@ -176,7 +230,7 @@ Status FileExpectedStateManager::Open() {
// this process is killed during setup, `Clean()` will take care of removing
// the incomplete expected values file.
std : : string temp_expected_state_file_path =
GetTempPathForFilename ( kLatestFilename ) ;
GetTempPathForFilename ( kLatestBasename + kState FilenameSuffix ) ;
FileExpectedState temp_expected_state ( temp_expected_state_file_path ,
max_key_ , num_column_families_ ) ;
if ( s . ok ( ) ) {
@ -196,23 +250,115 @@ Status FileExpectedStateManager::Open() {
return s ;
}
Status FileExpectedStateManager : : Clean ( ) {
// An incomplete `Open()` could have left behind an invalid temporary file.
std : : string temp_path = GetTempPathForFilename ( kLatestFilename ) ;
Status s = Env : : Default ( ) - > FileExists ( temp_path ) ;
# ifndef ROCKSDB_LITE
Status FileExpectedStateManager : : SaveAtAndAfter ( DB * db ) {
SequenceNumber seqno = db - > GetLatestSequenceNumber ( ) ;
std : : string state_filename = ToString ( seqno ) + kStateFilenameSuffix ;
std : : string state_file_temp_path = GetTempPathForFilename ( state_filename ) ;
std : : string state_file_path = GetPathForFilename ( state_filename ) ;
std : : string latest_file_path =
GetPathForFilename ( kLatestBasename + kStateFilenameSuffix ) ;
std : : string trace_filename = ToString ( seqno ) + kTraceFilenameSuffix ;
std : : string trace_file_path = GetPathForFilename ( trace_filename ) ;
// Populate a tempfile and then rename it to atomically create "<seqno>.state"
// with contents from "LATEST.state"
Status s =
CopyFile ( FileSystem : : Default ( ) , latest_file_path , state_file_temp_path ,
0 /* size */ , false /* use_fsync */ ) ;
if ( s . ok ( ) ) {
s = FileSystem : : Default ( ) - > RenameFile ( state_file_temp_path , state_file_path ,
IOOptions ( ) , nullptr /* dbg */ ) ;
}
SequenceNumber old_saved_seqno ;
if ( s . ok ( ) ) {
s = Env : : Default ( ) - > DeleteFile ( temp_path ) ;
} else if ( s . IsNotFound ( ) ) {
s = Status : : OK ( ) ;
old_saved_seqno = saved_seqno_ ;
saved_seqno_ = seqno ;
}
// If there is a crash now, i.e., after "<seqno>.state" was created but before
// "<seqno>.trace" is created, it will be treated as if "<seqno>.trace" were
// present but empty.
// Create "<seqno>.trace" directly. It is initially empty so no need for
// tempfile.
std : : unique_ptr < TraceWriter > trace_writer ;
if ( s . ok ( ) ) {
EnvOptions soptions ;
// Disable buffering so traces will not get stuck in application buffer.
soptions . writable_file_max_buffer_size = 0 ;
s = NewFileTraceWriter ( Env : : Default ( ) , soptions , trace_file_path ,
& trace_writer ) ;
}
if ( s . ok ( ) ) {
s = db - > StartTrace ( TraceOptions ( ) , std : : move ( trace_writer ) ) ;
}
// Delete old state/trace files. Deletion order does not matter since we only
// delete after successfully saving new files, so old files will never be used
// again, even if we crash.
if ( s . ok ( ) & & old_saved_seqno ! = kMaxSequenceNumber & &
old_saved_seqno ! = saved_seqno_ ) {
s = Env : : Default ( ) - > DeleteFile (
GetPathForFilename ( ToString ( old_saved_seqno ) + kStateFilenameSuffix ) ) ;
}
if ( s . ok ( ) & & old_saved_seqno ! = kMaxSequenceNumber & &
old_saved_seqno ! = saved_seqno_ ) {
s = Env : : Default ( ) - > DeleteFile (
GetPathForFilename ( ToString ( old_saved_seqno ) + kTraceFilenameSuffix ) ) ;
}
return s ;
}
# else // ROCKSDB_LITE
Status FileExpectedStateManager : : SaveAtAndAfter ( DB * /* db */ ) {
return Status : : NotSupported ( ) ;
}
# endif // ROCKSDB_LITE
Status FileExpectedStateManager : : Clean ( ) {
std : : vector < std : : string > expected_state_dir_children ;
Status s = Env : : Default ( ) - > GetChildren ( expected_state_dir_path_ ,
& expected_state_dir_children ) ;
// An incomplete `Open()` or incomplete `SaveAtAndAfter()` could have left
// behind invalid temporary files. An incomplete `SaveAtAndAfter()` could have
// also left behind stale state/trace files.
for ( size_t i = 0 ; s . ok ( ) & & i < expected_state_dir_children . size ( ) ; + + i ) {
const auto & filename = expected_state_dir_children [ i ] ;
if ( filename . rfind ( kTempFilenamePrefix , 0 /* pos */ ) = = 0 & &
filename . size ( ) > = kTempFilenameSuffix . size ( ) & &
filename . rfind ( kTempFilenameSuffix ) = =
filename . size ( ) - kTempFilenameSuffix . size ( ) ) {
// Delete all temp files.
s = Env : : Default ( ) - > DeleteFile ( GetPathForFilename ( filename ) ) ;
} else if ( filename . size ( ) > = kStateFilenameSuffix . size ( ) & &
filename . rfind ( kStateFilenameSuffix ) = =
filename . size ( ) - kStateFilenameSuffix . size ( ) & &
filename . rfind ( kLatestBasename , 0 ) = = std : : string : : npos & &
ParseUint64 ( filename . substr (
0 , filename . size ( ) - kStateFilenameSuffix . size ( ) ) ) <
saved_seqno_ ) {
assert ( saved_seqno_ ! = kMaxSequenceNumber ) ;
// Delete stale state files.
s = Env : : Default ( ) - > DeleteFile ( GetPathForFilename ( filename ) ) ;
} else if ( filename . size ( ) > = kTraceFilenameSuffix . size ( ) & &
filename . rfind ( kTraceFilenameSuffix ) = =
filename . size ( ) - kTraceFilenameSuffix . size ( ) & &
ParseUint64 ( filename . substr (
0 , filename . size ( ) - kTraceFilenameSuffix . size ( ) ) ) <
saved_seqno_ ) {
assert ( saved_seqno_ ! = kMaxSequenceNumber ) ;
// Delete stale trace files.
s = Env : : Default ( ) - > DeleteFile ( GetPathForFilename ( filename ) ) ;
}
}
return s ;
}
std : : string FileExpectedStateManager : : GetTempPathForFilename (
const std : : string & filename ) {
static const std : : string kTempFilenamePrefix = " . " ;
static const std : : string kTempFilenameSuffix = " .tmp " ;
assert ( ! expected_state_dir_path_ . empty ( ) ) ;
std : : string expected_state_dir_path_slash =
expected_state_dir_path_ . back ( ) = = ' / ' ? expected_state_dir_path_