MXS-1075: Sqlite db for GTID - file, pos mapping

Use sqlite3 database instead of hash tables for GTID - file, pos mapping
This commit is contained in:
MassimilianoPinto 2017-03-28 10:23:09 +02:00
parent 69ae85c400
commit a236b14eef
6 changed files with 247 additions and 120 deletions

View File

@ -1,11 +1,11 @@
add_library(binlogrouter SHARED blr.c blr_master.c blr_cache.c blr_slave.c blr_file.c)
set_target_properties(binlogrouter PROPERTIES INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${MAXSCALE_LIBDIR} VERSION "2.0.0")
set_target_properties(binlogrouter PROPERTIES LINK_FLAGS -Wl,-z,defs)
target_link_libraries(binlogrouter maxscale-common ${PCRE_LINK_FLAGS} uuid)
target_link_libraries(binlogrouter maxscale-common ${PCRE_LINK_FLAGS} uuid sqlite3)
install_module(binlogrouter core)
add_executable(maxbinlogcheck maxbinlogcheck.c blr_file.c blr_cache.c blr_master.c blr_slave.c blr.c)
target_link_libraries(maxbinlogcheck maxscale-common ${PCRE_LINK_FLAGS} uuid)
target_link_libraries(maxbinlogcheck maxscale-common ${PCRE_LINK_FLAGS} uuid sqlite3)
install_executable(maxbinlogcheck core)

View File

