Skip to content

Commit 57838e8

Browse files
Merge pull request #1011 from lainedfles/db_wal
Introduce Sqlite WAL journal mode
2 parents 6556eed + feee891 commit 57838e8

6 files changed

Lines changed: 94 additions & 18 deletions

File tree

ui/opensnitch/config.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,12 @@ class Config:
105105
DEFAULT_SERVER_ADDR = "global/server_address"
106106
DEFAULT_SERVER_MAX_MESSAGE_LENGTH = "global/server_max_message_length"
107107
DEFAULT_HIDE_SYSTRAY_WARN = "global/hide_systray_warning"
108-
DEFAULT_DB_TYPE_KEY = "database/type"
109-
DEFAULT_DB_FILE_KEY = "database/file"
110-
DEFAULT_DB_PURGE_OLDEST = "database/purge_oldest"
111-
DEFAULT_DB_MAX_DAYS = "database/max_days"
112-
DEFAULT_DB_PURGE_INTERVAL = "database/purge_interval"
108+
DEFAULT_DB_TYPE_KEY = "database/type"
109+
DEFAULT_DB_FILE_KEY = "database/file"
110+
DEFAULT_DB_PURGE_OLDEST = "database/purge_oldest"
111+
DEFAULT_DB_MAX_DAYS = "database/max_days"
112+
DEFAULT_DB_PURGE_INTERVAL = "database/purge_interval"
113+
DEFAULT_DB_JRNL_WAL = "database/jrnl_wal"
113114

114115
DEFAULT_TIMEOUT = 30
115116

@@ -169,6 +170,7 @@ def __init__(self):
169170
if self.settings.value(self.DEFAULT_DB_TYPE_KEY) == None:
170171
self.setSettings(self.DEFAULT_DB_TYPE_KEY, Database.DB_TYPE_MEMORY)
171172
self.setSettings(self.DEFAULT_DB_FILE_KEY, Database.DB_IN_MEMORY)
173+
self.setSettings(self.DEFAULT_DB_JRNL_WAL, Database.DB_JRNL_WAL)
172174

