Initial binlog prototype
Supports pulling binlogs from the master, caching to a local file and relaying to a slave. Only tested with a single slave and a single MaxScale thread.
This commit is contained in:
282
server/modules/routing/binlog/blr_file.c
Normal file
282
server/modules/routing/binlog/blr_file.c
Normal file
@ -0,0 +1,282 @@
|
||||
/*
|
||||
* This file is distributed as part of MaxScale. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright SkySQL Ab 2014
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file blr_file.c - contains code for the router binlog file management
|
||||
*
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 14/04/2014 Mark Riddoch Initial implementation
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <service.h>
|
||||
#include <server.h>
|
||||
#include <router.h>
|
||||
#include <atomic.h>
|
||||
#include <spinlock.h>
|
||||
#include <blr.h>
|
||||
#include <dcb.h>
|
||||
#include <spinlock.h>
|
||||
|
||||
#include <skygw_types.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
|
||||
|
||||
extern int lm_enabled_logfiles_bitmask;
|
||||
|
||||
static void blr_file_create(ROUTER_INSTANCE *router, char *file);
|
||||
static void blr_file_append(ROUTER_INSTANCE *router, char *file);
|
||||
static uint32_t extract_field(uint8_t *src, int bits);
|
||||
|
||||
/**
|
||||
* Initialise the binlog file for this instance. MaxScale will look
|
||||
* for all the binlogs that it has on local disk, determien the next
|
||||
* binlog to use and initialise it for writing, determining the
|
||||
* next record to be fetched from the real master.
|
||||
*
|
||||
* @param router The router instance this defines the master for this replication chain
|
||||
*/
|
||||
void
|
||||
blr_file_init(ROUTER_INSTANCE *router)
|
||||
{
|
||||
char *ptr, path[1024], filename[1050];
|
||||
int file_found, n = 1;
|
||||
|
||||
strcpy(path, "/usr/local/skysql/MaxScale");
|
||||
if ((ptr = getenv("MAXSCALE_HOME")) != NULL)
|
||||
{
|
||||
strcpy(path, ptr);
|
||||
}
|
||||
strcat(path, "/");
|
||||
strcat(path, router->service->name);
|
||||
|
||||
if (access(path, R_OK) == -1)
|
||||
mkdir(path, 0777);
|
||||
file_found = 0;
|
||||
do {
|
||||
sprintf(filename, "%s/" BINLOG_NAMEFMT, path, n);
|
||||
if (access(filename, R_OK) != -1)
|
||||
{
|
||||
file_found = 1;
|
||||
n++;
|
||||
}
|
||||
else
|
||||
file_found = 0;
|
||||
} while (file_found);
|
||||
n--;
|
||||
|
||||
if (n == 0) // No binlog files found
|
||||
{
|
||||
sprintf(filename, BINLOG_NAMEFMT, 1);
|
||||
blr_file_create(router, filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(filename, BINLOG_NAMEFMT, n);
|
||||
blr_file_append(router, filename);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
blr_file_rotate(ROUTER_INSTANCE *router, char *file, uint64_t pos)
|
||||
{
|
||||
blr_file_create(router, file);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new binlog file for the router to use.
|
||||
*
|
||||
* @param router The router instance
|
||||
* @param file The binlog file name
|
||||
*/
|
||||
static void
|
||||
blr_file_create(ROUTER_INSTANCE *router, char *file)
|
||||
{
|
||||
char *ptr, path[1024];
|
||||
int fd;
|
||||
unsigned char magic[] = BINLOG_MAGIC;
|
||||
|
||||
strcpy(path, "/usr/local/skysql/MaxScale");
|
||||
if ((ptr = getenv("MAXSCALE_HOME")) != NULL)
|
||||
{
|
||||
strcpy(path, ptr);
|
||||
}
|
||||
strcat(path, "/");
|
||||
strcat(path, router->service->name);
|
||||
strcat(path, "/");
|
||||
strcat(path, file);
|
||||
|
||||
if ((fd = open(path, O_RDWR|O_CREAT, 0666)) != -1)
|
||||
{
|
||||
write(fd, magic, 4);
|
||||
}
|
||||
fsync(fd);
|
||||
close(router->binlog_fd);
|
||||
strcpy(router->binlog_name, file);
|
||||
router->binlog_position = 4; /* Initial position after the magic number */
|
||||
router->binlog_fd = fd;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepare an existing binlog file to be appened to.
|
||||
*
|
||||
* @param router The router instance
|
||||
* @param file The binlog file name
|
||||
*/
|
||||
static void
|
||||
blr_file_append(ROUTER_INSTANCE *router, char *file)
|
||||
{
|
||||
char *ptr, path[1024];
|
||||
int fd;
|
||||
|
||||
strcpy(path, "/usr/local/skysql/MaxScale");
|
||||
if ((ptr = getenv("MAXSCALE_HOME")) != NULL)
|
||||
{
|
||||
strcpy(path, ptr);
|
||||
}
|
||||
strcat(path, "/");
|
||||
strcat(path, router->service->name);
|
||||
strcat(path, "/");
|
||||
strcat(path, file);
|
||||
|
||||
fd = open(path, O_RDWR|O_APPEND, 0666);
|
||||
fsync(fd);
|
||||
close(router->binlog_fd);
|
||||
strcpy(router->binlog_name, file);
|
||||
router->binlog_position = lseek(fd, 0L, SEEK_END);
|
||||
router->binlog_fd = fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a binlog entry to disk.
|
||||
*
|
||||
* @param router The router instance
|
||||
* @param buf The binlog record
|
||||
* @param len The length of the binlog record
|
||||
*/
|
||||
void
|
||||
blr_write_binlog_record(ROUTER_INSTANCE *router, REP_HEADER *hdr, uint8_t *buf)
|
||||
{
|
||||
pwrite(router->binlog_fd, buf, hdr->event_size, hdr->next_pos - hdr->event_size);
|
||||
router->binlog_position = hdr->next_pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the content of the binlog file to disk.
|
||||
*
|
||||
* @param router The binlog router
|
||||
*/
|
||||
void
|
||||
blr_file_flush(ROUTER_INSTANCE *router)
|
||||
{
|
||||
fsync(router->binlog_fd);
|
||||
}
|
||||
|
||||
int
|
||||
blr_open_binlog(ROUTER_INSTANCE *router, char *binlog)
|
||||
{
|
||||
char *ptr, path[1024];
|
||||
|
||||
strcpy(path, "/usr/local/skysql/MaxScale");
|
||||
if ((ptr = getenv("MAXSCALE_HOME")) != NULL)
|
||||
{
|
||||
strcpy(path, ptr);
|
||||
}
|
||||
strcat(path, "/");
|
||||
strcat(path, router->service->name);
|
||||
strcat(path, "/");
|
||||
strcat(path, binlog);
|
||||
|
||||
return open(path, O_RDONLY, 0666);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a replication event into a GWBUF structure.
|
||||
*
|
||||
* @param fd File descriptor of the binlog file
|
||||
* @param pos Position of binlog record to read
|
||||
* @param hdr Binlog header to populate
|
||||
* @return The binlog record wrapped in a GWBUF structure
|
||||
*/
|
||||
GWBUF *
|
||||
blr_read_binlog(int fd, unsigned int pos, REP_HEADER *hdr)
|
||||
{
|
||||
uint8_t hdbuf[19];
|
||||
GWBUF *result;
|
||||
unsigned char *data;
|
||||
|
||||
if (lseek(fd, pos, SEEK_SET) != pos)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Read the header information from the file */
|
||||
if (read(fd, hdbuf, 19) != 19)
|
||||
return NULL;
|
||||
hdr->timestamp = extract_field(hdbuf, 32);
|
||||
hdr->event_type = hdbuf[4];
|
||||
hdr->serverid = extract_field(&hdbuf[5], 32);
|
||||
hdr->event_size = extract_field(&hdbuf[9], 32);
|
||||
hdr->next_pos = extract_field(&hdbuf[13], 32);
|
||||
hdr->flags = extract_field(&hdbuf[17], 16);
|
||||
if ((result = gwbuf_alloc(hdr->event_size)) == NULL)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
|
||||
"Failed to allocate memory for binlog entry.\n")));
|
||||
return NULL;
|
||||
}
|
||||
data = GWBUF_DATA(result);
|
||||
memcpy(data, hdbuf, 19); // Copy the header in
|
||||
read(fd, &data[19], hdr->event_size - 19); // Read the balance
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a numeric field from a packet of the specified number of bits
|
||||
*
|
||||
* @param src The raw packet source
|
||||
* @param birs The number of bits to extract (multiple of 8)
|
||||
*/
|
||||
static uint32_t
|
||||
extract_field(uint8_t *src, int bits)
|
||||
{
|
||||
uint32_t rval = 0, shift = 0;
|
||||
|
||||
while (bits > 0)
|
||||
{
|
||||
rval |= (*src++) << shift;
|
||||
shift += 8;
|
||||
bits -= 8;
|
||||
}
|
||||
return rval;
|
||||
}
|
Reference in New Issue
Block a user