@ -117,8 +117,7 @@ static void destroyInstance(MXS_ROUTER *instance);
bool blr_extract_key(const char *linebuf, int nline, ROUTER_INSTANCE *router);
bool blr_get_encryption_key(ROUTER_INSTANCE *router);
int blr_parse_key_file(ROUTER_INSTANCE *router);
static MARIADB_GTID_INFO *mariadb_gtid_info_dup(const MARIADB_GTID_INFO *in);
static void mariadb_gtid_info_free(MARIADB_GTID_INFO *in);
static bool blr_open_gtid_maps_storage(ROUTER_INSTANCE *inst);
static void stats_func(void *);
@ -390,6 +389,11 @@ createInstance(SERVICE *service, char **options)
defuuid[8], defuuid[9], defuuid[10], defuuid[11],
defuuid[12], defuuid[13], defuuid[14], defuuid[15]);
}
else
{
free_instance(inst);
return NULL;
}
}
/*
@ -642,38 +646,6 @@ createInstance(SERVICE *service, char **options)
return NULL;
}
/* Enable MariaDB GTID repo */
if (inst->mariadb10_compat &&
inst->mariadb_gtid)
{
if (!inst->trx_safe)
{
MXS_ERROR("MariaDB GTID can be enabled only"
" with Transaction Safety feature."
" Please enable it with option 'transaction_safety = on'");
free_instance(inst);
return NULL;
}
if ((inst->gtid_repo = hashtable_alloc(1000,
hashtable_item_strhash,
hashtable_item_strcmp)) == NULL)
{
MXS_ERROR("Service %s, cannot allocate MariaDB GTID hashtable", service->name);
free_instance(inst);
return NULL;
}
hashtable_memory_fns(inst->gtid_repo,
hashtable_item_strdup,
(HASHCOPYFN)mariadb_gtid_info_dup,
hashtable_item_free,
(HASHFREEFN)mariadb_gtid_info_free);
MXS_NOTICE("%s: Service has MariaDB GTID otion set to ON",
service->name);
}
if (inst->serverid <= 0)
{
MXS_ERROR("Service %s, server-id is not configured. "
@ -712,6 +684,27 @@ createInstance(SERVICE *service, char **options)
}
}
/* Enable MariaDB the GTID maps store */
if (inst->mariadb10_compat &&
inst->mariadb_gtid)
{
if (!inst->trx_safe)
{
MXS_ERROR("MariaDB GTID can be enabled only"
" with Transaction Safety feature."
" Please enable it with option 'transaction_safety = on'");
free_instance(inst);
return NULL;
}
/* Create/Open R/W GTID sqlite3 storage */
if (!blr_open_gtid_maps_storage(inst))
{
free_instance(inst);
return NULL;
}
}
/* Dynamically allocate master_host server struct, not written in any cnf file */
if (service->dbref == NULL)
{
@ -724,6 +717,7 @@ createInstance(SERVICE *service, char **options)
MXS_ERROR("%s: Error for server_alloc in createInstance",
inst->service->name);
sqlite3_close_v2(inst->gtid_maps);
free_instance(inst);
return NULL;
}
@ -736,7 +730,7 @@ createInstance(SERVICE *service, char **options)
server_free(service->dbref->server);
MXS_FREE(service->dbref);
sqlite3_close_v2(inst->gtid_maps);
free_instance(inst);
return NULL;
}
@ -828,7 +822,7 @@ createInstance(SERVICE *service, char **options)
server_free(service->dbref->server);
MXS_FREE(service->dbref);
service->dbref = NULL;
sqlite3_close_v2(inst->gtid_maps);
free_instance(inst);
return NULL;
}
@ -873,6 +867,7 @@ createInstance(SERVICE *service, char **options)
service->dbref = NULL;
}
sqlite3_close_v2(inst->gtid_maps);
free_instance(inst);
return NULL;
}
@ -1044,6 +1039,8 @@ newSession(MXS_ROUTER *instance, MXS_SESSION *session)
slave->encryption_ctx = NULL;
slave->mariadb_gtid = NULL;
slave->gtid_maps = NULL;
/**
* Add this session to the list of active sessions.
*/
@ -2501,6 +2498,8 @@ destroyInstance(MXS_ROUTER *instance)
inst->master_state = BLRM_SLAVE_STOPPED;
}
spinlock_release(&inst->lock);
if (inst->client)
{
if (inst->client->state == DCB_STATE_POLLING)
@ -2525,10 +2524,8 @@ destroyInstance(MXS_ROUTER *instance)
inst->service->name, inst->binlog_name, inst->current_pos, inst->binlog_position);
}
/* Free GTID hashtable */
hashtable_free(inst->gtid_repo);
spinlock_release(&inst->lock);
/* Close GTID maps database */
sqlite3_close_v2(inst->gtid_maps);
}
/**
@ -2724,42 +2721,49 @@ int blr_parse_key_file(ROUTER_INSTANCE *router)
}
/**
* Free routine for GTID repo hashtable
* Create / Open R/W GTID maps database
*
* @param in The data to free
*/
static void mariadb_gtid_info_free(MARIADB_GTID_INFO *in)
static bool blr_open_gtid_maps_storage(ROUTER_INSTANCE *inst)
{
if (in)
{
MXS_FREE(in->gtid);
MXS_FREE(in->file);
MXS_FREE(in);
}
}
char dbpath[PATH_MAX + 1];
snprintf(dbpath, sizeof(dbpath), "/%s/%s",
inst->binlogdir, GTID_MAPS_DB);
/**
* Copy routine for GTID repo hashtable
*
* @param in The data to copy
* @return New allocated value or NULL
*/
static MARIADB_GTID_INFO *mariadb_gtid_info_dup(const MARIADB_GTID_INFO *in)
{
MARIADB_GTID_INFO *rval = (MARIADB_GTID_INFO *) MXS_CALLOC(1, sizeof(MARIADB_GTID_INFO));
char *gtid = MXS_STRDUP(in->gtid);
char *file = MXS_STRDUP(in->file);
if (!gtid || !rval)
/* Open/Create the GTID maps database */
if (sqlite3_open_v2(dbpath,
&inst->gtid_maps,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
NULL) != SQLITE_OK)
{
MXS_FREE(rval);
MXS_FREE(gtid);
MXS_FREE(file);
return NULL;
MXS_ERROR("Failed to open GTID maps SQLite database '%s': %s", dbpath,
sqlite3_errmsg(inst->gtid_maps));
return false;
}
rval->gtid = gtid;
rval->file = file;
rval->start = in-> start;
rval->end = in->end;
return (void *) rval;
char* errmsg;
/* Create the gtid_maps table */
int rc = sqlite3_exec(inst->gtid_maps,
"CREATE TABLE IF NOT EXISTS "
"gtid_maps(gtid varchar(255), "
"binlog_file varchar(255), "
"start_pos bigint, "
"end_pos bigint, "
"primary key(gtid));",
NULL, NULL, &errmsg);
if (rc != SQLITE_OK)
{
MXS_ERROR("Service %s, failed to create GTID index table 'gtid_maps': %s",
inst->service->name,
sqlite3_errmsg(inst->gtid_maps));
sqlite3_free(errmsg);
/* Close GTID maps database */
sqlite3_close_v2(inst->gtid_maps);
return false;
}
MXS_NOTICE("%s: Service has MariaDB GTID otion set to ON",
inst->service->name);
return true;
}

