summaryrefslogtreecommitdiffstats
path: root/libglusterfs/src/gfdb/gfdb_data_store.c
diff options
context:
space:
mode:
Diffstat (limited to 'libglusterfs/src/gfdb/gfdb_data_store.c')
-rw-r--r--libglusterfs/src/gfdb/gfdb_data_store.c649
1 files changed, 649 insertions, 0 deletions
diff --git a/libglusterfs/src/gfdb/gfdb_data_store.c b/libglusterfs/src/gfdb/gfdb_data_store.c
new file mode 100644
index 00000000000..b250ece91e4
--- /dev/null
+++ b/libglusterfs/src/gfdb/gfdb_data_store.c
@@ -0,0 +1,649 @@
+/*
+ Copyright (c) 2015 Red Hat, Inc. <http://www.redhat.com>
+ This file is part of GlusterFS.
+
+ This file is licensed to you under your choice of the GNU Lesser
+ General Public License, version 3 or any later version (LGPLv3 or
+ later), or the GNU General Public License, version 2 (GPLv2), in all
+ cases as published by the Free Software Foundation.
+*/
+
+#include "gfdb_data_store.h"
+#include "list.h"
+
+/******************************************************************************
+ *
+ * Database Connection utils/internals
+ *
+ * ****************************************************************************/
+
+/* GFDB Connection Node:
+ * ~~~~~~~~~~~~~~~~~~~~
+ * Represents the connection to the database while using libgfdb
+ * The connection node is not thread safe as far as fini_db is concerned.
+ * You can use a single connection node
+ * to do multithreaded db operations like insert/delete/find of records.
+ * But you need to wait for all the operating threads to complete i.e
+ * pthread_join() and then do fini_db() to kill the connection node.
+ * gfdb_conn_node_t is an opaque structure.
+ * */
+struct gfdb_conn_node_t {
+ gfdb_connection_t gfdb_connection;
+ struct list_head conn_list;
+};
+
+
+/*
+ * db_conn_list is the circular linked list which
+ * will have all the database connections for the process
+ *
+ * */
+static gfdb_conn_node_t *db_conn_list;
+
+/*
+ * db_conn_mutex is the mutex for db_conn_list
+ *
+ * */
+static pthread_mutex_t db_conn_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+
+/*Checks the sanity of the connection node*/
+#define CHECK_CONN_NODE(_conn_node)\
+do {\
+ GF_ASSERT (_conn_node);\
+ GF_ASSERT (_conn_node->gfdb_connection.gf_db_connection);\
+} while (0)
+
+
+/*Check if the conn node is first in the list*/
+#define IS_FIRST_NODE(db_conn_list, _conn_node)\
+ ((_conn_node == db_conn_list) ? _gf_true : _gf_false)
+
+
+/*Check if the conn node is the only node in the list*/
+#define IS_THE_ONLY_NODE(_conn_node)\
+((_conn_node->conn_list.next == _conn_node->conn_list.prev)\
+ ? _gf_true : _gf_false)
+
+
+
+/*Internal Function: Adds connection node to the end of
+ * the db connection list.*/
+static int
+add_connection_node (gfdb_conn_node_t *_conn_node) {
+ int ret = -1;
+
+ GF_ASSERT (_conn_node);
+
+ /*Lock the list*/
+ ret = pthread_mutex_lock (&db_conn_mutex);
+ if (ret) {
+ gf_log (GFDB_DATA_STORE, GF_LOG_ERROR,
+ "Failed lock db connection list %s", strerror(ret));
+ ret = -1;
+ goto out;
+ }
+
+ if (db_conn_list == NULL) {
+ db_conn_list = _conn_node;
+ } else {
+ list_add_tail (&_conn_node->conn_list,
+ &db_conn_list->conn_list);
+ }
+
+ /*unlock the list*/
+ ret = pthread_mutex_unlock (&db_conn_mutex);
+ if (ret) {
+ gf_log (GFDB_DATA_STORE, GF_LOG_ERROR,
+ "Failed unlock db connection list %s", strerror(ret));
+ ret = -1;
+ /*TODO What if the unlock fails.
+ * Will it lead to deadlock?
+ * Most of the gluster code
+ * no check for unlock or destory of mutex!*/
+ }
+ ret = 0;
+out:
+ return ret;
+}
+
+
+/*Internal Function:
+ * Delete connection node from the list*/
+static inline int
+delete_conn_node (gfdb_conn_node_t *_conn_node)
+{
+ int ret = -1;
+
+ GF_ASSERT (_conn_node);
+
+ /*Lock of the list*/
+ ret = pthread_mutex_lock (&db_conn_mutex);
+ if (ret) {
+ gf_log (GFDB_DATA_STORE, GF_LOG_ERROR,
+ "Failed lock on db connection list %s", strerror(ret));
+ goto out;
+ }
+
+ /*Remove the connection object from list*/
+ if (IS_THE_ONLY_NODE(_conn_node)) {
+ db_conn_list = NULL;
+ GF_FREE (_conn_node);
+ } else {
+ if (IS_FIRST_NODE(db_conn_list, _conn_node)) {
+ db_conn_list = list_entry (db_conn_list->conn_list.next,
+ gfdb_conn_node_t,
+ conn_list);
+ }
+ list_del(&_conn_node->conn_list);
+ GF_FREE (_conn_node);
+ }
+
+ /*Release the list lock*/
+ ret = pthread_mutex_unlock (&db_conn_mutex);
+ if (ret) {
+ gf_log (GFDB_DATA_STORE, GF_LOG_WARNING,
+ "Failed unlock on db connection"
+ " list %s", strerror(ret));
+ /*TODO What if the unlock fails.
+ * Will it lead to deadlock?
+ * Most of the gluster code
+ * no check for unlock or destory of mutex!*/
+ ret = -1;
+ goto out;
+ }
+ ret = 0;
+out:
+ return ret;
+}
+
+
+/*Internal function: Used initialize/map db operation of
+ * specified type of db plugin*/
+static int
+init_db_operations (gfdb_db_type_t gfdb_db_type,
+ gfdb_db_operations_t *gfdb_db_operations) {
+
+ int ret = -1;
+
+ GF_ASSERT (gfdb_db_operations);
+
+ /*Clear the gfdb_db_operations*/
+ gfdb_db_operations = memset(gfdb_db_operations, 0,
+ sizeof(*gfdb_db_operations));
+ switch (gfdb_db_type) {
+ case GFDB_SQLITE3:
+ gf_sqlite3_fill_db_operations (gfdb_db_operations);
+ ret = 0;
+ break;
+ case GFDB_HYPERDEX:
+ case GFDB_HASH_FILE_STORE:
+ case GFDB_ROCKS_DB:
+ gf_log (GFDB_DATA_STORE, GF_LOG_ERROR, "Plugin not supported");
+ break;
+ case GFDB_INVALID_DB:
+ case GFDB_DB_END:
+ gf_log (GFDB_DATA_STORE, GF_LOG_ERROR, "Invalid DB Type");
+ break;
+ }
+ return ret;
+}
+
+
+/******************************************************************************
+ *
+ * LIBGFDB API Functions
+ *
+ * ****************************************************************************/
+
+
+/*Libgfdb API Function: Used to initialize a db connection
+ * (Constructor function for db connection object)
+ * Arguments:
+ * args : Dictionary containing database specific parameters
+ * eg: For sqlite3, pagesize, cachesize, db name, db path
+ etc
+ * gfdb_db_type : Type of data base used i.e sqlite or hyperdex etc
+ * Returns : if successful return the GFDB Connection node to the caller or
+ * NULL in case of failure*/
+gfdb_conn_node_t *
+init_db (dict_t *args, gfdb_db_type_t gfdb_db_type)
+{
+ int ret = -1;
+ gfdb_conn_node_t *_conn_node = NULL;
+ gfdb_db_operations_t *db_operations_t = NULL;
+
+ /*Create data base connection object*/
+ _conn_node = GF_CALLOC (1, sizeof(gfdb_conn_node_t),
+ gf_mt_db_conn_node_t);
+ if (!_conn_node) {
+ gf_log (GFDB_DATA_STORE, GF_LOG_ERROR,
+ "Failed mem alloc for gfdb_conn_node_t!");
+ goto alloc_failed;
+ }
+
+ /*Init the list component of db conneciton object*/
+ INIT_LIST_HEAD (&_conn_node->conn_list);
+
+
+ /*Add created connection node to the list*/
+ ret = add_connection_node (_conn_node);
+ if (ret) {
+ gf_log (GFDB_DATA_STORE, GF_LOG_ERROR,
+ "Failed to add connection node to list");
+ goto _conn_failed;
+ }
+
+ db_operations_t = &_conn_node->gfdb_connection.gfdb_db_operations;
+
+ /*init the db ops object of db connection object*/
+ ret = init_db_operations(gfdb_db_type, db_operations_t);
+ if (ret) {
+ gf_log (GFDB_DATA_STORE, GF_LOG_ERROR,
+ "Failed initializing database operation failed.");
+ ret = -1;
+ goto init_db_failed;
+ }
+
+ /*Calling the init_db_op of the respected db type*/
+ GF_ASSERT (db_operations_t->init_db_op);
+ ret = db_operations_t->init_db_op (args, &_conn_node->gfdb_connection.
+ gf_db_connection);
+ if (ret) {
+ gf_log (GFDB_DATA_STORE, GF_LOG_ERROR,
+ "Failed initializing database");
+ ret = -1;
+ goto init_db_failed;
+ }
+ _conn_node->gfdb_connection.gfdb_db_type = gfdb_db_type;
+ ret = 0;
+
+ return _conn_node;
+
+ /*****Error Handling********/
+ /* If init_db_operations or init_db of plugin failed delete
+ * conn node from the list.
+ * connection node will be free by delete_conn_node*/
+init_db_failed:
+ ret = delete_conn_node (_conn_node);
+ if (ret) {
+ gf_log (GFDB_DATA_STORE, GF_LOG_ERROR,
+ "Failed deleting connection node from list");
+ }
+ return NULL;
+ /*if adding to the list failed free connection node*/
+_conn_failed:
+ GF_FREE (_conn_node);
+ /*if allocation failed*/
+alloc_failed:
+ return NULL;
+ /*****Error Handling********/
+}
+
+
+
+
+
+/*Libgfdb API Function: Used to terminate/de-initialize db connection
+ * (Destructor function for db connection object)
+ * Arguments:
+ * _conn_node : GFDB Connection node
+ * Returns : if successful return 0 or
+ * -ve value in case of failure*/
+int
+fini_db (gfdb_conn_node_t *_conn_node)
+{
+ int ret = -1;
+ gfdb_db_operations_t *db_operations_t = NULL;
+
+ CHECK_CONN_NODE(_conn_node);
+
+ db_operations_t = &_conn_node->gfdb_connection.gfdb_db_operations;
+
+ GF_ASSERT (db_operations_t->fini_db_op);
+
+ ret = db_operations_t->fini_db_op(&_conn_node->gfdb_connection.
+ gf_db_connection);
+ if (ret) {
+ gf_log (GFDB_DATA_STORE, GF_LOG_ERROR,
+ "Failed close the db connection");
+ goto out;
+ }
+
+ ret = delete_conn_node (_conn_node);
+ if (ret) {
+ gf_log (GFDB_DATA_STORE, GF_LOG_ERROR,
+ "Failed deleting connection node from list");
+ }
+
+ ret = 0;
+out:
+ return ret;
+}
+
+
+
+
+
+
+/*Libgfdb API Function: Used to insert/update records in the database
+ * NOTE: In current gfdb_sqlite plugin we use that
+ * same function to delete the record. Set the
+ * gfdb_fop_path to GFDB_FOP_UNDEL to delete the
+ * link of inode from GF_FLINK_TB and
+ * GFDB_FOP_UNDEL_ALL to delete all the records from
+ * GF_FLINK_TB and GF_FILE_TB.
+ * TODO: Should seperate this function into the
+ * delete_record function
+ * Refer CTR Xlator features/changetimerecorder for usage
+ * Arguments:
+ * _conn_node : GFDB Connection node
+ * gfdb_db_record : Record to be inserted/updated
+ * Returns : if successful return 0 or
+ * -ve value in case of failure*/
+int
+insert_record (gfdb_conn_node_t *_conn_node,
+ gfdb_db_record_t *gfdb_db_record)
+{
+ int ret = 0;
+ gfdb_db_operations_t *db_operations_t = NULL;
+ void *gf_db_connection = NULL;
+
+ CHECK_CONN_NODE(_conn_node);
+
+ db_operations_t = &_conn_node->gfdb_connection.gfdb_db_operations;
+ gf_db_connection = _conn_node->gfdb_connection.gf_db_connection;
+
+ if (db_operations_t->insert_record_op) {
+
+ ret = db_operations_t->insert_record_op (gf_db_connection,
+ gfdb_db_record);
+ if (ret) {
+ gf_log (GFDB_DATA_STORE, GF_LOG_ERROR,
+ "Insert/Update operation failed!");
+ }
+ }
+
+ return ret;
+}
+
+
+
+
+/*Libgfdb API Function: Used to delete record from the database
+ * NOTE: In the current gfdb_sqlite3 plugin
+ * implementation this function is dummy.
+ * Use the insert_record function.
+ * Refer CTR Xlator features/changetimerecorder for usage
+ * Arguments:
+ * _conn_node : GFDB Connection node
+ * gfdb_db_record : Record to be deleted
+ * Returns : if successful return 0 or
+ * -ve value in case of failure*/
+int
+delete_record (gfdb_conn_node_t *_conn_node,
+ gfdb_db_record_t *gfdb_db_record)
+{
+ int ret = 0;
+ gfdb_db_operations_t *db_operations_t = NULL;
+ void *gf_db_connection = NULL;
+
+ CHECK_CONN_NODE(_conn_node);
+
+ db_operations_t = &_conn_node->gfdb_connection.gfdb_db_operations;
+ gf_db_connection = _conn_node->gfdb_connection.gf_db_connection;
+
+ if (db_operations_t->delete_record_op) {
+
+ ret = db_operations_t->delete_record_op (gf_db_connection,
+ gfdb_db_record);
+ if (ret) {
+ gf_log (GFDB_DATA_STORE, GF_LOG_ERROR,
+ "Delete operation failed!");
+ }
+
+ }
+
+ return ret;
+}
+
+
+
+
+
+/*Libgfdb API Function: Query all the records from the database
+ * Arguments:
+ * _conn_node : GFDB Connection node
+ * query_callback : Call back function that will be called
+ * for every record found
+ * _query_cbk_args : Custom argument passed for the call back
+ * function query_callback
+ * Returns : if successful return 0 or
+ * -ve value in case of failure*/
+int
+find_all(gfdb_conn_node_t *_conn_node, gf_query_callback_t query_callback,
+ void *_query_cbk_args) {
+ int ret = 0;
+ gfdb_db_operations_t *db_operations_t = NULL;
+ void *gf_db_connection = NULL;
+
+ CHECK_CONN_NODE(_conn_node);
+
+ db_operations_t = &_conn_node->gfdb_connection.gfdb_db_operations;
+ gf_db_connection = _conn_node->gfdb_connection.gf_db_connection;
+
+ if (db_operations_t->find_all_op) {
+ ret = db_operations_t->find_all_op (gf_db_connection,
+ query_callback,
+ _query_cbk_args);
+ if (ret) {
+ gf_log (GFDB_DATA_STORE, GF_LOG_ERROR,
+ "Find all operation failed!");
+ }
+
+ }
+
+ return ret;
+}
+
+
+
+/*Libgfdb API Function: Query records/files that have not changed/accessed
+ * from a time in past to current time
+ * Arguments:
+ * _conn_node : GFDB Connection node
+ * query_callback : Call back function that will be called
+ * for every record found
+ * _query_cbk_args : Custom argument passed for the call back
+ * function query_callback
+ * for_time : Time from where the file/s are not
+ * changed/accessed
+ * Returns : if successful return 0 or
+ * -ve value in case of failure*/
+int
+find_unchanged_for_time(gfdb_conn_node_t *_conn_node,
+ gf_query_callback_t query_callback,
+ void *_query_cbk_args,
+ gfdb_time_t *for_time) {
+
+ int ret = 0;
+ gfdb_db_operations_t *db_operations_t = NULL;
+ void *gf_db_connection = NULL;
+
+ CHECK_CONN_NODE(_conn_node);
+
+ db_operations_t = &_conn_node->gfdb_connection.gfdb_db_operations;
+ gf_db_connection = _conn_node->gfdb_connection.gf_db_connection;
+
+ if (db_operations_t->find_unchanged_for_time_op) {
+
+ ret = db_operations_t->find_unchanged_for_time_op
+ (gf_db_connection,
+ query_callback,
+ _query_cbk_args,
+ for_time);
+ if (ret) {
+ gf_log (GFDB_DATA_STORE, GF_LOG_ERROR,
+ "Find unchanged operation failed!");
+ }
+
+ }
+
+ return ret;
+}
+
+/*Libgfdb API Function: Query records/files that have changed/accessed from a
+ * time in past to current time
+ * Arguments:
+ * _conn_node : GFDB Connection node
+ * query_callback : Call back function that will be called
+ * for every record found
+ * _query_cbk_args : Custom argument passed for the call back
+ * function query_callback
+ * for_time : Time from where the file/s are
+ * changed/accessed
+ * Returns : if successful return 0 or
+ * -ve value in case of failure*/
+int
+find_recently_changed_files(gfdb_conn_node_t *_conn_node,
+ gf_query_callback_t query_callback,
+ void *_query_cbk_args,
+ gfdb_time_t *from_time) {
+
+ int ret = 0;
+ gfdb_db_operations_t *db_operations_t = NULL;
+ void *gf_db_connection = NULL;
+
+ CHECK_CONN_NODE(_conn_node);
+
+ db_operations_t = &_conn_node->gfdb_connection.gfdb_db_operations;
+ gf_db_connection = _conn_node->gfdb_connection.gf_db_connection;
+
+ if (db_operations_t->find_recently_changed_files_op) {
+
+ ret = db_operations_t->find_recently_changed_files_op (
+ gf_db_connection,
+ query_callback,
+ _query_cbk_args,
+ from_time);
+ if (ret) {
+ gf_log (GFDB_DATA_STORE, GF_LOG_ERROR,
+ "Find changed operation failed!");
+ }
+
+ }
+
+ return ret;
+
+}
+
+/*Libgfdb API Function: Query records/files that have not changed/accessed
+ * from a time in past to current time, with
+ * a desired frequency
+ * Arguments:
+ * _conn_node : GFDB Connection node
+ * query_callback : Call back function that will be called
+ * for every record found
+ * _query_cbk_args : Custom argument passed for the call back
+ * function query_callback
+ * for_time : Time from where the file/s are not
+ * changed/accessed
+ * write_freq_thresold : Desired Write Frequency lower limit
+ * read_freq_thresold : Desired Read Frequency lower limit
+ * _clear_counters : If true, Clears all the frequency counters of
+ * all files.
+ * Returns : if successful return 0 or
+ * -ve value in case of failure*/
+int
+find_unchanged_for_time_freq(gfdb_conn_node_t *_conn_node,
+ gf_query_callback_t query_callback,
+ void *_query_cbk_args,
+ gfdb_time_t *for_time,
+ int write_freq_thresold,
+ int read_freq_thresold,
+ gf_boolean_t _clear_counters) {
+
+ int ret = 0;
+ gfdb_db_operations_t *db_operations_t = NULL;
+ void *gf_db_connection = NULL;
+
+ CHECK_CONN_NODE(_conn_node);
+
+ db_operations_t = &_conn_node->gfdb_connection.gfdb_db_operations;
+ gf_db_connection = _conn_node->gfdb_connection.gf_db_connection;
+
+ if (db_operations_t->find_unchanged_for_time_freq_op) {
+
+ ret = db_operations_t->find_unchanged_for_time_freq_op(
+ gf_db_connection,
+ query_callback,
+ _query_cbk_args,
+ for_time,
+ write_freq_thresold,
+ read_freq_thresold,
+ _clear_counters);
+ if (ret) {
+ gf_log (GFDB_DATA_STORE, GF_LOG_ERROR,
+ "Find unchanged with freq operation failed!");
+ }
+
+ }
+
+ return ret;
+}
+
+/*Libgfdb API Function: Query records/files that have changed/accessed from a
+ * time in past to current time, with
+ * a desired frequency
+ * Arguments:
+ * _conn_node : GFDB Connection node
+ * query_callback : Call back function that will be called
+ * for every record found
+ * _query_cbk_args : Custom argument passed for the call back
+ * function query_callback
+ * for_time : Time from where the file/s are
+ * changed/accessed
+ * write_freq_thresold : Desired Write Frequency lower limit
+ * read_freq_thresold : Desired Read Frequency lower limit
+ * _clear_counters : If true, Clears all the frequency counters of
+ * all files.
+ * Returns : if successful return 0 or
+ * -ve value in case of failure*/
+int
+find_recently_changed_files_freq(gfdb_conn_node_t *_conn_node,
+ gf_query_callback_t query_callback,
+ void *_query_cbk_args,
+ gfdb_time_t *from_time,
+ int write_freq_thresold,
+ int read_freq_thresold,
+ gf_boolean_t _clear_counters) {
+
+ int ret = 0;
+ gfdb_db_operations_t *db_operations_t = NULL;
+ void *gf_db_connection = NULL;
+
+ CHECK_CONN_NODE(_conn_node);
+
+ db_operations_t = &_conn_node->gfdb_connection.gfdb_db_operations;
+ gf_db_connection = _conn_node->gfdb_connection.gf_db_connection;
+
+ if (db_operations_t->find_recently_changed_files_freq_op) {
+
+ ret = db_operations_t->find_recently_changed_files_freq_op(
+ gf_db_connection,
+ query_callback,
+ _query_cbk_args,
+ from_time,
+ write_freq_thresold,
+ read_freq_thresold,
+ _clear_counters);
+ if (ret) {
+ gf_log (GFDB_DATA_STORE, GF_LOG_ERROR,
+ "Find changed with freq operation failed!");
+ }
+
+ }
+
+ return ret;
+
+}