Skip to content

Commit 29be121

Browse files
committed
Fix LevelDB stale LOCK recovery after crash or unclean shutdown
LevelDBStore::open() now handles stale LOCK files from crashed instances: 1. If Open fails, remove stale LOCK file and retry 2. If still fails (corruption), run RepairDB to salvage data, then retry 3. Only fatal-error if all recovery steps fail All 6 LevelDB databases (sharechain, LTC/DOGE headers, LTC/DOGE UTXO, found_blocks) funnel through this single open() path, so all are covered. Matches the recovery approach used by Bitcoin Core / litecoind for their LevelDB chainstate and block index databases.
1 parent e20f386 commit 29be121

1 file changed

Lines changed: 34 additions & 1 deletion

File tree

src/core/leveldb_store.cpp

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,40 @@ bool LevelDBStore::open()
5252

5353
leveldb::DB* db_ptr;
5454
leveldb::Status status = leveldb::DB::Open(db_options, m_db_path, &db_ptr);
55-
55+
56+
// Recovery after unclean shutdown (crash, kill -9, power loss).
57+
// LevelDB uses fcntl locks on Linux which are released on process
58+
// death, but a stale LOCK file can remain and block reopening if
59+
// a zombie child still holds the fd or the OS didn't clean up.
60+
// Standard approach (same as Bitcoin Core / litecoind):
61+
// 1. Remove stale LOCK file → retry Open
62+
// 2. If still fails (corruption) → RepairDB → retry Open
63+
if (!status.ok()) {
64+
auto lock_path = std::filesystem::path(m_db_path) / "LOCK";
65+
if (std::filesystem::exists(lock_path)) {
66+
LOG_WARNING << "LevelDB open failed at " << m_db_path
67+
<< ": " << status.ToString()
68+
<< " — removing stale LOCK file and retrying";
69+
std::error_code ec;
70+
std::filesystem::remove(lock_path, ec);
71+
status = leveldb::DB::Open(db_options, m_db_path, &db_ptr);
72+
}
73+
}
74+
75+
if (!status.ok()) {
76+
LOG_WARNING << "LevelDB open failed after LOCK removal at " << m_db_path
77+
<< ": " << status.ToString()
78+
<< " — attempting RepairDB (data will be preserved)";
79+
leveldb::Status repair = leveldb::RepairDB(m_db_path, db_options);
80+
if (repair.ok()) {
81+
LOG_INFO << "LevelDB RepairDB succeeded at " << m_db_path;
82+
status = leveldb::DB::Open(db_options, m_db_path, &db_ptr);
83+
} else {
84+
LOG_ERROR << "LevelDB RepairDB failed at " << m_db_path
85+
<< ": " << repair.ToString();
86+
}
87+
}
88+
5689
if (!status.ok()) {
5790
LOG_ERROR << "Failed to open LevelDB at " << m_db_path << ": " << status.ToString();
5891
return false;

0 commit comments

Comments
 (0)