Duplicate Queries

Developed In: Lua — Contributed by: Diego Medina

All queries going to one backend, will be duplicated to the other backends


Diego Medina
Lua
  1. --[[ $%BEGINLICENSE%$
  2.  Copyright (C) 2009 MySQL AB, 2009 Sun Microsystems, Inc
  3.  
  4.  This program is free software; you can redistribute it and/or modify
  5.  it under the terms of the GNU General Public License as published by
  6.  the Free Software Foundation; version 2 of the License.
  7.  
  8.  This program is distributed in the hope that it will be useful,
  9.  but WITHOUT ANY WARRANTY; without even the implied warranty of
  10.  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11.  GNU General Public License for more details.
  12.  
  13.  You should have received a copy of the GNU General Public License
  14.  along with this program; if not, write to the Free Software
  15.  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16.  
  17.  $%ENDLICENSE%$ --]]
  18.  
  19.  
  20.  
  21.  
  22. -- To avoid messing with the protocol
  23. local proto = assert(require("mysql.proto"))
  24. -- To overwrite print() and print to logs
  25. require("chassis")
  26. -- We assign the value of the same table to itself
  27. -- or initialize it if not existent
  28. proxy.global.db= proxy.global.db or {}
  29. proxy.global.last_node= proxy.global.last_node or 0
  30. proxy.global.query= proxy.global.query or ""
  31.  
  32.  
  33. -- connection pool
  34. if not proxy.global.config.rwsplit then
  35. proxy.global.config.rwsplit = {
  36. min_idle_connections = 6,
  37. max_idle_connections = 8,
  38. }
  39. end
  40. proxy.global.config.rwsplit.is_debug = false
  41.  
  42. --[[ ====================================================================== ]]--
  43.  
  44. function connect_server()
  45.  
  46. local is_debug = proxy.global.config.rwsplit.is_debug
  47.  
  48. -- make sure that we connect to each backend at least ones to
  49. -- keep the connections to the servers alive
  50. --
  51. -- on read_query we can switch the backends again to another backend
  52.  
  53. if is_debug then
  54. print("[connect_server] " .. proxy.connection.client.src.name)
  55. print("[connect_server] " .. proxy.connection.client.dst.name)
  56. end
  57.  
  58. local rw_ndx = 0
  59.  
  60. -- init all backends
  61. for i = 1, #proxy.global.backends do
  62. local s = proxy.global.backends[i]
  63. local pool = s.pool -- we don't have a username yet, try to find a connections which is idling
  64. local cur_idle = pool.users[""].cur_idle_connections
  65.  
  66. pool.min_idle_connections = proxy.global.config.rwsplit.min_idle_connections
  67. pool.max_idle_connections = proxy.global.config.rwsplit.max_idle_connections
  68.  
  69. if is_debug then
  70. print(" [".. i .."].connected_clients = " .. s.connected_clients)
  71. print(" [".. i .."].pool.cur_idle = " .. cur_idle)
  72. print(" [".. i .."].pool.max_idle = " .. pool.max_idle_connections)
  73. print(" [".. i .."].pool.min_idle = " .. pool.min_idle_connections)
  74. print(" [".. i .."].type = " .. s.type)
  75. print(" [".. i .."].state = " .. s.state)
  76. end
  77.  
  78. -- prefer connections to the master
  79. if s.type == proxy.BACKEND_TYPE_RW and
  80. s.state ~= proxy.BACKEND_STATE_DOWN and
  81. cur_idle < pool.min_idle_connections then
  82. proxy.connection.backend_ndx = i
  83. break
  84. elseif s.type == proxy.BACKEND_TYPE_RO and
  85. s.state ~= proxy.BACKEND_STATE_DOWN and
  86. cur_idle < pool.min_idle_connections then
  87. proxy.connection.backend_ndx = i
  88. break
  89. elseif s.type == proxy.BACKEND_TYPE_RW and
  90. s.state ~= proxy.BACKEND_STATE_DOWN and
  91. rw_ndx == 0 then
  92. rw_ndx = i
  93. end
  94. end
  95.  
  96. if proxy.connection.backend_ndx == 0 then
  97. if is_debug then
  98. print(" [" .. rw_ndx .. "] taking master as default")
  99. end
  100. proxy.connection.backend_ndx = rw_ndx
  101. end
  102.  
  103. -- pick a random backend
  104. --
  105. -- we somehow have to skip DOWN backends
  106.  
  107. -- ok, did we got a backend ?
  108.  
  109. if proxy.connection.server then
  110. if is_debug then
  111. print(" using pooled connection from: " .. proxy.connection.backend_ndx)
  112. end
  113.  
  114. -- stay with it
  115. return proxy.PROXY_IGNORE_RESULT
  116. end
  117.  
  118. if is_debug then
  119. print(" [" .. proxy.connection.backend_ndx .. "] idle-conns below min-idle")
  120. end
  121.  
  122.  
  123. end
  124.  
  125. --[[ ====================================================================== ]]--
  126.  
  127. function read_query( packet )
  128. -- we use this to send it to the other
  129. -- backends
  130. query = packet:sub(2)
  131.  
  132. if (string.byte(packet) == proxy.COM_QUIT)
  133. or (string.byte(packet) == proxy.COM_INIT_DB)
  134. then
  135. -- This gets called when you disconnect from the server
  136. proxy.response.type = proxy.MYSQLD_PACKET_OK
  137. return proxy.PROXY_SEND_RESULT
  138. elseif string.byte(packet) ~= proxy.COM_QUERY then
  139. proxy.response.type = proxy.MYSQLD_PACKET_OK
  140. return proxy.PROXY_SEND_RESULT
  141. elseif string.byte(packet) == proxy.COM_QUERY then
  142. proxy.global.query= query
  143.  
  144. proxy.queries:append(1, string.char(proxy.COM_QUERY) .. query, { resultset_is_needed = true } )
  145. return proxy.PROXY_SEND_QUERY
  146.  
  147. end
  148. end -- End of read_query()
  149. --[[ ====================================================================== ]]--
  150.  
  151. function error_result (msg, code, state)
  152. proxy.response = {
  153. type = proxy.MYSQLD_PACKET_ERR,
  154. errmsg = msg,
  155. errcode = code,
  156. sqlstate = state,
  157. }
  158. return proxy.PROXY_SEND_RESULT
  159. end
  160. --[[ ====================================================================== ]]--
  161.  
  162. -- Here we append the primary query to the queue
  163. function dup_query()
  164. proxy.queries:append(2, string.char(proxy.COM_QUERY) .. proxy.global.query,{ resultset_is_needed = false } )
  165. return proxy.PROXY_SEND_QUERY
  166. end
  167.  
  168. --[[ ====================================================================== ]]--
  169. function read_query_result (inj)
  170. -- the first backend already got the query
  171. -- so we start from current + 1
  172. local is_debug = proxy.global.config.rwsplit.is_debug
  173.  
  174. for i=proxy.connection.backend_ndx + 1, #proxy.global.backends do
  175. -- We loop throught the backends and send the same query
  176. -- This is where the replication happens :)
  177. proxy.connection.backend_ndx = i
  178. if is_debug then
  179. print("back_ndx " .. proxy.connection.backend_ndx )
  180. end
  181.  
  182. if inj.id == 1 then
  183. --We are now on the second server, so duplicate the query
  184. dup_query()
  185. return proxy.PROXY_IGNORE_RESULT
  186. end
  187. end
  188. -- when last node, reset back_ndx
  189. if proxy.connection.backend_ndx == #proxy.global.backends then
  190. proxy.connection.backend_ndx = 1
  191. end
  192. end
  193. --[[ ====================================================================== ]]--
  194.  
  195. function disconnect_client()
  196. local is_debug = proxy.global.config.rwsplit.is_debug
  197. if is_debug then
  198. print("[disconnect_client] " .. proxy.connection.server.dst.name)
  199. end
  200.  
  201. -- make sure we are disconnection from the connection
  202. -- to move the connection into the pool
  203. proxy.connection.backend_ndx = 0
  204. end
  205. --[[ ====================================================================== ]]--
  206.  
  207. --[[
  208.  
  209. How to use:
  210.  
  211. Create a pool of connections for the proxy to be able to connect
  212. to all backends
  213.  
  214. $ for i in {1 100} ; do mysql -h127.0.0.1 -P4040 -e "exit"; done ;
  215.  
  216.  
  217. Then send one query through the proxy and it will be
  218. sent to the main server, as well as all other backends.
  219.  
  220. --]]
  221.  

Current Tags

You must be logged in to tag this tool

No Comments yet

Votes

  • Rated 5.00 out of 5
Rated 5.00 out of 5 with 1 votes cast.
You must be logged in to vote.

Watches

1 members are watching this tool
You must be logged in to track this tool.

Provide Feedback

Please note:
HTML will be purified, but we allow for a number of HTML tags so that you have the flexibility to decorate your comment text to some extent. The comments allow the following HTML tags:

strong, b, em, blockquote, a, code, pre

To put code into your comment, simply encapsulate your code with
[code language="XXX"][/code], where XXX is any common language, for instance "PHP", "SQL", "C", etc.



You must be logged in to comment