Browse Source

Updated: retry when database is locked.

Steve Thielemann 4 years ago
parent
commit
08e0fd28cc
2 changed files with 128 additions and 1 deletions
  1. 126 1
      db.cpp
  2. 2 0
      db.h

+ 126 - 1
db.cpp

@@ -3,11 +3,17 @@
 
 #include <SQLiteCpp/VariadicBind.h>
 
+#include <chrono>
 #include <iomanip>
 #include <iostream>
 #include <sstream>
+#include <thread>
 
 /*
+database is locked
+
+This happens when more then one node plays the game.
+
 The database access is slow.
 
 So, make sure you set it up so that you do your writes right
@@ -20,7 +26,7 @@ Change the strategy so we only update when the game ends.
 
 DBData::DBData(void)
     : db("space-data.db", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE) {
-
+  locked_retries = 5;
   create_tables();
   stmt_getSet = std::make_unique<SQLite::Statement>(
       db, "SELECT value FROM settings WHERE username=? AND setting=?");
@@ -30,10 +36,15 @@ DBData::DBData(void)
 
 DBData::~DBData() {}
 
+#define DBLOCK "database is locked"
+
 /**
  * @brief create tables if they don't exist.
  */
 void DBData::create_tables(void) {
+  int tries = locked_retries;
+
+retry:
   try {
     db.exec("CREATE TABLE IF NOT EXISTS \
 settings(username TEXT, setting TEXT, value TEXT, \
@@ -47,6 +58,16 @@ PRIMARY KEY(\"username\", \"date\", \"hand\"));");
       get_logger() << "create_tables():" << std::endl;
       get_logger() << "SQLite exception: " << e.what() << std::endl;
     }
+    if (strcmp(e.what(), DBLOCK) == 0) {
+      --tries;
+      if (tries > 0) {
+        std::this_thread::sleep_for(std::chrono::milliseconds(50));
+        goto retry;
+      }
+      if (get_logger)
+        get_logger() << "giving up! " << locked_retries << " retries."
+                     << std::endl;
+    }
   }
 }
 
@@ -62,6 +83,9 @@ PRIMARY KEY(\"username\", \"date\", \"hand\"));");
  */
 std::string DBData::getSetting(const std::string &setting,
                                std::string ifMissing) {
+  int tries = locked_retries;
+
+retry:
   try {
     stmt_getSet->reset();
     stmt_getSet->bind(1, user);
@@ -77,6 +101,16 @@ std::string DBData::getSetting(const std::string &setting,
                    << " ): " << user << std::endl;
       get_logger() << "SQLite exception: " << e.what() << std::endl;
     }
+    if (strcmp(e.what(), DBLOCK) == 0) {
+      --tries;
+      if (tries > 0) {
+        std::this_thread::sleep_for(std::chrono::milliseconds(50));
+        goto retry;
+      }
+      if (get_logger)
+        get_logger() << "giving up! " << locked_retries << " retries."
+                     << std::endl;
+    }
   }
   return ifMissing;
 }
