packages: mysql-proxy/admin-1.lua (NEW) - add contrib script: http://forge....

glen glen at pld-linux.org
Tue May 18 12:46:31 CEST 2010


Author: glen                         Date: Tue May 18 10:46:31 2010 GMT
Module: packages                      Tag: HEAD
---- Log message:
- add contrib script: http://forge.mysql.com/tools/tool.php?id=252

---- Files affected:
packages/mysql-proxy:
   admin-1.lua (NONE -> 1.1)  (NEW)

---- Diffs:

================================================================
Index: packages/mysql-proxy/admin-1.lua
diff -u /dev/null packages/mysql-proxy/admin-1.lua:1.1
--- /dev/null	Tue May 18 12:46:31 2010
+++ packages/mysql-proxy/admin-1.lua	Tue May 18 12:46:26 2010
@@ -0,0 +1,311 @@
+--[[ $%BEGINLICENSE%$
+ Copyright (C) 2008 MySQL AB, 2008 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%$ --]]
+
+
+---
+-- a flexible statement based load balancer with connection pooling
+--
+-- * build a connection pool of min_idle_connections for each backend and 
+--   maintain its size
+-- * reusing a server-side connection when it is idling
+-- * By glenn at telenet.be:
+-- * Fixed issues with parameters for mysql-proxy 0.7.2.: (Lenny debian backports version)
+-- * Works on :
+-- mysql-proxy 0.7.2
+--  glib2: 2.16.6
+--  libevent: 1.3e
+--  lua: Lua 5.1.3
+--    LUA_PATH: /usr/lib/mysql-proxy/lua/?.lua
+--    LUA_CPATH: /usr/lib/mysql-proxy/lua/?.so
+--  == plugins ==
+--  admin: 0.7.0
+--  proxy: 0.7.0
+-- * I merged the reporter.lua plugin into this as well so I can be used with the admin-1.lua script without 
+-- * Having to use the multi-lua script setup.  I use this proxy to pool slave connection. I do not need the 
+-- * master/slave splitting (So be careful) as I only send slaves those queries.
+
+--- config
+proxy.global.query_counter = proxy.global.query_counter or 0
+
+--
+-- connection pool
+local min_idle_connections = 2
+local max_idle_connections = 6
+
+-- debug
+local is_debug = false
+
+--- end of config
+
+---
+-- read/write splitting sends all non-transactional SELECTs to the slaves
+--
+-- is_in_transaction tracks the state of the transactions
+local is_in_transaction = 0
+
+--- 
+-- get a connection to a backend
+--
+-- as long as we don't have enough connections in the pool, create new connections
+--
+function connect_server() 
+	-- 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()
+		print("[connect_server] ")
+	end
+
+	local least_idle_conns_ndx = 0
+	local least_idle_conns = 0
+
+	for i = 1, #proxy.global.backends do
+		local backend = proxy.global.backends[i]
+		-- we don't have a username yet, try to find a connections which is idling
+		local pool     = backend.pool
+		local cur_idle = (pool.users[""].cur_idle_connections or 0)
+
+		if is_debug then
+			print("  [".. i .."].backend.connected_clients = " .. (backend.connected_clients or "(nil)"))
+			print("  [".. i .."].backend.type = " .. (backend.type or "(nil)"))
+			print("  [".. i .."].backend.state = " .. (backend.state or "(nil)"))
+			-- print("  [".. i .."].backend.address = " .. (backend.dst.name or "(nil)"))
+			-- print("  Server address = " .. (backend.connection.server.dst.name or "(nil)"))
+			-- print("  [".. i .."].backend.dst = " .. (backend.dst or "(nil)"))
+			print("  [".. i .."].backend.uuid = " .. (backend.uuid or "(nil)"))
+			print("  [".. i .."].least_idle_conns = " .. (least_idle_conns or "(nil)"))
+			print("  [".. i .."].least_idle_conns_ndx = " .. (least_idle_conns_ndx or "(nil)"))
+			print("  [".. i .."].cur_idle = " .. (cur_idle or "(nil)"))
+			-- print("  [".. i .."].backend.idling_connections = " .. (bs.idling_connections or "(nil)"))
+		end
+
+		if backend.state ~= proxy.BACKEND_STATE_DOWN then
+			-- try to connect to each backend once at least
+			if cur_idle == 0 then
+				proxy.connection.backend_ndx = i
+				if is_debug then
+					print("  [".. i .."] open new connection")
+				print("[connect_server] " .. (proxy.connection.client.src.name or "(nil)"))
+					print("We have " .. #proxy.global.backends .. " backends:")
+				end
+				return
+			end
+
+			-- try to open at least min_idle_connections
+			if least_idle_conns_ndx == 0 or ( cur_idle < min_idle_connections and cur_idle < least_idle_conns ) then
+				least_idle_conns_ndx = i
+				-- least_idle_conns = backend.idling_connections
+				least_idle_conns = cur_idle
+			end
+		end
+	end
+
+	if least_idle_conns_ndx > 0 then
+		proxy.connection.backend_ndx = least_idle_conns_ndx
+	end
+
+	if proxy.connection.backend_ndx > 0 then 
+		local backend = proxy.global.backends[proxy.connection.backend_ndx]
+		local pool     = backend.pool -- we don't have a username yet, try to find a connections which is idling
+		local cur_idle = pool.users[""].cur_idle_connections
+
+		if cur_idle >= min_idle_connections then
+			-- we have 4 idling connections in the pool, that's good enough
+			if is_debug then
+				print("  using pooled connection from: " .. (proxy.connection.backend_ndx or "(nil)"))
+			end
+	
+			return proxy.PROXY_IGNORE_RESULT
+		end
+	end
+
+	if is_debug then
+		print("  opening new connection on: " .. (proxy.connection.backend_ndx or "(nil)"))
+	end
+
+	-- open a new connection 
+end
+
+--- 
+-- put the successfully authed connection into the connection pool
+--
+-- @param auth the context information for the auth
+--
+-- auth.packet is the packet
+function read_auth_result( auth )
+	local state = auth.packet:byte()
+	if state == proxy.MYSQLD_PACKET_OK then
+		proxy.global.initialize_process_table()
+		table.insert( proxy.global.process[proxy.connection.server.thread_id],
+			{ ip = proxy.connection.client.src.name, ts = os.time() } )
+		-- auth was fine, disconnect from the server
+		proxy.connection.backend_ndx = 0
+	elseif auth.packet:byte() == proxy.MYSQLD_PACKET_EOF then
+		-- we received either a 
+		-- 
+		-- * MYSQLD_PACKET_ERR and the auth failed or
+		-- * MYSQLD_PACKET_EOF which means a OLD PASSWORD (4.0) was sent
+		print("(read_auth_result) ... not ok yet");
+	elseif auth.packet:byte() == proxy.MYSQLD_PACKET_ERR then
+		-- auth failed
+	end
+end
+
+---
+-- from reporter.lua import
+--[[
+	See http://forge.mysql.com/tools/tool.php?id=78
+	(Thanks to Jan Kneschke)
+	See http://www.chriscalender.com/?p=41
+	(Thanks to Chris Calender)
+	See http://datacharmer.blogspot.com/2009/01/mysql-proxy-is-back.html
+	(Thanks Giuseppe Maxia)
+--]]
+
+function proxy.global.initialize_process_table()
+	if proxy.global.process == nil then
+		proxy.global.process = {}
+	end
+	if proxy.global.process[proxy.connection.server.thread_id] == nil then
+		proxy.global.process[proxy.connection.server.thread_id] = {}
+	end
+end
+-- end reporter.lua import
+
+--- 
+-- read/write splitting - read_query() can return a resultset
+--
+-- You can use read_query() to return a result-set.
+--
+-- @param packet the mysql-packet sent by the client
+--
+-- @return
+--   * nothing to pass on the packet as is,
+--   * proxy.PROXY_SEND_QUERY to send the queries from the proxy.queries queue
+--   * proxy.PROXY_SEND_RESULT to send your own result-set
+--
+function read_query( packet ) 
+	-- a new query came in in this connection
+	-- using proxy.global.* to make it available to the admin plugin
+	proxy.global.query_counter = proxy.global.query_counter + 1
+
+
+	if is_debug then
+		print("[read_query]")
+		print("  authed backend = " .. (proxy.connection.backend_ndx or "(nil)"))
+		print("  used db = " .. (proxy.connection.client.default_db or "(nil)"))
+		print("  Client address = " .. (proxy.connection.client.src.name or "(nil)"))
+	end
+--		print("  .least_idle_conns = " .. (least_idle_conns or "(nil)"))
+--		print("  .least_idle_conns_ndx = " .. (least_idle_conns_ndx or "(nil)"))
+--		print("  .cur_idle = " .. (cur_idle or "(nil)"))
+--		print("  .type = " .. (proxy.connection.backend.type or "(nil)"))
+--		print("  .state = " .. (proxy.connection.backend.state or "(nil)"))
+--		print("  .uuid = " .. (proxy.connection.backend.uuid or "(nil)"))
+--		print("  .src.name = " .. (proxy.connection.backend.src.name or "(nil)"))
+--		print("  .backend.idling_connections = " .. (proxy.connection.backend.idling_connections or "(nil)"))
+
+	if packet:byte() == proxy.COM_QUIT then
+		-- don't send COM_QUIT to the backend. We manage the connection
+		-- in all aspects.
+		proxy.response = {
+			type = proxy.MYSQLD_PACKET_OK,
+		}
+
+		return proxy.PROXY_SEND_RESULT
+	end
+
+	if proxy.connection.backend_ndx == 0 then
+		-- we don't have a backend right now
+		-- 
+		-- let's pick a master as a good default
+		for i = 1, #proxy.global.backends do
+			local backend = proxy.global.backends[i]
+			local pool     = backend.pool -- we don't have a username yet, try to find a connections which is idling
+			local cur_idle = pool.users[proxy.connection.client.username].cur_idle_connections
+			
+			if cur_idle > 0 and 
+				backend.state ~= proxy.BACKEND_STATE_DOWN and 
+				backend.type == proxy.BACKEND_TYPE_RW then
+				proxy.connection.backend_ndx = i
+				break
+			end
+		end
+	end
+
+	if true or proxy.connection.client.default_db and proxy.connection.client.default_db ~= proxy.connection.server.default_db then
+		-- sync the client-side default_db with the server-side default_db
+		proxy.queries:append(2, string.char(proxy.COM_INIT_DB) .. proxy.connection.client.default_db, { resultset_is_needed = true })
+	end
+	proxy.queries:append(1, packet)
+
+	return proxy.PROXY_SEND_QUERY
+end
+
+---
+-- as long as we are in a transaction keep the connection
+-- otherwise release it so another client can use it
+function read_query_result( inj ) 
+	local res      = assert(inj.resultset)
+	local flags    = res.flags
+
+	if inj.id ~= 1 then
+		-- ignore the result of the USE <default_db>
+		return proxy.PROXY_IGNORE_RESULT
+	end
+	is_in_transaction = flags.in_trans
+
+	if not is_in_transaction then
+		-- release the backend
+		proxy.connection.backend_ndx = 0
+	end
+end
+
+--- 
+-- close the connections if we have enough connections in the pool
+--
+-- @return nil - close connection 
+--         IGNORE_RESULT - store connection in the pool
+
+function disconnect_client()
+	if proxy.connection.backend_ndx == 0 then
+		-- currently we don't have a server backend assigned
+		--
+		-- pick a server which has too many idling connections and close one
+		for i = 1, #proxy.global.backends do
+			local backend = proxy.global.backends[i]
+			local pool     = backend.pool -- we don't have a username yet, try to find a connections which is idling
+			local cur_idle = pool.users[proxy.connection.client.username].cur_idle_connections
+			if is_debug then
+				print("  [".. i .."] idling: " .. (cur_idle or "(nil)"))
+			end
+
+			if backend.state ~= proxy.BACKEND_STATE_DOWN and cur_idle > max_idle_connections then
+				if is_debug then
+					print("  [".. i .."] closing connection, idling: " .. (cur_idle or "(nil)"))
+				end
+				-- try to disconnect a backend
+				proxy.connection.backend_ndx = i
+				return
+			end
+		end
+	end
+end
================================================================


More information about the pld-cvs-commit mailing list