View File

@ -57,6 +57,7 @@
#include <maxscale/thread.h>
#include <maxscale/protocol/mysql.h>
#include <maxscale/secrets.h>
#include <maxscale/sqlite3.h>
MXS_BEGIN_DECLS
@ -92,6 +93,11 @@ MXS_BEGIN_DECLS
/* MariaDB GTID string len */
#define GTID_MAX_LEN 42
/* GTID slite3 query buffer size */
#define GTID_SQL_BUFFER_SIZE 1024
/* GTID slite3 database name */
#define GTID_MAPS_DB "gtid_maps.db"
/**
* Supported Encryption algorithms
*
@ -451,6 +457,7 @@ typedef struct router_slave
void *encryption_ctx; /*< Encryption context */
bool gtid_strict_mode;/*< MariaDB 10 Slave sets gtid_strict_mode */
char *mariadb_gtid; /*< MariaDB 10 Slave connects with GTID */
sqlite3 *gtid_maps; /*< GTID storage client handle, read only*/
#if defined(SS_DEBUG)
skygw_chk_t rses_chk_tail;
#endif
@ -637,6 +644,7 @@ typedef struct router_instance
* This allows MariaDB 10 slave servers
* connecting with GTID */
HASHTABLE *gtid_repo; /*< Storage for MariaDB GTIDs */
sqlite3 *gtid_maps; /*< GTID storage */
struct router_instance *next;
} ROUTER_INSTANCE;
@ -881,6 +889,9 @@ extern int blr_check_encryption_algorithm(char *);
extern const char *blr_encryption_algorithm_list(void);
extern bool blr_get_encryption_key(ROUTER_INSTANCE *);
extern const char *blr_skip_leading_sql_comments(const char *);
extern bool blr_fetch_mariadb_gtid(ROUTER_SLAVE *,
const char *,
MARIADB_GTID_INFO *);
MXS_END_DECLS

View File