@@ -91,6 +125,9 @@ std::string DBData::getSetting(const std::string &setting,
  * @param value
  */
 void DBData::setSetting(const std::string &setting, const std::string &value) {
+  int tries = locked_retries;
+
+retry:
   try {
     stmt_setSet->reset();
     stmt_setSet->bind(1, user);
@@ -103,6 +140,16 @@ void DBData::setSetting(const std::string &setting, const std::string &value) {
                    << " ): " << user << std::endl;
       get_logger() << "SQLite exception: " << e.what() << std::endl;
     }
+    if (strcmp(e.what(), DBLOCK) == 0) {
+      --tries;
+      if (tries > 0) {
+        std::this_thread::sleep_for(std::chrono::milliseconds(50));
+        goto retry;
+      }
+      if (get_logger)
+        get_logger() << "giving up! " << locked_retries << " retries."
+                     << std::endl;
+    }
   }
 }
 
@@ -116,6 +163,9 @@ void DBData::setSetting(const std::string &setting, const std::string &value) {
  * @param score
  */
 void DBData::saveScore(time_t when, time_t date, int hand, int won, int score) {
+  int tries = locked_retries;
+
+retry:
   try {
     SQLite::Statement stmt(
         db, "INSERT INTO scores( \"username\", \"when\", "
@@ -133,6 +183,16 @@ void DBData::saveScore(time_t when, time_t date, int hand, int won, int score) {
                    << won << "," << score << " ): " << user << std::endl;
       get_logger() << "SQLite exception: " << e.what() << std::endl;
     }
+    if (strcmp(e.what(), DBLOCK) == 0) {
+      --tries;
+      if (tries > 0) {
+        std::this_thread::sleep_for(std::chrono::milliseconds(50));
+        goto retry;
+      }
+      if (get_logger)
+        get_logger() << "giving up! " << locked_retries << " retries."
+                     << std::endl;
+    }
   }
 }
 
@@ -144,6 +204,9 @@ void DBData::saveScore(time_t when, time_t date, int hand, int won, int score) {
  * @return int
  */
 int DBData::handsPlayedOnDay(time_t day) {
+  int tries = locked_retries;
+
+retry:
   try {
     SQLite::Statement stmt(
         db, "SELECT COUNT(*) FROM scores WHERE \"username\"=? AND \"DATE\"=?;");
@@ -160,6 +223,16 @@ int DBData::handsPlayedOnDay(time_t day) {
                    << std::endl;
       get_logger() << "SQLite exception: " << e.what() << std::endl;
     }
+    if (strcmp(e.what(), DBLOCK) == 0) {
+      --tries;
+      if (tries > 0) {
+        std::this_thread::sleep_for(std::chrono::milliseconds(50));
+        goto retry;
+      }
+      if (get_logger)
+        get_logger() << "giving up! " << locked_retries << " retries."
+                     << std::endl;
+    }
   }
   return 0;
 }
@@ -174,6 +247,9 @@ int DBData::handsPlayedOnDay(time_t day) {
 
 std::vector<scores_details> DBData::getScoresOnDay(time_t date) {
   std::vector<scores_details> scores;
+  int tries = locked_retries;
+
+retry:
   try {
     // \"when\",
     SQLite::Statement stmt(db, "SELECT \"username\", \"date\", \"hand\", "
@@ -195,6 +271,16 @@ std::vector<scores_details> DBData::getScoresOnDay(time_t date) {
       get_logger() << "SQLite exception: " << e.what() << std::endl;
     }
     scores.clear();
+    if (strcmp(e.what(), DBLOCK) == 0) {
+      --tries;
+      if (tries > 0) {
+        std::this_thread::sleep_for(std::chrono::milliseconds(50));
+        goto retry;
+      }
+      if (get_logger)
+        get_logger() << "giving up! " << locked_retries << " retries."
+                     << std::endl;
+    }
   }
   return scores;
 }
@@ -207,6 +293,9 @@ std::vector<scores_details> DBData::getScoresOnDay(time_t date) {
  */
 std::map<time_t, std::vector<scores_data>> DBData::getScores(void) {
   std::map<time_t, std::vector<scores_data>> scores;
+  int tries = locked_retries;
+
+retry:
   try {
     SQLite::Statement stmt(
         db, "SELECT \"date\",\"username\",SUM(score),SUM(won) FROM scores "
@@ -245,6 +334,16 @@ std::map<time_t, std::vector<scores_data>> DBData::getScores(void) {
       get_logger() << "SQLite exception: " << e.what() << std::endl;
     }
     scores.clear();
+    if (strcmp(e.what(), DBLOCK) == 0) {
+      --tries;
+      if (tries > 0) {
+        std::this_thread::sleep_for(std::chrono::milliseconds(50));
+        goto retry;
+      }
+      if (get_logger)
+        get_logger() << "giving up! " << locked_retries << " retries."
+                     << std::endl;
+    }
   }
   return scores;
 }
@@ -258,6 +357,9 @@ std::map<time_t, std::vector<scores_data>> DBData::getScores(void) {
  */
 std::map<time_t, int> DBData::getPlayed(void) {
   std::map<time_t, int> hands;
+  int tries = locked_retries;
+
+retry:
   try {
     SQLite::Statement stmt(
         // select date, count(hand) from scores where username='grinder' group
@@ -275,6 +377,16 @@ std::map<time_t, int> DBData::getPlayed(void) {
       get_logger() << "SQLite exception: " << e.what() << std::endl;
     }
     hands.clear();
+    if (strcmp(e.what(), DBLOCK) == 0) {
+      --tries;
+      if (tries > 0) {
+        std::this_thread::sleep_for(std::chrono::milliseconds(50));
+        goto retry;
+      }
+      if (get_logger)
+        get_logger() << "giving up! " << locked_retries << " retries."
+                     << std::endl;
+    }
   }
   return hands;
 }
@@ -290,6 +402,9 @@ std::map<time_t, int> DBData::whenPlayed(void) {
   // select "date", count(hand) from scores where username='?' group by
   // "date";
   std::map<time_t, int> plays;
+  int tries = locked_retries;
+
+retry:
   try {
     SQLite::Statement stmt(db, "SELECT \"date\", COUNT(hand) FROM scores WHERE "
                                "username='?' GROUP BY \"date\";");
@@ -304,6 +419,16 @@ std::map<time_t, int> DBData::whenPlayed(void) {
       get_logger() << "SQLite exception: " << e.what() << std::endl;
     }
     plays.clear();
+    if (strcmp(e.what(), DBLOCK) == 0) {
+      --tries;
+      if (tries > 0) {
+        std::this_thread::sleep_for(std::chrono::milliseconds(50));
+        goto retry;
+      }
+      if (get_logger)
+        get_logger() << "giving up! " << locked_retries << " retries."
+                     << std::endl;
+    }
   }
   return plays;
 }

+ 2 - 0
db.h

@@ -27,6 +27,8 @@ class DBData {
   SQLite::Database db;
   void create_tables(void);
   std::string user;
+  int locked_retries;
+  
   /*
   I thought some of my performance problems were from the prepared statement
   calls.  It was not the case, there's a weird delay when I save/hit the