Skip to content

Commit

Permalink
Fix duckdb#9342: WAL - instead of using FileExists -> OpenFile, use O…
Browse files Browse the repository at this point in the history
…penFile with FILE_FLAGS_NULL_IF_NOT_EXISTS
  • Loading branch information
Mytherin committed Mar 21, 2024
1 parent 4b4081d commit 2aac450
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 10 deletions.
6 changes: 6 additions & 0 deletions src/common/serializer/buffered_file_reader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ void BufferedFileReader::Seek(uint64_t location) {
read_data = offset = 0;
}

void BufferedFileReader::Reset() {
handle->Reset();
total_read = 0;
read_data = offset = 0;
}

uint64_t BufferedFileReader::CurrentOffset() {
return total_read + offset;
}
Expand Down
2 changes: 2 additions & 0 deletions src/include/duckdb/common/serializer/buffered_file_reader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class BufferedFileReader : public ReadStream {
return file_size;
}

//! Resets reading - beginning at position 0
void Reset();
void Seek(uint64_t location);
uint64_t CurrentOffset();

Expand Down
2 changes: 1 addition & 1 deletion src/include/duckdb/storage/write_ahead_log.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class WriteAheadLog {

public:
//! Replay the WAL
static bool Replay(AttachedDatabase &database, string &path);
static bool Replay(AttachedDatabase &database, unique_ptr<FileHandle> handle);

//! Returns the current size of the WAL in bytes
int64_t GetWALSize();
Expand Down
5 changes: 3 additions & 2 deletions src/storage/storage_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,10 @@ void SingleFileStorageManager::LoadDatabase() {

// check if the WAL file exists
auto wal_path = GetWALPath();
if (fs.FileExists(wal_path)) {
auto handle = fs.OpenFile(wal_path, FileFlags::FILE_FLAGS_READ | FileFlags::FILE_FLAGS_NULL_IF_NOT_EXISTS);
if (handle) {
// replay the WAL
if (WriteAheadLog::Replay(db, wal_path)) {
if (WriteAheadLog::Replay(db, std::move(handle))) {
fs.RemoveFile(wal_path);
}
}
Expand Down
15 changes: 8 additions & 7 deletions src/storage/wal_replay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,10 @@ class WriteAheadLogDeserializer {
//===--------------------------------------------------------------------===//
// Replay
//===--------------------------------------------------------------------===//
bool WriteAheadLog::Replay(AttachedDatabase &database, string &path) {
bool WriteAheadLog::Replay(AttachedDatabase &database, unique_ptr<FileHandle> handle) {
Connection con(database.GetDatabase());
auto initial_source = make_uniq<BufferedFileReader>(FileSystem::Get(database), path.c_str());
if (initial_source->Finished()) {
BufferedFileReader reader(FileSystem::Get(database), std::move(handle));
if (reader.Finished()) {
// WAL is empty
return false;
}
Expand All @@ -174,10 +174,10 @@ bool WriteAheadLog::Replay(AttachedDatabase &database, string &path) {
try {
while (true) {
// read the current entry (deserialize only)
auto deserializer = WriteAheadLogDeserializer::Open(checkpoint_state, *initial_source, true);
auto deserializer = WriteAheadLogDeserializer::Open(checkpoint_state, reader, true);
if (deserializer.ReplayEntry()) {
// check if the file is exhausted
if (initial_source->Finished()) {
if (reader.Finished()) {
// we finished reading the file: break
break;
}
Expand All @@ -196,7 +196,6 @@ bool WriteAheadLog::Replay(AttachedDatabase &database, string &path) {
Printer::Print("Unknown Exception in WAL playback during initial read");
return false;
} // LCOV_EXCL_STOP
initial_source.reset();
if (checkpoint_state.checkpoint_id.IsValid()) {
// there is a checkpoint flag: check if we need to deserialize the WAL
auto &manager = database.GetStorageManager();
Expand All @@ -208,9 +207,11 @@ bool WriteAheadLog::Replay(AttachedDatabase &database, string &path) {
}

// we need to recover from the WAL: actually set up the replay state
BufferedFileReader reader(FileSystem::Get(database), path.c_str());
ReplayState state(database, *con.context);

// reset the reader - we are going to read the WAL from the beginning again
reader.Reset();

// replay the WAL
// note that everything is wrapped inside a try/catch block here
// there can be errors in WAL replay because of a corrupt WAL file
Expand Down
34 changes: 34 additions & 0 deletions test/sql/storage/concurrent_attach.test_slow
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# name: test/sql/storage/concurrent_attach.test_slow
# description: Test concurrent attaching
# group: [storage]

concurrentforeach name foo bar

statement ok
attach '__TEST_DIR__/${name}.duckdb' AS ${name}

statement ok
create table ${name}.${name}(${name} bigint)

loop i 0 1000

statement ok
insert into ${name}.${name} select sum(i) from range((random()*1000000.0)::INT) r(i)

statement ok
checkpoint ${name}

statement ok
detach ${name}

statement ok
attach '__TEST_DIR__/${name}.duckdb' AS ${name}

endloop

query I
select count(*) FROM ${name}.${name}
----
1000

endloop

0 comments on commit 2aac450

Please sign in to comment.