Skip to content

Commit 6189897

Browse files
LimiNodeclaude
andcommitted
fix(mdbx): report initialization failures
Notify the configured error callback for constructor-time storage failures so logger registration can surface the cause before the exception prevents a broken backend from being added. Constraint: Keep MdbxLogger C++17-compatible and preserve constructor rethrow semantics Confidence: high Scope-risk: narrow Not-tested: Full test suite; only mdbx_logger_test was run Co-Authored-By: Claude Opus 4.8 <[email protected]>
1 parent 8cabb31 commit 6189897

2 files changed

Lines changed: 107 additions & 7 deletions

File tree

include/logit_cpp/logit/loggers/MdbxLogger.hpp

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ namespace logit {
4545
bool store_large_payloads_separately = true;///< Store large messages in `log_payloads`.
4646
MdbxPayloadCompression payload_compression = MdbxPayloadCompression::None; ///< Payload compression.
4747
int payload_compression_level = 6; ///< Compression level for gzip/zstd.
48-
std::function<void(const std::string&)> on_error; ///< Optional callback invoked on write errors instead of stderr.
48+
std::function<void(const std::string&)> on_error; ///< Optional callback invoked on initialization and write errors instead of stderr.
4949
};
5050

5151
/// \struct SessionView
@@ -70,12 +70,20 @@ namespace logit {
7070

7171
explicit MdbxLogger(const Config& config)
7272
: m_config(config) {
73-
normalize_config();
74-
validate_compression_config();
75-
open_storage();
76-
m_session_id = open_session();
77-
if (m_config.async) {
78-
m_worker = std::thread(&MdbxLogger::worker_loop, this);
73+
try {
74+
normalize_config();
75+
validate_compression_config();
76+
open_storage();
77+
m_session_id = open_session();
78+
if (m_config.async) {
79+
m_worker = std::thread(&MdbxLogger::worker_loop, this);
80+
}
81+
} catch (const std::exception& e) {
82+
report_init_error(std::string("MdbxLogger initialization error: ") + e.what());
83+
throw;
84+
} catch (...) {
85+
report_init_error("MdbxLogger initialization error");
86+
throw;
7987
}
8088
}
8189

@@ -623,12 +631,28 @@ namespace logit {
623631
db_config.no_subdir = true;
624632
db_config.sync_durable = true;
625633

634+
ensure_storage_parent(db_config);
626635
m_connection = mdbxc::Connection::create(db_config);
627636
m_sessions.reset(new SessionTable(m_connection, "log_sessions"));
628637
m_records.reset(new RecordTable(m_connection, "log_records_by_time"));
629638
m_payloads.reset(new PayloadTable(m_connection, "log_payloads"));
630639
}
631640

641+
void ensure_storage_parent(const mdbxc::Config& db_config) const {
642+
if (!db_config.read_only && db_config.no_subdir) {
643+
mdbxc::create_directories(db_config.pathname);
644+
}
645+
}
646+
647+
void report_init_error(const std::string& message) const noexcept {
648+
if (m_config.on_error) {
649+
try {
650+
m_config.on_error(message);
651+
} catch (...) {
652+
}
653+
}
654+
}
655+
632656
uint64_t open_session() {
633657
std::lock_guard<std::mutex> db_lock(m_db_mutex);
634658
auto txn = m_connection->transaction(mdbxc::TransactionMode::WRITABLE);

tests/mdbx_logger_test.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
#include <sstream>
1010
#include <string>
1111
#include <vector>
12+
#if __cplusplus >= 201703L
13+
#include <filesystem>
14+
#endif
1215

1316
namespace {
1417

@@ -23,11 +26,29 @@ std::string make_db_path(const std::string& suffix) {
2326
return os.str();
2427
}
2528

29+
std::string make_nested_db_path(const std::string& suffix) {
30+
std::ostringstream os;
31+
os << logit::get_exec_dir()
32+
<< "/mdbx_logger_test_"
33+
<< suffix
34+
<< "_"
35+
<< LOGIT_CURRENT_TIMESTAMP_MS()
36+
<< "/nested/logs.mdbx";
37+
return os.str();
38+
}
39+
2640
void cleanup_db(const std::string& path) {
2741
std::remove(path.c_str());
2842
std::remove((path + "-lck").c_str());
2943
}
3044

45+
void cleanup_path_tree(const std::string& path) {
46+
cleanup_db(path);
47+
#if __cplusplus >= 201703L
48+
std::filesystem::remove_all(std::filesystem::u8path(path).parent_path().parent_path());
49+
#endif
50+
}
51+
3152
logit::LogRecord make_record(logit::LogLevel level, int64_t timestamp_ms, int line) {
3253
return logit::LogRecord(
3354
level,
@@ -223,6 +244,59 @@ void test_on_error_callback() {
223244
cleanup_db(path);
224245
}
225246

247+
void test_nested_parent_directory_created() {
248+
const std::string path = make_nested_db_path("nested");
249+
cleanup_path_tree(path);
250+
251+
{
252+
logit::MdbxLogger::Config config;
253+
config.path = path;
254+
config.async = false;
255+
256+
logit::MdbxLogger logger(config);
257+
logger.log(make_record(logit::LogLevel::LOG_LVL_INFO, 6100, 51), "nested-ok");
258+
259+
auto records = logger.read_range(6100, 6101);
260+
assert(records.size() == 1);
261+
assert(records[0].message == "nested-ok");
262+
logger.shutdown();
263+
}
264+
265+
cleanup_path_tree(path);
266+
}
267+
268+
void test_init_error_callback_and_rethrow() {
269+
const std::string path = make_nested_db_path("init_error");
270+
cleanup_path_tree(path);
271+
272+
std::vector<std::string> errors;
273+
logit::MdbxLogger::Config config;
274+
config.path = path;
275+
config.async = false;
276+
config.on_error = [&errors](const std::string& msg) {
277+
errors.push_back(msg);
278+
};
279+
280+
const std::string blocker = path.substr(0, path.find("/nested/logs.mdbx"));
281+
FILE* blocker_file = std::fopen(blocker.c_str(), "wb");
282+
assert(blocker_file != nullptr);
283+
std::fclose(blocker_file);
284+
285+
bool thrown = false;
286+
try {
287+
logit::MdbxLogger logger(config);
288+
} catch (const std::exception&) {
289+
thrown = true;
290+
}
291+
292+
assert(thrown);
293+
assert(!errors.empty());
294+
assert(errors[0].find("MdbxLogger initialization error") != std::string::npos);
295+
296+
std::remove(blocker.c_str());
297+
cleanup_path_tree(path);
298+
}
299+
226300
void test_read_range_empty_and_limits() {
227301
const std::string path = make_db_path("range");
228302
cleanup_db(path);
@@ -412,6 +486,8 @@ int main() {
412486
#endif
413487
test_counters_zero_for_sync_writes();
414488
test_on_error_callback();
489+
test_nested_parent_directory_created();
490+
test_init_error_callback_and_rethrow();
415491
test_read_range_empty_and_limits();
416492
test_read_recent();
417493
test_callback_sync();

0 commit comments

Comments
 (0)