moving files to /server to make merge possible
This commit is contained in:
85
server/modules/routing/Makefile
Normal file
85
server/modules/routing/Makefile
Normal file
@ -0,0 +1,85 @@
|
||||
# This file is distributed as part of the SkySQL Gateway. 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 2013
|
||||
#
|
||||
# Revision History
|
||||
# Date Who Description
|
||||
# 13/06/13 Mark Riddoch Initial routing module development
|
||||
# 27/06/13 Vilho Raatikka Added logmanager-related libs and
|
||||
# headers so that liblog_manager.so can
|
||||
# be linked in.
|
||||
# 27/06/13 Mark Riddoch Addition of read write splitter
|
||||
|
||||
include ../../../build_gateway.inc
|
||||
|
||||
LOGPATH := $(ROOT_PATH)/log_manager
|
||||
UTILSPATH := $(ROOT_PATH)/utils
|
||||
|
||||
CC=cc
|
||||
CFLAGS=-c -fPIC -I/usr/include -I../include -I../../include -I$(LOGPATH) \
|
||||
-I$(UTILSPATH) -Wall -g
|
||||
|
||||
include ../../../makefile.inc
|
||||
|
||||
LDFLAGS=-shared -L$(LOGPATH) -Wl,-rpath,$(DEST)/lib \
|
||||
-Wl,-rpath,$(LOGPATH) -Wl,-rpath,$(UTILSPATH)
|
||||
|
||||
TESTSRCS=testroute.c
|
||||
TESTOBJ=$(TESTSRCS:.c=.o)
|
||||
READCONSRCS=readconnroute.c
|
||||
READCONOBJ=$(READCONSRCS:.c=.o)
|
||||
DEBUGCLISRCS=debugcli.c debugcmd.c
|
||||
DEBUGCLIOBJ=$(DEBUGCLISRCS:.c=.o)
|
||||
SRCS=$(TESTSRCS) $(READCONSRCS) $(DEBUGCLISRCS)
|
||||
OBJ=$(SRCS:.c=.o)
|
||||
LIBS=$(UTILSPATH)/skygw_utils.o -lssl -llog_manager
|
||||
MODULES= libdebugcli.so libreadconnroute.so libtestroute.so
|
||||
|
||||
|
||||
all: $(MODULES)
|
||||
|
||||
libtestroute.so: $(TESTOBJ)
|
||||
$(CC) $(LDFLAGS) $(TESTOBJ) $(LIBS) -o $@
|
||||
|
||||
libreadconnroute.so: $(READCONOBJ)
|
||||
$(CC) $(LDFLAGS) $(READCONOBJ) $(LIBS) -o $@
|
||||
|
||||
libdebugcli.so: $(DEBUGCLIOBJ)
|
||||
$(CC) $(LDFLAGS) $(DEBUGCLIOBJ) $(LIBS) -o $@
|
||||
|
||||
libreadwritesplit.so:
|
||||
# (cd readwritesplit; make; cp $@ ..)
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(OBJ) $(MODULES)
|
||||
(cd readwritesplit; make clean)
|
||||
|
||||
tags:
|
||||
ctags $(SRCS) $(HDRS)
|
||||
(cd readwritesplit; make tags)
|
||||
|
||||
depend:
|
||||
@rm -f depend.mk
|
||||
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
||||
(cd readwritesplit; make depend)
|
||||
|
||||
install: $(MODULES)
|
||||
install -D $(MODULES) $(DEST)/MaxScale/modules
|
||||
(cd readwritesplit; make DEST=$(DEST) install)
|
||||
|
||||
include depend.mk
|
||||
247
server/modules/routing/debugcli.c
Normal file
247
server/modules/routing/debugcli.c
Normal file
@ -0,0 +1,247 @@
|
||||
/*
|
||||
* This file is distributed as part of the SkySQL Gateway. 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 2013
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file debugcli.c - A "routing module" that in fact merely gives
|
||||
* access to debug commands within the gateway
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 18/06/13 Mark Riddoch Initial implementation
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <service.h>
|
||||
#include <session.h>
|
||||
#include <router.h>
|
||||
#include <modules.h>
|
||||
#include <atomic.h>
|
||||
#include <spinlock.h>
|
||||
#include <dcb.h>
|
||||
#include <poll.h>
|
||||
#include <debugcli.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
|
||||
static char *version_str = "V1.0.1";
|
||||
|
||||
/* The router entry points */
|
||||
static ROUTER *createInstance(SERVICE *service, char **options);
|
||||
static void *newSession(ROUTER *instance, SESSION *session);
|
||||
static void closeSession(ROUTER *instance, void *router_session);
|
||||
static int execute(ROUTER *instance, void *router_session, GWBUF *queue);
|
||||
static void diagnostics(ROUTER *instance, DCB *dcb);
|
||||
|
||||
/** The module object definition */
|
||||
static ROUTER_OBJECT MyObject = { createInstance, newSession, closeSession, execute, diagnostics, NULL };
|
||||
|
||||
extern int execute_cmd(CLI_SESSION *cli);
|
||||
|
||||
static SPINLOCK instlock;
|
||||
static CLI_INSTANCE *instances;
|
||||
|
||||
/**
|
||||
* Implementation of the mandatory version entry point
|
||||
*
|
||||
* @return version string of the module
|
||||
*/
|
||||
char *
|
||||
version()
|
||||
{
|
||||
return version_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module initialisation routine, called when the module
|
||||
* is first loaded.
|
||||
*/
|
||||
void
|
||||
ModuleInit()
|
||||
{
|
||||
skygw_log_write(NULL, LOGFILE_MESSAGE, "Initialise debug CLI router module %s.\n", version_str);
|
||||
spinlock_init(&instlock);
|
||||
instances = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module entry point routine. It is this routine that
|
||||
* must populate the structure that is referred to as the
|
||||
* "module object", this is a structure with the set of
|
||||
* external entry points for this module.
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
ROUTER_OBJECT *
|
||||
GetModuleObject()
|
||||
{
|
||||
return &MyObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the router for a particular service
|
||||
* within the gateway.
|
||||
*
|
||||
* @param service The service this router is being create for
|
||||
* @param options Any array of options for the query router
|
||||
*
|
||||
* @return The instance data for this new instance
|
||||
*/
|
||||
static ROUTER *
|
||||
createInstance(SERVICE *service, char **options)
|
||||
{
|
||||
CLI_INSTANCE *inst;
|
||||
|
||||
if ((inst = malloc(sizeof(CLI_INSTANCE))) == NULL)
|
||||
return NULL;
|
||||
|
||||
inst->service = service;
|
||||
spinlock_init(&inst->lock);
|
||||
inst->sessions = NULL;
|
||||
|
||||
|
||||
/*
|
||||
* We have completed the creation of the instance data, so now
|
||||
* insert this router instance into the linked list of routers
|
||||
* that have been created with this module.
|
||||
*/
|
||||
spinlock_acquire(&instlock);
|
||||
inst->next = instances;
|
||||
instances = inst;
|
||||
spinlock_release(&instlock);
|
||||
|
||||
return (ROUTER *)inst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a new session with this instance of the router.
|
||||
*
|
||||
* @param instance The router instance data
|
||||
* @param session The session itself
|
||||
* @return Session specific data for this session
|
||||
*/
|
||||
static void *
|
||||
newSession(ROUTER *instance, SESSION *session)
|
||||
{
|
||||
CLI_INSTANCE *inst = (CLI_INSTANCE *)instance;
|
||||
CLI_SESSION *client;
|
||||
|
||||
if ((client = (CLI_SESSION *)malloc(sizeof(CLI_SESSION))) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
client->session = session;
|
||||
|
||||
memset(client->cmdbuf, 0, 80);
|
||||
|
||||
spinlock_acquire(&inst->lock);
|
||||
client->next = inst->sessions;
|
||||
inst->sessions = client;
|
||||
spinlock_release(&inst->lock);
|
||||
|
||||
session->state = SESSION_STATE_READY;
|
||||
|
||||
dcb_printf(session->client, "Welcome the SkySQL MaxScale Debug Interface (%s).\n",
|
||||
version_str);
|
||||
dcb_printf(session->client, "Type help for a list of available commands.\n\n");
|
||||
|
||||
return (void *)client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a session with the router, this is the mechanism
|
||||
* by which a router may cleanup data structure etc.
|
||||
*
|
||||
* @param instance The router instance data
|
||||
* @param router_session The session being closed
|
||||
*/
|
||||
static void
|
||||
closeSession(ROUTER *instance, void *router_session)
|
||||
{
|
||||
CLI_INSTANCE *inst = (CLI_INSTANCE *)instance;
|
||||
CLI_SESSION *session = (CLI_SESSION *)router_session;
|
||||
|
||||
|
||||
spinlock_acquire(&inst->lock);
|
||||
if (inst->sessions == session)
|
||||
inst->sessions = session->next;
|
||||
else
|
||||
{
|
||||
CLI_SESSION *ptr = inst->sessions;
|
||||
while (ptr && ptr->next != session)
|
||||
ptr = ptr->next;
|
||||
if (ptr)
|
||||
ptr->next = session->next;
|
||||
}
|
||||
spinlock_release(&inst->lock);
|
||||
|
||||
/*
|
||||
* We are no longer in the linked list, free
|
||||
* all the memory and other resources associated
|
||||
* to the client session.
|
||||
*/
|
||||
free(session);
|
||||
}
|
||||
|
||||
/**
|
||||
* We have data from the client, we must route it to the backend.
|
||||
* This is simply a case of sending it to the connection that was
|
||||
* chosen when we started the client session.
|
||||
*
|
||||
* @param instance The router instance
|
||||
* @param router_session The router session returned from the newSession call
|
||||
* @param queue The queue of data buffers to route
|
||||
* @return The number of bytes sent
|
||||
*/
|
||||
static int
|
||||
execute(ROUTER *instance, void *router_session, GWBUF *queue)
|
||||
{
|
||||
CLI_SESSION *session = (CLI_SESSION *)router_session;
|
||||
|
||||
/* Extract the characters */
|
||||
while (queue)
|
||||
{
|
||||
strncat(session->cmdbuf, GWBUF_DATA(queue), GWBUF_LENGTH(queue));
|
||||
queue = gwbuf_consume(queue, GWBUF_LENGTH(queue));
|
||||
}
|
||||
|
||||
if (strrchr(session->cmdbuf, '\n'))
|
||||
{
|
||||
if (execute_cmd(session))
|
||||
dcb_printf(session->session->client, "MaxScale> ");
|
||||
else
|
||||
session->session->client->func.close(session->session->client);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display router diagnostics
|
||||
*
|
||||
* @param instance Instance of the router
|
||||
* @param dcb DCB to send diagnostics to
|
||||
*/
|
||||
static void
|
||||
diagnostics(ROUTER *instance, DCB *dcb)
|
||||
{
|
||||
return; /* Nothing to do currently */
|
||||
}
|
||||
572
server/modules/routing/debugcmd.c
Normal file
572
server/modules/routing/debugcmd.c
Normal file
@ -0,0 +1,572 @@
|
||||
/*
|
||||
* This file is distributed as part of the SkySQL Gateway. 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 2013
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file debugcmd.c - The debug CLI command line interpreter
|
||||
*
|
||||
* The command interpreter for the dbug user interface. The command
|
||||
* structure is such that there are a numerb of commands, notably
|
||||
* show and a set of subcommands, the things to show in this case.
|
||||
*
|
||||
* Each subcommand has a handler function defined for it that is passeed
|
||||
* the DCB to use to print the output of the commands and up to 3 arguments
|
||||
* as numeric values.
|
||||
*
|
||||
* There are two "built in" commands, the help command and the quit
|
||||
* command.
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 20/06/13 Mark Riddoch Initial implementation
|
||||
* 17/07/13 Mark Riddoch Additional commands
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <service.h>
|
||||
#include <session.h>
|
||||
#include <router.h>
|
||||
#include <modules.h>
|
||||
#include <atomic.h>
|
||||
#include <server.h>
|
||||
#include <spinlock.h>
|
||||
#include <dcb.h>
|
||||
#include <poll.h>
|
||||
#include <users.h>
|
||||
#include <dbusers.h>
|
||||
#include <config.h>
|
||||
#include <telnetd.h>
|
||||
#include <adminusers.h>
|
||||
#include <monitor.h>
|
||||
#include <debugcli.h>
|
||||
|
||||
#define MAXARGS 5
|
||||
|
||||
#define ARG_TYPE_ADDRESS 1
|
||||
#define ARG_TYPE_STRING 2
|
||||
/**
|
||||
* The subcommand structure
|
||||
*
|
||||
* These are the options that may be passed to a command
|
||||
*/
|
||||
struct subcommand {
|
||||
char *arg1;
|
||||
int n_args;
|
||||
void (*fn)();
|
||||
char *help;
|
||||
int arg_types[3];
|
||||
};
|
||||
|
||||
static void telnetdShowUsers(DCB *);
|
||||
/**
|
||||
* The subcommands of the show command
|
||||
*/
|
||||
struct subcommand showoptions[] = {
|
||||
{ "dcbs", 0, dprintAllDCBs, "Show all descriptor control blocks (network connections)",
|
||||
{0, 0, 0} },
|
||||
{ "dcb", 1, dprintDCB, "Show a single descriptor control block e.g. show dcb 0x493340",
|
||||
{ARG_TYPE_ADDRESS, 0, 0} },
|
||||
{ "dbusers", 1, dcb_usersPrint, "Show statistics and user names for a service's user table",
|
||||
{ARG_TYPE_ADDRESS, 0, 0} },
|
||||
{ "epoll", 0, dprintPollStats, "Show the poll statistics",
|
||||
{0, 0, 0} },
|
||||
{ "modules", 0, dprintAllModules, "Show all currently loaded modules",
|
||||
{0, 0, 0} },
|
||||
{ "monitors", 0, monitorShowAll, "Show the monitors that are configured",
|
||||
{0, 0, 0} },
|
||||
{ "server", 1, dprintServer, "Show details for a server, e.g. show server 0x485390",
|
||||
{ARG_TYPE_ADDRESS, 0, 0} },
|
||||
{ "servers", 0, dprintAllServers, "Show all configured servers",
|
||||
{0, 0, 0} },
|
||||
{ "services", 0, dprintAllServices, "Show all configured services in MaxScale",
|
||||
{0, 0, 0} },
|
||||
{ "session", 1, dprintSession, "Show a single session in MaxScale, e.g. show session 0x284830",
|
||||
{ARG_TYPE_ADDRESS, 0, 0} },
|
||||
{ "sessions", 0, dprintAllSessions, "Show all active sessions in MaxScale",
|
||||
{0, 0, 0} },
|
||||
{ "users", 0, telnetdShowUsers, "Show statistics and user names for the debug interface",
|
||||
{ARG_TYPE_ADDRESS, 0, 0} },
|
||||
{ NULL, 0, NULL, NULL,
|
||||
{0, 0, 0} }
|
||||
};
|
||||
|
||||
extern void shutdown_gateway();
|
||||
static void shutdown_service(DCB *dcb, SERVICE *service);
|
||||
static void shutdown_monitor(DCB *dcb, MONITOR *monitor);
|
||||
|
||||
/**
|
||||
* The subcommands of the shutdown command
|
||||
*/
|
||||
struct subcommand shutdownoptions[] = {
|
||||
{ "gateway", 0, shutdown_gateway, "Shutdown MaxScale",
|
||||
{0, 0, 0} },
|
||||
{ "maxscale", 0, shutdown_gateway, "Shutdown the MaxScale gateway",
|
||||
{0, 0, 0} },
|
||||
{ "monitor", 1, shutdown_monitor, "Shutdown a monitor, e.g. shutdown monitor 0x48381e0",
|
||||
{ARG_TYPE_ADDRESS, 0, 0} },
|
||||
{ "service", 1, shutdown_service, "Shutdown a service, e.g. shutdown service 0x4838320",
|
||||
{ARG_TYPE_ADDRESS, 0, 0} },
|
||||
{ NULL, 0, NULL, NULL,
|
||||
{0, 0, 0} }
|
||||
};
|
||||
|
||||
static void restart_service(DCB *dcb, SERVICE *service);
|
||||
static void restart_monitor(DCB *dcb, MONITOR *monitor);
|
||||
/**
|
||||
* The subcommands of the restart command
|
||||
*/
|
||||
struct subcommand restartoptions[] = {
|
||||
{ "monitor", 1, restart_monitor, "Restart a monitor, e.g. restart monitor 0x48181e0",
|
||||
{ARG_TYPE_ADDRESS, 0, 0} },
|
||||
{ "service", 1, restart_service, "Restart a service, e.g. restart service 0x4838320",
|
||||
{ARG_TYPE_ADDRESS, 0, 0} },
|
||||
{ NULL, 0, NULL, NULL,
|
||||
{0, 0, 0} }
|
||||
};
|
||||
|
||||
static void set_server(DCB *dcb, SERVER *server, char *bit);
|
||||
/**
|
||||
* The subcommands of the set command
|
||||
*/
|
||||
struct subcommand setoptions[] = {
|
||||
{ "server", 2, set_server, "Set the status of a server. E.g. set server 0x4838320 master",
|
||||
{ARG_TYPE_ADDRESS, ARG_TYPE_STRING, 0} },
|
||||
{ NULL, 0, NULL, NULL,
|
||||
{0, 0, 0} }
|
||||
};
|
||||
|
||||
static void clear_server(DCB *dcb, SERVER *server, char *bit);
|
||||
/**
|
||||
* The subcommands of the clear command
|
||||
*/
|
||||
struct subcommand clearoptions[] = {
|
||||
{ "server", 2, clear_server, "Clear the status of a server. E.g. clear server 0x4838320 master",
|
||||
{ARG_TYPE_ADDRESS, ARG_TYPE_STRING, 0} },
|
||||
{ NULL, 0, NULL, NULL,
|
||||
{0, 0, 0} }
|
||||
};
|
||||
|
||||
static void reload_users(DCB *dcb, SERVICE *service);
|
||||
static void reload_config(DCB *dcb);
|
||||
|
||||
/**
|
||||
* The subcommands of the reload command
|
||||
*/
|
||||
struct subcommand reloadoptions[] = {
|
||||
{ "config", 0, reload_config, "Reload the configuration data for MaxScale.",
|
||||
{ARG_TYPE_ADDRESS, 0, 0} },
|
||||
{ "users", 1, reload_users, "Reload the user data for a service. E.g. reload users 0x849420",
|
||||
{ARG_TYPE_ADDRESS, 0, 0} },
|
||||
{ NULL, 0, NULL, NULL,
|
||||
{0, 0, 0} }
|
||||
};
|
||||
|
||||
static void telnetdAddUser(DCB *, char *, char *);
|
||||
/**
|
||||
* The subcommands of the add command
|
||||
*/
|
||||
struct subcommand addoptions[] = {
|
||||
{ "user", 2, telnetdAddUser, "Add a new user for the debug interface. E.g. add user john today",
|
||||
{ARG_TYPE_STRING, ARG_TYPE_STRING, 0} },
|
||||
{ NULL, 0, NULL, NULL,
|
||||
{0, 0, 0} }
|
||||
};
|
||||
|
||||
/**
|
||||
* The debug command table
|
||||
*/
|
||||
static struct {
|
||||
char *cmd;
|
||||
struct subcommand *options;
|
||||
} cmds[] = {
|
||||
{ "add", addoptions },
|
||||
{ "clear", clearoptions },
|
||||
{ "restart", restartoptions },
|
||||
{ "set", setoptions },
|
||||
{ "show", showoptions },
|
||||
{ "shutdown", shutdownoptions },
|
||||
{ "reload", reloadoptions },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Convert a string argument to a numeric, observing prefixes
|
||||
* for number bases, e.g. 0x for hex, 0 for octal
|
||||
*
|
||||
* @param arg The string representation of the argument
|
||||
* @param arg_type The target type for the argument
|
||||
* @return The argument as a long integer
|
||||
*/
|
||||
static unsigned long
|
||||
convert_arg(char *arg, int arg_type)
|
||||
{
|
||||
switch (arg_type)
|
||||
{
|
||||
case ARG_TYPE_ADDRESS:
|
||||
return (unsigned long)strtol(arg, NULL, 0);
|
||||
case ARG_TYPE_STRING:
|
||||
return (unsigned long)arg;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* We have a complete line from the user, lookup the commands and execute them
|
||||
*
|
||||
* Commands are tokenised based on white space and then the firt
|
||||
* word is checked againts the cmds table. If a amtch is found the
|
||||
* second word is compared to the different options for that command.
|
||||
*
|
||||
* Commands may also take up to 3 additional arguments, these are all
|
||||
* assumed to the numeric values and will be converted before being passed
|
||||
* to the handler function for the command.
|
||||
*
|
||||
* @param cli The CLI_SESSION
|
||||
* @return Returns 0 if the interpreter should exit
|
||||
*/
|
||||
int
|
||||
execute_cmd(CLI_SESSION *cli)
|
||||
{
|
||||
DCB *dcb = cli->session->client;
|
||||
int argc, i, j, found = 0;
|
||||
char *args[MAXARGS];
|
||||
char *saveptr, *delim = " \t\r\n";
|
||||
unsigned long arg1, arg2, arg3;
|
||||
|
||||
/* Tokenize the input string */
|
||||
args[0] = strtok_r(cli->cmdbuf, delim, &saveptr);
|
||||
i = 0;
|
||||
do {
|
||||
i++;
|
||||
args[i] = strtok_r(NULL, delim, &saveptr);
|
||||
} while (args[i] != NULL && i < MAXARGS);
|
||||
|
||||
if (args[0] == NULL)
|
||||
return 1;
|
||||
argc = i - 2; /* The number of extra arguments to commands */
|
||||
|
||||
|
||||
if (!strcasecmp(args[0], "help"))
|
||||
{
|
||||
if (args[1] == NULL)
|
||||
{
|
||||
found = 1;
|
||||
dcb_printf(dcb, "Available commands:\n");
|
||||
for (i = 0; cmds[i].cmd; i++)
|
||||
{
|
||||
for (j = 0; cmds[i].options[j].arg1; j++)
|
||||
{
|
||||
dcb_printf(dcb, " %s %s\n", cmds[i].cmd, cmds[i].options[j].arg1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; cmds[i].cmd; i++)
|
||||
{
|
||||
if (!strcasecmp(args[1], cmds[i].cmd))
|
||||
{
|
||||
found = 1;
|
||||
dcb_printf(dcb, "Available options to the %s command:\n", args[1]);
|
||||
for (j = 0; cmds[i].options[j].arg1; j++)
|
||||
{
|
||||
dcb_printf(dcb, " %-10s %s\n", cmds[i].options[j].arg1,
|
||||
cmds[i].options[j].help);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found == 0)
|
||||
{
|
||||
dcb_printf(dcb, "No command %s to offer help with\n", args[1]);
|
||||
}
|
||||
}
|
||||
found = 1;
|
||||
}
|
||||
else if (!strcasecmp(args[0], "quit"))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (argc >= 0)
|
||||
{
|
||||
for (i = 0; cmds[i].cmd; i++)
|
||||
{
|
||||
if (strcasecmp(args[0], cmds[i].cmd) == 0)
|
||||
{
|
||||
for (j = 0; cmds[i].options[j].arg1; j++)
|
||||
{
|
||||
if (strcasecmp(args[1], cmds[i].options[j].arg1) == 0)
|
||||
{
|
||||
if (argc != cmds[i].options[j].n_args)
|
||||
{
|
||||
dcb_printf(dcb, "Incorrect number of arguments: %s %s expects %d arguments\n",
|
||||
cmds[i].cmd, cmds[i].options[j].arg1,
|
||||
cmds[i].options[j].n_args);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (cmds[i].options[j].n_args)
|
||||
{
|
||||
case 0:
|
||||
cmds[i].options[j].fn(dcb);
|
||||
break;
|
||||
case 1:
|
||||
arg1 = convert_arg(args[2],cmds[i].options[j].arg_types[0]);
|
||||
if (arg1)
|
||||
cmds[i].options[j].fn(dcb, arg1);
|
||||
else
|
||||
dcb_printf(dcb, "Invalid argument: %s\n",
|
||||
args[2]);
|
||||
break;
|
||||
case 2:
|
||||
arg1 = convert_arg(args[2],cmds[i].options[j].arg_types[0]);
|
||||
arg2 = convert_arg(args[3],cmds[i].options[j].arg_types[1]);
|
||||
if (arg1 && arg2)
|
||||
cmds[i].options[j].fn(dcb, arg1, arg2);
|
||||
else if (arg1 == 0)
|
||||
dcb_printf(dcb, "Invalid argument: %s\n",
|
||||
args[2]);
|
||||
else
|
||||
dcb_printf(dcb, "Invalid argument: %s\n",
|
||||
args[3]);
|
||||
break;
|
||||
case 3:
|
||||
arg1 = convert_arg(args[2],cmds[i].options[j].arg_types[0]);
|
||||
arg2 = convert_arg(args[3],cmds[i].options[j].arg_types[1]);
|
||||
arg3 = convert_arg(args[4],cmds[i].options[j].arg_types[2]);
|
||||
if (arg1 && arg2 && arg3)
|
||||
cmds[i].options[j].fn(dcb, arg1, arg2, arg3);
|
||||
else if (arg1 == 0)
|
||||
dcb_printf(dcb, "Invalid argument: %s\n",
|
||||
args[2]);
|
||||
else if (arg2 == 0)
|
||||
dcb_printf(dcb, "Invalid argument: %s\n",
|
||||
args[3]);
|
||||
else if (arg3 == 0)
|
||||
dcb_printf(dcb, "Invalid argument: %s\n",
|
||||
args[4]);
|
||||
}
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
dcb_printf(dcb,
|
||||
"Unknown option for the %s command. Valid sub-commands are:\n",
|
||||
cmds[i].cmd);
|
||||
for (j = 0; cmds[i].options[j].arg1; j++)
|
||||
{
|
||||
dcb_printf(dcb, " %-10s %s\n", cmds[i].options[j].arg1,
|
||||
cmds[i].options[j].help);
|
||||
}
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (argc == -1)
|
||||
{
|
||||
dcb_printf(dcb,
|
||||
"Commands must consist of at least two words. Type help for a list of commands\n");
|
||||
found = 1;
|
||||
}
|
||||
if (!found)
|
||||
dcb_printf(dcb,
|
||||
"Command '%s' not known, type help for a list of available commands\n", args[0]);
|
||||
memset(cli->cmdbuf, 0, 80);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug command to stop a service
|
||||
*
|
||||
* @param dcb The DCB to print any output to
|
||||
* @param service The service to shutdown
|
||||
*/
|
||||
static void
|
||||
shutdown_service(DCB *dcb, SERVICE *service)
|
||||
{
|
||||
serviceStop(service);
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug command to restart a stopped service
|
||||
*
|
||||
* @param dcb The DCB to print any output to
|
||||
* @param service The service to restart
|
||||
*/
|
||||
static void
|
||||
restart_service(DCB *dcb, SERVICE *service)
|
||||
{
|
||||
serviceRestart(service);
|
||||
}
|
||||
|
||||
static struct {
|
||||
char *str;
|
||||
unsigned int bit;
|
||||
} ServerBits[] = {
|
||||
{ "running", SERVER_RUNNING },
|
||||
{ "master", SERVER_MASTER },
|
||||
{ "slave", SERVER_SLAVE },
|
||||
{ "joined", SERVER_JOINED },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
/**
|
||||
* Map the server status bit
|
||||
*
|
||||
* @param str String representation
|
||||
* @return bit value or 0 on error
|
||||
*/
|
||||
static unsigned int
|
||||
server_map_status(char *str)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; ServerBits[i].str; i++)
|
||||
if (!strcasecmp(str, ServerBits[i].str))
|
||||
return ServerBits[i].bit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the status bit of a server
|
||||
*
|
||||
* @param dcb DCB to send output to
|
||||
* @param server The server to set the status of
|
||||
* @param bit String representation of the status bit
|
||||
*/
|
||||
static void
|
||||
set_server(DCB *dcb, SERVER *server, char *bit)
|
||||
{
|
||||
unsigned int bitvalue;
|
||||
|
||||
if ((bitvalue = server_map_status(bit)) != 0)
|
||||
server_set_status(server, bitvalue);
|
||||
else
|
||||
dcb_printf(dcb, "Unknown status bit %s\n", bit);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clear the status bit of a server
|
||||
*
|
||||
* @param dcb DCB to send output to
|
||||
* @param server The server to set the status of
|
||||
* @param bit String representation of the status bit
|
||||
*/
|
||||
static void
|
||||
clear_server(DCB *dcb, SERVER *server, char *bit)
|
||||
{
|
||||
unsigned int bitvalue;
|
||||
|
||||
if ((bitvalue = server_map_status(bit)) != 0)
|
||||
server_clear_status(server, bitvalue);
|
||||
else
|
||||
dcb_printf(dcb, "Unknown status bit %s\n", bit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload the authenticaton data from the backend database of a service.
|
||||
*
|
||||
* @param dcb DCB to send output
|
||||
* @param service The service to update
|
||||
*/
|
||||
static void
|
||||
reload_users(DCB *dcb, SERVICE *service)
|
||||
{
|
||||
dcb_printf(dcb, "Loaded %d database users for server %s.\n",
|
||||
reload_mysql_users(service), service->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Relaod the configuration data from the config file
|
||||
*
|
||||
* @param dcb DCB to use to send output
|
||||
*/
|
||||
static void
|
||||
reload_config(DCB *dcb)
|
||||
{
|
||||
dcb_printf(dcb, "Reloading configuration from file.\n");
|
||||
config_reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new admin user
|
||||
*
|
||||
* @param dcb The DCB for messages
|
||||
* @param user The user name
|
||||
* @param passwd The Password of the user
|
||||
*/
|
||||
static void
|
||||
telnetdAddUser(DCB *dcb, char *user, char *passwd)
|
||||
{
|
||||
char *err;
|
||||
|
||||
if (admin_test_user(user))
|
||||
{
|
||||
dcb_printf(dcb, "User %s already exists.\n", user);
|
||||
return;
|
||||
}
|
||||
if ((err = admin_add_user(user, passwd)) == NULL)
|
||||
dcb_printf(dcb, "User %s has been successfully added.\n", user);
|
||||
else
|
||||
dcb_printf(dcb, "Failed to add new user. %s\n", err);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the adminsitration users
|
||||
*
|
||||
* @param dcb The DCB to print the user data to
|
||||
*/
|
||||
static void
|
||||
telnetdShowUsers(DCB *dcb)
|
||||
{
|
||||
dcb_printf(dcb, "Administration interface users:\n");
|
||||
dcb_PrintAdminUsers(dcb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Command to shutdown a running monitor
|
||||
*
|
||||
* @param dcb The DCB to use to print messages
|
||||
* @param monitor The monitor to shutdown
|
||||
*/
|
||||
static void
|
||||
shutdown_monitor(DCB *dcb, MONITOR *monitor)
|
||||
{
|
||||
monitorStop(monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Command to restart a stopped monitor
|
||||
*
|
||||
* @param dcb The DCB to use to print messages
|
||||
* @param monitor The monitor to restart
|
||||
*/
|
||||
static void
|
||||
restart_monitor(DCB *dcb, MONITOR *monitor)
|
||||
{
|
||||
monitorStart(monitor);
|
||||
}
|
||||
128
server/modules/routing/depend.mk
Normal file
128
server/modules/routing/depend.mk
Normal file
@ -0,0 +1,128 @@
|
||||
testroute.o: testroute.c /usr/include/stdio.h /usr/include/features.h \
|
||||
/usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \
|
||||
/usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stddef.h \
|
||||
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
|
||||
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdarg.h \
|
||||
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
|
||||
../../include/router.h ../../include/service.h /usr/include/time.h \
|
||||
/usr/include/bits/time.h /usr/include/xlocale.h ../../include/spinlock.h \
|
||||
../../include/thread.h /usr/include/pthread.h /usr/include/endian.h \
|
||||
/usr/include/bits/endian.h /usr/include/bits/byteswap.h \
|
||||
/usr/include/sched.h /usr/include/bits/sched.h \
|
||||
/usr/include/bits/pthreadtypes.h /usr/include/bits/setjmp.h \
|
||||
../../include/dcb.h ../../include/buffer.h ../../include/gwbitmask.h \
|
||||
../../include/server.h ../../include/session.h
|
||||
readconnroute.o: readconnroute.c /usr/include/stdio.h \
|
||||
/usr/include/features.h /usr/include/sys/cdefs.h \
|
||||
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
|
||||
/usr/include/gnu/stubs-64.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stddef.h \
|
||||
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
|
||||
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdarg.h \
|
||||
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
|
||||
/usr/include/stdlib.h /usr/include/bits/waitflags.h \
|
||||
/usr/include/bits/waitstatus.h /usr/include/endian.h \
|
||||
/usr/include/bits/endian.h /usr/include/bits/byteswap.h \
|
||||
/usr/include/sys/types.h /usr/include/time.h /usr/include/sys/select.h \
|
||||
/usr/include/bits/select.h /usr/include/bits/sigset.h \
|
||||
/usr/include/bits/time.h /usr/include/sys/sysmacros.h \
|
||||
/usr/include/bits/pthreadtypes.h /usr/include/alloca.h \
|
||||
/usr/include/string.h /usr/include/xlocale.h ../../include/service.h \
|
||||
../../include/spinlock.h ../../include/thread.h /usr/include/pthread.h \
|
||||
/usr/include/sched.h /usr/include/bits/sched.h \
|
||||
/usr/include/bits/setjmp.h ../../include/dcb.h ../../include/buffer.h \
|
||||
../../include/gwbitmask.h ../../include/server.h ../../include/router.h \
|
||||
../../include/session.h ../../include/atomic.h \
|
||||
../include/readconnection.h \
|
||||
/home/mriddoch/Repository/skygateway/utils/skygw_types.h \
|
||||
/usr/include/math.h /usr/include/bits/huge_val.h \
|
||||
/usr/include/bits/huge_valf.h /usr/include/bits/huge_vall.h \
|
||||
/usr/include/bits/inf.h /usr/include/bits/nan.h \
|
||||
/usr/include/bits/mathdef.h /usr/include/bits/mathcalls.h \
|
||||
/home/mriddoch/Repository/skygateway/utils/skygw_utils.h \
|
||||
/home/mriddoch/Repository/skygateway/utils/skygw_types.h \
|
||||
/home/mriddoch/Repository/skygateway/utils/skygw_debug.h \
|
||||
/usr/include/assert.h /usr/include/unistd.h \
|
||||
/usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
|
||||
/usr/include/bits/confname.h /usr/include/getopt.h \
|
||||
/home/mriddoch/Repository/skygateway/log_manager/log_manager.h \
|
||||
../include/mysql_client_server_protocol.h /usr/include/stdint.h \
|
||||
/usr/include/bits/wchar.h /usr/include/openssl/sha.h \
|
||||
/usr/include/openssl/e_os2.h /usr/include/openssl/opensslconf.h \
|
||||
/usr/include/openssl/opensslconf-x86_64.h /usr/include/sys/ioctl.h \
|
||||
/usr/include/bits/ioctls.h /usr/include/asm/ioctls.h \
|
||||
/usr/include/asm-generic/ioctls.h /usr/include/linux/ioctl.h \
|
||||
/usr/include/asm/ioctl.h /usr/include/asm-generic/ioctl.h \
|
||||
/usr/include/bits/ioctl-types.h /usr/include/sys/ttydefaults.h \
|
||||
/usr/include/errno.h /usr/include/bits/errno.h \
|
||||
/usr/include/linux/errno.h /usr/include/asm/errno.h \
|
||||
/usr/include/asm-generic/errno.h /usr/include/asm-generic/errno-base.h \
|
||||
/usr/include/sys/socket.h /usr/include/sys/uio.h /usr/include/bits/uio.h \
|
||||
/usr/include/bits/socket.h /usr/include/bits/sockaddr.h \
|
||||
/usr/include/asm/socket.h /usr/include/asm-generic/socket.h \
|
||||
/usr/include/asm/sockios.h /usr/include/asm-generic/sockios.h \
|
||||
/usr/include/netinet/in.h /usr/include/bits/in.h \
|
||||
/usr/include/arpa/inet.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdbool.h \
|
||||
/usr/include/fcntl.h /usr/include/bits/fcntl.h /usr/include/bits/stat.h \
|
||||
../../include/poll.h ../../include/users.h ../../include/hashtable.h
|
||||
debugcli.o: debugcli.c /usr/include/stdio.h /usr/include/features.h \
|
||||
/usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \
|
||||
/usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stddef.h \
|
||||
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
|
||||
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdarg.h \
|
||||
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
|
||||
/usr/include/stdlib.h /usr/include/bits/waitflags.h \
|
||||
/usr/include/bits/waitstatus.h /usr/include/endian.h \
|
||||
/usr/include/bits/endian.h /usr/include/bits/byteswap.h \
|
||||
/usr/include/sys/types.h /usr/include/time.h /usr/include/sys/select.h \
|
||||
/usr/include/bits/select.h /usr/include/bits/sigset.h \
|
||||
/usr/include/bits/time.h /usr/include/sys/sysmacros.h \
|
||||
/usr/include/bits/pthreadtypes.h /usr/include/alloca.h \
|
||||
/usr/include/string.h /usr/include/xlocale.h ../../include/service.h \
|
||||
../../include/spinlock.h ../../include/thread.h /usr/include/pthread.h \
|
||||
/usr/include/sched.h /usr/include/bits/sched.h \
|
||||
/usr/include/bits/setjmp.h ../../include/dcb.h ../../include/buffer.h \
|
||||
../../include/gwbitmask.h ../../include/server.h ../../include/session.h \
|
||||
../../include/router.h ../../include/modules.h ../../include/atomic.h \
|
||||
../../include/poll.h ../include/debugcli.h \
|
||||
/home/mriddoch/Repository/skygateway/utils/skygw_utils.h \
|
||||
/home/mriddoch/Repository/skygateway/utils/skygw_types.h \
|
||||
/usr/include/math.h /usr/include/bits/huge_val.h \
|
||||
/usr/include/bits/huge_valf.h /usr/include/bits/huge_vall.h \
|
||||
/usr/include/bits/inf.h /usr/include/bits/nan.h \
|
||||
/usr/include/bits/mathdef.h /usr/include/bits/mathcalls.h \
|
||||
/home/mriddoch/Repository/skygateway/utils/skygw_debug.h \
|
||||
/usr/include/assert.h /usr/include/unistd.h \
|
||||
/usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
|
||||
/usr/include/bits/confname.h /usr/include/getopt.h \
|
||||
/home/mriddoch/Repository/skygateway/log_manager/log_manager.h
|
||||
debugcmd.o: debugcmd.c /usr/include/stdio.h /usr/include/features.h \
|
||||
/usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \
|
||||
/usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stddef.h \
|
||||
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
|
||||
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdarg.h \
|
||||
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
|
||||
/usr/include/stdlib.h /usr/include/bits/waitflags.h \
|
||||
/usr/include/bits/waitstatus.h /usr/include/endian.h \
|
||||
/usr/include/bits/endian.h /usr/include/bits/byteswap.h \
|
||||
/usr/include/sys/types.h /usr/include/time.h /usr/include/sys/select.h \
|
||||
/usr/include/bits/select.h /usr/include/bits/sigset.h \
|
||||
/usr/include/bits/time.h /usr/include/sys/sysmacros.h \
|
||||
/usr/include/bits/pthreadtypes.h /usr/include/alloca.h \
|
||||
/usr/include/string.h /usr/include/xlocale.h ../../include/service.h \
|
||||
../../include/spinlock.h ../../include/thread.h /usr/include/pthread.h \
|
||||
/usr/include/sched.h /usr/include/bits/sched.h \
|
||||
/usr/include/bits/setjmp.h ../../include/dcb.h ../../include/buffer.h \
|
||||
../../include/gwbitmask.h ../../include/server.h ../../include/session.h \
|
||||
../../include/router.h ../../include/modules.h ../../include/atomic.h \
|
||||
../../include/poll.h ../../include/users.h ../../include/hashtable.h \
|
||||
../../include/dbusers.h ../../include/config.h ../include/telnetd.h \
|
||||
../../include/adminusers.h ../include/debugcli.h
|
||||
452
server/modules/routing/readconnroute.c
Normal file
452
server/modules/routing/readconnroute.c
Normal file
@ -0,0 +1,452 @@
|
||||
/*
|
||||
* This file is distributed as part of the SkySQL Gateway. 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 2013
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file readconnroute.c - Read Connection Load Balancing Query Router
|
||||
*
|
||||
* This is the implementation of a simple query router that balances
|
||||
* read connections. It assumes the service is configured with a set
|
||||
* of slaves and that the application clients already split read and write
|
||||
* queries. It offers a service to balance the client read connections
|
||||
* over this set of slave servers. It does this once only, at the time
|
||||
* the connection is made. It chooses the server that currently has the least
|
||||
* number of connections by keeping a count for each server of how
|
||||
* many connections the query router has made to the server.
|
||||
*
|
||||
* When two servers have the same number of current connections the one with
|
||||
* the least number of connections since startup will be used.
|
||||
*
|
||||
* The router may also have options associated to it that will limit the
|
||||
* choice of backend server. Currently two options are supported, the "master"
|
||||
* option will cause the router to only connect to servers marked as masters
|
||||
* and the "slave" option will limit connections to routers that are marked
|
||||
* as slaves. If neither option is specified the router will connect to either
|
||||
* masters or slaves.
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 14/06/2013 Mark Riddoch Initial implementation
|
||||
* 25/06/2013 Mark Riddoch Addition of checks for current server state
|
||||
* 26/06/2013 Mark Riddoch Use server with least connections since
|
||||
* startup if the number of current
|
||||
* connections is the same for two servers
|
||||
* Addition of master and slave options
|
||||
* 27/06/2013 Vilho Raatikka Added skygw_log_write command as an example
|
||||
* and necessary headers.
|
||||
* 17/07/2013 Massimiliano Pinto Added clientReply routine:
|
||||
* called by backend server to send data to client
|
||||
* Included mysql_client_server_protocol.h
|
||||
* with macros and MySQL commands with MYSQL_ prefix
|
||||
* avoiding any conflict with the standard ones
|
||||
* in mysql.h
|
||||
* 22/07/13 Mark Riddoch Addition of joined router option for Galera
|
||||
* clusters
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <service.h>
|
||||
#include <server.h>
|
||||
#include <router.h>
|
||||
#include <atomic.h>
|
||||
#include <spinlock.h>
|
||||
#include <readconnection.h>
|
||||
#include <dcb.h>
|
||||
#include <spinlock.h>
|
||||
|
||||
#include <skygw_types.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
|
||||
#include <mysql_client_server_protocol.h>
|
||||
|
||||
static char *version_str = "V1.0.2";
|
||||
|
||||
/* The router entry points */
|
||||
static ROUTER *createInstance(SERVICE *service, char **options);
|
||||
static void *newSession(ROUTER *instance, SESSION *session);
|
||||
static void closeSession(ROUTER *instance, void *router_session);
|
||||
static int routeQuery(ROUTER *instance, void *router_session, GWBUF *queue);
|
||||
static void diagnostics(ROUTER *instance, DCB *dcb);
|
||||
static void clientReply(ROUTER* instance, void* router_session, GWBUF* queue, DCB *backend_dcb);
|
||||
|
||||
/** The module object definition */
|
||||
static ROUTER_OBJECT MyObject = { createInstance, newSession, closeSession, routeQuery, diagnostics, clientReply };
|
||||
|
||||
static SPINLOCK instlock;
|
||||
static INSTANCE *instances;
|
||||
|
||||
/**
|
||||
* Implementation of the mandatory version entry point
|
||||
*
|
||||
* @return version string of the module
|
||||
*/
|
||||
char *
|
||||
version()
|
||||
{
|
||||
return version_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module initialisation routine, called when the module
|
||||
* is first loaded.
|
||||
*/
|
||||
void
|
||||
ModuleInit()
|
||||
{
|
||||
skygw_log_write(NULL,
|
||||
LOGFILE_MESSAGE,
|
||||
"Initialise readconnroute router module %s.\n", version_str);
|
||||
spinlock_init(&instlock);
|
||||
instances = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module entry point routine. It is this routine that
|
||||
* must populate the structure that is referred to as the
|
||||
* "module object", this is a structure with the set of
|
||||
* external entry points for this module.
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
ROUTER_OBJECT *
|
||||
GetModuleObject()
|
||||
{
|
||||
return &MyObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the router for a particular service
|
||||
* within the gateway.
|
||||
*
|
||||
* @param service The service this router is being create for
|
||||
* @param options An array of options for this query router
|
||||
*
|
||||
* @return The instance data for this new instance
|
||||
*/
|
||||
static ROUTER *
|
||||
createInstance(SERVICE *service, char **options)
|
||||
{
|
||||
INSTANCE *inst;
|
||||
SERVER *server;
|
||||
int i, n;
|
||||
|
||||
if ((inst = malloc(sizeof(INSTANCE))) == NULL)
|
||||
return NULL;
|
||||
|
||||
memset(&inst->stats, 0, sizeof(ROUTER_STATS));
|
||||
|
||||
inst->service = service;
|
||||
spinlock_init(&inst->lock);
|
||||
inst->connections = NULL;
|
||||
|
||||
/*
|
||||
* We need an array of the backend servers in the instance structure so
|
||||
* that we can maintain a count of the number of connections to each
|
||||
* backend server.
|
||||
*/
|
||||
for (server = service->databases, n = 0; server; server = server->nextdb)
|
||||
n++;
|
||||
|
||||
inst->servers = (BACKEND **)calloc(n + 1, sizeof(BACKEND *));
|
||||
if (!inst->servers)
|
||||
{
|
||||
free(inst);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (server = service->databases, n = 0; server; server = server->nextdb)
|
||||
{
|
||||
if ((inst->servers[n] = malloc(sizeof(BACKEND))) == NULL)
|
||||
{
|
||||
for (i = 0; i < n; i++)
|
||||
free(inst->servers[i]);
|
||||
free(inst->servers);
|
||||
free(inst);
|
||||
return NULL;
|
||||
}
|
||||
inst->servers[n]->server = server;
|
||||
inst->servers[n]->count = 0;
|
||||
n++;
|
||||
}
|
||||
inst->servers[n] = NULL;
|
||||
|
||||
/*
|
||||
* Process the options
|
||||
*/
|
||||
inst->bitmask = 0;
|
||||
inst->bitvalue = 0;
|
||||
if (options)
|
||||
{
|
||||
for (i = 0; options[i]; i++)
|
||||
{
|
||||
if (!strcasecmp(options[i], "master"))
|
||||
{
|
||||
inst->bitmask |= (SERVER_MASTER|SERVER_SLAVE);
|
||||
inst->bitvalue |= SERVER_MASTER;
|
||||
}
|
||||
else if (!strcasecmp(options[i], "slave"))
|
||||
{
|
||||
inst->bitmask |= (SERVER_MASTER|SERVER_SLAVE);
|
||||
inst->bitvalue |= SERVER_SLAVE;
|
||||
}
|
||||
else if (!strcasecmp(options[i], "joined"))
|
||||
{
|
||||
inst->bitmask |= (SERVER_JOINED);
|
||||
inst->bitvalue |= SERVER_JOINED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We have completed the creation of the instance data, so now
|
||||
* insert this router instance into the linked list of routers
|
||||
* that have been created with this module.
|
||||
*/
|
||||
spinlock_acquire(&instlock);
|
||||
inst->next = instances;
|
||||
instances = inst;
|
||||
spinlock_release(&instlock);
|
||||
|
||||
return (ROUTER *)inst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a new session with this instance of the router.
|
||||
*
|
||||
* @param instance The router instance data
|
||||
* @param session The session itself
|
||||
* @return Session specific data for this session
|
||||
*/
|
||||
static void *
|
||||
newSession(ROUTER *instance, SESSION *session)
|
||||
{
|
||||
INSTANCE *inst = (INSTANCE *)instance;
|
||||
CLIENT_SESSION *client;
|
||||
BACKEND *candidate = NULL;
|
||||
int i;
|
||||
|
||||
if ((client = (CLIENT_SESSION *)malloc(sizeof(CLIENT_SESSION))) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
* Find a backend server to connect to. This is the extent of the
|
||||
* load balancing algorithm we need to implement for this simple
|
||||
* connection router.
|
||||
*/
|
||||
|
||||
/* First find a running server to set as our initial candidate server */
|
||||
for (i = 0; inst->servers[i]; i++)
|
||||
{
|
||||
if (inst->servers[i] && SERVER_IS_RUNNING(inst->servers[i]->server)
|
||||
&& (inst->servers[i]->server->status & inst->bitmask) == inst->bitvalue)
|
||||
{
|
||||
candidate = inst->servers[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Loop over all the servers and find any that have fewer connections than our
|
||||
* candidate server.
|
||||
*
|
||||
* If a server has less connections than the current candidate we mark this
|
||||
* as the new candidate to connect to.
|
||||
*
|
||||
* If a server has the same number of connections currently as the candidate
|
||||
* and has had less connections over time than the candidate it will also
|
||||
* become the new candidate. This has the effect of spreading the connections
|
||||
* over different servers during periods of very low load.
|
||||
*/
|
||||
for (i = 1; inst->servers[i]; i++)
|
||||
{
|
||||
if (inst->servers[i] && SERVER_IS_RUNNING(inst->servers[i]->server)
|
||||
&& (inst->servers[i]->server->status & inst->bitmask) == inst->bitvalue)
|
||||
{
|
||||
if (inst->servers[i]->count < candidate->count)
|
||||
{
|
||||
candidate = inst->servers[i];
|
||||
}
|
||||
else if (inst->servers[i]->count == candidate->count &&
|
||||
inst->servers[i]->server->stats.n_connections
|
||||
< candidate->server->stats.n_connections)
|
||||
{
|
||||
candidate = inst->servers[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We now have the server with the least connections.
|
||||
* Bump the connection count for this server
|
||||
*/
|
||||
atomic_add(&candidate->count, 1);
|
||||
|
||||
client->backend = candidate;
|
||||
|
||||
/*
|
||||
* Open a backend connection, putting the DCB for this
|
||||
* connection in the client->dcb
|
||||
*/
|
||||
|
||||
if ((client->dcb = dcb_connect(candidate->server, session,
|
||||
candidate->server->protocol)) == NULL)
|
||||
{
|
||||
atomic_add(&candidate->count, -1);
|
||||
free(client);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inst->stats.n_sessions++;
|
||||
|
||||
/* Add this session to the list of active sessions */
|
||||
spinlock_acquire(&inst->lock);
|
||||
client->next = inst->connections;
|
||||
inst->connections = client;
|
||||
spinlock_release(&inst->lock);
|
||||
return (void *)client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a session with the router, this is the mechanism
|
||||
* by which a router may cleanup data structure etc.
|
||||
*
|
||||
* @param instance The router instance data
|
||||
* @param router_session The session being closed
|
||||
*/
|
||||
static void
|
||||
closeSession(ROUTER *instance, void *router_session)
|
||||
{
|
||||
INSTANCE *inst = (INSTANCE *)instance;
|
||||
CLIENT_SESSION *session = (CLIENT_SESSION *)router_session;
|
||||
|
||||
/*
|
||||
* Close the connection to the backend
|
||||
*/
|
||||
session->dcb->func.close(session->dcb);
|
||||
atomic_add(&session->backend->count, -1);
|
||||
atomic_add(&session->backend->server->stats.n_current, -1);
|
||||
|
||||
spinlock_acquire(&inst->lock);
|
||||
if (inst->connections == session)
|
||||
inst->connections = session->next;
|
||||
else
|
||||
{
|
||||
CLIENT_SESSION *ptr = inst->connections;
|
||||
while (ptr && ptr->next != session)
|
||||
ptr = ptr->next;
|
||||
if (ptr)
|
||||
ptr->next = session->next;
|
||||
}
|
||||
spinlock_release(&inst->lock);
|
||||
|
||||
/*
|
||||
* We are no longer in the linked list, free
|
||||
* all the memory and other resources associated
|
||||
* to the client session.
|
||||
*/
|
||||
free(session);
|
||||
}
|
||||
|
||||
/**
|
||||
* We have data from the client, we must route it to the backend.
|
||||
* This is simply a case of sending it to the connection that was
|
||||
* chosen when we started the client session.
|
||||
*
|
||||
* @param instance The router instance
|
||||
* @param router_session The router session returned from the newSession call
|
||||
* @param queue The queue of data buffers to route
|
||||
* @return The number of bytes sent
|
||||
*/
|
||||
static int
|
||||
routeQuery(ROUTER *instance, void *router_session, GWBUF *queue)
|
||||
{
|
||||
INSTANCE *inst = (INSTANCE *)instance;
|
||||
CLIENT_SESSION *session = (CLIENT_SESSION *)router_session;
|
||||
uint8_t *payload = GWBUF_DATA(queue);
|
||||
int mysql_command = -1;
|
||||
|
||||
inst->stats.n_queries++;
|
||||
|
||||
mysql_command = MYSQL_GET_COMMAND(payload);
|
||||
|
||||
switch(mysql_command) {
|
||||
case MYSQL_COM_CHANGE_USER:
|
||||
return session->dcb->func.auth(session->dcb, NULL, session->dcb->session, queue);
|
||||
default:
|
||||
return session->dcb->func.write(session->dcb, queue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display router diagnostics
|
||||
*
|
||||
* @param instance Instance of the router
|
||||
* @param dcb DCB to send diagnostics to
|
||||
*/
|
||||
static void
|
||||
diagnostics(ROUTER *instance, DCB *dcb)
|
||||
{
|
||||
INSTANCE *inst = (INSTANCE *)instance;
|
||||
CLIENT_SESSION *session;
|
||||
int i = 0;
|
||||
|
||||
spinlock_acquire(&inst->lock);
|
||||
session = inst->connections;
|
||||
while (session)
|
||||
{
|
||||
i++;
|
||||
session = session->next;
|
||||
}
|
||||
spinlock_release(&inst->lock);
|
||||
|
||||
dcb_printf(dcb, "\tNumber of router sessions: %d\n", inst->stats.n_sessions);
|
||||
dcb_printf(dcb, "\tCurrent no. of router sessions: %d\n", i);
|
||||
dcb_printf(dcb, "\tNumber of queries forwarded: %d\n", inst->stats.n_queries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Client Reply routine
|
||||
*
|
||||
* The routine will reply to client data from backend server
|
||||
*
|
||||
* @param instance The router instance
|
||||
* @param router_session The router session
|
||||
* @param backend_dcb The backend DCB
|
||||
* @param queue The GWBUF with reply data
|
||||
*/
|
||||
static void
|
||||
clientReply(ROUTER* instance, void* router_session, GWBUF* queue, DCB *backend_dcb)
|
||||
{
|
||||
INSTANCE* inst = NULL;
|
||||
DCB *client = NULL;
|
||||
CLIENT_SESSION* session = NULL;
|
||||
|
||||
inst = (INSTANCE *)instance;
|
||||
session = (CLIENT_SESSION *)router_session;
|
||||
|
||||
client = backend_dcb->session->client;
|
||||
|
||||
client->func.write(client, queue);
|
||||
}
|
||||
///
|
||||
65
server/modules/routing/readwritesplit/Makefile
Normal file
65
server/modules/routing/readwritesplit/Makefile
Normal file
@ -0,0 +1,65 @@
|
||||
# This file is distributed as part of the SkySQL Gateway. 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 2013
|
||||
#
|
||||
# Revision History
|
||||
# Date Who Description
|
||||
# 27/06/13 Mark Riddoch Initial framework put in place
|
||||
|
||||
include ../../../../build_gateway.inc
|
||||
|
||||
LOGPATH := $(ROOT_PATH)/log_manager
|
||||
UTILSPATH := $(ROOT_PATH)/utils
|
||||
QCLASSPATH := $(ROOT_PATH)/query_classifier
|
||||
|
||||
CC=cc
|
||||
CFLAGS=-c -fPIC -I/usr/include -I../../include -I../../../include \
|
||||
-I$(LOGPATH) -I$(UTILSPATH) -I$(QCLASSPATH) \
|
||||
-I$(MARIADB_SRC_PATH)/include -Wall -g
|
||||
|
||||
include ../../../../makefile.inc
|
||||
|
||||
LDFLAGS=-shared -L$(LOGPATH) -L$(QCLASSPATH) -L$(MARIADB_SRC_PATH)/libmysqld \
|
||||
-Wl,-rpath,$(DEST)/lib \
|
||||
-Wl,-rpath,$(LOGPATH) -Wl,-rpath,$(UTILSPATH) -Wl,-rpath,$(QCLASSPATH) \
|
||||
-Wl,-rpath,$(MARIADB_SRC_PATH)/libmysqld
|
||||
|
||||
SRCS=readwritesplit.c
|
||||
OBJ=$(SRCS:.c=.o)
|
||||
LIBS=-lssl -pthread -llog_manager -lquery_classifier -lmysqld
|
||||
MODULES=libreadwritesplit.so
|
||||
|
||||
all: $(MODULES)
|
||||
|
||||
libreadwritesplit.so: $(OBJ)
|
||||
$(CC) $(LDFLAGS) $(OBJ) $(UTILSPATH)/skygw_utils.o $(LIBS) -o $@
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(OBJ) $(MODULES)
|
||||
|
||||
tags:
|
||||
ctags $(SRCS) $(HDRS)
|
||||
|
||||
depend:
|
||||
@rm -f depend.mk
|
||||
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
||||
|
||||
install: $(MODULES)
|
||||
install -D $(MODULES) $(DEST)/MaxScale/modules
|
||||
|
||||
include depend.mk
|
||||
43
server/modules/routing/readwritesplit/depend.mk
Normal file
43
server/modules/routing/readwritesplit/depend.mk
Normal file
@ -0,0 +1,43 @@
|
||||
readwritesplit.o: readwritesplit.c /usr/include/stdio.h \
|
||||
/usr/include/features.h /usr/include/sys/cdefs.h \
|
||||
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
|
||||
/usr/include/gnu/stubs-64.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stddef.h \
|
||||
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
|
||||
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
|
||||
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdarg.h \
|
||||
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
|
||||
/usr/include/strings.h /usr/include/xlocale.h /usr/include/string.h \
|
||||
../../../include/router.h ../../../include/service.h /usr/include/time.h \
|
||||
/usr/include/bits/time.h ../../../include/spinlock.h \
|
||||
../../../include/thread.h /usr/include/pthread.h /usr/include/endian.h \
|
||||
/usr/include/bits/endian.h /usr/include/bits/byteswap.h \
|
||||
/usr/include/sched.h /usr/include/bits/sched.h \
|
||||
/usr/include/bits/pthreadtypes.h /usr/include/bits/setjmp.h \
|
||||
../../../include/dcb.h ../../../include/buffer.h \
|
||||
../../../include/gwbitmask.h ../../../include/server.h \
|
||||
../../../include/session.h ../../include/readwritesplit.h \
|
||||
/usr/include/stdlib.h /usr/include/bits/waitflags.h \
|
||||
/usr/include/bits/waitstatus.h /usr/include/sys/types.h \
|
||||
/usr/include/sys/select.h /usr/include/bits/select.h \
|
||||
/usr/include/bits/sigset.h /usr/include/sys/sysmacros.h \
|
||||
/usr/include/alloca.h /packages/mariadb-5.5.25/include/mysql.h \
|
||||
/packages/mariadb-5.5.25/include/mysql_version.h \
|
||||
/packages/mariadb-5.5.25/include/mysql_com.h \
|
||||
/packages/mariadb-5.5.25/include/mysql_time.h \
|
||||
/packages/mariadb-5.5.25/include/my_list.h \
|
||||
/packages/mariadb-5.5.25/include/typelib.h \
|
||||
/packages/mariadb-5.5.25/include/my_alloc.h \
|
||||
/home/mriddoch/Repository/skygateway/utils/skygw_utils.h \
|
||||
/home/mriddoch/Repository/skygateway/utils/skygw_types.h \
|
||||
/usr/include/math.h /usr/include/bits/huge_val.h \
|
||||
/usr/include/bits/huge_valf.h /usr/include/bits/huge_vall.h \
|
||||
/usr/include/bits/inf.h /usr/include/bits/nan.h \
|
||||
/usr/include/bits/mathdef.h /usr/include/bits/mathcalls.h \
|
||||
/home/mriddoch/Repository/skygateway/utils/skygw_debug.h \
|
||||
/usr/include/assert.h /usr/include/unistd.h \
|
||||
/usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
|
||||
/usr/include/bits/confname.h /usr/include/getopt.h \
|
||||
/home/mriddoch/Repository/skygateway/log_manager/log_manager.h \
|
||||
/home/mriddoch/Repository/skygateway/query_classifier/query_classifier.h \
|
||||
/home/mriddoch/Repository/skygateway/query_classifier/../utils/skygw_utils.h
|
||||
585
server/modules/routing/readwritesplit/readwritesplit.c
Normal file
585
server/modules/routing/readwritesplit/readwritesplit.c
Normal file
@ -0,0 +1,585 @@
|
||||
/*
|
||||
* This file is distributed as part of the SkySQL Gateway. 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 2013
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <strings.h>
|
||||
#include <string.h>
|
||||
#include <router.h>
|
||||
#include <readwritesplit.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <mysql.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
#include <query_classifier.h>
|
||||
#include <dcb.h>
|
||||
#include <spinlock.h>
|
||||
|
||||
/**
|
||||
* @file readwritesplit.c The entry points for the read/write query splitting
|
||||
* router module.
|
||||
*
|
||||
* This file contains the entry points that comprise the API to the read write
|
||||
* query splitting router.
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 01/07/2013 Vilho Raatikka Initial implementation
|
||||
* 15/07/2013 Massimiliano Pinto Added clientReply
|
||||
* from master only in case of session change
|
||||
* 17/07/2013 Massimiliano Pinto clientReply is now used by mysql_backend
|
||||
* for all reply situations
|
||||
* 18/07/2013 Massimiliano Pinto routeQuery now handles COM_QUIT
|
||||
* as QUERY_TYPE_SESSION_WRITE
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
static char *version_str = "V1.0.2";
|
||||
|
||||
static ROUTER* createInstance(SERVICE *service, char **options);
|
||||
static void* newSession(ROUTER *instance, SESSION *session);
|
||||
static void closeSession(ROUTER *instance, void *session);
|
||||
static int routeQuery(ROUTER *instance, void *session, GWBUF *queue);
|
||||
static void diagnostic(ROUTER *instance, DCB *dcb);
|
||||
static void clientReply(ROUTER* instance, void* router_session, GWBUF* queue, DCB *backend_dcb);
|
||||
|
||||
static ROUTER_OBJECT MyObject =
|
||||
{ createInstance,
|
||||
newSession,
|
||||
closeSession,
|
||||
routeQuery,
|
||||
diagnostic,
|
||||
clientReply };
|
||||
|
||||
static SPINLOCK instlock;
|
||||
static INSTANCE* instances;
|
||||
|
||||
/**
|
||||
* Implementation of the mandatory version entry point
|
||||
*
|
||||
* @return version string of the module
|
||||
*/
|
||||
char *
|
||||
version()
|
||||
{
|
||||
return version_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module initialisation routine, called when the module
|
||||
* is first loaded.
|
||||
*/
|
||||
void
|
||||
ModuleInit()
|
||||
{
|
||||
skygw_log_write_flush(NULL,
|
||||
LOGFILE_MESSAGE,
|
||||
"Initialize read/write split router module.\n");
|
||||
spinlock_init(&instlock);
|
||||
instances = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module entry point routine. It is this routine that
|
||||
* must populate the structure that is referred to as the
|
||||
* "module object", this is a structure with the set of
|
||||
* external entry points for this module.
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
ROUTER_OBJECT* GetModuleObject() {
|
||||
skygw_log_write(NULL,
|
||||
LOGFILE_TRACE,
|
||||
"Returning readwritesplit router module object.");
|
||||
return &MyObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the router for a particular service
|
||||
* within the gateway.
|
||||
*
|
||||
* The job of ths entry point is to create the service wide data needed
|
||||
* for the query router. This is information needed to route queries that
|
||||
* is not related to any individual client session, exmaples of data that
|
||||
* might be stored in the ROUTER object for a particular query router are
|
||||
* connections counts, last used connection etc so that balancing may
|
||||
* take place.
|
||||
*
|
||||
* @param service The service this router is being create for
|
||||
* @param options The options for this query router
|
||||
*
|
||||
* @return The instance data for this new instance
|
||||
*/
|
||||
static ROUTER* createInstance(
|
||||
SERVICE* service,
|
||||
char** options)
|
||||
{
|
||||
INSTANCE* inst;
|
||||
SERVER* server;
|
||||
int n;
|
||||
int i;
|
||||
|
||||
if ((inst = calloc(1, sizeof(INSTANCE))) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
inst->service = service;
|
||||
spinlock_init(&inst->lock);
|
||||
inst->connections = NULL;
|
||||
|
||||
/** Calculate number of servers */
|
||||
for (server = service->databases, n = 0; server; server = server->nextdb) {
|
||||
n++;
|
||||
}
|
||||
inst->servers = (BACKEND **)calloc(n + 1, sizeof(BACKEND *));
|
||||
|
||||
if (!inst->servers) {
|
||||
free(inst);
|
||||
return NULL;
|
||||
}
|
||||
/**
|
||||
* We need an array of the backend servers in the instance structure so
|
||||
* that we can maintain a count of the number of connections to each
|
||||
* backend server.
|
||||
*/
|
||||
for (server = service->databases, n = 0; server; server = server->nextdb) {
|
||||
|
||||
if ((inst->servers[n] = malloc(sizeof(BACKEND))) == NULL) {
|
||||
for (i = 0; i < n; i++) {
|
||||
free(inst->servers[i]);
|
||||
}
|
||||
free(inst->servers);
|
||||
free(inst);
|
||||
return NULL;
|
||||
}
|
||||
inst->servers[n]->server = server;
|
||||
inst->servers[n]->count = 0;
|
||||
n++;
|
||||
}
|
||||
inst->servers[n] = NULL;
|
||||
|
||||
/**
|
||||
* We have completed the creation of the instance data, so now
|
||||
* insert this router instance into the linked list of routers
|
||||
* that have been created with this module.
|
||||
*/
|
||||
spinlock_acquire(&instlock);
|
||||
inst->next = instances;
|
||||
instances = inst;
|
||||
spinlock_release(&instlock);
|
||||
|
||||
return (ROUTER *)inst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a new session with this instance of the router.
|
||||
*
|
||||
* The session is used to store all the data required for a particular
|
||||
* client connection.
|
||||
*
|
||||
* @param instance The router instance data
|
||||
* @param session The session itself
|
||||
* @return Session specific data for this session
|
||||
*/
|
||||
static void* newSession(
|
||||
ROUTER* instance,
|
||||
SESSION* session)
|
||||
{
|
||||
BACKEND* candidate = NULL;
|
||||
CLIENT_SESSION* client;
|
||||
INSTANCE* inst = (INSTANCE *)instance;
|
||||
int i;
|
||||
|
||||
if ((client = (CLIENT_SESSION *)malloc(sizeof(CLIENT_SESSION))) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a backend server to connect to. This is the extent of the
|
||||
* load balancing algorithm we need to implement for this simple
|
||||
* connection router.
|
||||
*/
|
||||
for (i = 0; inst->servers[i]; i++)
|
||||
{
|
||||
|
||||
if (inst->servers[i] && SERVER_IS_SLAVE(inst->servers[i]->server))
|
||||
{
|
||||
candidate = inst->servers[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loop over all the servers and find any that have fewer connections than our
|
||||
* candidate server.
|
||||
*
|
||||
* If a server has less connections than the current candidate we mark this
|
||||
* as the new candidate to connect to.
|
||||
*
|
||||
* If a server has the same number of connections currently as the candidate
|
||||
* and has had less connections over time than the candidate it will also
|
||||
* become the new candidate. This has the effect of spreading the connections
|
||||
* over different servers during periods of very low load.
|
||||
*/
|
||||
for (i = 0; inst->servers[i]; i++) {
|
||||
|
||||
if (inst->servers[i]
|
||||
&& SERVER_IS_RUNNING(inst->servers[i]->server))
|
||||
{
|
||||
if (SERVER_IS_SLAVE(inst->servers[i]->server))
|
||||
{
|
||||
if (inst->servers[i]->count < candidate->count) {
|
||||
candidate = inst->servers[i];
|
||||
} else if (inst->servers[i]->count == candidate->count &&
|
||||
inst->servers[i]->server->stats.n_connections
|
||||
< candidate->server->stats.n_connections)
|
||||
{
|
||||
candidate = inst->servers[i];
|
||||
}
|
||||
} else if (SERVER_IS_MASTER(inst->servers[i]->server)) {
|
||||
/** master is found */
|
||||
inst->master = inst->servers[i];
|
||||
}
|
||||
}
|
||||
} /* for */
|
||||
|
||||
if (inst->master == NULL) {
|
||||
inst->master = inst->servers[i-1];
|
||||
}
|
||||
/**
|
||||
* We now have a master and a slave server with the least connections.
|
||||
* Bump the connection counts for these servers.
|
||||
*/
|
||||
atomic_add(&candidate->count, 1);
|
||||
client->slave = candidate;
|
||||
atomic_add(&inst->master->count, 1);
|
||||
client->master = inst->master;
|
||||
ss_dassert(client->master->server != candidate->server);
|
||||
|
||||
/**
|
||||
* Open the slave connection.
|
||||
*/
|
||||
if ((client->slaveconn = dcb_connect(candidate->server, session,
|
||||
candidate->server->protocol)) == NULL)
|
||||
{
|
||||
atomic_add(&candidate->count, -1);
|
||||
free(client);
|
||||
return NULL;
|
||||
}
|
||||
/**
|
||||
* Open the master connection.
|
||||
*/
|
||||
if ((client->masterconn = dcb_connect(client->master->server, session,
|
||||
client->master->server->protocol)) == NULL)
|
||||
{
|
||||
atomic_add(&client->master->count, -1);
|
||||
free(client);
|
||||
return NULL;
|
||||
}
|
||||
inst->stats.n_sessions += 1;
|
||||
|
||||
/* Add this session to end of the list of active sessions */
|
||||
spinlock_acquire(&inst->lock);
|
||||
client->next = inst->connections;
|
||||
inst->connections = client;
|
||||
spinlock_release(&inst->lock);
|
||||
|
||||
return (void *)client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a session with the router, this is the mechanism
|
||||
* by which a router may cleanup data structure etc.
|
||||
*
|
||||
* @param instance The router instance data
|
||||
* @param session The session being closed
|
||||
*/
|
||||
static void closeSession(
|
||||
ROUTER* instance,
|
||||
void* router_session)
|
||||
{
|
||||
INSTANCE* inst = (INSTANCE *)instance;
|
||||
CLIENT_SESSION* session = (CLIENT_SESSION *)router_session;
|
||||
|
||||
/**
|
||||
* Close the connection to the backend servers
|
||||
*/
|
||||
session->slaveconn->func.close(session->slaveconn);
|
||||
session->masterconn->func.close(session->masterconn);
|
||||
atomic_add(&session->slave->count, -1);
|
||||
atomic_add(&session->master->count, -1);
|
||||
atomic_add(&session->slave->server->stats.n_current, -1);
|
||||
atomic_add(&session->master->server->stats.n_current, -1);
|
||||
|
||||
spinlock_acquire(&inst->lock);
|
||||
if (inst->connections == session) {
|
||||
inst->connections = session->next;
|
||||
} else {
|
||||
CLIENT_SESSION* ptr = inst->connections;
|
||||
|
||||
while (ptr && ptr->next != session) {
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
if (ptr) {
|
||||
ptr->next = session->next;
|
||||
}
|
||||
}
|
||||
spinlock_release(&inst->lock);
|
||||
|
||||
/*
|
||||
* We are no longer in the linked list, free
|
||||
* all the memory and other resources associated
|
||||
* to the client session.
|
||||
*/
|
||||
free(session);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The main routing entry, this is called with every packet that is
|
||||
* received and has to be forwarded to the backend database.
|
||||
*
|
||||
* The routeQuery will make the routing decision based on the contents
|
||||
* of the instance, session and the query itself in the queue. The
|
||||
* data in the queue may not represent a complete query, it represents
|
||||
* the data that has been received. The query router itself is responsible
|
||||
* for buffering the partial query, a later call to the query router will
|
||||
* contain the remainder, or part thereof of the query.
|
||||
*
|
||||
* @param instance The query router instance
|
||||
* @param session The session associated with the client
|
||||
* @param queue Gateway buffer queue with the packets received
|
||||
*
|
||||
* @return The number of queries forwarded
|
||||
*/
|
||||
static int routeQuery(
|
||||
ROUTER* instance,
|
||||
void* router_session,
|
||||
GWBUF* queue)
|
||||
{
|
||||
skygw_query_type_t qtype = QUERY_TYPE_UNKNOWN;
|
||||
char* querystr = NULL;
|
||||
char* startpos;
|
||||
size_t len;
|
||||
unsigned char packet_type, *packet;
|
||||
int ret = 0;
|
||||
GWBUF *cq = NULL;
|
||||
|
||||
INSTANCE* inst = (INSTANCE *)instance;
|
||||
CLIENT_SESSION* session = (CLIENT_SESSION *)router_session;
|
||||
inst->stats.n_queries++;
|
||||
|
||||
packet = GWBUF_DATA(queue);
|
||||
packet_type = packet[4];
|
||||
startpos = (char *)&packet[5];
|
||||
len = packet[0];
|
||||
len += 255*packet[1];
|
||||
len += 255*255*packet[2];
|
||||
|
||||
switch(packet_type) {
|
||||
case COM_QUIT: /**< 1 QUIT will close all sessions */
|
||||
case COM_INIT_DB: /**< 2 DDL must go to the master */
|
||||
case COM_REFRESH: /**< 7 - I guess this is session but not sure */
|
||||
case COM_DEBUG: /**< 0d all servers dump debug info to stdout */
|
||||
case COM_PING: /**< 0e all servers are pinged */
|
||||
case COM_CHANGE_USER: /**< 11 all servers change it accordingly */
|
||||
qtype = QUERY_TYPE_SESSION_WRITE;
|
||||
break;
|
||||
|
||||
case COM_CREATE_DB: /**< 5 DDL must go to the master */
|
||||
case COM_DROP_DB: /**< 6 DDL must go to the master */
|
||||
qtype = QUERY_TYPE_WRITE;
|
||||
break;
|
||||
|
||||
case COM_QUERY:
|
||||
querystr = (char *)malloc(len);
|
||||
memcpy(querystr, startpos, len-1);
|
||||
memset(&querystr[len-1], 0, 1);
|
||||
qtype = skygw_query_classifier_get_type(querystr, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
case COM_SHUTDOWN: /**< 8 where should shutdown be routed ? */
|
||||
case COM_STATISTICS: /**< 9 ? */
|
||||
case COM_PROCESS_INFO: /**< 0a ? */
|
||||
case COM_CONNECT: /**< 0b ? */
|
||||
case COM_PROCESS_KILL: /**< 0c ? */
|
||||
case COM_TIME: /**< 0f should this be run in gateway ? */
|
||||
case COM_DELAYED_INSERT: /**< 10 ? */
|
||||
case COM_DAEMON: /**< 1d ? */
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(SS_DEBUG_)
|
||||
skygw_log_write(NULL, LOGFILE_TRACE, "String\t\"%s\"", querystr);
|
||||
skygw_log_write(NULL,
|
||||
LOGFILE_TRACE,
|
||||
"Packet type\t%s",
|
||||
STRPACKETTYPE(packet_type));
|
||||
#endif
|
||||
|
||||
switch (qtype) {
|
||||
case QUERY_TYPE_WRITE:
|
||||
#if defined(SS_DEBUG_)
|
||||
skygw_log_write(NULL,
|
||||
LOGFILE_TRACE,
|
||||
"Query type\t%s, routing to Master.",
|
||||
STRQTYPE(qtype));
|
||||
#endif
|
||||
ret = session->masterconn->func.write(session->masterconn, queue);
|
||||
atomic_add(&inst->stats.n_master, 1);
|
||||
goto return_ret;
|
||||
break;
|
||||
|
||||
case QUERY_TYPE_READ:
|
||||
#if defined(SS_DEBUG_)
|
||||
skygw_log_write(NULL,
|
||||
LOGFILE_TRACE,
|
||||
"Query type\t%s, routing to Slave.",
|
||||
STRQTYPE(qtype));
|
||||
#endif
|
||||
ret = session->slaveconn->func.write(session->slaveconn, queue);
|
||||
atomic_add(&inst->stats.n_slave, 1);
|
||||
goto return_ret;
|
||||
break;
|
||||
|
||||
|
||||
case QUERY_TYPE_SESSION_WRITE:
|
||||
#if defined(SS_DEBUG_)
|
||||
skygw_log_write(NULL,
|
||||
LOGFILE_TRACE,
|
||||
"Query type\t%s, routing to All servers.",
|
||||
STRQTYPE(qtype));
|
||||
#endif
|
||||
/**
|
||||
* TODO! Connection to all servers must be established, and
|
||||
* the command must be executed in them.
|
||||
*/
|
||||
|
||||
cq = gwbuf_clone(queue);
|
||||
|
||||
switch(packet_type) {
|
||||
case COM_QUIT:
|
||||
ret = session->masterconn->func.write(session->masterconn, queue);
|
||||
session->slaveconn->func.write(session->slaveconn, cq);
|
||||
break;
|
||||
case COM_CHANGE_USER:
|
||||
session->masterconn->func.auth(session->masterconn, NULL, session->masterconn->session, queue);
|
||||
session->slaveconn->func.auth(session->slaveconn, NULL, session->masterconn->session, cq);
|
||||
break;
|
||||
default:
|
||||
ret = session->masterconn->func.session(session->masterconn, (void *)queue);
|
||||
session->slaveconn->func.session(session->slaveconn, (void *)cq);
|
||||
break;
|
||||
}
|
||||
|
||||
atomic_add(&inst->stats.n_all, 1);
|
||||
goto return_ret;
|
||||
break;
|
||||
|
||||
default:
|
||||
#if defined(SS_DEBUG_)
|
||||
skygw_log_write(NULL,
|
||||
LOGFILE_TRACE,
|
||||
"Query type\t%s, routing to Master by default.",
|
||||
STRQTYPE(qtype));
|
||||
#endif
|
||||
/** Is this really ok? */
|
||||
ret = session->masterconn->func.write(session->masterconn, queue);
|
||||
atomic_add(&inst->stats.n_master, 1);
|
||||
goto return_ret;
|
||||
break;
|
||||
}
|
||||
|
||||
return_ret:
|
||||
free(querystr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Diagnostics routine
|
||||
*
|
||||
* Print query router statistics to the DCB passed in
|
||||
*
|
||||
* @param instance The router instance
|
||||
* @param dcb The DCB for diagnostic output
|
||||
*/
|
||||
static void
|
||||
diagnostic(ROUTER *instance, DCB *dcb)
|
||||
{
|
||||
CLIENT_SESSION *session;
|
||||
INSTANCE *inst = (INSTANCE *)instance;
|
||||
int i = 0;
|
||||
|
||||
spinlock_acquire(&inst->lock);
|
||||
session = inst->connections;
|
||||
while (session)
|
||||
{
|
||||
i++;
|
||||
session = session->next;
|
||||
}
|
||||
spinlock_release(&inst->lock);
|
||||
|
||||
dcb_printf(dcb, "\tNumber of router sessions: %d\n", inst->stats.n_sessions);
|
||||
dcb_printf(dcb, "\tCurrent no. of router sessions: %d\n", i);
|
||||
dcb_printf(dcb, "\tNumber of queries forwarded: %d\n", inst->stats.n_queries);
|
||||
dcb_printf(dcb, "\tNumber of queries forwarded to master: %d\n", inst->stats.n_master);
|
||||
dcb_printf(dcb, "\tNumber of queries forwarded to slave: %d\n", inst->stats.n_slave);
|
||||
dcb_printf(dcb, "\tNumber of queries forwarded to all: %d\n", inst->stats.n_all);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Client Reply routine
|
||||
*
|
||||
* The routine will reply to client for session change with master server data
|
||||
*
|
||||
* @param instance The router instance
|
||||
* @param router_session The router session
|
||||
* @param backend_dcb The backend DCB
|
||||
* @param queue The GWBUF with reply data
|
||||
*/
|
||||
static void
|
||||
clientReply(ROUTER* instance, void* router_session, GWBUF* queue, DCB *backend_dcb)
|
||||
{
|
||||
INSTANCE* inst = NULL;
|
||||
DCB *master = NULL;
|
||||
DCB *client = NULL;
|
||||
CLIENT_SESSION* session = NULL;
|
||||
|
||||
inst = (INSTANCE *)instance;
|
||||
session = (CLIENT_SESSION *)router_session;
|
||||
master = session->masterconn;
|
||||
client = backend_dcb->session->client;
|
||||
|
||||
if (backend_dcb->command == ROUTER_CHANGE_SESSION) {
|
||||
/* if backend_dcb is the master we can reply to the client */
|
||||
if (backend_dcb == master) {
|
||||
master->session->client->func.write(master->session->client, queue);
|
||||
} else {
|
||||
/* just consume the gwbuf without writing to the client */
|
||||
gwbuf_consume(queue, gwbuf_length(queue));
|
||||
}
|
||||
} else {
|
||||
/* normal flow */
|
||||
client->func.write(client, queue);
|
||||
}
|
||||
}
|
||||
///
|
||||
123
server/modules/routing/testroute.c
Normal file
123
server/modules/routing/testroute.c
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* This file is distributed as part of the SkySQL Gateway. 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 2013
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <router.h>
|
||||
|
||||
static char *version_str = "V1.0.0";
|
||||
|
||||
static ROUTER *createInstance(SERVICE *service, char **options);
|
||||
static void *newSession(ROUTER *instance, SESSION *session);
|
||||
static void closeSession(ROUTER *instance, void *session);
|
||||
static int routeQuery(ROUTER *instance, void *session, GWBUF *queue);
|
||||
static void diagnostic(ROUTER *instance, DCB *dcb);
|
||||
|
||||
static ROUTER_OBJECT MyObject = { createInstance, newSession, closeSession, routeQuery, diagnostic, NULL };
|
||||
|
||||
/**
|
||||
* Implementation of the mandatory version entry point
|
||||
*
|
||||
* @return version string of the module
|
||||
*/
|
||||
char *
|
||||
version()
|
||||
{
|
||||
return version_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module initialisation routine, called when the module
|
||||
* is first loaded.
|
||||
*/
|
||||
void
|
||||
ModuleInit()
|
||||
{
|
||||
fprintf(stderr, "Initial test router module.\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* The module entry point routine. It is this routine that
|
||||
* must populate the structure that is referred to as the
|
||||
* "module object", this is a structure with the set of
|
||||
* external entry points for this module.
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
ROUTER_OBJECT *
|
||||
GetModuleObject()
|
||||
{
|
||||
fprintf(stderr, "Returing test router module object.\n");
|
||||
return &MyObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the router for a particular service
|
||||
* within the gateway.
|
||||
*
|
||||
* @param service The service this router is being create for
|
||||
* @param options The options for this query router
|
||||
*
|
||||
* @return The instance data for this new instance
|
||||
*/
|
||||
static ROUTER *
|
||||
createInstance(SERVICE *service, char **options)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a new session with this instance of the router.
|
||||
*
|
||||
* @param instance The router instance data
|
||||
* @param session The session itself
|
||||
* @return Session specific data for this session
|
||||
*/
|
||||
static void *
|
||||
newSession(ROUTER *instance, SESSION *session)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a session with the router, this is the mechanism
|
||||
* by which a router may cleanup data structure etc.
|
||||
*
|
||||
* @param instance The router instance data
|
||||
* @param session The session being closed
|
||||
*/
|
||||
static void
|
||||
closeSession(ROUTER *instance, void *session)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
routeQuery(ROUTER *instance, void *session, GWBUF *queue)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Diagnostics routine
|
||||
*
|
||||
* @param instance The router instance
|
||||
* @param dcb The DCB for diagnostic output
|
||||
*/
|
||||
static void
|
||||
diagnostic(ROUTER *instance, DCB *dcb)
|
||||
{
|
||||
}
|
||||
Reference in New Issue
Block a user