<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
  <title>~jk - Home</title>
  <id>tag:jan.kneschke.de,2010:mephisto/</id>
  <generator uri="http://mephistoblog.com" version="0.8.0">Mephisto Drax</generator>
  <link href="http://jan.kneschke.de/feed/atom.xml" rel="self" type="application/atom+xml"/>
  <link href="http://jan.kneschke.de/" rel="alternate" type="text/html"/>
  <updated>2010-06-02T20:55:51Z</updated>
  <entry xml:base="http://jan.kneschke.de/">
    <author>
      <name>jan</name>
    </author>
    <id>tag:jan.kneschke.de,2010-06-02:570</id>
    <published>2010-06-02T18:45:00Z</published>
    <updated>2010-06-02T20:55:51Z</updated>
    <link href="http://jan.kneschke.de/2010/6/2/finding-memleaks-in-win32-with-valgrind" rel="alternate" type="text/html"/>
    <title>Finding memleaks in win32 with valgrind</title>
<summary type="html">&lt;p&gt;I use MacOS X for most of my development work, but part of my job is writing apps that run on all major Unixes and Win32. Most of the time bugs and memleaks are &quot;portable&quot;, meaning that they appear on all platforms in the same way and fixing them on your dev-platforms, fixes them everywhere else.&lt;/p&gt;

&lt;p&gt;Well, &quot;most of the time&quot;.&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;I use MacOS X for most of my development work, but part of my job is writing apps that run on all major Unixes and Win32. Most of the time bugs and memleaks are &quot;portable&quot;, meaning that they appear on all platforms in the same way and fixing them on your dev-platforms, fixes them everywhere else.&lt;/p&gt;

&lt;p&gt;Well, &quot;most of the time&quot;.&lt;/p&gt;
&lt;p&gt;Today I had the joy of a win32 only mem-leak. We actually ''bisect''ed it down to the locking support for openssl that we just added &lt;a href=&quot;http://www.openssl.org/docs/crypto/threads.html&quot;&gt;http://www.openssl.org/docs/crypto/threads.html&lt;/a&gt;. But starring at the code didn't reveal anything obvious. So, back to the tools. Let them find it.&lt;/p&gt;

&lt;p&gt;Going through the web on the search for &quot;how to find mem-leaks on win32&quot; I came along:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://wiki.winehq.org/Wine_and_Valgrind&quot;&gt;http://wiki.winehq.org/Wine_and_Valgrind&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The idea is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use wine to run the win32 app&lt;/li&gt;
&lt;li&gt;run wine in valgrind&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You get all the benefits of valgrind and can work on the OS you are used to. But not so fast:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;wine from MacPorts is 32bit only &lt;a href=&quot;http://trac.macports.org/browser/trunk/dports/x11/wine-devel/Portfile&quot;&gt;http://trac.macports.org/browser/trunk/dports/x11/wine-devel/Portfile&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;... but all your macports (at least on Mac OS X 10.6) dependencies are 64bit only. After rebuilding the dependencies with +universal, wine starts up nicely:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[jan@macbook bin]$ file mysql-proxy.exe 
mysql-proxy.exe: PE32 executable for MS Windows (console) Intel 80386 32-bit
[jan@macbook bin]$ wine mysql-proxy.exe --version
mysql-proxy 0.8.0
  chassis: mysql-proxy 0.8.0
  glib2: 2.20.3
  libevent: 1.4.12-stable
  LUA: Lua 5.1.2
    package.path: Z:\Users\jan\projects\in-targz\mysql-monitor-agent-2.2.0.1717-win32-x86\lib
    package.cpath: Z:\Users\jan\projects\in-targz\mysql-monitor-agent-2.2.0.1717-win32-x86\bin\lua-?.dll
-- modules
  admin: 0.7.0
  proxy: 0.7.0
&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;valgrind fails to build as +universal if built from MacPorts &lt;a href=&quot;http://trac.macports.org/browser/trunk/dports/devel/valgrind-devel/Portfile&quot;&gt;http://trac.macports.org/browser/trunk/dports/devel/valgrind-devel/Portfile&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;... and ends up as 64bit app ... which can't run a 32bit 'wine'. Back to &quot;build valgrind by hand&quot;. Using the Portfile as template I set up the valgrind source tree and built it with '--enable-32bitonly' to get a 32bit valgrind.&lt;/p&gt;

&lt;p&gt;Everything in place that I need I did hit a bug in valgrind: &lt;a href=&quot;https://bugs.kde.org/show_bug.cgi?id=194757&quot;&gt;https://bugs.kde.org/show_bug.cgi?id=194757&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Anyway, some real hints how to track down the mem-leaks on win32:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://mcfunley.com/277/using-leakdiag-to-debug-unmanaged-memory-leaks/comment-page-1&quot;&gt;http://mcfunley.com/277/using-leakdiag-to-debug-unmanaged-memory-leaks/comment-page-1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://thetweaker.wordpress.com/2009/04/09/native-memory-leaks-part-1-leakdiag/&quot;&gt;http://thetweaker.wordpress.com/2009/04/09/native-memory-leaks-part-1-leakdiag/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
          </content>  </entry>
  <entry xml:base="http://jan.kneschke.de/">
    <author>
      <name>jan</name>
    </author>
    <id>tag:jan.kneschke.de,2010-04-10:556</id>
    <published>2010-04-10T21:20:00Z</published>
    <updated>2010-04-27T06:01:47Z</updated>
    <category term="mysql"/>
    <link href="http://jan.kneschke.de/2010/4/10/mysql-5-5-s-semi-sync-replication-the-protocol-side" rel="alternate" type="text/html"/>
    <title>MySQL 5.5's Semi Sync Replication: the protocol side</title>
<summary type="html">&lt;p&gt;I'm preparing the code for my MySQLConf 2010 session &quot;MySQL Proxy meets: Memcache&quot; where I'll present how to replicate from MySQL to memcache by using the MySQL Proxy.&lt;/p&gt;

&lt;p&gt;Part of it will be using the semi-sync replication support for MySQL 5.5 to implement a synchronous MySQL-to-Memcache replication. All I need is the network protocol definition for semi-sync ...&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;I'm preparing the code for my MySQLConf 2010 session &quot;MySQL Proxy meets: Memcache&quot; where I'll present how to replicate from MySQL to memcache by using the MySQL Proxy.&lt;/p&gt;

