Browse Source

Create periodic backup copies of controller.db in network controller from the main process itself to facilitate easier and safer backups of controller.db.

Adam Ierymenko 9 years ago
parent
commit
7903f24a8f

+ 55 - 0
controller/SqliteNetworkController.cpp

@@ -71,6 +71,9 @@
 // than this (ms).
 #define ZT_NETCONF_MIN_REQUEST_PERIOD 1000
 
+// Delay between backups in milliseconds
+#define ZT_NETCONF_BACKUP_PERIOD 60000
+
 namespace ZeroTier {
 
 namespace {
@@ -122,6 +125,7 @@ struct NetworkRecord {
 
 SqliteNetworkController::SqliteNetworkController(Node *node,const char *dbPath,const char *circuitTestPath) :
 	_node(node),
+	_backupThreadRun(true),
 	_dbPath(dbPath),
 	_circuitTestPath(circuitTestPath),
 	_db((sqlite3 *)0)
@@ -247,10 +251,15 @@ SqliteNetworkController::SqliteNetworkController(Node *node,const char *dbPath,c
 			throw std::runtime_error("SqliteNetworkController unable to read instanceId (it's NULL)");
 		_instanceId = iid;
 	}
+
+	_backupThread = Thread::start(this);
 }
 
 SqliteNetworkController::~SqliteNetworkController()
 {
+	_backupThreadRun = false;
+	Thread::join(_backupThread);
+
 	Mutex::Lock _l(_lock);
 	if (_db) {
 		sqlite3_finalize(_sGetNetworkById);
@@ -991,6 +1000,52 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpDELETE(
 	return 404;
 }
 
+void SqliteNetworkController::threadMain()
+	throw()
+{
+	uint64_t lastBackupTime = 0;
+	while (_backupThreadRun) {
+		if ((OSUtils::now() - lastBackupTime) >= ZT_NETCONF_BACKUP_PERIOD) {
+			lastBackupTime = OSUtils::now();
+
+			char backupPath[4096],backupPath2[4096];
+			Utils::snprintf(backupPath,sizeof(backupPath),"%s.backupInProgress",_dbPath.c_str());
+			Utils::snprintf(backupPath2,sizeof(backupPath),"%s.backup",_dbPath.c_str());
+			OSUtils::rm(backupPath); // delete any unfinished backups
+
+			sqlite3 *bakdb = (sqlite3 *)0;
+			sqlite3_backup *bak = (sqlite3_backup *)0;
+			if (sqlite3_open_v2(backupPath,&bakdb,SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE,(const char *)0) != SQLITE_OK) {
+				fprintf(stderr,"SqliteNetworkController: CRITICAL: backup failed on sqlite3_open_v2()"ZT_EOL_S);
+				continue;
+			}
+			bak = sqlite3_backup_init(bakdb,"main",_db,"main");
+			if (!bak) {
+				sqlite3_close(bakdb);
+				OSUtils::rm(backupPath); // delete any unfinished backups
+				fprintf(stderr,"SqliteNetworkController: CRITICAL: backup failed on sqlite3_backup_init()"ZT_EOL_S);
+				continue;
+			}
+
+			int rc = SQLITE_OK;
+			for(;;) {
+				rc = sqlite3_backup_step(bak,1);
+				if ((rc == SQLITE_OK)||(rc == SQLITE_LOCKED)||(rc == SQLITE_BUSY))
+					Thread::sleep(100);
+				else break;
+			}
+
+			sqlite3_backup_finish(bak);
+			sqlite3_close(bakdb);
+
+			OSUtils::rm(backupPath2);
+			::rename(backupPath,backupPath2);
+		}
+
+		Thread::sleep(500);
+	}
+}
+
 unsigned int SqliteNetworkController::_doCPGet(
 	const std::vector<std::string> &path,
 	const std::map<std::string,std::string> &urlArgs,

+ 7 - 0
controller/SqliteNetworkController.hpp

@@ -39,6 +39,7 @@
 #include "../node/Constants.hpp"
 #include "../node/NetworkController.hpp"
 #include "../node/Mutex.hpp"
+#include "../osdep/Thread.hpp"
 
 // Number of in-memory last log entries to maintain per user
 #define ZT_SQLITENETWORKCONTROLLER_IN_MEMORY_LOG_SIZE 32
@@ -86,6 +87,10 @@ public:
 		std::string &responseBody,
 		std::string &responseContentType);
 
+	// threadMain() for backup thread -- do not call directly
+	void threadMain()
+		throw();
+
 private:
 	enum IpAssignmentType {
 		// IP assignment is a static IP address
@@ -112,6 +117,8 @@ private:
 	static void _circuitTestCallback(ZT_Node *node,ZT_CircuitTest *test,const ZT_CircuitTestReport *report);
 
 	Node *_node;
+	Thread _backupThread;
+	volatile bool _backupThreadRun;
 	std::string _dbPath;
 	std::string _circuitTestPath;
 	std::string _instanceId;

+ 0 - 1
osdep/OSUtils.hpp

@@ -95,7 +95,6 @@ public:
 	static inline bool rm(const std::string &path) throw() { return rm(path.c_str()); }
 
 	static inline bool mkdir(const char *path)
-		throw()
 	{
 #ifdef __WINDOWS__
 		if (::PathIsDirectoryA(path))