--[[ $%BEGINLICENSE%$
Copyright (C) 2009 MySQL AB, 2009 Sun Microsystems, Inc
This program 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 of the License.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
$%ENDLICENSE%$ --]]
-- To avoid messing with the protocol
local proto = assert(require("mysql.proto"))
-- To overwrite print() and print to logs
require("chassis")
-- We assign the value of the same table to itself
-- or initialize it if not existent
proxy.global.db= proxy.global.db or {}
proxy.global.last_node= proxy.global.last_node or 0
proxy.global.query= proxy.global.query or ""
-- connection pool
if not proxy.global.config.rwsplit then
proxy.global.config.rwsplit = {
min_idle_connections = 6,
max_idle_connections = 8,
}
end
proxy.global.config.rwsplit.is_debug = false
--[[ ====================================================================== ]]--
function connect_server()
local is_debug = proxy.global.config.rwsplit.is_debug
-- make sure that we connect to each backend at least ones to
-- keep the connections to the servers alive
--
-- on read_query we can switch the backends again to another backend
if is_debug then
print("[connect_server] " .. proxy.connection.client.src.name)
print("[connect_server] " .. proxy.connection.client.dst.name)
end
local rw_ndx = 0
-- init all backends
for i = 1, #proxy.global.backends do
local s = proxy.global.backends[i]
local pool = s.pool -- we don't have a username yet, try to find a connections which is idling
local cur_idle = pool.users[""].cur_idle_connections
pool.min_idle_connections = proxy.global.config.rwsplit.min_idle_connections
pool.max_idle_connections = proxy.global.config.rwsplit.max_idle_connections
if is_debug then
print(" [".. i .."].connected_clients = " .. s.connected_clients)
print(" [".. i .."].pool.cur_idle = " .. cur_idle)
print(" [".. i .."].pool.max_idle = " .. pool.max_idle_connections)
print(" [".. i .."].pool.min_idle = " .. pool.min_idle_connections)
print(" [".. i .."].type = " .. s.type)
print(" [".. i .."].state = " .. s.state)
end
-- prefer connections to the master
if s.type == proxy.BACKEND_TYPE_RW and
s.state ~= proxy.BACKEND_STATE_DOWN and
cur_idle < pool.min_idle_connections then
proxy.connection.backend_ndx = i
break
elseif s.type == proxy.BACKEND_TYPE_RO and
s.state ~= proxy.BACKEND_STATE_DOWN and
cur_idle < pool.min_idle_connections then
proxy.connection.backend_ndx = i
break
elseif s.type == proxy.BACKEND_TYPE_RW and
s.state ~= proxy.BACKEND_STATE_DOWN and
rw_ndx == 0 then
rw_ndx = i
end
end
if proxy.connection.backend_ndx == 0 then
if is_debug then
print(" [" .. rw_ndx .. "] taking master as default")
end
proxy.connection.backend_ndx = rw_ndx
end
-- pick a random backend
--
-- we somehow have to skip DOWN backends
-- ok, did we got a backend ?
if proxy.connection.server then
if is_debug then
print(" using pooled connection from: " .. proxy.connection.backend_ndx)
end
-- stay with it
return proxy.PROXY_IGNORE_RESULT
end
if is_debug then
print(" [" .. proxy.connection.backend_ndx .. "] idle-conns below min-idle")
end
end
--[[ ====================================================================== ]]--
function read_query( packet )
-- we use this to send it to the other
-- backends
query = packet:sub(2)
if (string.byte(packet) == proxy.COM_QUIT)
or (string.byte(packet) == proxy.COM_INIT_DB)
then
-- This gets called when you disconnect from the server
proxy.response.type = proxy.MYSQLD_PACKET_OK
return proxy.PROXY_SEND_RESULT
elseif string.byte(packet) ~= proxy.COM_QUERY then
proxy.response.type = proxy.MYSQLD_PACKET_OK
return proxy.PROXY_SEND_RESULT
elseif string.byte(packet) == proxy.COM_QUERY then
proxy.global.query= query
proxy.queries:append(1, string.char(proxy.COM_QUERY) .. query, { resultset_is_needed = true } )
return proxy.PROXY_SEND_QUERY
end
end -- End of read_query()
--[[ ====================================================================== ]]--
function error_result (msg, code, state)
proxy.response = {
type = proxy.MYSQLD_PACKET_ERR,
errmsg = msg,
errcode = code,
sqlstate = state,
}
return proxy.PROXY_SEND_RESULT
end
--[[ ====================================================================== ]]--
-- Here we append the primary query to the queue
function dup_query()
proxy.queries:append(2, string.char(proxy.COM_QUERY) .. proxy.global.query,{ resultset_is_needed = false } )
return proxy.PROXY_SEND_QUERY
end
--[[ ====================================================================== ]]--
function read_query_result (inj)
-- the first backend already got the query
-- so we start from current + 1
local is_debug = proxy.global.config.rwsplit.is_debug
for i=proxy.connection.backend_ndx + 1, #proxy.global.backends do
-- We loop throught the backends and send the same query
-- This is where the replication happens :)
proxy.connection.backend_ndx = i
if is_debug then
print("back_ndx " .. proxy.connection.backend_ndx )
end
if inj.id == 1 then
--We are now on the second server, so duplicate the query
dup_query()
return proxy.PROXY_IGNORE_RESULT
end
end
-- when last node, reset back_ndx
if proxy.connection.backend_ndx == #proxy.global.backends then
proxy.connection.backend_ndx = 1
end
end
--[[ ====================================================================== ]]--
function disconnect_client()
local is_debug = proxy.global.config.rwsplit.is_debug
if is_debug then
print("[disconnect_client] " .. proxy.connection.server.dst.name)
end
-- make sure we are disconnection from the connection
-- to move the connection into the pool
proxy.connection.backend_ndx = 0
end
--[[ ====================================================================== ]]--
--[[
How to use:
Create a pool of connections for the proxy to be able to connect
to all backends
$ for i in {1 100} ; do mysql -h127.0.0.1 -P4040 -e "exit"; done ;
Then send one query through the proxy and it will be
sent to the main server, as well as all other backends.
--]]