[asterisk-commits] twilson: branch twilson/sqlite_astdb r323319 - in /team/twilson/sqlite_astdb:...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Jun 13 17:52:15 CDT 2011


Author: twilson
Date: Mon Jun 13 17:52:12 2011
New Revision: 323319

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=323319
Log:
Handle the locking ourselves so we can chunk writes

This update removes all of the lovely threadstorage code so that we can easily
chunk writes to disk in 1 second bursts by wrapping statments in transactions.
Since we are handling the serialization ourselves, we now open the database in
single threaded mode so SQLite will no longer do any locking on its own.

As a result of this, I'm no longer making the begin/end/rollback transaction
functions public because you can't start a transaction from within another
transaction.

Of course, doing this means that we aren't guaranteeing writes to a caller of
an ast_db_ function, but that is just the way things go I suppose.

We dropped from being 5x slower than BDB to 4x. Wheeee!

Modified:
    team/twilson/sqlite_astdb/include/asterisk/astdb.h
    team/twilson/sqlite_astdb/main/db.c
    team/twilson/sqlite_astdb/tests/test_db.c

Modified: team/twilson/sqlite_astdb/include/asterisk/astdb.h
URL: http://svnview.digium.com/svn/asterisk/team/twilson/sqlite_astdb/include/asterisk/astdb.h?view=diff&rev=323319&r1=323318&r2=323319
==============================================================================
--- team/twilson/sqlite_astdb/include/asterisk/astdb.h (original)
+++ team/twilson/sqlite_astdb/include/asterisk/astdb.h Mon Jun 13 17:52:12 2011
@@ -66,9 +66,6 @@
 /*!\brief Free structure created by ast_db_gettree() */
 void ast_db_freetree(struct ast_db_entry *entry);
 
-int ast_db_begin_transaction(void);
-int ast_db_commit_transaction(void);
-int ast_db_rollback_transaction(void);
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif

Modified: team/twilson/sqlite_astdb/main/db.c
URL: http://svnview.digium.com/svn/asterisk/team/twilson/sqlite_astdb/main/db.c?view=diff&rev=323319&r1=323318&r2=323319
==============================================================================
--- team/twilson/sqlite_astdb/main/db.c (original)
+++ team/twilson/sqlite_astdb/main/db.c Mon Jun 13 17:52:12 2011
@@ -100,17 +100,51 @@
  ***/
 
 #define MAX_DB_FIELD 256