@ -3297,7 +3297,14 @@ static void blr_report_checksum(REP_HEADER hdr, const uint8_t *buffer, char *out
*/
bool blr_save_mariadb_gtid(ROUTER_INSTANCE *inst)
{
static const char insert_tpl[] = "INSERT OR IGNORE INTO gtid_maps("
"gtid, "
"binlog_file, "
"start_pos, end_pos) "
"VALUES (\"%s\", \"%s\", %lu, %lu);";
MARIADB_GTID_INFO gtid_info;
char *errmsg;
char insert_sql[GTID_SQL_BUFFER_SIZE];
gtid_info.gtid = inst->pending_transaction.gtid;
gtid_info.file = inst->binlog_name;
@ -3305,41 +3312,107 @@ bool blr_save_mariadb_gtid(ROUTER_INSTANCE *inst)
gtid_info.end = inst->pending_transaction.end_pos;
/* Save GTID into repo */
if (!hashtable_add(inst->gtid_repo,
inst->pending_transaction.gtid,
&gtid_info))
{
MXS_ERROR("Service %s: error saving mariadb GTID %s into repo",
inst->service->name,
inst->pending_transaction.gtid);
return false;
}
snprintf(insert_sql,
GTID_SQL_BUFFER_SIZE,
insert_tpl,
gtid_info.gtid,
gtid_info.file,
gtid_info.start,
gtid_info.end);
MXS_DEBUG("Saved MariaDB GTID '%s', %s:%lu:%lu",
/* Save GTID into repo */
if (sqlite3_exec(inst->gtid_maps, insert_sql, NULL, NULL,
&errmsg) != SQLITE_OK)
{
MXS_ERROR("Service %s: failed to insert GTID %s for %s:%lu,%lu "
"into gtid_maps database: %s",
inst->service->name,
gtid_info.gtid,
gtid_info.file,
gtid_info.start,
gtid_info.end,
errmsg);
return false;
}
sqlite3_free(errmsg);
MXS_DEBUG("Saved MariaDB GTID '%s', %s:%lu,%lu, insert SQL [%s]",
gtid_info.gtid,
inst->binlog_name,
gtid_info.start,
gtid_info.end);
gtid_info.end,
insert_sql);
return true;
}
/**
* Get MariaDB GTID frim repo
* GTID select callbck for sqlite3 database
*
* @param inst The router instance
* @return Found data or NULL
* @param data Data pointer from caller
* @param cols Number of columns
* @param values The values
* @param names The column names
*
* @return 0 on success, 1 otherwise
*/
static int gtid_select_cb(void *data, int cols, char** values, char** names)
{
MARIADB_GTID_INFO *result = (MARIADB_GTID_INFO *)data;
ss_dassert(cols >= 4);
if (values[0] &&
values[1] &&
values[2] &&
values[3])
{
result->gtid = MXS_STRDUP_A(values[0]);
result->file = MXS_STRDUP_A(values[1]);
result->start = atol(values[2]);
result->end = atol(values[3]);
}
return 0;
}
/**
* Get MariaDB GTID from repo
*
* @param slave The current slave instance
* @param gtid The GTID to look for
* @param result The (allocated) ouput data to fill
* @return True if with found GTID or false
*/
MARIADB_GTID_INFO *blr_fetch_mariadb_gtid(ROUTER_INSTANCE *inst, char *gtid)
bool blr_fetch_mariadb_gtid(ROUTER_SLAVE *slave,
const char *gtid,
MARIADB_GTID_INFO *result)
{
if (!gtid)
{
return NULL;
}
else
{
return (MARIADB_GTID_INFO *)hashtable_fetch(inst->gtid_repo,
gtid);
}
char *errmsg = NULL;
char select_query[GTID_SQL_BUFFER_SIZE];
static const char select_tpl[] = "SELECT gtid, binlog_file, start_pos, end_pos "
"FROM gtid_maps "
"WHERE gtid = '%s' LIMIT 1;";
ss_dassert(gtid != NULL);
snprintf(select_query,
GTID_SQL_BUFFER_SIZE,
select_tpl,
gtid);
/* Find the GTID */
if (sqlite3_exec(slave->gtid_maps,
select_query,
gtid_select_cb,
result,
&errmsg) != SQLITE_OK)
{
MXS_ERROR("Failed to select GTID %s from GTID maps DB: %s, select [%s]",
gtid, errmsg, select_query);
sqlite3_free(errmsg);
return false;
}
return result->gtid ? true : false;
}

View File

@ -166,7 +166,6 @@ static int blr_slave_send_heartbeat(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave
static int blr_set_master_ssl(ROUTER_INSTANCE *router, CHANGE_MASTER_OPTIONS config, char *error_message);
static int blr_slave_read_ste(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, uint32_t fde_end_pos);
static GWBUF *blr_slave_read_fde(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave);
extern MARIADB_GTID_INFO *blr_fetch_mariadb_gtid(ROUTER_INSTANCE *inst, char *gtid);
static bool blr_handle_select_smt(ROUTER_INSTANCE *router,
ROUTER_SLAVE *slave,
char *select_stmt);
@ -2118,7 +2117,7 @@ blr_slave_binlog_dump(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue
return 1;
}
/* Set the received filename */
/* Set the received filename: it could be changed later */
memcpy(slave->binlogfile, (char *)ptr, binlognamelen);
slave->binlogfile[binlognamelen] = 0;
}
@ -2144,15 +2143,39 @@ blr_slave_binlog_dump(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue
}
else
{
/* TODO */
/* Shall we avoid the lookup if file & pos is set? */
MARIADB_GTID_INFO f_gtid = {};
char dbpath[PATH_MAX + 1];
snprintf(dbpath, sizeof(dbpath), "/%s/%s",
router->binlogdir, GTID_MAPS_DB);
/* Fetch the GTID from the storage */
MARIADB_GTID_INFO *f_gtid = blr_fetch_mariadb_gtid(router,
slave->mariadb_gtid);
/* Result set init */
f_gtid.gtid = NULL;
/* Open GTID maps read-only database */
if (sqlite3_open_v2(dbpath,
&slave->gtid_maps,
SQLITE_OPEN_READONLY,
NULL) != SQLITE_OK)
{
MXS_ERROR("Slave %lu: failed to open GTID maps db '%s': %s",
(unsigned long)slave->serverid,
dbpath,
sqlite3_errmsg(slave->gtid_maps));
slave->gtid_maps = NULL;
}
else
{
/* Fetch the GTID from the maps storage */
blr_fetch_mariadb_gtid(slave, slave->mariadb_gtid, &f_gtid);
/* Close GTID maps database */
sqlite3_close_v2(slave->gtid_maps);
slave->gtid_maps = NULL;
}
/* Not Found */
if (!f_gtid)
if (!f_gtid.gtid)
{
MXS_WARNING("Requested MariaDB GTID '%s' by server %lu"
" has not been found",
@ -2178,7 +2201,7 @@ blr_slave_binlog_dump(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue
else
{
/**
* Right now: use current router binlog file pos 4
* Right now: just use current router binlog file pos 4
*/
}
}
@ -2188,8 +2211,8 @@ blr_slave_binlog_dump(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue
MXS_INFO("Found GTID '%s' for slave %lu at %s:%lu",
slave->mariadb_gtid,
(unsigned long)slave->serverid,
f_gtid->file,
f_gtid->end);
f_gtid.file,
f_gtid.end);
/**
* Check whether GTID request has file & pos:
@ -2201,30 +2224,46 @@ blr_slave_binlog_dump(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue
*/
/* Set binlog file to the GTID one */
strcpy(slave->binlogfile, f_gtid->file);
strcpy(slave->binlogfile, f_gtid.file);
/**
* Set position to GTID event pos:
* i.e the next pos of COMMIT)
*/
slave->binlog_pos = f_gtid->end;
slave->binlog_pos = f_gtid.end;
}
else
{
/**
* The slave has requested a GTID while
* requesting a file & pos which can be different
* from file @ pos of that GTID:
* The slave has requested a GTID with
* binlog file & position.
*
* i.e: Log file is different (after rotate)
* or other events (if any) could have been
* written to binlog after GTID.
* The log file which GTID belongs to
* could be different: this could happen
* after a rotate event or other non GTID events
* could have been written to binlog after that GTID.
*
* Binlog file is the requested one, so just set
* the pos to the requested one
* If the GTID file is the requested one, use GTID info,
* otherwise just set the pos to the requested one.
* In this case the file is the one from the request.
*/
slave->binlog_pos = requested_pos;
if (strcmp(slave->binlogfile, f_gtid.file) == 0)
{
/* Set binlog file to the GTID one */
strcpy(slave->binlogfile, f_gtid.file);
/* Set position to GTID event next_pos */
slave->binlog_pos = f_gtid.end;
}
else
{
/* Set the requested pos */
slave->binlog_pos = requested_pos;
}
}
/* Free gtid and file from result */
MXS_FREE(f_gtid.gtid);
MXS_FREE(f_gtid.file);
}
}
}

View File

@ -1,5 +1,5 @@
if(BUILD_TESTS)
add_executable(testbinlogrouter testbinlog.c ../blr.c ../blr_slave.c ../blr_master.c ../blr_file.c ../blr_cache.c)
target_link_libraries(testbinlogrouter maxscale-common ${PCRE_LINK_FLAGS} uuid)
target_link_libraries(testbinlogrouter maxscale-common ${PCRE_LINK_FLAGS} uuid sqlite3)
add_test(NAME TestBinlogRouter COMMAND ./testbinlogrouter WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
endif()