Compare commits

...
2 Commits
Author SHA1 Message Date
jwinterm 93d13a6568 v5.0.3: harden embedded-node restart handling + version bump
On top of Lucas's #119 (--db-sync-mode safe):
- DaemonManager: age out the crash-retry budget (m_restarts) so legit node
  toggles/restarts don't trip "maximum retries exceeded" after ~4 spawns over a
  session; the cap now only bites a genuine rapid crash-loop.
- DaemonManager: clear the m_stopping latch on start(), so the crash auto-restart
  keeps working after the node has been toggled off then on (it was a one-shot
  before).
- bump version 5.0.2 -> 5.0.3.
2026-06-27 13:10:54 -04:00
Lucas 64326119eb Updated DaemonManager to ensure new blocks are
committed immediately by disabling LMDB batch write mode.
2026-06-26 21:57:42 +10:00
3 changed files with 24 additions and 1 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.18) cmake_minimum_required(VERSION 3.18)
project(feather project(feather
VERSION "5.0.2" VERSION "5.0.3"
DESCRIPTION "A free Monero desktop wallet" DESCRIPTION "A free Monero desktop wallet"
LANGUAGES CXX C ASM LANGUAGES CXX C ASM
) )
+22
View File
@@ -4,6 +4,7 @@
#include "utils/DaemonManager.h" #include "utils/DaemonManager.h"
#include <QCoreApplication> #include <QCoreApplication>
#include <QDateTime>
#include <QDir> #include <QDir>
#include <QDirIterator> #include <QDirIterator>
#include <QEventLoop> #include <QEventLoop>
@@ -62,6 +63,11 @@ void DaemonManager::start() {
if (state == QProcess::ProcessState::Running || state == QProcess::ProcessState::Starting) if (state == QProcess::ProcessState::Running || state == QProcess::ProcessState::Starting)
return; return;
// wowlet: a deliberate (re)start clears the stop latch, so a later UNEXPECTED exit can auto-restart
// again. Without this, m_stopping stays true after the first node toggle-off and the crash
// auto-restart is silently dead for the rest of the session.
m_stopping = false;
if (!this->unpackBins()) { if (!this->unpackBins()) {
this->setErrorMessage("Unable to unpack the embedded wownerod binary."); this->setErrorMessage("Unable to unpack the embedded wownerod binary.");
return; return;
@@ -85,6 +91,14 @@ void DaemonManager::start() {
// and surfaces a clear error via handleProcessError(). // and surfaces a clear error via handleProcessError().
} }
// wowlet: the retry cap guards against a rapid crash-loop, not legit restarts. If the node had been
// up a while before this (re)start, clear the budget — otherwise a few node toggles over a session
// trip "maximum retries exceeded" and it won't start again until wowlet is relaunched.
const qint64 nowMs = QDateTime::currentMSecsSinceEpoch();
if (m_lastSpawnMs != 0 && (nowMs - m_lastSpawnMs) > 60000)
m_restarts = 0;
m_lastSpawnMs = nowMs;
m_restarts += 1; m_restarts += 1;
if (m_restarts > 4) { if (m_restarts > 4) {
this->setErrorMessage("wownerod failed to start: maximum retries exceeded"); this->setErrorMessage("wownerod failed to start: maximum retries exceeded");
@@ -108,6 +122,14 @@ void DaemonManager::start() {
arguments << "--no-igd"; // no UPnP port mapping arguments << "--no-igd"; // no UPnP port mapping
arguments << "--out-peers" << "16"; arguments << "--out-peers" << "16";
arguments << "--log-file" << QDir(dataDir).filePath("wownerod.log"); arguments << "--log-file" << QDir(dataDir).filePath("wownerod.log");
// wowlet: disable LMDB batch write mode so every block is committed in its own transaction.
// Without this, wownero accumulates blocks in a single large write transaction that is ABORTED
// (not committed) when the daemon shuts down mid-sync — causing all progress since the last
// batch boundary to be silently lost. This is visible in wownerod.log: sessions consistently
// revert to a stale height despite a full graceful shutdown sequence. Using "safe" mode is
// slower for initial sync but is the only correct choice for an embedded node that starts and
// stops with the wallet; the wownero chain is small enough that the overhead is acceptable.
arguments << "--db-sync-mode" << "safe";
// Full archival by default (wownero's chain is small); prune is an explicit opt-in. // Full archival by default (wownero's chain is small); prune is an explicit opt-in.
if (conf()->get(Config::pruneBlockchain).toBool()) if (conf()->get(Config::pruneBlockchain).toBool())
+1
View File
@@ -73,6 +73,7 @@ private:
bool m_synced = false; bool m_synced = false;
bool m_stopping = false; bool m_stopping = false;
int m_restarts = 0; int m_restarts = 0;
qint64 m_lastSpawnMs = 0; // wowlet: wall-clock of last spawn — ages out the crash-retry budget
static QPointer<DaemonManager> m_instance; static QPointer<DaemonManager> m_instance;
}; };