diff options
author | Joseph Fernandes <josferna@redhat.com> | 2015-02-18 19:45:23 +0530 |
---|---|---|
committer | Vijay Bellur <vbellur@redhat.com> | 2015-03-18 10:36:42 -0700 |
commit | 87c7fa3cfdadca4ee883daf84373302a42ad5fdc (patch) | |
tree | e61b744ea3c2058a95a5057bb8c2fe7763eb101f /libglusterfs/src/gfdb/gfdb_data_store.c | |
parent | dbd62a8d2b50392fbed0a0781a4f241dadb8f506 (diff) |
Adding Libgfdb to GlusterFS
*************************************************************************
Libgfdb |
*************************************************************************
Libgfdb provides abstract mechanism to record extra/rich metadata
required for data maintenance, such as data tiering/classification.
It provides consumer with API for recording and querying, keeping
the consumer abstracted from the data store used beneath for storing data.
It works in a plug-and-play model, where data stores can be plugged-in.
Presently we have plugin for Sqlite3. In the future will provide recording
and querying performance optimizer. In the current implementation the schema
of metadata is fixed.
Schema:
~~~~~~
GF_FILE_TB Table:
~~~~~~~~~~~~~~~~~
This table has one entry per file inode. It holds the metadata required to
make decisions in data maintenance.
GF_ID (Primary key) : File GFID (Universal Unique IDentifier in the namespace)
W_SEC, W_MSEC : Write wind time in sec & micro-sec
UW_SEC, UW_MSEC : Write un-wind time in sec & micro-sec
W_READ_SEC, W_READ_MSEC : Read wind time in sec & micro-sec
UW_READ_SEC, UW_READ_MSEC : Read un-wind time in sec & micro-sec
WRITE_FREQ_CNTR INTEGER : Write Frequency Counter
READ_FREQ_CNTR INTEGER : Read Frequency Counter
GF_FLINK_TABLE:
~~~~~~~~~~~~~~
This table has all the hardlinks to a file inode.
GF_ID : File GFID (Composite Primary Key)``|
GF_PID : Parent Directory GFID (Composite Primary Key) |-> Primary Key
FNAME : File Base Name (Composite Primary Key)__|
FPATH : File Full Path (Its redundant for now, this will go)
W_DEL_FLAG : This Flag is used for crash consistancy, when a link is unlinked.
i.e Set to 1 during unlink wind and during unwind this record
is deleted
LINK_UPDATE : This Flag is used when a link is changed i.e rename.
Set to 1 when rename wind and set to 0 in rename unwind
Libgfdb API:
~~~~~~~~~~~
Refer libglusterfs/src/gfdb/gfdb_data_store.h
Change-Id: I2e9fbab3878ce630a7f41221ef61017dc43db11f
BUG: 1194753
Signed-off-by: Joseph Fernandes <josferna@redhat.com>
Signed-off-by: Dan Lambright <dlambrig@redhat.com>
Signed-off-by: Joseph Fernandes <josferna@redhat.com>
Reviewed-on: http://review.gluster.org/9683
Tested-by: Gluster Build System <jenkins@build.gluster.com>
Reviewed-by: Vijay Bellur <vbellur@redhat.com>
Diffstat (limited to 'libglusterfs/src/gfdb/gfdb_data_store.c')
-rw-r--r-- | libglusterfs/src/gfdb/gfdb_data_store.c | 649 |
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; + +} |