&lt;p&gt;Part of it will be using the semi-sync replication support for MySQL 5.5 to implement a synchronous MySQL-to-Memcache replication. All I need is the network protocol definition for semi-sync ...&lt;/p&gt;
&lt;p&gt;The semi-sync replication is a implemented as replication plugin and lives in:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;plugins/semisync/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The master sends a binlog event with a indication that it wants to get a reply for it. Compare these two events:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[0000]  28 00 00 01|00|00 00 00 00 04 01 00 00 00 27 00  (.............'.
[0010]  00 00 00 00 00 00 20 00 14 01 00 00 00 00 00 00  ...... .........
[0020]  6c 6f 63 61 6c 2e 30 30 30 30 30 31              local.000001
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;... and now with semi-sync:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[0000]  2a 00 00 01|00|ef 00|00 00 00 00 04 01 00 00 00  *...............
[0010]  27 00 00 00 00 00 00 00 20 00 14 01 00 00 00 00  '....... .......
[0020]  00 00 6c 6f 63 61 6c 2e 30 30 30 30 30 31        ..local.000001
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;em&gt;0xef&lt;/em&gt; is the semi-sync magic and the &lt;em&gt;0x00&lt;/em&gt; it follows is the semi-sync flag. As it is 0, the master doesn't expect us to ACK it.&lt;/p&gt;

&lt;p&gt;If the slave side is asked to ACK a command it sends back:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/* The layout of a semisync slave reply packet:
   1 byte for the magic num
   8 bytes for the binlog positon
   n bytes for the binlog filename, terminated with a '\0'
*/
#define REPLY_MAGIC_NUM_LEN 1
#define REPLY_BINLOG_POS_LEN 8
#define REPLY_BINLOG_NAME_LEN (FN_REFLEN + 1)
#define REPLY_MAGIC_NUM_OFFSET 0
#define REPLY_BINLOG_POS_OFFSET (REPLY_MAGIC_NUM_OFFSET + REPLY_MAGIC_NUM_LEN)
#define REPLY_BINLOG_NAME_OFFSET (REPLY_BINLOG_POS_OFFSET + REPLY_BINLOG_POS_LEN)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Beware, the &lt;em&gt;binlog filename&lt;/em&gt; is 255 chars max: &lt;a href=&quot;http://bugs.mysql.com/bug.php?id=52748&quot;&gt;http://bugs.mysql.com/bug.php?id=52748&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The master will wait until a slave sent a ACK to its event before it commits the transaction.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[0000]  15 00 00 00|ef|fe 03 00 00 00 00 00 00|6c 6f 63  .............loc
[0010]  61 6c 2e 30 30 30 30 30 32                       al.000002
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Import is that you send the packet-id &lt;em&gt;0x00&lt;/em&gt;. Otherwise the master will ignore the packet. The master will send a OK packet as response to your ACK:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[0000]  07 00 00 01|00 00 00 02 00 00 00
&lt;/code&gt;&lt;/pre&gt;
          </content>  </entry>
  <entry xml:base="http://jan.kneschke.de/">
    <author>
      <name>jan</name>
    </author>
    <id>tag:jan.kneschke.de,2010-01-26:549</id>
    <published>2010-01-26T14:30:00Z</published>
    <updated>2010-01-26T14:34:31Z</updated>
    <category term="mysql"/>
    <link href="http://jan.kneschke.de/2010/1/26/mysql-proxy-0-8-0-release" rel="alternate" type="text/html"/>
    <title>MySQL Proxy: 0.8.0 released</title>
<summary type="html">&lt;p&gt;MySQL Proxy 0.8.0 has been released at &lt;a href=&quot;http://dev.mysql.com/downloads/mysql-proxy/&quot;&gt;http://dev.mysql.com/downloads/mysql-proxy/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While it took a while to get it out, it contains a lot of good stuff and will make a few more things possible for the future. First of all, it is the first release with a threaded network-backend allowing to use plenty of CPUs. &lt;/p&gt;

&lt;p&gt;The other big infrastructure change is mostly for developers: out-of-tree plugins can now be written. Now you can write plugins for the MySQL Proxy against a installed MySQL Proxy. No need to have the MySQL Proxy's source code around.&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;MySQL Proxy 0.8.0 has been released at &lt;a href=&quot;http://dev.mysql.com/downloads/mysql-proxy/&quot;&gt;http://dev.mysql.com/downloads/mysql-proxy/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While it took a while to get it out, it contains a lot of good stuff and will make a few more things possible for the future. First of all, it is the first release with a threaded network-backend allowing to use plenty of CPUs. &lt;/p&gt;

&lt;p&gt;The other big infrastructure change is mostly for developers: out-of-tree plugins can now be written. Now you can write plugins for the MySQL Proxy against a installed MySQL Proxy. No need to have the MySQL Proxy's source code around.&lt;/p&gt;
&lt;h2&gt;ChangeLog&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;added a threaded event handling layer&lt;/li&gt;
&lt;li&gt;added support to change the auth-response on the way to the backend&lt;/li&gt;
&lt;li&gt;added timing infrastruture&lt;/li&gt;
&lt;li&gt;added out-of-tree plugins builds and pkg-config support&lt;/li&gt;
&lt;li&gt;fixed handling of broken length encoded data in resultsets&lt;/li&gt;
&lt;li&gt;fixed version encoding in win32 (#45996)&lt;/li&gt;
&lt;li&gt;fixed too small listen backlog() (#43278)&lt;/li&gt;
&lt;li&gt;fixed configure check if flex is really installed (#45766)&lt;/li&gt;
&lt;li&gt;fixed handling of &gt;16M packets (#35202)&lt;/li&gt;
&lt;li&gt;fixed parsing of invalid IP-addresses (#47273)&lt;/li&gt;
&lt;li&gt;fixed off-by-one line-numbers in Lua stackstraces (#47348)&lt;/li&gt;
&lt;/ul&gt;
          </content>  </entry>
  <entry xml:base="http://jan.kneschke.de/">
    <author>
      <name>jan</name>
    </author>
    <id>tag:jan.kneschke.de,2009-08-18:451</id>
    <published>2009-08-18T08:37:00Z</published>
    <updated>2009-08-18T08:43:27Z</updated>
    <category term="mysql"/>
    <link href="http://jan.kneschke.de/2009/8/18/mysql-proxy-froscon-2009" rel="alternate" type="text/html"/>
    <title>MySQL Proxy: FrosCon 2009</title>
<content type="html">
            &lt;p&gt;I'll be at the &lt;a href=&quot;http://opensqlcamp.org/Main_Page#OpenSQL_Camp_2009.2C_European_Edition&quot;&gt;OpenSQLCamp 2009&lt;/a&gt; which is part of the &lt;a href=&quot;http://froscon.org&quot;&gt;FrosCon&lt;/a&gt; this weekend and give a talk about &lt;em&gt;drumroll&lt;/em&gt; MySQL Proxy.&lt;/p&gt;

&lt;p&gt;http://programm.froscon.org/2009/track/OpenSQLCamp/index.de.html has all the sessions incl. my&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;MySQL Proxy: a MySQL toolbox

Architecture and Concepts of misuse
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It will be an extension of my MySQL Conference talk from this year with binlogs, frm-files, ... and what happens if you expose MySQLs core functionality as libraries and wrap it with a scripting layer.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://jan.kneschke.de/">
    <author>
      <name>jan</name>
    </author>
    <id>tag:jan.kneschke.de,2009-08-06:446</id>
    <published>2009-08-06T17:27:00Z</published>
    <updated>2009-08-07T11:56:24Z</updated>
    <category term="mysql"/>
    <link href="http://jan.kneschke.de/2009/8/6/mysql-proxy-profiling-0-8" rel="alternate" type="text/html"/>
    <title>MySQL Proxy: profiling 0.8</title>
<summary type="html">&lt;p&gt;In MySQL Proxy 0.8 we are added a multi-threaded network-subsystem allowing several networks events be processed in parallel. Early benchmarks show that what we have in trunk basicly works.&lt;/p&gt;

&lt;p&gt;But the benchmarks weren't as good as we expected. That's the time where you prepare to get dirty.&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;In MySQL Proxy 0.8 we are added a multi-threaded network-subsystem allowing several networks events be processed in parallel. Early benchmarks show that what we have in trunk basicly works.&lt;/p&gt;

&lt;p&gt;But the benchmarks weren't as good as we expected. That's the time where you prepare to get dirty.&lt;/p&gt;
&lt;p&gt;While &lt;a href=&quot;http://blogs.sun.com/kay/entry/mysql_proxy_benchmarking_and_profiling&quot;&gt;Kay&lt;/a&gt; went with &lt;code&gt;lockstat&lt;/code&gt; to analyze the proxy on solaris and found that &lt;code&gt;g_atomic_int_get()&lt;/code&gt; isn't using native code if built with Sun's CC, I attacked the Linux side with &lt;code&gt;oprofile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After rebuilding the proxy with &lt;a href=&quot;http://mituzas.lt/2009/07/26/on-binaries-and-fomit-frame-pointer/&quot;&gt;&lt;code&gt;-fno-omit-frame-pointer&lt;/code&gt;&lt;/a&gt; I got the information I was looking for from &lt;code&gt;oprofile&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ opcontrol --vmlinux=...
$ opcontrol --callgraph=5
$ opcontrol --separate=lib,kernel
$ opcontrol --start
$ sysbench ...
$ opcontrol --stop
$ opreport \
    --debug-info \
    --callgraph \
    --symbols \
    --merge tgid \
    '.../mysql-proxy'
....
-------------------------------------------------------------------------------
  1224     20.3966  event.c:468                 libevent-1.4.so.2.1.1    event_base_loop
  4777     79.6034  network-mysqld.c:446        libmysql-proxy.so.0.0.0  plugin_call
102069    2.7567  (no location information)   libpthread-2.5.so        pthread_mutex_lock
  102069   100.000  (no location information)   libpthread-2.5.so        pthread_mutex_lock [self]
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The global Lua lock, the lock we wanted to remove for 0.9 anyway. Perhaps we can do something simple and not take the lock if there is no Lua script loaded. In 0.9 we will change the implementation and give each connection its own Lua state instead of shared the state between all connections.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://jan.kneschke.de/">
    <author>
      <name>jan</name>
    </author>
    <id>tag:jan.kneschke.de,2009-06-25:441</id>
    <published>2009-06-25T15:54:00Z</published>
    <updated>2009-06-25T17:51:08Z</updated>
    <category term="mysql"/>
    <link href="http://jan.kneschke.de/2009/6/25/mysql-proxy-roles" rel="alternate" type="text/html"/>
    <title>MySQL Proxy: Roles</title>
<summary type="html">&lt;p&gt;On the MySQL Proxy channel we get questions from time to time if the authentication can be intercepted and replaced data from a external source.&lt;/p&gt;

&lt;p&gt;From now on, you can. For example if you want to get data from a external source (like LDAP) or want to implement roles.&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;On the MySQL Proxy channel we get questions from time to time if the authentication can be intercepted and replaced data from a external source.&lt;/p&gt;

&lt;p&gt;From now on, you can. For example if you want to get data from a external source (like LDAP) or want to implement roles.&lt;/p&gt;
&lt;h1&gt;Mapping Accounts to &quot;Roles&quot;&lt;/h1&gt;

&lt;p&gt;There isn't much needed to implement Roles for MySQL with the help of the MySQL Proxy.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mysql.user&lt;/code&gt; doesn't contain users, but roles instead&lt;/li&gt;
&lt;li&gt;the proxy maps user-accounts to role-accounts with a script like above&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It works like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;login to the proxy&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ mysql --user=jan --password=secret --port=4040
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;proxy looks up username password, finds a role for him&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;proxy replaces credentials ad hoc&lt;/li&gt;
&lt;li&gt;mysql-server sees the role-name and role-password and lets the user in&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This works nicely as long as you only have 1:n mapping between roles and users. One user has one role, but one role has many users.&lt;/p&gt;

&lt;p&gt;All you need is a storage that has user's password in one of three forms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cleartext as in the example below&lt;/li&gt;
&lt;li&gt;SHA1(cleartext) as you may have in LDAP&lt;/li&gt;
&lt;li&gt;SHA1(SHA1(cleartext)) as in &lt;code&gt;mysql.user&lt;/code&gt;: &lt;code&gt;SELECT CONCAT(&quot;*&quot;, SHA1(UNHEX(SHA1(&quot;pass&quot;)))), PASSWORD(&quot;pass&quot;);&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;How the Auth works&lt;/h1&gt;

&lt;p&gt;The MySQL Proxy has access to all protocol data and exposes it as part of different stages of the authentication to the scripting layer:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;---
-- show how to use the mysql.password functions 
function read_auth()
    local c = proxy.connection.client
    local s = proxy.connection.server
    print((&quot;for challenge %q the client sent %q&quot;):format(
            s.scramble_buffer,
            c.scrambled_password
    ))
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The challenge and response secures the password and is works like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;SERVER:  scramble_buffer=create_random_string()
         send(scramble_buffer)

CLIENT:  recv(scramble_buffer)
         hash_stage1=sha1(&quot;password&quot;)
         hash_stage2=sha1(hash_stage1)
         scrambled_password=xor(hash_stage1, sha1(scramble_buffer, hash_stage2)

         send(scrambled_password)

SERVER:  recv(scrambled_password)
         hash_stage1=xor(scrambled_password, sha1(scramble_buffer,hash_stage2))
         candidate_hash2=sha1(hash_stage1)
         check(candidate_hash2==hash_stage2)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That's taken from &lt;code&gt;sql/password.c&lt;/code&gt; of the MySQL Server Source. The &lt;code&gt;hash_stage2&lt;/code&gt; is the same as result of the &lt;code&gt;PASSWORD()&lt;/code&gt; function without the &lt;code&gt;*&lt;/code&gt; and the &lt;code&gt;HEX()&lt;/code&gt; and is stored in &lt;code&gt;mysql.user&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;Replacing the auth response&lt;/h1&gt;

&lt;p&gt;Based on the &lt;code&gt;read_auth()&lt;/code&gt; from above you can also replace the credentials on the fly:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    ---
    -- map replace:me with root:secret
    --
    if c.username == &quot;replace&quot; and -- the username we want to map
            password.check(
                    s.scramble_buffer,
                    c.scrambled_password,
                    password.hash(password.hash(&quot;me&quot;)) -- its valid password
            ) then 

            proxy.queries:append(1,
                    proto.to_response_packet({
                            username = &quot;root&quot;, 
                            response = password.scramble(s.scramble_buffer, password.hash(&quot;secret&quot;)),
                            charset  = 8, -- default charset
                            database = c.default_db,
                            max_packet_size = 1 * 1024 * 1024
                    })
            )

            return proxy.PROXY_SEND_QUERY  -- works if you use lp:mysql-proxy r694 or later
    end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;At&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://bazaar.launchpad.net/~mysql-proxy-developers/mysql-proxy/trunk/annotate/head%3A/examples/tutorial-scramble.lua&quot;&gt;http://bazaar.launchpad.net/~mysql-proxy-developers/mysql-proxy/trunk/annotate/head%3A/examples/tutorial-scramble.lua&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;you can see the full version of it.&lt;/p&gt;

&lt;p&gt;Let's see if it works:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ mysql-proxy --proxy-lua-script=tutorial-scramble.lua
$ mysql --port=4040 --user=replace --password=me
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2702
Server version: 5.1.34-log MySQL Community Server (GPL)

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

root@127.0.0.1 [(none)]&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I logged in as the &lt;code&gt;root&lt;/code&gt; user.&lt;/p&gt;

&lt;h1&gt;Fixing the hostcheck&lt;/h1&gt;

&lt;p&gt;This can also be used to rewrite the username to something that includes his original ip. Like&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;...,
username = c.username .. &quot;-&quot; .c.src.address,
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;to make the server see something else than the proxy's client-address if you use the host-field in the &lt;code&gt;mysql.user&lt;/code&gt; tables.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://jan.kneschke.de/">
    <author>
      <name>jan</name>
    </author>
    <id>tag:jan.kneschke.de,2009-06-05:438</id>
    <published>2009-06-05T08:25:00Z</published>
    <updated>2009-06-05T09:30:28Z</updated>
    <category term="mysql"/>
    <link href="http://jan.kneschke.de/2009/6/5/mysql-proxy-hackathon-2009" rel="alternate" type="text/html"/>
    <title>MySQL Proxy: Hackathon 2009</title>
<content type="html">
            &lt;p&gt;At MySQL Conference in Santa Clara this April the idea was brought up, now we do it:&lt;/p&gt;

&lt;blockquote&gt;
    &lt;p&gt;a MySQL Proxy hackathon this summer in Sun Office in Hamburg, Germany.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We want bring together everyone who is interested in&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;hacking on MySQL Proxy, &lt;/li&gt;
&lt;li&gt;sharing ideas how it is used and &lt;/li&gt;
&lt;li&gt;how it should develop.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More at &lt;a href=&quot;http://forge.mysql.com/wiki/MySQL_Proxy_Hackathon_2009&quot;&gt;http://forge.mysql.com/wiki/MySQL_Proxy_Hackathon_2009&lt;/a&gt;&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://jan.kneschke.de/">
    <author>
      <name>jan</name>
    </author>
    <id>tag:jan.kneschke.de,2009-05-28:433</id>
    <published>2009-05-28T16:21:00Z</published>
    <updated>2009-05-28T16:56:04Z</updated>
    <category term="mysql"/>
    <link href="http://jan.kneschke.de/2009/5/28/wormhole-se-joins" rel="alternate" type="text/html"/>
    <title>Wormhole SE: JOINs</title>
<summary type="html">&lt;p&gt;In my rare spare time work on the &lt;a href=&quot;http://jan.kneschke.de/2009/5/4/binlog-storage-engine-mysql-proxy-edition&quot;&gt;http://jan.kneschke.de/2009/5/4/binlog-storage-engine-mysql-proxy-edition&lt;/a&gt; and for a few days I was wondering why my index-based JOINs didn't worked. With a index it works, with an index nothing is returned.&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;In my rare spare time work on the &lt;a href=&quot;http://jan.kneschke.de/2009/5/4/binlog-storage-engine-mysql-proxy-edition&quot;&gt;http://jan.kneschke.de/2009/5/4/binlog-storage-engine-mysql-proxy-edition&lt;/a&gt; and for a few days I was wondering why my index-based JOINs didn't worked. With a index it works, with an index nothing is returned.&lt;/p&gt;
&lt;p&gt;I narrowed it down to a simple test-case:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;select * from test AS a JOIN test AS b USING (event_pos) WHERE a.event_pos IN (106);
...
1 row in set (0.03 sec)

EXPLAIN
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
|  1 | SIMPLE      | a     | const | PRIMARY       | PRIMARY | 4       | const |    1 |       | 
|  1 | SIMPLE      | b     | const | PRIMARY       | PRIMARY | 4       | const |    1 |       | 
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;vs.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;select * from test AS a JOIN test AS b USING (event_pos) WHERE a.event_pos IN (106, 177);
0 row in set (0.03 sec)

EXPLAIN ...
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+-------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref              | rows | Extra       |
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+-------------+
|  1 | SIMPLE      | a     | range  | PRIMARY       | PRIMARY | 4       | NULL             |    2 | Using where | 
|  1 | SIMPLE      | b     | eq_ref | PRIMARY       | PRIMARY | 4       | test.a.event_pos |    1 |             | 
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+-------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A &lt;code&gt;self-JOIN&lt;/code&gt; on the &lt;code&gt;PRIMARY KEY&lt;/code&gt; with two PK-values that exist.&lt;/p&gt;

&lt;p&gt;The EXPLAINs look fine, &lt;code&gt;eq_ref&lt;/code&gt; is what we want. But, we get no rows back. Looking at the debug output from MySQL and the wormhole storage engine it all looked fine. The right functions are called, the right return values are given.&lt;/p&gt;

&lt;p&gt;At &lt;code&gt;#mysql-dev&lt;/code&gt; &lt;a href=&quot;http://s.petrunia.net/blog/&quot;&gt;Sergey Petrunia&lt;/a&gt; did take a look at my problem and we stepped through the code until I stumbled over:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;static int
join_read_key(JOIN_TAB *tab)
{
    ...
    return table-&amp;gt;status ? -1 : 0;
}

(gdb) p table-&amp;gt;status
3 (aka STATUS_NO_RECORD)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Scanning the code in &lt;code&gt;sql/sql_select.cc&lt;/code&gt; where that comes from:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;case JT_EQ_REF:
  table-&amp;gt;status=STATUS_NO_RECORD;
  if (tab-&amp;gt;select) 
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Who is supposed to change that value ? Me ? Checking the other storage engines it looks like there are &lt;em&gt;two&lt;/em&gt; return codes: the obvious one and the hidden one. Depending on the execution plan (as seen above) one or the other is used. &lt;em&gt;grmpf&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Long story, simple fix: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;=== modified file 'src/ha_wormhole.cc'
--- src/ha_wormhole.cc  2009-05-25 20:55:14 +0000
+++ src/ha_wormhole.cc  2009-05-28 13:28:06 +0000
@@ -1071,6 +1071,8 @@
        if (0 == wormhole_hook_call(L, 2)) {
                /* no thing pushed */
        } else if (lua_istable(L, -1)) {
+               table-&amp;gt;status = 0; /** _WOOT_ ... we have to set it to 0 to make the eq_ref work */
+
                lua_getrow();
                lua_pop(L, 1); /* pop the table */
                DBUG_RETURN(0);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;in the storage-engine's &lt;code&gt;index_read()&lt;/code&gt; function and my JOINs on index work.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://jan.kneschke.de/">
    <author>
      <name>jan</name>
    </author>
    <id>tag:jan.kneschke.de,2009-05-23:432</id>
    <published>2009-05-23T11:09:00Z</published>
    <updated>2009-05-23T11:09:45Z</updated>
    <link href="http://jan.kneschke.de/2009/5/23/mysql-proxy-0-7-1-released" rel="alternate" type="text/html"/>
    <title>MySQL Proxy: 0.7.1 released</title>
<summary type="html">&lt;p&gt;We are happy to announce that &lt;a href=&quot;http://dev.mysql.com/downloads/mysql-proxy/index.html&quot;&gt;MySQL Proxy 0.7.1&lt;/a&gt; is available in a source and binary release for many popular platforms.&lt;/p&gt;

&lt;p&gt;This release contains a few minor bugfixes and changes in directory layout over the previous 0.7.0 release.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;moved plugins to &lt;code&gt;lib/mysql-proxy/plugins&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;moved lua modules to &lt;code&gt;lib/mysql-proxy/lua&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;moved libs to &lt;code&gt;lib/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please report any problems on &lt;a href=&quot;http://bugs.mysql.com&quot;&gt;http://bugs.mysql.com&lt;/a&gt;, our Launchpad discussion mailing list at &lt;a href=&quot;https://launchpad.net/~mysql-proxy-discuss&quot;&gt;https://launchpad.net/~mysql-proxy-discuss&lt;/a&gt; or on IRC: &lt;code&gt;#mysql-proxy&lt;/code&gt; on &lt;code&gt;irc.freenode.net&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Please note that the binary for Windows is currently still the old 0.6.1 release and will be updated soon.&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;We are happy to announce that &lt;a href=&quot;http://dev.mysql.com/downloads/mysql-proxy/index.html&quot;&gt;MySQL Proxy 0.7.1&lt;/a&gt; is available in a source and binary release for many popular platforms.&lt;/p&gt;

&lt;p&gt;This release contains a few minor bugfixes and changes in directory layout over the previous 0.7.0 release.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;moved plugins to &lt;code&gt;lib/mysql-proxy/plugins&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;moved lua modules to &lt;code&gt;lib/mysql-proxy/lua&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;moved libs to &lt;code&gt;lib/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please report any problems on &lt;a href=&quot;http://bugs.mysql.com&quot;&gt;http://bugs.mysql.com&lt;/a&gt;, our Launchpad discussion mailing list at &lt;a href=&quot;https://launchpad.net/~mysql-proxy-discuss&quot;&gt;https://launchpad.net/~mysql-proxy-discuss&lt;/a&gt; or on IRC: &lt;code&gt;#mysql-proxy&lt;/code&gt; on &lt;code&gt;irc.freenode.net&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Please note that the binary for Windows is currently still the old 0.6.1 release and will be updated soon.&lt;/p&gt;
&lt;h1&gt;ChangeLog&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;fixed connection close at &lt;code&gt;COM_REFRESH&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;fixed detection of the &lt;code&gt;--basedir&lt;/code&gt; for relative execs&lt;/li&gt;
&lt;li&gt;fixed decoding of &lt;code&gt;binlog.event_type&lt;/code&gt; on sparc&lt;/li&gt;
&lt;li&gt;fixed build on freebsd and hpux&lt;/li&gt;
&lt;/ul&gt;
          </content>  </entry>
  <entry xml:base="http://jan.kneschke.de/">
    <author>
      <name>jan</name>
    </author>
    <id>tag:jan.kneschke.de,2009-05-04:427</id>
    <published>2009-05-04T10:30:00Z</published>
    <updated>2009-05-25T22:56:04Z</updated>
    <category term="mysql"/>
    <link href="http://jan.kneschke.de/2009/5/4/binlog-storage-engine-mysql-proxy-edition" rel="alternate" type="text/html"/>
    <title>Binlog Storage Engine - MySQL Proxy Edition</title>
<summary type="html">&lt;p&gt;A binlog storage engine was talked about many times already. Being able to JOIN against the binlogs, the just SELECT it, search for entries ... is not possible with current SQL commands in the MySQL Server.&lt;/p&gt;

&lt;p&gt;As you have read the previous articles here, I wrote a &lt;a href=&quot;http://jan.kneschke.de/projects/mysql/mysql-proxy-meets-binlog-the-examples&quot;&gt;binlog encoder/decoder for MySQL Proxy&lt;/a&gt; and wrapped with with a Lua interface. Some years ago I also wrote a &lt;a href=&quot;http://jan.kneschke.de/projects/mysql/wormhole-storage-engine&quot;&gt;scriptable storage engine&lt;/a&gt; which can call lua scripts to return data for SELECTs. Putting the two together gives us: a Binlog Storage Engine.&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;A binlog storage engine was talked about many times already. Being able to JOIN against the binlogs, the just SELECT it, search for entries ... is not possible with current SQL commands in the MySQL Server.&lt;/p&gt;

&lt;p&gt;As you have read the previous articles here, I wrote a &lt;a href=&quot;http://jan.kneschke.de/projects/mysql/mysql-proxy-meets-binlog-the-examples&quot;&gt;binlog encoder/decoder for MySQL Proxy&lt;/a&gt; and wrapped with with a Lua interface. Some years ago I also wrote a &lt;a href=&quot;http://jan.kneschke.de/projects/mysql/wormhole-storage-engine&quot;&gt;scriptable storage engine&lt;/a&gt; which can call lua scripts to return data for SELECTs. Putting the two together gives us: a Binlog Storage Engine.&lt;/p&gt;
&lt;p&gt;In the datadir we just create &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ ls -la test/binlog_*.*
-rw-r--r--  1 jan  staff  1342  4 Mai 12:30 test/binlog_000016.lua

mysql&amp;gt; select * from test.binlog_000016;
Query OK, 0 rows affected (0.00 sec)

+----------------------------------+------------+----------------+
| type                             | event_size | next_event_pos |
+----------------------------------+------------+----------------+
| FORMAT_DESCRIPTION_EVENT         |        102 |            106 |
| QUERY_EVENT                      |         71 |            177 |
| QUERY_EVENT                      |         71 |            248 |
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That &lt;code&gt;binlog_000016&lt;/code&gt; table is a &lt;code&gt;.lua&lt;/code&gt; script that the scriptable storage engine can load:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;local binlog = assert(require(&quot;mysql.binlog&quot;))

local binlog_table = { }

-- Lua does OOP and and inheritance ...
function binlog_table:new(o)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    return o
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As we don't want to mess with &lt;code&gt;CREATE TABLE&lt;/code&gt; statements, we just create the &lt;code&gt;.frm&lt;/code&gt; files on the fly by providing a &lt;code&gt;discover()&lt;/code&gt; function.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-- return a the table structure
function binlog_table:discover()
    return {
            { name = &quot;type&quot;, type = 254, length = 32 },
            { name = &quot;event_size&quot;, type = 3, length = 32 },
            { name = &quot;next_event_pos&quot;, type = 3, length = 32 }
    }
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After &lt;code&gt;SELECT&lt;/code&gt;ing from the table the first time the mysql-server will create the &lt;code&gt;.frm&lt;/code&gt; files for us automaticly:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ ls -la test/binlog_*.*
-rw-rw----  1 jan  staff  8652  4 Mai 12:29 test/binlog_000016.frm
-rw-r--r--  1 jan  staff  1342  4 Mai 12:30 test/binlog_000016.lua
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The most basic command is &lt;code&gt;get row for a table-scan&lt;/code&gt;. Our &lt;code&gt;:next()&lt;/code&gt; method sets up the iterator and returns the iterator-function that is called until it returns &lt;code&gt;nil&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-- returns a iterator
function binlog_table:next()
    -- set the iterator
    self.f:seek(4)
    self.f_iter = self.f:next()

    if not self.f_iter then return nil end
    -- return the iterator function
    return function ()
            self.event = self.f_iter()

            if not self.event then return nil end

            return { self.event.type, self.event.event_size, self.event.log_pos }
    end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To create our binlog table we just inherit from that &lt;code&gt;binlog_table&lt;/code&gt; class and return it:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-- this table is a binlog-table
local tbl = binlog_table:new({ })

function tbl:open()
    self.f = binlog.open(&quot;/Users/jan/sandboxes/msb_5_1_35/data/msandbox.000016&quot;)

    return (self.f ~= nil)
end

return tbl
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The last piece is the storage engine plugin:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ mysql &amp;lt;&amp;lt;EOF
FLUSH TABLES;
UNINSTALL PLUGIN lua;
EOF
$ make install
$ mysql &amp;lt;&amp;lt; EOF
INSTALL PLUGIN lua SONAME 'libwormhole_engine.so';
EOF
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If everything works out nicely you should see:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ mysql 
mysql&amp;gt; show plugins;
+------------+--------+----------------+-----------------------+---------+
| Name       | Status | Type           | Library               | License |
+------------+--------+----------------+-----------------------+---------+
...
| LUA        | ACTIVE | STORAGE ENGINE | libwormhole_engine.so | GPL     |
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;Update&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Properly normalized you can now decode the different events by just &lt;code&gt;JOIN&lt;/code&gt;ing the &lt;code&gt;..._query&lt;/code&gt; table based on the &lt;code&gt;event_pos&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; select * from test.test_query AS q join test.test AS e using (event_pos) where e.type = &quot;QUERY_EVENT&quot; and event_pos = 106;
+-----------+-------------+-----------+--------------+-----------+-------------+------------+-----------------+------------+
| event_pos | type        | thread_id | query        | server_id | type        | event_size | next_event_posH | timestamp  |
+-----------+-------------+-----------+--------------+-----------+-------------+------------+-----------------+------------+
|       106 | QUERY_EVENT |         2 | FLUSH TABLES |         1 | QUERY_EVENT |         71 |             177 | 1241432054 | 
+-----------+-------------+-----------+--------------+-----------+-------------+------------+-----------------+------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And that is supposed to be fast as &lt;code&gt;event_pos&lt;/code&gt; is our &lt;code&gt;PRIMARY KEY&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; explain select * from test.test_query AS q join test.test AS e using (event_pos) where e.type = &quot;QUERY_EVENT&quot; and event_pos = 106;
+----+-------------+-------+------+---------------+---------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key     | key_len | ref   | rows | Extra       |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-------------+
|  1 | SIMPLE      | q     | ref  | PRIMARY       | PRIMARY | 5       | const |   10 | Using where |
|  1 | SIMPLE      | e     | ref  | PRIMARY       | PRIMARY | 5       | const |   10 | Using where |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;On the implementation side the &lt;code&gt;..._query&lt;/code&gt; table just inherits from the base-table and overwrites the &lt;code&gt;discover&lt;/code&gt; (for the new table-structure) and the &lt;code&gt;get_row&lt;/code&gt; (to return the corresponding information) methods. Everything else stays the same.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;local binlog_table = assert(require(&quot;wormhole.binlog_table&quot;))

-- this table is a binlog-table
local tbl = binlog_table:new()

function tbl:discover()
    return {
            { name = &quot;event_pos&quot;, type = 3, is_nullable = false, is_primary = true },
            { name = &quot;type&quot;, type = 254, length = 64 * 3, is_nullable = false },
            { name = &quot;thread_id&quot;, type = 3, is_nullable = false },
            { name = &quot;query&quot;, type = 254, length = 256, is_nullable = false },
    }
end

function tbl:get_row()
    local e = self.event
    if e.type == &quot;QUERY_EVENT&quot; then
            return { e.log_pos - e.event_size,
                    e.type,
                    e.query.thread_id,
                    e.query.query,
            }
    else
            return { e.log_pos - e.event_size,
                    e.type,
                    nil,
                    nil
            }
    end
end

function tbl:open()
    self.f = binlog.open(&quot;/Users/jan/sandboxes/msb_5_1_35/data/msandbox.000016&quot;)

    return (self.f ~= nil)
end

return tbl
&lt;/code&gt;&lt;/pre&gt;
          </content>  </entry>
  <entry xml:base="http://jan.kneschke.de/">
    <author>
      <name>jan</name>
    </author>
    <id>tag:jan.kneschke.de,2009-04-28:422</id>
    <published>2009-04-28T11:58:00Z</published>
    <updated>2009-04-29T12:27:12Z</updated>
    <category term="mysql"/>
    <link href="http://jan.kneschke.de/2009/4/28/mysqls-hidden-loader-interface" rel="alternate" type="text/html"/>
    <title>MySQLs hidden Loader Interface</title>
<summary type="html">&lt;p&gt;After getting the &lt;a href=&quot;http://jan.kneschke.de/2009/4/22/druckbetankung&quot;&gt;http://jan.kneschke.de/2009/4/22/druckbetankung&lt;/a&gt; stuff working and discussing the idea with the replication team we figured out that my presented approach has a nice hack-value, but otherwise is perhaps a bit too complicated. The same result can be achieved by a more simplified approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;turn our input data into a RBR based base64-encoded BINLOG-stmt using binlog&lt;/li&gt;
&lt;li&gt;use the &lt;code&gt;mysql&lt;/code&gt; client to push the data into the server&lt;/li&gt;
&lt;/ul&gt;</summary><content type="html">
            &lt;p&gt;After getting the &lt;a href=&quot;http://jan.kneschke.de/2009/4/22/druckbetankung&quot;&gt;http://jan.kneschke.de/2009/4/22/druckbetankung&lt;/a&gt; stuff working and discussing the idea with the replication team we figured out that my presented approach has a nice hack-value, but otherwise is perhaps a bit too complicated. The same result can be achieved by a more simplified approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;turn our input data into a RBR based base64-encoded BINLOG-stmt using binlog&lt;/li&gt;
&lt;li&gt;use the &lt;code&gt;mysql&lt;/code&gt; client to push the data into the server&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;a href=&quot;http://dev.mysql.com/doc/refman/5.1/en/binlog.html&quot;&gt;BINLOG&lt;/a&gt; stmt is used by &lt;code&gt;mysqlbinlog&lt;/code&gt; to pass the RBR-events through the mysql client to the mysql-server. Usually it wraps a &lt;code&gt;FORMAT_DESCRIPTION_EVENT&lt;/code&gt; and the &lt;code&gt;TABLE_MAP_EVENT&lt;/code&gt; and the &lt;code&gt;*_ROWS_EVENTS&lt;/code&gt;. BUT ... it can handle any binlog-event. Just try&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ mysqlbinlog --base64-output=always
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;to see how that looks like. &lt;/p&gt;

&lt;p&gt;A generated example looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# FORMAT_DESCRIPTION
BINLOG '5hX3SQ8CAAAAZgAAAGYAAAAAAAQANS4xLjI2LW15c3FsLXByb3h5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADmFfdJEzgNAAgAEgAEBAQEEgAAUwAEGggAAAAICAgC';
# DROP TABLE IF EXISTS ...
BINLOG '5hX3SQICAAAAPgAAAKQAAAAAAAEAAAAAAAAABAAAAAB0ZXN0AERST1AgVEFCTEUgSUYgRVhJU1RTIHB1bXA=';
# CREATE TABLE 
BINLOG '5hX3SQICAAAAYgAAAAYBAAAAAAEAAAAAAAAABAAAAAB0ZXN0AENSRUFURSBUQUJMRSBwdW1wICggZjEgVkFSQ0hBUigxNikgTk9UIE5VTEwsIGYyIElOVCwgZjMgSU5UICk=';
# BEGIN
BINLOG '5hX3SQICAAAAKgAAADABAAAAAAEAAAAAAAAABAAAAAB0ZXN0AEJFR0lO';
# INSERT ...
BINLOG '5hX3SQICAAAAUQAAAIEBAAAAAAEAAAAAAAAABAAAAAB0ZXN0AElOU0VSVCBJTlRPIHB1bXAgVkFMVUVTICggJ2l0IHdvcmtzJywgMSwgMiAp';
 # TABLE_MAP_EVENT + WRITE_ROWS_EVENT
BINLOG '5hX3SRMCAAAALwAAALABAAAAAAEAAAAAAAAABHRlc3QABHB1bXAAAw8DAwIQAAY=
5hX3SRcCAAAANwAAAOcBAAAAAAEAAAAAAAkAAwcAA2FiYwIAAAADAAAAAANkZWYDAAAABQAAAA==';
# INSERT ...
BINLOG '5hX3SQICAAAATQAAADQCAAAAAAEAAAAAAAAABAAAAAB0ZXN0AElOU0VSVCBJTlRPIHB1bXAgVkFMVUVTICggJ2RvbmUnLCAxLCAyICk=';
# XID .. (aka COMMIT)
BINLOG '5hX3SRACAAAAGwAAAE8CAAAAALYKAAAAAAAA';
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Feel free to execute it in your client. It will create a table &lt;code&gt;test.pump&lt;/code&gt; and insert 4 rows into it. 2 with normal &lt;code&gt;INSERT&lt;/code&gt; stmts, 2 with a RBR-&lt;code&gt;WRITE_ROWS_EVENT&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;To make it a loader interface what is faster then &lt;code&gt;LOAD DATA INFILE&lt;/code&gt; or &lt;code&gt;multi-row INSERT&lt;/code&gt; you have to convert all your data into &lt;code&gt;WRITE_ROWS_EVENT&lt;/code&gt;s and push it. As the data is already in the native form it should/may be faster than the 2 other approaches.&lt;/p&gt;

&lt;p&gt;Now the interesting question: how much faster is a &lt;code&gt;WRITE_ROWS_EVENT&lt;/code&gt; with BASE64 encoding than a multi-row INSERT ?&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://jan.kneschke.de/">
    <author>
      <name>jan</name>
    </author>
    <id>tag:jan.kneschke.de,2009-04-22:418</id>
    <published>2009-04-22T18:18:00Z</published>
    <updated>2009-04-27T08:55:15Z</updated>
    <category term="mysql"/>
    <link href="http://jan.kneschke.de/2009/4/22/druckbetankung" rel="alternate" type="text/html"/>
    <title>Druckbetankung</title>
<summary type="html">&lt;p&gt;... or how to misuse RBR to fill a MySQL table in the hopefully fastest way. &lt;/p&gt;

&lt;p&gt;In my session yesterday I didn't had time to talk about this. The &lt;a href=&quot;http://www.mysqlconf.com/mysql2009/public/schedule/detail/7056&quot;&gt;slide-desk&lt;/a&gt; covers it and I still wanted to document the idea at least. &lt;/p&gt;</summary><content type="html">
            &lt;p&gt;... or how to misuse RBR to fill a MySQL table in the hopefully fastest way. &lt;/p&gt;

&lt;p&gt;In my session yesterday I didn't had time to talk about this. The &lt;a href=&quot;http://www.mysqlconf.com/mysql2009/public/schedule/detail/7056&quot;&gt;slide-desk&lt;/a&gt; covers it and I still wanted to document the idea at least. &lt;/p&gt;
&lt;p&gt;The starting point is &quot;Row Based Replication stores data in internal MySQL row-format.&quot; The slave can apply these events without any SQL parsing involved.&lt;/p&gt;

&lt;p&gt;The idea is to create a &lt;code&gt;master&lt;/code&gt; plugin that &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;takes a file or something else that you want to INSERT into the mysql-server&lt;/li&gt;
&lt;li&gt;creates a binlog-stream using row-based replication events from it&lt;/li&gt;
&lt;li&gt;expose the binlog-stream and pretend to be a mysql-master (listen on port :3306 and handle &lt;code&gt;COM_BINLOG_DUMP&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;let the MySQL-server connect to &lt;code&gt;master&lt;/code&gt;-plugin using &lt;code&gt;CHANGE MASTER TO&lt;/code&gt; and &lt;code&gt;START SLAVE&lt;/code&gt; and fetch and apply the binlog-stream natively&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The final implementation is in &lt;a href=&quot;https://code.launchpad.net/~jan-kneschke/mysql-proxy/replication&quot;&gt;replication-tree&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The master plugin takes a lua script when does all the magic and will hide the details. To give you an idea what is going on behind the scenes a few snippets of Lua code, like how the different binlog-events are represented:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    {
            server_id = 2,
            type = &quot;QUERY_EVENT&quot;,
            query = {
                    db_name = &quot;test&quot;,
                    query = &quot;DROP TABLE IF EXISTS pump&quot;
            }
    },
    {
            server_id = 2,
            type = &quot;QUERY_EVENT&quot;,
            query = {
                    db_name = &quot;test&quot;,
                    query = &quot;CREATE TABLE pump ( f1 CHAR(16) NOT NULL, f2 INT, f3 INT )&quot;
            }
    },
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;generates the two &lt;code&gt;QUERY_EVENT&lt;/code&gt;s to create the table we want to insert into:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    -- define a table-map event matching our CREATE TABLE from above
    {
            server_id = 2,
            type = &quot;TABLE_MAP_EVENT&quot;,
            table_map = {
                    table_id = 1,
                    db_name = &quot;test&quot;,
                    table_name = &quot;pump&quot;,
                    fields = {
                            { type = proxy.MYSQL_TYPE_STRING, is_nullable = false },
                            { type = proxy.MYSQL_TYPE_LONG, is_nullable = true },
                            { type = proxy.MYSQL_TYPE_LONG }, -- is_null defaults to true
                    },
            }
    },
    -- full the table with 2 rows
    {
            server_id = 2,
            type = &quot;WRITE_ROWS_EVENT&quot;,
            rbr = {
                    table_id = 1,
                    flags    = 0,
                    rows = {
                            {
                                    before  = { &quot;abc&quot;, 2, 3 },
                            },
                            {
                                    before  = { &quot;def&quot;, 3, 5 },
                            },
                    }
            }
    },
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;adds the two events that represent a &lt;code&gt;INSERT&lt;/code&gt; statement for Row-based-replication-speak. The events are passed to the binlog-generator and are pass out as response to a &lt;code&gt;COM_BINLOG_DUMP&lt;/code&gt; response:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;local f = binlog.new()
f:seek(4)
... 
local event = f:to_event(event_tbl[event_ndx])
return &quot;\000&quot; ... f:append(event)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To give it a test we load the &lt;code&gt;master&lt;/code&gt;-plugin and the script &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ mysql-proxy \
    --plugins=master \
    --master-address=:4041 \
    --master-lua-script=master.lua
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;and let the MySQL Server connect to it:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;STOP SLAVE;
RESET SLAVE;
CHANGE MASTER TO master_host='127.0.0.1', \
    master_port=4041, \
    master_user='root', \
    master_password='secret';
START SLAVE;
-- importing done ... let's verify it
USE test;
SHOW TABLES;
SELECT SLEEP(0.1); -- wait until the slave has caught up
SELECT * FROM pump;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;outputs:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;+-----+------+------+
| f1  | f2   | f3   |
+-----+------+------+
| abc |    2 |    3 |
| def |    3 |    5 |
+-----+------+------+
&lt;/code&gt;&lt;/pre&gt;
          </content>  </entry>
  <entry xml:base="http://jan.kneschke.de/">
    <author>
      <name>jan</name>
    </author>
    <id>tag:jan.kneschke.de,2009-04-21:417</id>
    <published>2009-04-21T18:28:00Z</published>
    <updated>2009-04-21T21:47:06Z</updated>
    <category term="mysql"/>
    <link href="http://jan.kneschke.de/2009/4/21/mysql-proxy-meets-binlog-the-examples" rel="alternate" type="text/html"/>
    <title>MySQL Proxy meets: binlog - the examples</title>
<summary type="html">&lt;p&gt;I just pushed the code for my replication changes on launchpad:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ bzr branch lp:~jan-kneschke/mysql-proxy/replication
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The presentation should be available ... soon.&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;I just pushed the code for my replication changes on launchpad:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ bzr branch lp:~jan-kneschke/mysql-proxy/replication
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The presentation should be available ... soon.&lt;/p&gt;
&lt;p&gt;One of the first examples is about filtering binlogs. If you want to remove all &lt;code&gt;ALTER&lt;/code&gt; statements from the replication stream (or in this case the binlog files) you can just iterate the binlog and copy everything to a new binlog, but the &lt;code&gt;ALTER&lt;/code&gt; statement&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;local binlog = require(&quot;mysql.binlog&quot;)
local tokenizer = require(&quot;proxy.tokenizer&quot;)

local f_in = assert(binlog.open(&quot;/tmp/binlog-test.log&quot;))
local f_out = assert(binlog.open(&quot;/tmp/binlog-test-filtered.log&quot;, &quot;w&quot;))
print(&quot;filtering /tmp/binlog-test.log to /tmp/binlog-test-filtered.log&quot;)

for event in f_in:next() do
    if event.type == &quot;QUERY_EVENT&quot; then
            local tokens = tokenizer.tokenize(event.query.query)
            local first_token = tokenizer.first_stmt_token(tokens)

            if first_token.token_name == &quot;TK_SQL_ALTER&quot; then
                    print(&quot;removing &quot; .. event.query.query)
            else
                    -- log it
                    f_out:append(event)
            end
    else
            f_out:append(event)
    end
end

f_in:close()
f_out:close()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Straight forward. &lt;/p&gt;

&lt;p&gt;You can also merge binlogs like it happens in sharded setups:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-- open the files 
local in_files = {
    assert(binlog.open(&quot;/tmp/binlog-merge-1.log&quot;)),
    assert(binlog.open(&quot;/tmp/binlog-merge-2.log&quot;))
}
f_merged = assert(binlog.open(&quot;/tmp/binlog-merged.log&quot;, &quot;w&quot;))
print(&quot;merging to /tmp/binlog-merge.log&quot;)

local iterators = {}

-- get the iterators for all the binlogs
for i, f in ipairs(in_files) do
    iterators[i] = f:next()
end

local cur_events = {}
for i, f in ipairs(iterators) do
    cur_events[i] = f()
end

--
-- copy the oldest timestamp from all the binlogs to the out-binlog
repeat
    -- get the event with the oldest timestamp
    local o_ndx
    local o_ts
    for i = 1, #cur_events do
            local event = cur_events[i]
            if event and (not o_ts or event.timestamp &amp;lt; o_ts) then
                    o_ndx = i
                    o_ts = event.timestamp
            end
    end

    -- append the oldest event
    if o_ndx then
            f_merged:append(cur_events[o_ndx])
            cur_events[o_ndx] = iterators[o_ndx]()
    end
until not o_ndx


for i, f in ipairs(in_files) do
    f:close()
end

f_merged:close()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That's just the foundation for more fun stuff like: &quot;synchronized replication&quot;. More of that in the session.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://jan.kneschke.de/">
    <author>
      <name>jan</name>
    </author>
    <id>tag:jan.kneschke.de,2009-04-18:414</id>
    <published>2009-04-18T16:13:00Z</published>
    <updated>2009-04-18T16:41:35Z</updated>
    <category term="mysql"/>
    <link href="http://jan.kneschke.de/2009/4/18/mysql-proxy-meets-binlogs-sneak-peek-ii" rel="alternate" type="text/html"/>
    <title>MySQL Proxy meets: binlogs - sneak peek II</title>
<summary type="html">&lt;p&gt;The slides are written, uploaded and the code snippets work. I'm ready to present. &lt;/p&gt;

&lt;p&gt;One of the topics will be &lt;code&gt;merging binlogs&lt;/code&gt; which is what multi-master replication is all about on the low level. A common example is sharding where you have several masters which share the same table-structures, but store the data independently. This is great for scaling out, but tricky if you have to run a query over the full dataset.&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;The slides are written, uploaded and the code snippets work. I'm ready to present. &lt;/p&gt;

&lt;p&gt;One of the topics will be &lt;code&gt;merging binlogs&lt;/code&gt; which is what multi-master replication is all about on the low level. A common example is sharding where you have several masters which share the same table-structures, but store the data independently. This is great for scaling out, but tricky if you have to run a query over the full dataset.&lt;/p&gt;
&lt;p&gt;The logfiles look like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ mysqlbinlog binlog-merge-1.log -s
SET TIMESTAMP=1240066015/*!*/;
INSERT INTO tbl VALUES ( 1 )
/*!*/;
SET TIMESTAMP=1240066017/*!*/;
INSERT INTO tbl VALUES ( 3 )
/*!*/;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;and &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ mysqlbinlog binlog-merge-2.log -s
SET TIMESTAMP=1240066016/*!*/;
INSERT INTO tbl VALUES ( 2 )
/*!*/;
SET TIMESTAMP=1240066018/*!*/;
INSERT INTO tbl VALUES ( 4 )
/*!*/;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As the two binlogs are independent and can't contain collisions we can just merge the two binlogs based on the timestamp:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ mysqlbinlog binlog-merged.log -s
SET TIMESTAMP=1240066015/*!*/;
INSERT INTO tbl VALUES ( 1 )
/*!*/;
SET TIMESTAMP=1240066016/*!*/;
INSERT INTO tbl VALUES ( 2 )
/*!*/;
SET TIMESTAMP=1240066017/*!*/;
INSERT INTO tbl VALUES ( 3 )
/*!*/;
SET TIMESTAMP=1240066018/*!*/;
INSERT INTO tbl VALUES ( 4 )
/*!*/;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In my session I'll show you what is going in the background to merge any number of binlogs into one binlog. It is less than 50 lines of lua-code.&lt;/p&gt;

&lt;p&gt;Just add a network-interface to it and another problem to make MySQL &quot;sharding-complete&quot; is solved.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://jan.kneschke.de/">
    <author>
      <name>jan</name>
    </author>
    <id>tag:jan.kneschke.de,2009-04-15:412</id>
    <published>2009-04-15T10:33:00Z</published>
    <updated>2009-04-15T11:34:14Z</updated>
    <category term="mysql"/>
    <link href="http://jan.kneschke.de/2009/4/15/mysql-proxy-meets-binlogs-sneak-peek-i" rel="alternate" type="text/html"/>
    <title>MySQL Proxy: meets binlogs - sneak peek I</title>
<content type="html">
            &lt;p&gt;... is the title of &lt;a href=&quot;http://www.mysqlconf.com/mysql2009/public/schedule/detail/7056&quot;&gt;my session&lt;/a&gt; at the &lt;a href=&quot;http://www.mysqlconf.com/mysql2009/&quot;&gt;MySQL Conf 2009&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'll unveal the full thing at the session at next week Tuesday, but for now let me show you this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;local f = binlog.open(&quot;my-binlog&quot;, &quot;w&quot;)
f:append({       
    server_id = 1,
    type = &quot;INCIDENT_EVENT&quot;,
    incident = {
        incident = 42,
        message = &quot;The answer&quot;
    }       
})
f:close()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;... gives me:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ mysqlbinlog my-binlog
...
#090415 12:02:32 server id 1  end_log_pos 350 
# Incident: UåìEuø}ü}
                        DED$F$ègö# at 350
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What ever I did: I can &lt;a href=&quot;http://bugs.mysql.com/44287&quot;&gt;break mysqlbinlog&lt;/a&gt; :)&lt;/p&gt;

&lt;p&gt;Hint: the above is lua code. A working encoder/decoder for binlogs with a c-library and a lua-wrapper on top.&lt;/p&gt;

&lt;p&gt;If you have some ideas what this could be useful for add it as comment to this article and let's see what I can show at the MySQL Conf.&lt;/p&gt;
          </content>  </entry>
</feed>

