mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 23:21:59 +00:00
Merge pull request #40592 from azat/inmemory-WAL-cleanup
Remove completely processed WAL files
This commit is contained in:
commit
f11b7499d1
@ -1177,14 +1177,10 @@ void MergeTreeData::loadDataPartsFromDisk(
|
||||
void MergeTreeData::loadDataPartsFromWAL(
|
||||
DataPartsVector & /* broken_parts_to_detach */,
|
||||
DataPartsVector & duplicate_parts_to_remove,
|
||||
MutableDataPartsVector & parts_from_wal,
|
||||
DataPartsLock & part_lock)
|
||||
MutableDataPartsVector & parts_from_wal)
|
||||
{
|
||||
for (auto & part : parts_from_wal)
|
||||
{
|
||||
if (getActiveContainingPart(part->info, DataPartState::Active, part_lock))
|
||||
continue;
|
||||
|
||||
part->modification_time = time(nullptr);
|
||||
/// Assume that all parts are Active, covered parts will be detected and marked as Outdated later
|
||||
part->setState(DataPartState::Active);
|
||||
@ -1212,7 +1208,6 @@ void MergeTreeData::loadDataParts(bool skip_sanity_checks)
|
||||
|
||||
auto metadata_snapshot = getInMemoryMetadataPtr();
|
||||
const auto settings = getSettings();
|
||||
MutableDataPartsVector parts_from_wal;
|
||||
Strings part_file_names;
|
||||
|
||||
auto disks = getStoragePolicy()->getDisks();
|
||||
@ -1269,16 +1264,14 @@ void MergeTreeData::loadDataParts(bool skip_sanity_checks)
|
||||
|
||||
/// Collect part names by disk.
|
||||
std::map<String, std::vector<std::pair<String, DiskPtr>>> disk_part_map;
|
||||
std::map<String, MutableDataPartsVector> disk_wal_part_map;
|
||||
ThreadPool pool(disks.size());
|
||||
std::mutex wal_init_lock;
|
||||
|
||||
for (const auto & disk_ptr : disks)
|
||||
{
|
||||
if (disk_ptr->isBroken())
|
||||
continue;
|
||||
|
||||
auto & disk_parts = disk_part_map[disk_ptr->getName()];
|
||||
auto & disk_wal_parts = disk_wal_part_map[disk_ptr->getName()];
|
||||
|
||||
pool.scheduleOrThrowOnError([&, disk_ptr]()
|
||||
{
|
||||
@ -1291,34 +1284,11 @@ void MergeTreeData::loadDataParts(bool skip_sanity_checks)
|
||||
|
||||
if (!startsWith(it->name(), MergeTreeWriteAheadLog::WAL_FILE_NAME))
|
||||
disk_parts.emplace_back(std::make_pair(it->name(), disk_ptr));
|
||||
else if (it->name() == MergeTreeWriteAheadLog::DEFAULT_WAL_FILE_NAME && settings->in_memory_parts_enable_wal)
|
||||
{
|
||||
std::lock_guard lock(wal_init_lock);
|
||||
if (write_ahead_log != nullptr)
|
||||
throw Exception(
|
||||
"There are multiple WAL files appeared in current storage policy. You need to resolve this manually",
|
||||
ErrorCodes::CORRUPTED_DATA);
|
||||
|
||||
write_ahead_log = std::make_shared<MergeTreeWriteAheadLog>(*this, disk_ptr, it->name());
|
||||
for (auto && part : write_ahead_log->restore(metadata_snapshot, getContext()))
|
||||
disk_wal_parts.push_back(std::move(part));
|
||||
}
|
||||
else if (settings->in_memory_parts_enable_wal)
|
||||
{
|
||||
MergeTreeWriteAheadLog wal(*this, disk_ptr, it->name());
|
||||
for (auto && part : wal.restore(metadata_snapshot, getContext()))
|
||||
disk_wal_parts.push_back(std::move(part));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pool.wait();
|
||||
|
||||
for (auto & [_, disk_wal_parts] : disk_wal_part_map)
|
||||
parts_from_wal.insert(
|
||||
parts_from_wal.end(), std::make_move_iterator(disk_wal_parts.begin()), std::make_move_iterator(disk_wal_parts.end()));
|
||||
|
||||
size_t num_parts = 0;
|
||||
std::queue<std::vector<std::pair<String, DiskPtr>>> parts_queue;
|
||||
for (auto & [_, disk_parts] : disk_part_map)
|
||||
@ -1332,13 +1302,6 @@ void MergeTreeData::loadDataParts(bool skip_sanity_checks)
|
||||
auto part_lock = lockParts();
|
||||
data_parts_indexes.clear();
|
||||
|
||||
if (num_parts == 0 && parts_from_wal.empty())
|
||||
{
|
||||
resetObjectColumnsFromActiveParts(part_lock);
|
||||
LOG_DEBUG(log, "There are no data parts");
|
||||
return;
|
||||
}
|
||||
|
||||
DataPartsVector broken_parts_to_detach;
|
||||
DataPartsVector duplicate_parts_to_remove;
|
||||
|
||||
@ -1346,8 +1309,65 @@ void MergeTreeData::loadDataParts(bool skip_sanity_checks)
|
||||
loadDataPartsFromDisk(
|
||||
broken_parts_to_detach, duplicate_parts_to_remove, pool, num_parts, parts_queue, skip_sanity_checks, settings);
|
||||
|
||||
if (!parts_from_wal.empty())
|
||||
loadDataPartsFromWAL(broken_parts_to_detach, duplicate_parts_to_remove, parts_from_wal, part_lock);
|
||||
if (settings->in_memory_parts_enable_wal)
|
||||
{
|
||||
std::map<String, MutableDataPartsVector> disk_wal_part_map;
|
||||
|
||||
std::mutex wal_init_lock;
|
||||
for (const auto & disk_ptr : disks)
|
||||
{
|
||||
if (disk_ptr->isBroken())
|
||||
continue;
|
||||
|
||||
auto & disk_wal_parts = disk_wal_part_map[disk_ptr->getName()];
|
||||
|
||||
pool.scheduleOrThrowOnError([&, disk_ptr]()
|
||||
{
|
||||
for (auto it = disk_ptr->iterateDirectory(relative_data_path); it->isValid(); it->next())
|
||||
{
|
||||
if (!startsWith(it->name(), MergeTreeWriteAheadLog::WAL_FILE_NAME))
|
||||
continue;
|
||||
|
||||
if (it->name() == MergeTreeWriteAheadLog::DEFAULT_WAL_FILE_NAME)
|
||||
{
|
||||
std::lock_guard lock(wal_init_lock);
|
||||
if (write_ahead_log != nullptr)
|
||||
throw Exception(
|
||||
"There are multiple WAL files appeared in current storage policy. You need to resolve this manually",
|
||||
ErrorCodes::CORRUPTED_DATA);
|
||||
|
||||
write_ahead_log = std::make_shared<MergeTreeWriteAheadLog>(*this, disk_ptr, it->name());
|
||||
for (auto && part : write_ahead_log->restore(metadata_snapshot, getContext(), part_lock))
|
||||
disk_wal_parts.push_back(std::move(part));
|
||||
}
|
||||
else
|
||||
{
|
||||
MergeTreeWriteAheadLog wal(*this, disk_ptr, it->name());
|
||||
for (auto && part : wal.restore(metadata_snapshot, getContext(), part_lock))
|
||||
disk_wal_parts.push_back(std::move(part));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pool.wait();
|
||||
|
||||
MutableDataPartsVector parts_from_wal;
|
||||
for (auto & [_, disk_wal_parts] : disk_wal_part_map)
|
||||
parts_from_wal.insert(
|
||||
parts_from_wal.end(), std::make_move_iterator(disk_wal_parts.begin()), std::make_move_iterator(disk_wal_parts.end()));
|
||||
|
||||
loadDataPartsFromWAL(broken_parts_to_detach, duplicate_parts_to_remove, parts_from_wal);
|
||||
|
||||
num_parts += parts_from_wal.size();
|
||||
}
|
||||
|
||||
if (num_parts == 0)
|
||||
{
|
||||
resetObjectColumnsFromActiveParts(part_lock);
|
||||
LOG_DEBUG(log, "There are no data parts");
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto & part : broken_parts_to_detach)
|
||||
{
|
||||
|
@ -1339,8 +1339,7 @@ private:
|
||||
void loadDataPartsFromWAL(
|
||||
DataPartsVector & broken_parts_to_detach,
|
||||
DataPartsVector & duplicate_parts_to_remove,
|
||||
MutableDataPartsVector & parts_from_wal,
|
||||
DataPartsLock & part_lock);
|
||||
MutableDataPartsVector & parts_from_wal);
|
||||
|
||||
void resetObjectColumnsFromActiveParts(const DataPartsLock & lock);
|
||||
void updateObjectColumns(const DataPartPtr & part, const DataPartsLock & lock);
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <Storages/MergeTree/MergeTreeData.h>
|
||||
#include <Storages/MergeTree/MergeTreeDataWriter.h>
|
||||
#include <Storages/MergeTree/MergedBlockOutputStream.h>
|
||||
#include <Storages/MergeTree/MergeTreeDataPartState.h>
|
||||
#include <IO/MemoryReadWriteBuffer.h>
|
||||
#include <IO/ReadHelpers.h>
|
||||
#include <IO/copyData.h>
|
||||
@ -122,7 +123,10 @@ void MergeTreeWriteAheadLog::rotate(const std::unique_lock<std::mutex> &)
|
||||
init();
|
||||
}
|
||||
|
||||
MergeTreeData::MutableDataPartsVector MergeTreeWriteAheadLog::restore(const StorageMetadataPtr & metadata_snapshot, ContextPtr context)
|
||||
MergeTreeData::MutableDataPartsVector MergeTreeWriteAheadLog::restore(
|
||||
const StorageMetadataPtr & metadata_snapshot,
|
||||
ContextPtr context,
|
||||
std::unique_lock<std::mutex> & parts_lock)
|
||||
{
|
||||
std::unique_lock lock(write_mutex);
|
||||
|
||||
@ -172,6 +176,9 @@ MergeTreeData::MutableDataPartsVector MergeTreeWriteAheadLog::restore(const Stor
|
||||
part->uuid = metadata.part_uuid;
|
||||
|
||||
block = block_in.read();
|
||||
|
||||
if (storage.getActiveContainingPart(part->info, MergeTreeDataPartState::Active, parts_lock))
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -238,6 +245,15 @@ MergeTreeData::MutableDataPartsVector MergeTreeWriteAheadLog::restore(const Stor
|
||||
std::copy_if(parts.begin(), parts.end(), std::back_inserter(result),
|
||||
[&dropped_parts](const auto & part) { return dropped_parts.count(part->name) == 0; });
|
||||
|
||||
/// All parts in WAL had been already committed into the disk -> clear the WAL
|
||||
if (result.empty())
|
||||
{
|
||||
LOG_DEBUG(log, "WAL file '{}' had been completely processed. Removing.", path);
|
||||
disk->removeFile(path);
|
||||
init();
|
||||
return {};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,10 @@ public:
|
||||
|
||||
void addPart(DataPartInMemoryPtr & part);
|
||||
void dropPart(const String & part_name);
|
||||
std::vector<MergeTreeMutableDataPartPtr> restore(const StorageMetadataPtr & metadata_snapshot, ContextPtr context);
|
||||
std::vector<MergeTreeMutableDataPartPtr> restore(
|
||||
const StorageMetadataPtr & metadata_snapshot,
|
||||
ContextPtr context,
|
||||
std::unique_lock<std::mutex> & parts_lock);
|
||||
|
||||
using MinMaxBlockNumber = std::pair<Int64, Int64>;
|
||||
static std::optional<MinMaxBlockNumber> tryParseMinMaxBlockNumber(const String & filename);
|
||||
|
@ -0,0 +1,35 @@
|
||||
-- { echo }
|
||||
|
||||
DROP TABLE IF EXISTS in_memory;
|
||||
CREATE TABLE in_memory (a UInt32) ENGINE = MergeTree ORDER BY a SETTINGS min_rows_for_compact_part = 1000, min_bytes_for_wide_part = 10485760;
|
||||
INSERT INTO in_memory VALUES (1);
|
||||
INSERT INTO in_memory VALUES (2);
|
||||
SELECT name, active, part_type FROM system.parts WHERE database = currentDatabase() AND table = 'in_memory';
|
||||
all_1_1_0 1 InMemory
|
||||
all_2_2_0 1 InMemory
|
||||
SELECT * FROM in_memory ORDER BY a;
|
||||
1
|
||||
2
|
||||
-- no WAL remove since parts are still in use
|
||||
DETACH TABLE in_memory;
|
||||
ATTACH TABLE in_memory;
|
||||
SELECT name, active, part_type FROM system.parts WHERE database = currentDatabase() AND table = 'in_memory';
|
||||
all_1_1_0 1 InMemory
|
||||
all_2_2_0 1 InMemory
|
||||
SELECT * FROM in_memory ORDER BY a;
|
||||
1
|
||||
2
|
||||
-- WAL should be removed, since on disk part covers all parts in WAL
|
||||
OPTIMIZE TABLE in_memory;
|
||||
DETACH TABLE in_memory;
|
||||
ATTACH TABLE in_memory;
|
||||
SELECT name, active, part_type FROM system.parts WHERE database = currentDatabase() AND table = 'in_memory';
|
||||
all_1_2_1 1 Compact
|
||||
-- check that the WAL will be reinitialized after remove
|
||||
INSERT INTO in_memory VALUES (3);
|
||||
DETACH TABLE in_memory;
|
||||
ATTACH TABLE in_memory;
|
||||
SELECT * FROM in_memory ORDER BY a;
|
||||
1
|
||||
2
|
||||
3
|
27
tests/queries/0_stateless/02410_inmemory_wal_cleanup.sql
Normal file
27
tests/queries/0_stateless/02410_inmemory_wal_cleanup.sql
Normal file
@ -0,0 +1,27 @@
|
||||
-- { echo }
|
||||
|
||||
DROP TABLE IF EXISTS in_memory;
|
||||
|
||||
CREATE TABLE in_memory (a UInt32) ENGINE = MergeTree ORDER BY a SETTINGS min_rows_for_compact_part = 1000, min_bytes_for_wide_part = 10485760;
|
||||
INSERT INTO in_memory VALUES (1);
|
||||
INSERT INTO in_memory VALUES (2);
|
||||
SELECT name, active, part_type FROM system.parts WHERE database = currentDatabase() AND table = 'in_memory';
|
||||
SELECT * FROM in_memory ORDER BY a;
|
||||
|
||||
-- no WAL remove since parts are still in use
|
||||
DETACH TABLE in_memory;
|
||||
ATTACH TABLE in_memory;
|
||||
SELECT name, active, part_type FROM system.parts WHERE database = currentDatabase() AND table = 'in_memory';
|
||||
SELECT * FROM in_memory ORDER BY a;
|
||||
|
||||
-- WAL should be removed, since on disk part covers all parts in WAL
|
||||
OPTIMIZE TABLE in_memory;
|
||||
DETACH TABLE in_memory;
|
||||
ATTACH TABLE in_memory;
|
||||
SELECT name, active, part_type FROM system.parts WHERE database = currentDatabase() AND table = 'in_memory';
|
||||
|
||||
-- check that the WAL will be reinitialized after remove
|
||||
INSERT INTO in_memory VALUES (3);
|
||||
DETACH TABLE in_memory;
|
||||
ATTACH TABLE in_memory;
|
||||
SELECT * FROM in_memory ORDER BY a;
|
Loading…
Reference in New Issue
Block a user