173175
self.setRulesDurationFilter(
174176
self.getBool(self.DEFAULT_IGNORE_RULES),

ui/opensnitch/database/__init__.py

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@ class Database:
1010
DB_IN_MEMORY = ":memory:"
1111
DB_TYPE_MEMORY = 0
1212
DB_TYPE_FILE = 1
13+
DB_JRNL_WAL = False
14+
15+
# Sqlite3 journal modes
16+
DB_JOURNAL_MODE_LIST = {
17+
0: "DELETE",
18+
1: "TRUNCATE",
19+
2: "PERSIST",
20+
3: "MEMORY",
21+
4: "WAL",
22+
5: "OFF",
23+
}
1324

1425
# increase accordingly whenever the schema is updated
1526
DB_VERSION = 3
@@ -24,11 +35,16 @@ def __init__(self, dbname="db"):
2435
self._lock = threading.RLock()
2536
self.db = None
2637
self.db_file = Database.DB_IN_MEMORY
38+
self.db_jrnl_wal = Database.DB_JRNL_WAL
2739
self.db_name = dbname
2840

29-
def initialize(self, dbtype=DB_TYPE_MEMORY, dbfile=DB_IN_MEMORY, db_name="db"):
41+
def initialize(self, dbtype=DB_TYPE_MEMORY, dbfile=DB_IN_MEMORY, dbjrnl_wal=DB_JRNL_WAL, db_name="db"):
3042
if dbtype != Database.DB_TYPE_MEMORY:
3143
self.db_file = dbfile
44+
self.db_jrnl_wal = dbjrnl_wal
45+
else:
46+
# Always disable under pure memory mode
47+
self.db_jrnl_wal = False
3248

3349
is_new_file = not os.path.isfile(self.db_file)
3450

@@ -87,19 +103,16 @@ def get_db_name(self):
87103
return self.db_name
88104

89105
def _create_tables(self):
90-
# https://www.sqlite.org/wal.html
91106
if self.db_file == Database.DB_IN_MEMORY:
92107
self.set_schema_version(self.DB_VERSION)
93-
q = QSqlQuery("PRAGMA journal_mode = OFF", self.db)
94-
q.exec_()
95-
q = QSqlQuery("PRAGMA synchronous = OFF", self.db)
96-
q.exec_()
97-
q = QSqlQuery("PRAGMA cache_size=10000", self.db)
98-
q.exec_()
108+
# Disable journal (default)
109+
self.set_journal_mode(5)
110+
elif self.db_jrnl_wal is True:
111+
# Set WAL mode (file+memory)
112+
self.set_journal_mode(4)
99113
else:
100-
q = QSqlQuery("PRAGMA synchronous = NORMAL", self.db)
101-
q.exec_()
102-
114+
# Set DELETE mode (file)
115+
self.set_journal_mode(0)
103116
q = QSqlQuery("create table if not exists connections (" \
104117
"time text, " \
105118
"node text, " \
@@ -200,6 +213,41 @@ def set_schema_version(self, version):
200213
if q.exec_() == False:
201214
print("Error updating updating schema version:", q.lastError().text())
202215

216+
def get_journal_mode(self):
217+
q = QSqlQuery("PRAGMA journal_mode;", self.db)
218+
q.exec_()
219+
if q.next():
220+
return str(q.value(0))
221+
222+
return str("unknown")
223+
224+
def set_journal_mode(self, mode):
225+
# https://www.sqlite.org/wal.html
226+
mode_str = Database.DB_JOURNAL_MODE_LIST[mode]
227+
if self.get_journal_mode().lower() != mode_str.lower():
228+
print("Setting journal_mode: ", mode_str)
229+
q = QSqlQuery("PRAGMA journal_mode = {modestr};".format(modestr = mode_str), self.db)
230+
if q.exec_() == False:
231+
print("Error updating PRAGMA journal_mode:", q.lastError().text())
232+
return False
233+
if mode == 3 or mode == 5:
234+
print("Setting DB memory optimizations")
235+
q = QSqlQuery("PRAGMA synchronous = OFF;", self.db)
236+
if q.exec_() == False:
237+
print("Error updating PRAGMA synchronous:", q.lastError().text())
238+
return False
239+
q = QSqlQuery("PRAGMA cache_size=10000;", self.db)
240+
if q.exec_() == False:
241+
print("Error updating PRAGMA cache_size:", q.lastError().text())
242+
return False
243+
else:
244+
print("Setting synchronous = NORMAL")
245+
q = QSqlQuery("PRAGMA synchronous = NORMAL;", self.db)
246+
if q.exec_() == False:
247+
print("Error updating PRAGMA synchronous:", q.lastError().text())
248+
249+
return True
250+
203251
def _upgrade_db_schema(self):
204252
migrations_path = os.path.dirname(os.path.realpath(__file__)) + "/migrations"
205253
schema_version = self.get_schema_version()

ui/opensnitch/dialogs/preferences.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ def showEvent(self, event):
203203

204204
self.comboDBType.currentIndexChanged.connect(self._cb_db_type_changed)
205205
self.checkDBMaxDays.toggled.connect(self._cb_db_max_days_toggled)
206+
self.checkDBJrnlWal.toggled.connect(self._cb_db_jrnl_wal_toggled)
206207

207208
# True when any node option changes
208209
self._node_needs_update = False
@@ -323,8 +324,10 @@ def _load_settings(self):
323324
self.dbLabel.setVisible(True)
324325
self.dbLabel.setText(self._cfg.getSettings(self._cfg.DEFAULT_DB_FILE_KEY))
325326
dbMaxDays = self._cfg.getInt(self._cfg.DEFAULT_DB_MAX_DAYS, 1)
327+
dbJrnlWal = self._cfg.getBool(self._cfg.DEFAULT_DB_JRNL_WAL)
326328
dbPurgeInterval = self._cfg.getInt(self._cfg.DEFAULT_DB_PURGE_INTERVAL, 5)
327329
self._enable_db_cleaner_options(self._cfg.getBool(Config.DEFAULT_DB_PURGE_OLDEST), dbMaxDays)
330+
self._enable_db_jrnl_wal(self._cfg.getBool(Config.DEFAULT_DB_PURGE_OLDEST), dbJrnlWal)
328331
self.spinDBMaxDays.setValue(dbMaxDays)
329332
self.spinDBPurgeInterval.setValue(dbPurgeInterval)
330333

@@ -528,6 +531,7 @@ def _save_db_config(self):
528531
self._cfg.setSettings(Config.DEFAULT_DB_PURGE_OLDEST, bool(self.checkDBMaxDays.isChecked()))
529532
self._cfg.setSettings(Config.DEFAULT_DB_MAX_DAYS, int(self.spinDBMaxDays.value()))
530533
self._cfg.setSettings(Config.DEFAULT_DB_PURGE_INTERVAL, int(self.spinDBPurgeInterval.value()))
534+
self._cfg.setSettings(Config.DEFAULT_DB_JRNL_WAL, bool(self.checkDBJrnlWal.isChecked()))
531535
self.dbType = self.comboDBType.currentIndex()
532536

533537
return True
@@ -781,6 +785,10 @@ def _enable_db_cleaner_options(self, enable, db_max_days):
781785
self.cmdDBPurgesUp.setEnabled(enable)
782786
self.cmdDBPurgesDown.setEnabled(enable)
783787

788+
def _enable_db_jrnl_wal(self, enable, db_jrnl_wal):
789+
self.checkDBJrnlWal.setChecked(db_jrnl_wal)
790+
self.checkDBJrnlWal.setEnabled(enable)
791+
784792
@QtCore.pyqtSlot(ui_pb2.NotificationReply)
785793
def _cb_notification_callback(self, reply):
786794
#print(self.LOG_TAG, "Config notification received: ", reply.id, reply.code)
@@ -819,6 +827,8 @@ def _cb_db_type_changed(self):
819827
self.dbLabel.setVisible(not isDBMem)
820828
self.checkDBMaxDays.setEnabled(not isDBMem)
821829
self.checkDBMaxDays.setChecked(not isDBMem)
830+
self.checkDBJrnlWal.setEnabled(not isDBMem)
831+
self.checkDBJrnlWal.setChecked(False)
822832

823833
def _cb_accept_button_clicked(self):
824834
self.accept()
@@ -884,6 +894,9 @@ def _cb_combo_node_auth_type_changed(self, index):
884894
def _cb_db_max_days_toggled(self, state):
885895
self._enable_db_cleaner_options(state, 1)
886896

897+
def _cb_db_jrnl_wal_toggled(self, state):
898+
self._changes_needs_restart = QC.translate("preferences", "DB journal_mode changed")
899+
887900
def _cb_cmd_spin_clicked(self, spinWidget, operation):
888901
if operation == self.SUM:
889902
spinWidget.setValue(spinWidget.value() + 1)

ui/opensnitch/res/preferences.ui

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1916,6 +1916,16 @@ Temporary rules will still be valid, and you can use them when prompted to allow
19161916
</property>
19171917
</widget>
19181918
</item>
1919+
<item row="2" column="0">
1920+
<widget class="QCheckBox" name="checkDBJrnlWal">
1921+
<property name="enabled">
1922+
<bool>false</bool>
1923+
</property>
1924+
<property name="text">
1925+
<string>Enable DB Write-Ahead Logging (WAL)</string>
1926+
</property>
1927+
</widget>
1928+
</item>
19191929
</layout>
19201930
</item>
19211931
</layout>

ui/opensnitch/service.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,11 @@ def __init__(self, app, on_exit, start_in_bg=False):
6161
self._cfg = Config.init()
6262
self._db = Database.instance()
6363
db_file=self._cfg.getSettings(self._cfg.DEFAULT_DB_FILE_KEY)
64+
db_jrnl_wal=self._cfg.getBool(Config.DEFAULT_DB_JRNL_WAL)
6465
db_status, db_error = self._db.initialize(
6566
dbtype=self._cfg.getInt(self._cfg.DEFAULT_DB_TYPE_KEY),
66-
dbfile=db_file
67+
dbfile=db_file,
68+
dbjrnl_wal=db_jrnl_wal
6769
)
6870
if db_status is False:
6971
Message.ok(

ui/opensnitch/utils/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,8 @@ def __init__(self, _interval, _callback):
235235
self.db = Database("db-cleaner-connection")
236236
self.db_status, db_error = self.db.initialize(
237237
dbtype=self._cfg.getInt(self._cfg.DEFAULT_DB_TYPE_KEY),
238-
dbfile=self._cfg.getSettings(self._cfg.DEFAULT_DB_FILE_KEY)
238+
dbfile=self._cfg.getSettings(self._cfg.DEFAULT_DB_FILE_KEY),
239+
dbjrnl_wal=self._cfg.getBool(self._cfg.DEFAULT_DB_JRNL_WAL)
239240
)
240241

241242
def run(self):

0 commit comments

Comments
 (0)