aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRogier <rogier777@gmail.com>2015-12-23 11:38:50 +0100
committerest31 <MTest31@outlook.com>2015-12-30 00:08:56 +0100
commitc6bb6f99d1fc0acd0161307a17dd31eea4a56fff (patch)
tree0cc80dae5fe1e3c88df9e8962dc44cb694d89e5d /src
parent848b050a567e360e857577c50dee82494b14973b (diff)
downloadminetest-c6bb6f99d1fc0acd0161307a17dd31eea4a56fff.tar.gz
minetest-c6bb6f99d1fc0acd0161307a17dd31eea4a56fff.tar.bz2
minetest-c6bb6f99d1fc0acd0161307a17dd31eea4a56fff.zip
Handle SQLITE_BUSY errors gracefully
This allows other applications (e.g. minetestmapper) to interrogate the database while minetest is running, without causing an almost certain minetest crash.
Diffstat (limited to 'src')
-rw-r--r--src/database-sqlite3.cpp60
-rw-r--r--src/database-sqlite3.h4
2 files changed, 64 insertions, 0 deletions
diff --git a/src/database-sqlite3.cpp b/src/database-sqlite3.cpp
index da3e2c86b..d2af042e6 100644
--- a/src/database-sqlite3.cpp
+++ b/src/database-sqlite3.cpp
@@ -31,10 +31,19 @@ SQLite format specification:
#include "filesys.h"
#include "exceptions.h"
#include "settings.h"
+#include "porting.h"
#include "util/string.h"
#include <cassert>
+// When to print messages when the database is being held locked by another process
+// Note: I've seen occasional delays of over 250ms while running minetestmapper.
+#define BUSY_INFO_TRESHOLD 100 // Print first informational message after 100ms.
+#define BUSY_WARNING_TRESHOLD 250 // Print warning message after 250ms. Lag is increased.
+#define BUSY_ERROR_TRESHOLD 1000 // Print error message after 1000ms. Significant lag.
+#define BUSY_FATAL_TRESHOLD 3000 // Allow SQLITE_BUSY to be returned, which will cause a minetest crash.
+#define BUSY_ERROR_INTERVAL 10000 // Safety net: report again every 10 seconds
+
#define SQLRES(s, r) \
if ((s) != (r)) { \
@@ -56,6 +65,51 @@ SQLite format specification:
sqlite3_errmsg(m_database)); \
}
+int Database_SQLite3::busyHandler(void *data, int count)
+{
+ s64 &first_time = reinterpret_cast<s64 *>(data)[0];
+ s64 &prev_time = reinterpret_cast<s64 *>(data)[1];
+ s64 cur_time = getTimeMs();
+
+ if (count == 0) {
+ first_time = cur_time;
+ prev_time = first_time;
+ } else {
+ while (cur_time < prev_time)
+ cur_time += s64(1)<<32;
+ }
+
+ if (cur_time - first_time < BUSY_INFO_TRESHOLD) {
+ ; // do nothing
+ } else if (cur_time - first_time >= BUSY_INFO_TRESHOLD &&
+ prev_time - first_time < BUSY_INFO_TRESHOLD) {
+ infostream << "SQLite3 database has been locked for "
+ << cur_time - first_time << " ms." << std::endl;
+ } else if (cur_time - first_time >= BUSY_WARNING_TRESHOLD &&
+ prev_time - first_time < BUSY_WARNING_TRESHOLD) {
+ warningstream << "Sqlite3 database has been locked for "
+ << cur_time - first_time << " ms." << std::endl;
+ } else if (cur_time - first_time >= BUSY_ERROR_TRESHOLD &&
+ prev_time - first_time < BUSY_ERROR_TRESHOLD) {
+ errorstream << "SQLite3 database has been locked for "
+ << cur_time - first_time << " ms; this causes lag." << std::endl;
+ } else if (cur_time - first_time >= BUSY_FATAL_TRESHOLD &&
+ prev_time - first_time < BUSY_FATAL_TRESHOLD) {
+ errorstream << "Sqlite3 database has been locked for "
+ << cur_time - first_time << " ms - giving up!" << std::endl;
+ } else if ((cur_time - first_time) / BUSY_ERROR_INTERVAL !=
+ (prev_time - first_time) / BUSY_ERROR_INTERVAL) {
+ // Safety net: keep reporting every BUSY_ERROR_INTERVAL
+ errorstream << "SQLite3 database has been locked for "
+ << (cur_time - first_time) / 1000 << " seconds!" << std::endl;
+ }
+
+ prev_time = cur_time;
+
+ // Make sqlite transaction fail if delay exceeds BUSY_FATAL_TRESHOLD
+ return cur_time - first_time < BUSY_FATAL_TRESHOLD;
+}
+
Database_SQLite3::Database_SQLite3(const std::string &savedir) :
m_initialized(false),
@@ -107,6 +161,12 @@ void Database_SQLite3::openDatabase()
throw FileNotGoodException("Cannot open database file");
}
+ if (sqlite3_busy_handler(m_database, sqlite3BusyHandler, busy_handler_data) != SQLITE_OK) {
+ errorstream << "SQLite3 database failed to set busy handler: "
+ << sqlite3_errmsg(m_database) << std::endl;
+ throw FileNotGoodException("Failed to set busy handler for sqlite connection");
+ }
+
if (needs_create) {
createDatabase();
}
diff --git a/src/database-sqlite3.h b/src/database-sqlite3.h
index a775742be..04a1825d9 100644
--- a/src/database-sqlite3.h
+++ b/src/database-sqlite3.h
@@ -63,6 +63,10 @@ private:
sqlite3_stmt *m_stmt_delete;
sqlite3_stmt *m_stmt_begin;
sqlite3_stmt *m_stmt_end;
+
+ s64 m_busy_handler_data[2];
+
+ static int busyHandler(void *data, int count);
};
#endif