From 29e2527b6b1aa7dfc4662a0bb7761afd4af8dab3 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Sat, 18 Jul 2026 17:33:29 +0300 Subject: [PATCH] consolidate 3 SQLite connections into 1 shared handle - utun_node.cpp: open topo_sqlite_db with FULLMUTEX + all PRAGMAs (WAL, FK, NORMAL sync, checkpoint) - db_sync.c: reuse inst->topo_sqlite_db if available (shared_db flag) - chat_core.c: reuse inst->topo_sqlite_db if available (shared_db flag) - db_sync_destroy/chat_core_destroy: skip close on shared handle - Only topo_groups_destroy closes the shared handle - Fallback: each module opens own connection if topo_sqlite_db is NULL --- src/db_sync.c | 21 +++++++++++++-------- tools/chatgui/transport/chat_core.c | 25 ++++++++++++++++--------- tools/chatgui/transport/utun_node.cpp | 22 ++++++++++++++++++++++ 3 files changed, 51 insertions(+), 17 deletions(-) diff --git a/src/db_sync.c b/src/db_sync.c index 47e98eb2..c27b5f15 100644 --- a/src/db_sync.c +++ b/src/db_sync.c @@ -58,6 +58,7 @@ struct DB_SYNC_INSTANCE { struct DB_SYNC { struct UTUN_INSTANCE* inst; sqlite3* db; + uint8_t shared_db; /* db == inst->topo_sqlite_db, do not close */ uint64_t last_connected_tb; struct DB_SYNC_INSTANCE* instances; int instance_count, instance_capacity; @@ -110,7 +111,7 @@ static int db_sqlite_open(struct DB_SYNC* db, const char* path) static void db_sqlite_close(struct DB_SYNC* db) { DEBUG_TRACE(DEBUG_CATEGORY_DB_SYNC, ""); - if (!db->db) return; + if (!db->db || db->shared_db) return; sqlite3_close(db->db); db->db = NULL; DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "db_sync: SQLite closed"); @@ -1376,13 +1377,17 @@ int db_sync_init(struct UTUN_INSTANCE* inst) inst->db_sync = db; const char* dp = inst->config->global.db_path; - char sp[512]; - if (dp[0]) snprintf(sp, sizeof(sp), "%s/chats.db", dp); - else snprintf(sp, sizeof(sp), "/tmp/utun_db_sync"); - - if (db_sqlite_open(db, sp) != 0) { - DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC, "SQLite open failed, sync disabled"); - db->enabled = 0; + if (inst->topo_sqlite_db) { + db->db = inst->topo_sqlite_db; db->shared_db = 1; + DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "db_sync: using shared SQLite db=%p", (void*)db->db); + } else { + char sp[512]; + if (dp[0]) snprintf(sp, sizeof(sp), "%s/chats.db", dp); + else snprintf(sp, sizeof(sp), "/tmp/utun_db_sync"); + if (db_sqlite_open(db, sp) != 0) { + DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC, "SQLite open failed, sync disabled"); + db->enabled = 0; + } } etcp_bind(inst, ETCP_RT_ID_DB_SYNC, db_sync_recv_cb); diff --git a/tools/chatgui/transport/chat_core.c b/tools/chatgui/transport/chat_core.c index 7f3e029d..6b15fea8 100644 --- a/tools/chatgui/transport/chat_core.c +++ b/tools/chatgui/transport/chat_core.c @@ -37,6 +37,7 @@ static struct chat_core_ctx { struct UTUN_INSTANCE* inst; sqlite3* db; + uint8_t shared_db; /* db == inst->topo_sqlite_db, do not close */ uint64_t my_node_id; /* db_sync instances per channel */ @@ -91,15 +92,20 @@ int chat_core_init(struct UTUN_INSTANCE* inst, const char* db_path) { if (!inst || !db_path) return -1; memset(&g_cc, 0, sizeof(g_cc)); - int rc = sqlite3_open_v2(db_path, &g_cc.db, - SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL); - if (rc != SQLITE_OK) { - DEBUG_ERROR(DEBUG_CATEGORY_CONNECTIVITY, "%s: cannot open DB %s: %s", - CC_ID, db_path, sqlite3_errmsg(g_cc.db)); - sqlite3_close(g_cc.db); g_cc.db = NULL; return -1; + if (inst->topo_sqlite_db) { + g_cc.db = inst->topo_sqlite_db; g_cc.shared_db = 1; + DEBUG_INFO(DEBUG_CATEGORY_CONNECTIVITY, "%s: using shared SQLite db=%p", CC_ID, (void*)g_cc.db); + } else { + int rc = sqlite3_open_v2(db_path, &g_cc.db, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL); + if (rc != SQLITE_OK) { + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTIVITY, "%s: cannot open DB %s: %s", + CC_ID, db_path, sqlite3_errmsg(g_cc.db)); + sqlite3_close(g_cc.db); g_cc.db = NULL; return -1; + } + sqlite3_exec(g_cc.db, "PRAGMA journal_mode=WAL", NULL, NULL, NULL); + sqlite3_exec(g_cc.db, "PRAGMA foreign_keys=ON", NULL, NULL, NULL); } - sqlite3_exec(g_cc.db, "PRAGMA journal_mode=WAL", NULL, NULL, NULL); - sqlite3_exec(g_cc.db, "PRAGMA foreign_keys=ON", NULL, NULL, NULL); g_cc.inst = inst; g_cc.my_node_id = inst->node_id; @@ -229,7 +235,8 @@ void chat_core_destroy(struct UTUN_INSTANCE* inst) { if (!g_cc.initialized) return; g_cc.initialized = 0; - if (g_cc.db) { sqlite3_close(g_cc.db); g_cc.db = NULL; } + if (g_cc.db && !g_cc.shared_db) { sqlite3_close(g_cc.db); } + g_cc.db = NULL; g_cc.inst = NULL; DEBUG_INFO(DEBUG_CATEGORY_CONNECTIVITY, "%s: destroyed", CC_ID); } diff --git a/tools/chatgui/transport/utun_node.cpp b/tools/chatgui/transport/utun_node.cpp index 25d2f0b6..ff02718a 100644 --- a/tools/chatgui/transport/utun_node.cpp +++ b/tools/chatgui/transport/utun_node.cpp @@ -26,6 +26,7 @@ extern "C" { #include "chat_core.h" #include "db_sync.h" #include "gui_bridge.h" +#include "../src/topo_node_sqlite.h" } #define ETCP_RT_ID_CHAT 0x11 @@ -260,6 +261,27 @@ void UtunNode::runLoop() { snprintf(m_instance->config->global.db_path, sizeof(m_instance->config->global.db_path), "%s", m_dbPath.toUtf8().constData()); + /* topo_groups_init did not open topo_sqlite_db because db_path was empty at that time. + * Open the single shared SQLite connection here with FULLMUTEX (GUI reads from another thread). + * db_sync_init and chat_core_init will reuse this handle. */ + if (m_instance->config->global.db_path[0] && !m_instance->topo_sqlite_db) { + char db_file[512]; + snprintf(db_file, sizeof(db_file), "%s/chats.db", m_instance->config->global.db_path); + int rc = sqlite3_open_v2(db_file, &m_instance->topo_sqlite_db, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL); + if (rc == SQLITE_OK && m_instance->topo_sqlite_db) { + sqlite3_exec(m_instance->topo_sqlite_db, "PRAGMA journal_mode=WAL", NULL, NULL, NULL); + sqlite3_exec(m_instance->topo_sqlite_db, "PRAGMA foreign_keys=ON", NULL, NULL, NULL); + sqlite3_exec(m_instance->topo_sqlite_db, "PRAGMA synchronous=NORMAL", NULL, NULL, NULL); + sqlite3_exec(m_instance->topo_sqlite_db, "PRAGMA wal_autocheckpoint=10000", NULL, NULL, NULL); + topo_node_sqlite_init(m_instance->topo_sqlite_db); + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "SQLite (shared): %s db=%p", db_file, (void*)m_instance->topo_sqlite_db); + } else { + DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "SQLite open failed: %s rc=%d", db_file, rc); + if (m_instance->topo_sqlite_db) { sqlite3_close(m_instance->topo_sqlite_db); m_instance->topo_sqlite_db = NULL; } + } + } + /* set ua for gui_bridge before init, so GUI can post */ gui_bridge_set_uasync(ua);