-
-/* Give each thread their own DB connection
- * Some functions like sqlite3_errmsg and sqlite_changes can be stepped on
- * by other threads. SQLite can handle the threading on its own if we have
- * a connection per thread, so we just create thread-local DB connections.
- */
-static int db_init(void *astdb);
-static void db_close(void *astdb);
-AST_THREADSTORAGE_CUSTOM(astdb_ts, db_init, db_close);
-
-static int db_open(sqlite3 **astdb)
+AST_MUTEX_DEFINE_STATIC(dblock);
+static ast_cond_t dbcond;
+static sqlite3 *astdb;
+
+static void db_sync(void);
+
+#define DEFINE_SQL_STATEMENT(stmt,sql) static sqlite3_stmt *stmt; \
+	const char stmt##_sql[] = sql;
+
+DEFINE_SQL_STATEMENT(put_stmt, "INSERT OR REPLACE INTO astdb (key, value) VALUES (?, ?)")
+DEFINE_SQL_STATEMENT(get_stmt, "SELECT value FROM astdb WHERE key=?")
+DEFINE_SQL_STATEMENT(del_stmt, "DELETE FROM astdb WHERE key=?")
+DEFINE_SQL_STATEMENT(deltree_stmt, "DELETE FROM astdb WHERE key LIKE ? || '/' || '%'")
+DEFINE_SQL_STATEMENT(deltree_all_stmt, "DELETE FROM astdb")
+DEFINE_SQL_STATEMENT(gettree_stmt, "SELECT key, value FROM astdb WHERE key LIKE ? || '/' || '%'")
+DEFINE_SQL_STATEMENT(gettree_all_stmt, "SELECT key, value FROM astdb")
+DEFINE_SQL_STATEMENT(showkey_stmt, "SELECT key, value FROM astdb WHERE key LIKE '%' || ?")
+DEFINE_SQL_STATEMENT(create_astdb_stmt, "CREATE TABLE IF NOT EXISTS astdb(key VARCHAR(256), value VARCHAR(256), PRIMARY KEY(key))")
+
+static int init_stmt(sqlite3_stmt **stmt, const char *sql, size_t len)
+{
+	ast_mutex_lock(&dblock);
+	if (sqlite3_prepare_v2(astdb, sql, len, stmt, NULL) != SQLITE_OK) {
+		ast_log(LOG_WARNING, "Couldn't prepare statement '%s': %s\n", sql, sqlite3_errmsg(astdb));
+		ast_mutex_unlock(&dblock);
+		return -1;
+	}
+	ast_mutex_unlock(&dblock);
+
+	return 0;
+}
+
+static int init_statements(void)
+{
+	return init_stmt(&get_stmt, get_stmt_sql, sizeof(get_stmt_sql))
+	|| init_stmt(&del_stmt, del_stmt_sql, sizeof(del_stmt_sql))
+	|| init_stmt(&deltree_stmt, deltree_stmt_sql, sizeof(deltree_stmt_sql))
+	|| init_stmt(&deltree_all_stmt, deltree_all_stmt_sql, sizeof(deltree_all_stmt_sql))
+	|| init_stmt(&gettree_stmt, gettree_stmt_sql, sizeof(gettree_stmt_sql))
+	|| init_stmt(&gettree_all_stmt, gettree_all_stmt_sql, sizeof(gettree_all_stmt_sql))
+	|| init_stmt(&showkey_stmt, showkey_stmt_sql, sizeof(showkey_stmt_sql))
+	|| init_stmt(&put_stmt, put_stmt_sql, sizeof(put_stmt_sql));
+}
+
+static int db_open(void)
 {
 	char *dbname;
 	if (!(dbname = alloca(strlen(ast_config_AST_DB) + sizeof(".sqlite3")))) {
@@ -118,381 +152,243 @@
 	}
 	strcpy(dbname, ast_config_AST_DB);
 	strcat(dbname, ".sqlite3");
-	if (sqlite3_open_v2(dbname, astdb, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL) != SQLITE_OK) {
-		ast_log(LOG_WARNING, "Unable to open Asterisk database '%s': %s\n", "/var/lib/asterisk/astdb.sqlite3", sqlite3_errmsg(*astdb));
-		sqlite3_close(*astdb);
+
+	ast_mutex_lock(&dblock);
+	if (sqlite3_open_v2(dbname, &astdb, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX, NULL) != SQLITE_OK) {
+		ast_log(LOG_WARNING, "Unable to open Asterisk database '%s': %s\n", dbname, sqlite3_errmsg(astdb));
+		sqlite3_close(astdb);
+		ast_mutex_unlock(&dblock);
 		return -1;
 	}
+	ast_mutex_unlock(&dblock);
+
 	return 0;
 }
 
-static int db_create_astdb(sqlite3 *astdb)
-{
-	static sqlite3_stmt *createStmt = NULL;
-	static const char createSQL[] =
-		"CREATE TABLE IF NOT EXISTS astdb("
-		"	key VARCHAR(256),"
-		"	value VARCHAR(256),"
-		"	PRIMARY KEY(key))";
-	static int created = 0;
-
-	if (created) {
+static int db_create_astdb(void)
+{
+	int res = 0;
+
+	if (!create_astdb_stmt) {
+		init_stmt(&create_astdb_stmt, create_astdb_stmt_sql, sizeof(create_astdb_stmt_sql));
+	}
+
+	ast_mutex_lock(&dblock);
+	if (sqlite3_step(create_astdb_stmt) != SQLITE_DONE) {
+		ast_log(LOG_WARNING, "Couldn't create astdb table: %s\n", sqlite3_errmsg(astdb));
+		res = -1;
+	}
+	sqlite3_reset(create_astdb_stmt);
+	db_sync();
+	ast_mutex_unlock(&dblock);
+
+	return res;
+}
+
+static int db_init(void) 
+{
+	if (astdb) {
 		return 0;
 	}
-	if (!createStmt) {
-		if (sqlite3_prepare_v2(astdb, createSQL, sizeof(createSQL), &createStmt, NULL) != SQLITE_OK) {
-			ast_log(LOG_WARNING, "Couldn't prepare statement to create astdb table: %s\n", sqlite3_errmsg(astdb));
-			return -1;
-		}
-	}
-
-	if (sqlite3_step(createStmt) != SQLITE_DONE) {
-		ast_log(LOG_WARNING, "Couldn't create astdb table: %s\n", sqlite3_errmsg(astdb));
-		sqlite3_reset(createStmt);
+
+	if (db_open() || db_create_astdb() || init_statements()) {
 		return -1;
 	}
 
-	sqlite3_reset(createStmt);
-	created = 1;
 	return 0;
 }
 
-static int db_init(void *data) 
-{
-	struct sqlite3 **db = data;
-	if (db_open(db)) {
-		return -1;
-	}
-	if (db_create_astdb(*db)) {
-		return -1;
-	}
-
-	return 0;
-}
-
-static void db_close(void *data)
-{
-	sqlite3 **astdb = data;
-
-	sqlite3_close(*astdb);
-}
-
-static sqlite3 *get_db_ts(void)
-{
-	sqlite3 **astdb;
-	if (!(astdb = ast_threadstorage_get(&astdb_ts, sizeof(astdb)))) {
-		return NULL;
-	}
-	return *astdb;
-}
-
-/* This is currently only useful for operations that don't return rows */
-static int db_execute_sql(const char *sql)
-{
-	sqlite3 *astdb;
+/* This is currently only useful for operations that don't
+ * return rows and that shouldn't affect syncing--i.e. transaction calls.
+ * We purposely don't lock around the sqlite3 call because the transaction
+ * calls will be called with the database lock held. */
+static int db_execute_transaction_sql(const char *sql)
+{
 	char *errmsg = NULL;
-	if (!(astdb = get_db_ts())) {
-		return -1;
-	}
+	int res =0;
+
 	sqlite3_exec(astdb, sql, NULL, NULL, &errmsg);
 	if (errmsg) {
 		ast_log(LOG_WARNING, "Error executing SQL: %s\n", errmsg);
-		return -1;
-	}
-
-	return 0;
-}
-int ast_db_begin_transaction(void)
-{
-	return db_execute_sql("BEGIN TRANSACTION");
-}
-
-int ast_db_commit_transaction(void)
-{
-	return db_execute_sql("COMMIT");
-}
-
-int ast_db_rollback_transaction(void)
-{
-	return db_execute_sql("ROLLBACK");
-}
-
-static int init_stmt(sqlite3_stmt **stmt, const char *sql, size_t len)
-{
-	sqlite3 *astdb;
-
-	if (!(astdb = get_db_ts())) {
-		ast_log(LOG_WARNING, "Couldn't get thread local db connection\n");
-		return -1;
-	}
-
-	if (sqlite3_prepare_v2(astdb, sql, len, stmt, NULL) != SQLITE_OK) {
-		ast_log(LOG_WARNING, "Couldn't prepare statement to create put data: %s\n", sqlite3_errmsg(astdb));
-		return -1;
-	}
-	return 0;
-}
-
-static void finalize_stmt(void *data)
-{
-	sqlite3_stmt **putStmt = data;
-	sqlite3_finalize(*putStmt);
-}
-
-#define STMT_THREADSTORAGE(name,sql) static int __xinit_##name(void *__data) \
-{ \
-	sqlite3_stmt **__stmt = __data; \
-	static const char __sql[] = sql; \
-	return init_stmt(__stmt, __sql, sizeof(__sql)); \
-} \
-AST_THREADSTORAGE_CUSTOM(name, __xinit_##name, finalize_stmt)
-
-STMT_THREADSTORAGE(put_stmt,"INSERT OR REPLACE INTO astdb (key, value) VALUES (?, ?)");
-STMT_THREADSTORAGE(get_stmt, "SELECT value FROM astdb WHERE key=?");
-STMT_THREADSTORAGE(del_stmt, "DELETE FROM astdb WHERE key=?");
-STMT_THREADSTORAGE(deltree_stmt, "DELETE FROM astdb WHERE key LIKE ? || '/' || '%'");
-STMT_THREADSTORAGE(deltree_all_stmt, "DELETE FROM astdb");
-STMT_THREADSTORAGE(gettree_stmt, "SELECT key, value FROM astdb WHERE key LIKE ? || '/' || '%'");
-STMT_THREADSTORAGE(gettree_all_stmt, "SELECT key, value FROM astdb");
-STMT_THREADSTORAGE(showkey_stmt, "SELECT key, value FROM astdb WHERE key LIKE '%' || ?");
-
-static sqlite3_stmt *get_stmt_ts(struct ast_threadstorage *stmt_ts)
-{
-	sqlite3_stmt **stmt;
-	if (!(stmt = ast_threadstorage_get(stmt_ts, sizeof(stmt)))) {
-		return NULL;
-	}
-	return *stmt;
+		res = -1;
+	}
+
+	return res;
+}
+
+static int ast_db_begin_transaction(void)
+{
+	return db_execute_transaction_sql("BEGIN TRANSACTION");
+}
+
+static int ast_db_commit_transaction(void)
+{
+	return db_execute_transaction_sql("COMMIT");
+}
+
+static int ast_db_rollback_transaction(void)
+{
+	return db_execute_transaction_sql("ROLLBACK");
 }
 
 int ast_db_put(const char *family, const char *key, const char *value)
 {
-	static sqlite3_stmt *putStmt;
 	char fullkey[MAX_DB_FIELD];
-	sqlite3 *astdb;
-
-	if (!(putStmt = get_stmt_ts(&put_stmt))) {
-		return -1;
-	}
-
-	if (strlen(family) + strlen(key) + 2 > sizeof(fullkey) -1) {
+	size_t fullkey_len;
+	int res = 0;
+
+	if (strlen(family) + strlen(key) + 2 > sizeof(fullkey) - 1) {
 		ast_log(LOG_WARNING, "Family and key length must be less than %zu bytes\n", sizeof(fullkey) - 3);
 		return -1;
 	}
 
-	snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, key);
-
-	if (!(astdb = get_db_ts())) {
-		ast_log(LOG_WARNING, "Couldn't get thread local db connection\n");
-		return -1;
-	}
-
-	if (sqlite3_bind_text(putStmt, 1, fullkey, -1, SQLITE_STATIC) != SQLITE_OK) {
+	fullkey_len = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, key);
+
+	ast_mutex_lock(&dblock);
+	if (sqlite3_bind_text(put_stmt, 1, fullkey, fullkey_len, SQLITE_STATIC) != SQLITE_OK) {
 		ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(astdb));
-		sqlite3_reset(putStmt);
-		return -1;
-	}
-
-	if (sqlite3_bind_text(putStmt, 2, value, -1, SQLITE_STATIC) != SQLITE_OK) {
+		res = -1;
+	} else if (sqlite3_bind_text(put_stmt, 2, value, -1, SQLITE_STATIC) != SQLITE_OK) {
 		ast_log(LOG_WARNING, "Couldn't bind value to stmt: %s\n", sqlite3_errmsg(astdb));
-		sqlite3_reset(putStmt);
-		return -1;
-	}
-
-	if (sqlite3_step(putStmt) != SQLITE_DONE) {
-		ast_log(LOG_WARNING, "Couldn't create execute statment: %s\n", sqlite3_errmsg(astdb));
-		sqlite3_reset(putStmt);
-		return -1;
-	}
-
-	sqlite3_reset(putStmt);
-
-	return 0;
+		res = -1;
+	} else if (sqlite3_step(put_stmt) != SQLITE_DONE) {
+		ast_log(LOG_WARNING, "Couldn't execute statment: %s\n", sqlite3_errmsg(astdb));
+		res = -1;
+	}
+	sqlite3_reset(put_stmt);
+	ast_mutex_unlock(&dblock);
+
+	return res; 
 }
 
 int ast_db_get(const char *family, const char *key, char *value, int valuelen)
 {
-	static sqlite3_stmt *getStmt;
 	const unsigned char *result;
 	char fullkey[MAX_DB_FIELD];
-	sqlite3 *astdb;
-
-	if (!(getStmt = get_stmt_ts(&get_stmt))) {
-		return -1;
-	}
-
-	if (!(astdb = get_db_ts())) {
-		ast_log(LOG_WARNING, "Couldn't get thread local db connection\n");
-		return -1;
-	}
-
-	if (strlen(family) + strlen(key) + 2 > sizeof(fullkey) -1) {
+	size_t fullkey_len;
+	int res = 0;
+
+	if (strlen(family) + strlen(key) + 2 > sizeof(fullkey) - 1) {
 		ast_log(LOG_WARNING, "Family and key length must be less than %zu bytes\n", sizeof(fullkey) - 3);
 		return -1;
 	}
 
-	snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, key);
-
-	if (sqlite3_bind_text(getStmt, 1, fullkey, -1, SQLITE_STATIC) != SQLITE_OK) {
+	fullkey_len = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, key);
+
+	ast_mutex_lock(&dblock);
+	if (sqlite3_bind_text(get_stmt, 1, fullkey, fullkey_len, SQLITE_STATIC) != SQLITE_OK) {
 		ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(astdb));
-		sqlite3_reset(getStmt);
-		return -1;
-	}
-
-	if (sqlite3_step(getStmt) != SQLITE_ROW) {
+		res = -1;
+	} else if (sqlite3_step(get_stmt) != SQLITE_ROW) {
 		ast_debug(1, "Unable to find key '%s' in family '%s'\n", key, family);
-		sqlite3_reset(getStmt);
-		return -1;
-	}
-
-	if ((result = sqlite3_column_text(getStmt, 0))) {
+		res = -1;
+	} else if (!(result = sqlite3_column_text(get_stmt, 0))) {
+		ast_log(LOG_WARNING, "Couldn't get value\n");
+		res = -1;
+	} else {
 		strncpy(value, (const char *) result, valuelen);
-	} else {
-		ast_log(LOG_WARNING, "Couldn't get value\n");
-		sqlite3_reset(getStmt);
-		return -1;
-	}
-
-	sqlite3_reset(getStmt);
-
-	return 0;
+	}
+	sqlite3_reset(get_stmt);
+	ast_mutex_unlock(&dblock);
+
+	return res;
 }
 
 int ast_db_del(const char *family, const char *key)
 {
-	static sqlite3_stmt *delStmt;
 	char fullkey[MAX_DB_FIELD];
-	sqlite3 *astdb;
-
-	if (!(delStmt = get_stmt_ts(&del_stmt))) {
-		return -1;
-	}
-
-	if (!(astdb = get_db_ts())) {
-		ast_log(LOG_WARNING, "Couldn't get thread local db connection\n");
-		return -1;
-	}
-
-	if (strlen(family) + strlen(key) + 2 > sizeof(fullkey) -1) {
+	size_t fullkey_len;
+	int res = 0;
+
+	if (strlen(family) + strlen(key) + 2 > sizeof(fullkey) - 1) {
 		ast_log(LOG_WARNING, "Family and key length must be less than %zu bytes\n", sizeof(fullkey) - 3);
 		return -1;
 	}
 
-	snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, key);
-
-	if (sqlite3_bind_text(delStmt, 1, fullkey, -1, SQLITE_STATIC) != SQLITE_OK) {
+	fullkey_len = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, key);
+
+	ast_mutex_lock(&dblock);
+	if (sqlite3_bind_text(del_stmt, 1, fullkey, fullkey_len, SQLITE_STATIC) != SQLITE_OK) {
 		ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(astdb));
-		sqlite3_reset(delStmt);
-		return -1;
-	}
-
-	if (sqlite3_step(delStmt) != SQLITE_DONE) {
+		res = -1;
+	} else if (sqlite3_step(del_stmt) != SQLITE_DONE) {
 		ast_debug(1, "Unable to find key '%s' in family '%s'\n", key, family);
-		sqlite3_reset(delStmt);
-		return -1;
-	}
-
-	sqlite3_reset(delStmt);
-
-	return 0;
+		res = -1;
+	}
+	sqlite3_reset(del_stmt);
+	ast_mutex_unlock(&dblock);
+
+	return res;
 }
 
 int ast_db_deltree(const char *family, const char *subfamily)
 {
-	sqlite3_stmt *deltreeStmt;
+	sqlite3_stmt *stmt = deltree_stmt;
 	char prefix[MAX_DB_FIELD];
-	sqlite3 *astdb;
-
-	if (!(astdb = get_db_ts())) {
-		ast_log(LOG_WARNING, "Couldn't get thread local db connection\n");
-		return -1;
-	}
-
-	if (!(ast_strlen_zero(family) || ast_strlen_zero(subfamily))) {
-		snprintf(prefix, sizeof(prefix), "/%s/%s", family, subfamily);
-		if (!(deltreeStmt = get_stmt_ts(&deltree_stmt))) {
-			return -1;
-		}
-	} else if (!ast_strlen_zero(family)) {
-		snprintf(prefix, sizeof(prefix), "/%s", family);
-		if (!(deltreeStmt = get_stmt_ts(&deltree_stmt))) {
-			return -1;
-		}
-	} else if (!ast_strlen_zero(subfamily)) {
-		ast_log(LOG_WARNING, "Can't have just a subfamily\n");
-		return -1;
-	} else {
-		prefix[0] = '\0';
-		if (!(deltreeStmt = get_stmt_ts(&deltree_all_stmt))) {
-			return -1;
-		}
-	}
-
-	if (!ast_strlen_zero(prefix)) {
-		if (sqlite3_bind_text(deltreeStmt, 1, prefix, -1, SQLITE_STATIC) != SQLITE_OK) {
-			ast_log(LOG_WARNING, "Could bind %s to stmt: %s\n", prefix, sqlite3_errmsg(astdb));
-			sqlite3_reset(deltreeStmt);
-			return -1;
-		}
-	}
-
-	if (sqlite3_step(deltreeStmt) != SQLITE_DONE) {
-		ast_log(LOG_WARNING, "Couldn't execute stmt: %s\n", sqlite3_errmsg(astdb));
-		sqlite3_reset(deltreeStmt);
-		return -1;
-	}
-
-	sqlite3_reset(deltreeStmt);
-	return sqlite3_changes(astdb);;
-}
-
-struct ast_db_entry *ast_db_gettree(const char *family, const char *subfamily)
-{
-	char prefix[MAX_DB_FIELD];
-	sqlite3_stmt *gettreeStmt;
-	struct ast_db_entry *cur, *last = NULL, *ret = NULL;
-	sqlite3 *astdb;
-
-	if (!(astdb = get_db_ts())) {
-		ast_log(LOG_WARNING, "Couldn't get thread local db connection\n");
-		return NULL;
-	}
+	int res = 0;
+
 
 	if (!ast_strlen_zero(family)) {
 		if (!ast_strlen_zero(subfamily)) {
 			/* Family and key tree */
 			snprintf(prefix, sizeof(prefix), "/%s/%s", family, subfamily);
-			if (!(gettreeStmt = get_stmt_ts(&gettree_stmt))) {
-				return NULL;
-			}
 		} else {
 			/* Family only */
 			snprintf(prefix, sizeof(prefix), "/%s", family);
-			if (!(gettreeStmt = get_stmt_ts(&gettree_stmt))) {
-				return NULL;
-			}
 		}
 	} else {
 		prefix[0] = '\0';
-		if (!(gettreeStmt = get_stmt_ts(&gettree_all_stmt))) {
-			return NULL;
-		}
-	}
-
-	if (!ast_strlen_zero(prefix)) {
-		if (sqlite3_bind_text(gettreeStmt, 1, prefix, -1, SQLITE_STATIC) != SQLITE_OK) {
-			ast_log(LOG_WARNING, "Could bind %s to stmt: %s\n", prefix, sqlite3_errmsg(astdb));
-			sqlite3_reset(gettreeStmt);
-			return NULL;
-		}
-	}
-
-	while (sqlite3_step(gettreeStmt) == SQLITE_ROW) {
+		stmt = deltree_all_stmt;
+	}
+
+	ast_mutex_lock(&dblock);
+	if (!ast_strlen_zero(prefix) && (sqlite3_bind_text(stmt, 1, prefix, -1, SQLITE_STATIC) != SQLITE_OK)) {
+		ast_log(LOG_WARNING, "Could bind %s to stmt: %s\n", prefix, sqlite3_errmsg(astdb));
+		res = -1;
+	} else if (sqlite3_step(stmt) != SQLITE_DONE) {
+		ast_log(LOG_WARNING, "Couldn't execute stmt: %s\n", sqlite3_errmsg(astdb));
+		res = -1;
+	}
+	res = sqlite3_changes(astdb);
+	sqlite3_reset(stmt);
+	ast_mutex_unlock(&dblock);
+
+	return res;
+}
+
+struct ast_db_entry *ast_db_gettree(const char *family, const char *subfamily)
+{
+	char prefix[MAX_DB_FIELD];
+	sqlite3_stmt *stmt = gettree_stmt;
+	struct ast_db_entry *cur, *last = NULL, *ret = NULL;
+
+	if (!ast_strlen_zero(family)) {
+		if (!ast_strlen_zero(subfamily)) {
+			/* Family and key tree */
+			snprintf(prefix, sizeof(prefix), "/%s/%s", family, subfamily);
+		} else {
+			/* Family only */
+			snprintf(prefix, sizeof(prefix), "/%s", family);
+		}
+	} else {
+		prefix[0] = '\0';
+		stmt = gettree_all_stmt;
+	}
+
+	ast_mutex_lock(&dblock);
+	if (!ast_strlen_zero(prefix) && (sqlite3_bind_text(stmt, 1, prefix, -1, SQLITE_STATIC) != SQLITE_OK)) {
+		ast_log(LOG_WARNING, "Could bind %s to stmt: %s\n", prefix, sqlite3_errmsg(astdb));
+		sqlite3_reset(stmt);
+		ast_mutex_unlock(&dblock);
+		return NULL;
+	}
+
+	while (sqlite3_step(stmt) == SQLITE_ROW) {
 		const char *key_s, *value_s;
-		if (!(key_s = (const char *) sqlite3_column_text(gettreeStmt, 0))) {
+		if (!(key_s = (const char *) sqlite3_column_text(stmt, 0))) {
 			break;
 		}
-		if (!(value_s = (const char *) sqlite3_column_text(gettreeStmt, 1))) {
+		if (!(value_s = (const char *) sqlite3_column_text(stmt, 1))) {
 			break;
 		}
 		if (!(cur = ast_malloc(sizeof(*cur) + strlen(key_s) + strlen(value_s) + 2))) {
@@ -509,8 +405,9 @@
 		}
 		last = cur;
 	}
-
-	sqlite3_reset(gettreeStmt);
+	sqlite3_reset(stmt);
+	ast_mutex_unlock(&dblock);
+
 	return ret;
 }
 
@@ -641,7 +538,6 @@
 {
 	char prefix[MAX_DB_FIELD];
 	int counter = 0;
-	sqlite3_stmt *gettreeStmt;
 
 	switch (cmd) {
 	case CLI_INIT:
@@ -668,23 +564,21 @@
 		return CLI_SHOWUSAGE;
 	}
 
-	if (!(gettreeStmt = get_stmt_ts(&gettree_all_stmt))) {
-		return CLI_SUCCESS;
-	}
-
-	while (sqlite3_step(gettreeStmt) == SQLITE_ROW) {
+	ast_mutex_lock(&dblock);
+	while (sqlite3_step(gettree_stmt) == SQLITE_ROW) {
 		const char *key_s, *value_s;
-		if (!(key_s = (const char *) sqlite3_column_text(gettreeStmt, 0))) {
+		if (!(key_s = (const char *) sqlite3_column_text(gettree_stmt, 0))) {
 			break;
 		}
-		if (!(value_s = (const char *) sqlite3_column_text(gettreeStmt, 1))) {
+		if (!(value_s = (const char *) sqlite3_column_text(gettree_stmt, 1))) {
 			break;
 		}
 		++counter;
 		ast_cli(a->fd, "%-50s: %-25s\n", key_s, value_s);
 	}
 
-	sqlite3_reset(gettreeStmt);
+	sqlite3_reset(gettree_stmt);
+	ast_mutex_unlock(&dblock);
 
 	ast_cli(a->fd, "%d results found.\n", counter);
 	return CLI_SUCCESS;
@@ -694,7 +588,6 @@
 {
 	char suffix[MAX_DB_FIELD];
 	int counter = 0;
-	sqlite3_stmt *showkeyStmt;
 
 	switch (cmd) {
 	case CLI_INIT:
@@ -714,31 +607,27 @@
 		return CLI_SHOWUSAGE;
 	}
 
-	if (!(showkeyStmt = get_stmt_ts(&showkey_stmt))) {
-		return CLI_SUCCESS;
-	}
-
-	if (!ast_strlen_zero(suffix)) {
-		if (sqlite3_bind_text(showkeyStmt, 1, suffix, -1, SQLITE_STATIC) != SQLITE_OK) {
-			ast_log(LOG_WARNING, "Could bind %s to stmt: %s\n", suffix, sqlite3_errmsg(sqlite3_db_handle(showkeyStmt)));
-			sqlite3_reset(showkeyStmt);
-			return NULL;
-		}
-	}
-
-	while (sqlite3_step(showkeyStmt) == SQLITE_ROW) {
+	ast_mutex_lock(&dblock);
+	if (!ast_strlen_zero(suffix) && (sqlite3_bind_text(showkey_stmt, 1, suffix, -1, SQLITE_STATIC) != SQLITE_OK)) {
+		ast_log(LOG_WARNING, "Could bind %s to stmt: %s\n", suffix, sqlite3_errmsg(astdb));
+		sqlite3_reset(showkey_stmt);
+		ast_mutex_unlock(&dblock);
+		return NULL;
+	}
+
+	while (sqlite3_step(showkey_stmt) == SQLITE_ROW) {
 		const char *key_s, *value_s;
-		if (!(key_s = (const char *) sqlite3_column_text(showkeyStmt, 0))) {
+		if (!(key_s = (const char *) sqlite3_column_text(showkey_stmt, 0))) {
 			break;
 		}
-		if (!(value_s = (const char *) sqlite3_column_text(showkeyStmt, 1))) {
+		if (!(value_s = (const char *) sqlite3_column_text(showkey_stmt, 1))) {
 			break;
 		}
 		++counter;
 		ast_cli(a->fd, "%-50s: %-25s\n", key_s, value_s);
 	}
-
-	sqlite3_reset(showkeyStmt);
+	sqlite3_reset(showkey_stmt);
+	ast_mutex_unlock(&dblock);
 
 	ast_cli(a->fd, "%d results found.\n", counter);
 	return CLI_SUCCESS;
@@ -868,9 +757,59 @@
 	return 0;
 }
 
+/*!
+ * \internal
+ * \brief Signal the astdb sync thread to do its thing.
+ *
+ * \note dblock is assumed to be held when calling this function.
+ */
+static void db_sync(void)
+{
+	ast_cond_signal(&dbcond);
+}
+
+/*!
+ * \internal
+ * \brief astdb sync thread
+ *
+ * This thread is in charge of syncing astdb to disk after a change.
+ * By pushing it off to this thread to take care of, this I/O bound operation
+ * will not block other threads from performing other critical processing.
+ * If changes happen rapidly, this thread will also ensure that the sync
+ * operations are rate limited.
+ */
+static void *db_sync_thread(void *data)
+{
+	ast_mutex_lock(&dblock);
+	ast_db_begin_transaction();
+	for (;;) {
+		/* We're ok with spurious wakeups, so we don't worry about a predicate */
+		ast_cond_wait(&dbcond, &dblock);
+		if (ast_db_commit_transaction()) {
+			ast_db_rollback_transaction();
+		}
+		ast_mutex_unlock(&dblock);
+		sleep(1);
+		ast_mutex_lock(&dblock);
+		ast_db_begin_transaction();
+	}
+
+	return NULL;
+}
+
 int astdb_init(void)
 {
-	sqlite3_config(SQLITE_CONFIG_SERIALIZED);
+	pthread_t dont_care;
+
+	ast_cond_init(&dbcond, NULL);
+	if (ast_pthread_create_background(&dont_care, NULL, db_sync_thread, NULL)) {
+		return -1;
+	}
+
+	sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
+	if (db_init()) {
+		return -1;
+	}
 	ast_cli_register_multiple(cli_database, ARRAY_LEN(cli_database));
 	ast_manager_register_xml("DBGet", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_dbget);
 	ast_manager_register_xml("DBPut", EVENT_FLAG_SYSTEM, manager_dbput);

Modified: team/twilson/sqlite_astdb/tests/test_db.c
URL: http://svnview.digium.com/svn/asterisk/team/twilson/sqlite_astdb/tests/test_db.c?view=diff&rev=323319&r1=323318&r2=323319
==============================================================================
--- team/twilson/sqlite_astdb/tests/test_db.c (original)
+++ team/twilson/sqlite_astdb/tests/test_db.c Mon Jun 13 17:52:12 2011
@@ -223,12 +223,10 @@
 		break;
 	}
 
-	ast_db_begin_transaction();
 	for (x = 0; x < 100000; x++) {
 		sprintf(buf, "%zu", x);
 		ast_db_put("astdbtest", buf, buf);
 	}
-	ast_db_commit_transaction();
 	ast_db_deltree("astdbtest", NULL);
 
 	return res;




More information about the asterisk-commits mailing list