<?xml version="1.0"?>
<rss version="2.0" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/" xmlns:yt="http://gdata.youtube.com/schemas/2007">
   <channel>
      <title>Filtered  planetmysql</title>
      <description>Show posts from planetmysql that interest me</description>
      <link>http://pipes.yahoo.com/pipes/pipe.info?_id=BnuXNQR03RG8AKHv073fcQ</link>
      <pubDate>Sun, 29 Nov 2009 18:36:11 -0800</pubDate>
      <generator>http://pipes.yahoo.com/pipes/</generator>
      <item>
         <title>[MySQL][Spider]Spider-2.9 released</title>
         <link>http://wild-growth.blogspot.com/2009/11/mysqlspiderspider-29-released.html</link>
         <description>I'm pleased to announce the release of Spider storage engine version 2.9(beta).Spider is a Storage Engine for database sharding.http://spiderformysql.com/The main changes in this version are following.- Add UDFs &quot;spider_ping_table&quot;.- Add table parameter &quot;monitoring_kind&quot;, &quot;monitoring_limit&quot; and &quot;monitoring_server_id&quot;.- Add server parameter &quot;spider_udf_table_mon_mutex_count&quot;.&amp;nbsp;&amp;nbsp;Add Spider's link fault monitor at this release.Please see &quot;99_change_logs.txt&quot; in the download documents for checking other changes.Thanks to shinichiro and merikonjatta for bug report.Enjoy!</description>
         <guid isPermaLink="false">tag:blogger.com,1999:blog-7870178081855084823.post-9136494560471780518</guid>
         <pubDate>Sat, 28 Nov 2009 09:02:00 -0800</pubDate>
         <content:encoded><![CDATA[I'm pleased to announce the release of Spider storage engine version 2.9(beta).<br />Spider is a Storage Engine for database sharding.<br /><a rel="nofollow" target="_blank" href="http://spiderformysql.com/">http://spiderformysql.com/</a><br /><br />The main changes in this version are following.<br />- Add UDFs "spider_ping_table".<br />- Add table parameter "monitoring_kind", "monitoring_limit" and "monitoring_server_id".<br />- Add server parameter "spider_udf_table_mon_mutex_count".<br />&nbsp;&nbsp;Add Spider's link fault monitor at this release.<br /><br />Please see "99_change_logs.txt" in the download documents for checking other changes.<br />Thanks to shinichiro and merikonjatta for bug report.<br /><br />Enjoy!<div><img width="1" height="1" src="https://blogger.googleusercontent.com/tracker/7870178081855084823-9136494560471780518?l=wild-growth.blogspot.com" alt=""/></div><br/>PlanetMySQL Voting: <a rel="nofollow" target="_blank" href="http://planet.mysql.com/entry/vote/?entry_id=22429&vote=1&apivote=1">Vote UP</a> / <a rel="nofollow" target="_blank" href="http://planet.mysql.com/entry/vote/?entry_id=22429&vote=-1&apivote=1">Vote DOWN</a>]]></content:encoded>
      </item>
      <item>
         <title>Shooting with Crossbows into Zones</title>
         <link>http://schlueters.de/blog/archives/123-Shooting-with-Crossbows-into-Zones.html</link>
         <description>Ok, so this site (and some other stuff) is now running on OpenSolaris. The previous previous article was mostly a test entry for me to see whether the DNS update was through but as some people wonder why I'm using this system that &quot;fails while trying to copy Linux&quot; I decided to discuss some of the reasons in more detail.Some people already know that my main system meanwhile runs OpenSolaris. The reason there is DTrace - a great way to see what the system, from the kernel, over userspaces programs, into a VM like the JVM or PHP's Zend VM, ... is doing which is a big help while debugging and developing applications. Even though DTrace is meant to do such analysis on live machines this wasn't the main reason for this choice on the server. For the server I actually didn't plan a change, ok, the old Linux box wasn't maintained well but it worked good enough for the few things it does, but then David came along and had the idea to share a server so I started thinking about dropping the old contract and getting a new machine for us both - and possible some other friends. And there we find the actual reasons for the OS choice:ZonesSo we were planing to share a box as both of us are doing Web/PHP-related stuff it was clear that it's likely that both of us would might need special versions and configurations of some software components which will then conflict with each other. Additionally I want to be able to do a killall apache in case I configured something wrong and I don't want the others to be affected too much while configuring my web servers as I need/want them. The obious solution these days? - Virtualization.Now virtualization comes in many flavors. The simple one most people know is Desktop Virtualization, so you take a software like VirtualBox, which is running as a regular userspace application and holds a complete operating stack. In there one has a kernel of the virtualized system which thinks it's running directly on physical hardware. The big benefit is that one can run any operating system in the VM but also has negative effects in areas like disk buffers (the virtualized and the host kernel buffer independently) or overall process scheduling (the VM is scheduled by the host and then schedules itself again..) or syscalls (an application running in the VM does a syscall to the VM's kernel which then calls a Hypervisor-provided hardware emulation function which then triggers a syscall on the hostsystem)Another approach is Operating System Virtualization like Solaris Zones. Here the operating system handles the virtualiztion. With zones this works in a way were one has a single kernel and multiple userland instances. By this one has one kernel with one scheduler (ok, Solaris allows using different schedulers and so on - let's ignore this and look at the default) and one disk IO layer. Inside a Zone one has Zone-specific userland with service management an own network device (see more on this below), an own user database (/etc/passwd, LDAP, ...) and so on. But as of the syscall interface it all runs on one kernel which also means that all processes are handled equally by the kernel (unless configured otherwise)The result of using Solaris Zones is that one has a lightweight isolation of independent userland environments. Now as said the virtualisation has one boundary at the syscall layer, so the userland has to be Solaris - one thinks. But that's not true: There are Branded Zones which emulate another syscall interface,by that one can run a Linux userland on a Solaris kernel so Linux-only apps benefit from stuff like ZFS and DTrace - but that's not relevant for me here.So to summarize: Zones are great for lightweight isolation (and other stuff)CrossbowNow I was mentioning that each Zone can have it's own network interface assigned.This is nice if you have a box with many network devices - now a typical server you get as a root-server for little money usually has just one. Now what you traditionally can do is assigning multiple IPs to that device and then use the single device shared over multiple zones. That works but is inconvenient as you can't really check the status (which device/zone is producing how much traffic?) or add bandwidth limitations (I want to be able to reduce one zones bandwidth in case an article is slashdotted without going to deep into everything to keep other parts of the system running) and additionally IP addresses are limited and I don't want all zones to be publicly accessible - for instance my MySQL zone can't be reached from the outside.Now crossbow - that's the name of the Solaris network virtualization layer introduced with OpenSolaris 2009.06 - for me always was a so what thing till I started using it. Well yes you can create virtual switches and virtual network interfaces. So what? Well combined with zones I can achieve what I described in the above paragraph.So let's build a network:dladm create-etherstub mystub0dladm create-vnic -l mystub0 vnic0dladm create-vnic -l mystub0 vinc1That's all that's needed to create an internal ethernet with two devices. Next step is to assign them to zones and configure IP for this network. In my current setup I have a zone for this web site and one zone for the MySQL server. The MySQL zone has a vnic for an internal network, the web-zone has two vnics - one is used for the internal network and the second is configured to work on top of the physical networking device so it can talk to the outside using its own public IP address. For limiting resources and stuff there's the flowadm tool for simple access to control network resource limits or service priorities (ssh connections have higher priorities so the system can be controlled in case the network is busy)And even for me, who tries to stay above the TCP layer, this is quite trivial to setup.ZFSNow one of the most cited features of Solaris is the zfs filesystem. While zfs is more than just a filesystem - it's a combination of volume manager, raid controller and other related things. The key feature there for me is snapshotting: zfs is using a copy on write mechanism so zfs can create snapshot which in itself has barely no costs. Only if data is changed a new block is being written and the old one is kept untouched by that the snapshots costs only the space the difference needs. Additionally this allows clones so one gets a copy of a directory and it will cost space only if data is changed - that's of special interest with zones. As said each zone is it's own userspace system. By using zfs clones they share the same blocks on disk. Really useful. In the next version this will even be better thanks to deduplication in zfs ...ProblemsComing from Linux there are - of course - different problems, as I'm using OpenSolaris on other boxes for sometime now I'm used to many administration tools but I learn new things every time i work on the system.A bit more problematic is that the main OpenSolaris package repository doesn't offer as much software as typical linux distributions, but for most software packages can be found in other repositories, too. This is a bit annoying but as one can see the growth and has access to above mentioned features this is no big problem - especially on a server where most of the tools exist for Solaris, too.Oh, and for the German speakers: David and I discussed some experience while installing the server in the latest HELDENFunk podcast.</description>
         <guid isPermaLink="false">http://schlueters.de/blog/archives/123-guid.html</guid>
         <pubDate>Sat, 28 Nov 2009 07:28:00 -0800</pubDate>
         <content:encoded><![CDATA[<p>Ok, so this site (and some other stuff) is <a rel="nofollow" target="_blank" href="http://schlueters.de/blog/archives/122-Now-running-on-OpenSolaris.html">now running on OpenSolaris</a>. The previous previous article was mostly a test entry for me to see whether the DNS update was through but as some people wonder why I'm using <i>this system</i> that "fails while trying to copy Linux" I decided to discuss some of the reasons in more detail.</p><p>Some people already know that my main system meanwhile runs <a rel="nofollow" target="_blank" href="http://www.opensolaris.org/">OpenSolaris</a>. The reason there is <a rel="nofollow" target="_blank" href="http://schlueters.de/blog/plugin/tag/DTrace">DTrace</a> - a great way to see what the system, from the kernel, over userspaces programs, into a VM like the JVM or PHP's Zend VM, ... is doing which is a big help while debugging and developing applications. Even though DTrace is meant to do such analysis on live machines this wasn't the main reason for this choice on the server. For the server I actually didn't plan a change, ok, the old Linux box wasn't maintained well but it worked good enough for the few things it does, but then <a rel="nofollow" target="_blank" href="http://blog.experimentalworks.net/">David</a> came along and had the idea to share a server so I started thinking about dropping the old contract and getting a new machine for us both - and possible some other friends. And there we find the actual reasons for the OS choice:</p><h4>Zones</h4><p>So we were planing to share a box as both of us are doing Web/PHP-related stuff it was clear that it's likely that both of us would might need special versions and configurations of some software components which will then conflict with each other. Additionally I want to be able to do a <i>killall apache</i> in case I configured something wrong and I don't want the others to be affected too much while configuring my web servers as I need/want them. The obious solution these days? - Virtualization.</p><p>Now virtualization comes in many flavors. The simple one most people know is Desktop Virtualization, so you take a software like <a rel="nofollow" target="_blank" href="http://www.virtualbox.org/">VirtualBox</a>, which is running as a regular userspace application and holds a complete operating stack. In there one has a kernel of the virtualized system which thinks it's running directly on physical hardware. The big benefit is that one can run any operating system in the VM but also has negative effects in areas like disk buffers (the virtualized and the host kernel buffer independently) or overall process scheduling (the VM is scheduled by the host and then schedules itself again..) or syscalls (an application running in the VM does a syscall to the VM's kernel which then calls a Hypervisor-provided hardware emulation function which then triggers a syscall on the hostsystem)</p><p>Another approach is Operating System Virtualization like Solaris Zones. Here the operating system handles the virtualiztion. With zones this works in a way were one has a single kernel and multiple userland instances. By this one has one kernel with one scheduler (ok, Solaris allows using different schedulers and so on - let's ignore this and look at the default) and one disk IO layer. Inside a Zone one has Zone-specific userland with service management an own network device (see more on this below), an own user database (/etc/passwd, LDAP, ...) and so on. But as of the syscall interface it all runs on one kernel which also means that all processes are handled equally by the kernel (unless configured otherwise)</p><p>The result of using Solaris Zones is that one has a lightweight isolation of independent userland environments. Now as said the virtualisation has one boundary at the syscall layer, so the userland has to be Solaris - one thinks. But that's not true: There are Branded Zones which emulate another syscall interface,by that one can run a Linux userland on a Solaris kernel so Linux-only apps benefit from stuff like ZFS and DTrace - but that's not relevant for me here.</p><p>So to summarize: Zones are great for lightweight isolation (and other stuff)</p><h4>Crossbow</h4><p>Now I was mentioning that each Zone can have it's own network interface assigned.This is nice if you have a box with many network devices - now a typical server you get as a root-server for little money usually has just one. Now what you traditionally can do is assigning multiple IPs to that device and then use the single device shared over multiple zones. That works but is inconvenient as you can't really check the status (which device/zone is producing how much traffic?) or add bandwidth limitations (I want to be able to reduce one zones bandwidth in case an article is <i>slashdotted</i> without going to deep into everything to keep other parts of the system running) and additionally IP addresses are limited and I don't want all zones to be publicly accessible - for instance my MySQL zone can't be reached from the outside.</p><p>Now crossbow - that's the name of the Solaris network virtualization layer introduced with OpenSolaris 2009.06 - for me always was a <i>so what</i> thing till I started using it. Well yes you can create virtual switches and virtual network interfaces. So what? Well combined with zones I can achieve what I described in the above paragraph.</p><p>So let's build a network:</p><blockquote><p>dladm create-etherstub mystub0<br />dladm create-vnic -l mystub0 vnic0<br />dladm create-vnic -l mystub0 vinc1</p></blockquote><p>That's all that's needed to create an internal ethernet with two devices. Next step is to assign them to zones and configure IP for this network. In my current setup I have a zone for this web site and one zone for the MySQL server. The MySQL zone has a vnic for an internal network, the web-zone has two vnics - one is used for the internal network and the second is configured to work on top of the physical networking device so it can talk to the outside using its own public IP address. For limiting resources and stuff there's the flowadm tool for simple access to control network resource limits or service priorities (ssh connections have higher priorities so the system can be controlled in case the network is busy)</p><p>And even for me, who tries to stay above the TCP layer, this is quite trivial to setup.</p><h4>ZFS</h4><p>Now one of the most cited features of Solaris is the zfs filesystem. While zfs is more than just a filesystem - it's a combination of volume manager, raid controller and other related things. The key feature there for me is snapshotting: zfs is using a copy on write mechanism so zfs can create snapshot which in itself has barely no costs. Only if data is changed a new block is being written and the old one is kept untouched by that the snapshots costs only the space the difference needs. Additionally this allows clones so one gets a copy of a directory and it will cost space only if data is changed - that's of special interest with zones. As said each zone is it's own userspace system. By using zfs clones they share the same blocks on disk. Really useful. In the next version this will even be better thanks to deduplication in zfs ...</p><h4>Problems</h4><p>Coming from Linux there are - of course - different problems, as I'm using OpenSolaris on other boxes for sometime now I'm used to many administration tools but I learn new things every time i work on the system.</p><p>A bit more problematic is that the main OpenSolaris package repository doesn't offer as much software as typical linux distributions, but for most software packages can be found in other repositories, too. This is a bit annoying but as one can see the growth and has access to above mentioned features this is no big problem - especially on a server where most of the tools exist for Solaris, too.</p><p>Oh, and for the German speakers: David and I discussed some experience while installing the server in the <a rel="nofollow" target="_blank" href="http://www.systemhelden.com/?p=254">latest HELDENFunk podcast</a>.</p><br/>PlanetMySQL Voting: <a rel="nofollow" target="_blank" href="http://planet.mysql.com/entry/vote/?entry_id=22425&vote=1&apivote=1">Vote UP</a> / <a rel="nofollow" target="_blank" href="http://planet.mysql.com/entry/vote/?entry_id=22425&vote=-1&apivote=1">Vote DOWN</a>]]></content:encoded>
      </item>
      <item>
         <title>Node failure handling - take 2</title>
         <link>http://johanandersson.blogspot.com/2009/11/node-failure-handling-take-2.html</link>
         <description>Jonas improved the node failure handling in MySQL Cluster 7.0.9 (and 6.3.29) so here comes a re-run of the a previous blog post. And the node failure handling time has improved a lot - see below.I created in total 11345 tables, each with 128 columns, and then hit this bug.When all tables were created, I stopped one node and measured how long time it takes for the other node to perform the node failure handling. Here is what was written into the cluster log (look at the bold lines):2009-11-27 13:39:21 [MgmtSrvr] ALERT -- Node 4: Node 3 Disconnected2009-11-27 13:39:21 [MgmtSrvr] ALERT -- Node 4: Network partitioning - arbitration required2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: President restarts arbitration thread [state=7]2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: Communication to Node 3 closed2009-11-27 13:39:21 [MgmtSrvr] ALERT -- Node 1: Node 3 Disconnected2009-11-27 13:39:21 [MgmtSrvr] ALERT -- Node 4: Arbitration won - positive reply from node 12009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: GCP Take over started2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: Node 4 taking over as DICT master2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: GCP Take over completed2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: kk: 9670/4 0 02009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: LCP Take over started2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: ParticipatingDIH = 00000000000000102009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: ParticipatingLQH = 00000000000000102009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: m_LCP_COMPLETE_REP_Counter_DIH = [SignalCounter: m_count=0 0000000000000000]2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: m_LCP_COMPLETE_REP_Counter_LQH = [SignalCounter: m_count=1 0000000000000010]2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: m_LAST_LCP_FRAG_ORD = [SignalCounter: m_count=0 0000000000000000]2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: m_LCP_COMPLETE_REP_From_Master_Received = 02009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: LCP Take over completed (state = 5)2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: ParticipatingDIH = 00000000000000102009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: ParticipatingLQH = 00000000000000102009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: m_LCP_COMPLETE_REP_Counter_DIH = [SignalCounter: m_count=1 0000000000000010]2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: m_LCP_COMPLETE_REP_Counter_LQH = [SignalCounter: m_count=1 0000000000000010]2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: m_LAST_LCP_FRAG_ORD = [SignalCounter: m_count=1 0000000000000010]2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: m_LCP_COMPLETE_REP_From_Master_Received = 02009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 3: Node shutdown completed.2009-11-27 13:39:21 [MgmtSrvr] ALERT -- Node 1: Node 3 Disconnected2009-11-27 13:39:22 [MgmtSrvr] INFO -- Node 4: Started arbitrator node 1 [ticket=2a74000295047d39]2009-11-27 13:40:20 [MgmtSrvr] WARNING -- Node 4: Failure handling of node 3 has not completed in 1 min. - state = 62009-11-27 13:41:03 [MgmtSrvr] INFO -- Node 4: Communication to Node 3 openedNow, the node failure handling was completed in 1 minute 42 seconds! This is quite some improvement from the 17 minutes (although measured with 16000 tables and I will comeback when the bug mentioned above is fixed) measured before Jonas made his magic.What does it mean? It means that we can start to recover a failed data nodes much earlier now!</description>
         <guid isPermaLink="false">tag:blogger.com,1999:blog-19281624.post-4247086962194240283</guid>
         <pubDate>Fri, 27 Nov 2009 04:42:00 -0800</pubDate>
         <content:encoded><![CDATA[Jonas improved the node failure handling in MySQL Cluster 7.0.9 (and 6.3.29) so here comes a re-run of the a <a rel="nofollow" target="_blank" href="http://johanandersson.blogspot.com/2009/09/node-failure-handling.html">previous blog post</a>. And the node failure handling time has improved a lot - see below.<br /><br />I created in total 11345 tables, each with 128 columns, and then hit this <a rel="nofollow" target="_blank" href="http://bugs.mysql.com/bug.php?id=49156">bug</a>.<br /><br />When all tables were created, I stopped one node and measured how long time it takes for the other node to perform the node failure handling. Here is what was written into the cluster log (look at the <span>bold </span>lines):<br /><pre><br /><span>2009-11-27 13:39:21 [MgmtSrvr] ALERT -- Node 4: Node 3 Disconnected</span><br /><br />2009-11-27 13:39:21 [MgmtSrvr] ALERT -- Node 4: Network partitioning - arbitration required<br /><br />2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: President restarts arbitration thread [state=7]<br /><br />2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: Communication to Node 3 closed<br /><br />2009-11-27 13:39:21 [MgmtSrvr] ALERT -- Node 1: Node 3 Disconnected<br /><br />2009-11-27 13:39:21 [MgmtSrvr] ALERT -- Node 4: Arbitration won - positive reply from node 1<br /><br />2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: GCP Take over started<br /><br />2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: Node 4 taking over as DICT master<br /><br />2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: GCP Take over completed<br /><br />2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: kk: 9670/4 0 0<br /><br />2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: LCP Take over started<br /><br />2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: ParticipatingDIH = 0000000000000010<br /><br />2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: ParticipatingLQH = 0000000000000010<br /><br />2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: m_LCP_COMPLETE_REP_Counter_DIH = [SignalCounter: m_count=0 0000000000000000]<br /><br />2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: m_LCP_COMPLETE_REP_Counter_LQH = [SignalCounter: m_count=1 0000000000000010]<br /><br />2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: m_LAST_LCP_FRAG_ORD = [SignalCounter: m_count=0 0000000000000000]<br /><br />2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: m_LCP_COMPLETE_REP_From_Master_Received = 0<br /><br />2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: LCP Take over completed (state = 5)<br /><br />2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: ParticipatingDIH = 0000000000000010<br /><br />2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: ParticipatingLQH = 0000000000000010<br /><br />2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: m_LCP_COMPLETE_REP_Counter_DIH = [SignalCounter: m_count=1 0000000000000010]<br /><br />2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: m_LCP_COMPLETE_REP_Counter_LQH = [SignalCounter: m_count=1 0000000000000010]<br /><br />2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: m_LAST_LCP_FRAG_ORD = [SignalCounter: m_count=1 0000000000000010]<br /><br />2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 4: m_LCP_COMPLETE_REP_From_Master_Received = 0<br /><br />2009-11-27 13:39:21 [MgmtSrvr] INFO -- Node 3: Node shutdown completed.<br /><br />2009-11-27 13:39:21 [MgmtSrvr] ALERT -- Node 1: Node 3 Disconnected<br /><br />2009-11-27 13:39:22 [MgmtSrvr] INFO -- Node 4: Started arbitrator node 1 [ticket=2a74000295047d39]<br /><br />2009-11-27 13:40:20 [MgmtSrvr] WARNING -- Node 4: Failure handling of node 3 has not completed in 1 min. - state = 6<br /><br /><span>2009-11-27 13:41:03 [MgmtSrvr] INFO -- Node 4: Communication to Node 3 opened</span><br /></pre><br />Now, the node failure handling was completed in <span>1 minute 42 seconds</span>! This is quite some improvement from the <span>17 minutes</span> (although measured with 16000 tables and I will comeback when the bug mentioned above is fixed) measured before Jonas made his magic.<br /><br />What does it mean? It means that we can start to recover a failed data nodes much earlier now!<div><img width="1" height="1" src="https://blogger.googleusercontent.com/tracker/19281624-4247086962194240283?l=johanandersson.blogspot.com" alt=""/></div><br/>PlanetMySQL Voting: <a rel="nofollow" target="_blank" href="http://planet.mysql.com/entry/vote/?entry_id=22420&vote=1&apivote=1">Vote UP</a> / <a rel="nofollow" target="_blank" href="http://planet.mysql.com/entry/vote/?entry_id=22420&vote=-1&apivote=1">Vote DOWN</a>]]></content:encoded>
      </item>
      <item>
         <title>Versioning MySQL data: Multi-table records</title>
         <link>http://www.adaniels.nl/articles/versioning-mysql-data-multi-table-records/</link>
         <description>In the article &amp;#8216;Versioning MySQL data&amp;#8216;, I showed the basics of implementing a revisioning system using trigger. As Jens Schauder already pointed out, often the data of a record is spread across multiple tables, like an invoice with multiple invoice lines. Having each invoice line versioned individually isn&amp;#8217;t really useful. Instead we want a new revision of the whole invoice on each change.
The perfect solution
Ideally a change of one or more parts of the invoice would be changed, a new revision would be created. There are several issues in actually creating this those. Detecting the change of multiple parts of the invoice at once, generating a single revision, would mean we need to know if the actions are done within the same transaction. Unfortunately there is a connection_id(), but no transaction_id() function in MySQL. Also, the query would fail when a query inserts or updates a record in the child table, using the parent table. We need to come up with something else.
In the implementation we currently have in production, we version the rows in the parent as well in the child tables. For each version of the parent row, we register which versions of the child rows ware set. This however has really complicated the trigger code and tends to need a lot of checking an querying slowing the write process down. Since nobody ever looks at the versions of the child rows, the application forces a new version of the parent row. The benefits of versioning both are therefor minimal.
Only versioning the parent
For this new (simplified) implementation, we will only have one revision number across all tables of the record. Changing data from the parent table, will trigger a new version. This will not only copy the parent row to the revisioning table, but also the rows of the children.
Writing to the child will not trigger a new version, instead it will update the data in the revisioning table. This means that when changing the record, you need to write to the parent table, before writing to the child tables. To force a new version without changing values use UPDATE mytable SET _revision=NULL WHERE id=$id The parent and child tables are defined as CREATE TABLE `mytable` &amp;#40; `id` int&amp;#40;10&amp;#41; UNSIGNED NOT NULL AUTO_INCREMENT, `name` varchar&amp;#40;255&amp;#41; NOT NULL DEFAULT '', `description` text, PRIMARY KEY &amp;#40;`id`&amp;#41;, UNIQUE KEY `name` &amp;#40;`name`&amp;#41;
&amp;#41; ENGINE=InnoDB
&amp;nbsp;
CREATE TABLE `mychild` &amp;#40; `id` int&amp;#40;10&amp;#41; UNSIGNED NOT NULL AUTO_INCREMENT, `mytable_id` int&amp;#40;10&amp;#41; UNSIGNED NOT NULL DEFAULT '0', `title` varchar&amp;#40;255&amp;#41; NOT NULL DEFAULT '', PRIMARY KEY &amp;#40;`id`&amp;#41;, KEY `mytable_id` &amp;#40;`mytable_id`&amp;#41;, CONSTRAINT `mychild_ibfk_1` FOREIGN KEY &amp;#40;`mytable_id`&amp;#41; REFERENCES `mytable` &amp;#40;`id`&amp;#41; ON DELETE CASCADE
&amp;#41; ENGINE=InnoDB Note that we are using InnoDB tables here. MyISAM doesn&amp;#8217;t have foreign key constraints, therefor it&amp;#8217;s not possible to define a parent-child relationship.
Insert, update and delete
In the parent trigger, to different things happen concerning the child rows. When a new version is created, the data of `mychild` is copied to the revisioning table. On a revision switch, data will be copied from the revisioning table into `mychild`. The &amp;#8220;`_revision_action` IS NULL&amp;#8221; condition, means that `_revision_mytable` is only updated when a new revision is created. CREATE TRIGGER `mytable-afterupdate` AFTER UPDATE ON `mytable` FOR EACH ROW BEGIN DECLARE `newrev` BOOLEAN;
&amp;nbsp; UPDATE `_revision_mytable` SET `id` = NEW.`id`, `name` = NEW.`name`, `description` = NEW.`description`, `_revision_action`='update' WHERE `_revision`=NEW.`_revision` AND `_revision_action` IS NULL; SET newrev = &amp;#40;ROW_COUNT&amp;#40;&amp;#41; &amp;gt; 0&amp;#41;; INSERT INTO `_revhistory_mytable` VALUES &amp;#40;NEW.`id`, NEW.`_revision`, @auth_uid, NOW&amp;#40;&amp;#41;&amp;#41;;
&amp;nbsp; IF newrev THEN INSERT INTO `_revision_mychild` SELECT *, NEW.`_revision` FROM `mychild` WHERE `mytable_id` = NEW.`id`; ELSE DELETE `t`.* FROM `mychild` AS `t` LEFT JOIN `_revision_mychild` AS `r` ON 0=1 WHERE `t`.`mytable_id` = NEW.`id`; INSERT INTO `mychild` SELECT `id`, `mytable_id`, `title` FROM `_revision_mychild` WHERE `_revision` = NEW.`_revision`; END IF; END
&amp;nbsp;
CREATE TRIGGER `mychild-afterinsert` AFTER INSERT ON `mychild` FOR EACH ROW BEGIN DECLARE CONTINUE HANDLER FOR 1442 BEGIN END; INSERT IGNORE INTO `_revision_mychild` &amp;#40;`id`, `mytable_id`, `title`, `_revision`&amp;#41; SELECT NEW.`id`, NEW.`mytable_id`, NEW.`title`, `_revision` FROM `mytable` AS `p` WHERE `p`.`id`=NEW.`mytable_id`; END
&amp;nbsp;
CREATE TRIGGER `mychild-afterupdate` AFTER UPDATE ON `mychild` FOR EACH ROW BEGIN REPLACE INTO `_revision_mychild` &amp;#40;`id`, `mytable_id`, `title`, `_revision`&amp;#41; SELECT NEW.`id`, NEW.`mytable_id`, NEW.`title`, `_revision` FROM `mytable` AS `p` WHERE `p`.`id`=NEW.`mytable_id`; END
&amp;nbsp;
CREATE TRIGGER `mychild-afterdelete` AFTER DELETE ON `mychild` FOR EACH ROW BEGIN DECLARE CONTINUE HANDLER FOR 1442 BEGIN END; DELETE `r`.* FROM `_revision_mychild` AS `r` INNER JOIN `mytable` AS `p` ON `r`.`_revision` = `p`.`_revision` WHERE `r`.`id` = OLD.`id`; END Changing data in table `mychild` simply updates the data in the revisioning table. The revision number is grabbed from the field in the parent table.
Switching the revision can only be done through the parent table. This will also automatically change the data in the child tables. We simply delete all rows of the record and replace them with data from the revisioning table. This would however trigger the deletion of the data in `_revision_child` on which the insert has nothing to do. To prevent this, we can abuse that fact that a trigger can&amp;#8217;t update data of a table using in the insert/update/delete query. This causes error 1442. With a continue handler we can ignore this silently.
The InnoDB constraints will handle the cascading delete. Deleting child data won&amp;#8217;t activate the deletion trigger, which is all the better in this case.
Without a primary key
A primary key is not required for the child table, since versioning is done purely based on the id of `mytable`. CREATE TABLE `mypart` &amp;#40; `mytable_id` int&amp;#40;10&amp;#41; UNSIGNED NOT NULL, `reference` varchar&amp;#40;255&amp;#41; NOT NULL, KEY `mytable_id` &amp;#40;`mytable_id`&amp;#41;, CONSTRAINT `mypart_ibfk_1` FOREIGN KEY &amp;#40;`mytable_id`&amp;#41; REFERENCES `mytable` &amp;#40;`id`&amp;#41; ON DELETE CASCADE
&amp;#41; ENGINE=InnoDB This does cause an issue for the update and delete triggers of the child table. It can&amp;#8217;t use the primary to id to locate the current version of the modified/removed row. This can be solved by a trick I got from PhpMyAdmin. We can simply locate the record by comparing the old values of all fields. There is no constraint for the table enforcing the uniqueness of a row, so we could be targeting multiple identical rows. Since they are identical, it doesn&amp;#8217;t matter which one we target, as long as we limit to 1 row. CREATE TRIGGER `mypart-afterupdate` AFTER UPDATE ON `mypart` FOR EACH ROW BEGIN DELETE FROM `_revision_mypart` WHERE `_revision` IN &amp;#40;SELECT `_revision` FROM `mytable` WHERE `id` = OLD.`mytable_id`&amp;#41; AND `mytable_id` = OLD.`mytable_id` AND `reference` = OLD.`reference` LIMIT 1; INSERT INTO `_revision_mypart` &amp;#40;`mytable_id`, `reference`, `_revision`&amp;#41; SELECT NEW.`mytable_id`, NEW.`reference`, `_revision` FROM `mytable` AS `p` WHERE `p`.`id`=NEW.`mytable_id`; END
&amp;nbsp;
CREATE TRIGGER `mypart-afterdelete` AFTER DELETE ON `mypart` FOR EACH ROW BEGIN DECLARE CONTINUE HANDLER FOR 1442 BEGIN END; DELETE FROM `_revision_mypart` WHERE `_revision` IN &amp;#40;SELECT `_revision` FROM `mytable` WHERE `id` = OLD.`mytable_id`&amp;#41; AND `mytable_id` = OLD.`mytable_id` AND `reference` = OLD.`reference` LIMIT 1; END Unique keys
The revisioning table has multiple versions of a record. Unique indexes from the original table should be converted to non-unique indexes in the revisioning table. This information can be fetched using INFORMATION_SCHEMA. SELECT c.CONSTRAINT_NAME, GROUP_CONCAT&amp;#40;CONCAT&amp;#40;'`', k.COLUMN_NAME, '`'&amp;#41;&amp;#41; AS cols FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS `c` INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS `k` ON c.TABLE_SCHEMA=k.TABLE_SCHEMA AND c.TABLE_NAME=k.TABLE_NAME AND c.CONSTRAINT_NAME=k.CONSTRAINT_NAME WHERE c.TABLE_SCHEMA=DATABASE&amp;#40;&amp;#41; AND c.TABLE_NAME='mytable' AND c.CONSTRAINT_TYPE='UNIQUE' AND c.CONSTRAINT_NAME != '_revision' GROUP BY c.CONSTRAINT_NAME Revisioning and replication
Baron Schwartz pointed out a race condition when relying on auto-increment keys in triggers with replication. Actions carried out through triggers on a master are not replicated to a slave server. Instead, triggers on the slave will be invoked, which should do the same action as on the master.
It probably isn&amp;#8217;t needed to have a copy of the revisioning tables on the slave. This would mean that we could simply omit the triggers. Unfortunately this causes problems when changing the revision. In that case we are forced to move switching of a revision out of the database. Instead the application needs to select the data from all revisioning tables and write that to the original tables. Any other thoughts on solving this issue are welcome.
Download
&amp;#8211;&amp;gt; Download mysql-revisioning script</description>
         <guid isPermaLink="false">http://www.adaniels.nl/?p=342</guid>
         <pubDate>Wed, 25 Nov 2009 17:41:45 -0800</pubDate>
         <content:encoded><![CDATA[<p>In the article &#8216;<a rel="nofollow" target="_blank" href="http://www.adaniels.nl/articles/versioning-mysql-data/">Versioning MySQL data</a>&#8216;, I showed the basics of implementing a revisioning system using trigger. As Jens Schauder already pointed out, often the data of a record is spread across multiple tables, like an invoice with multiple invoice lines. Having each invoice line versioned individually isn&#8217;t really useful. Instead we want a new revision of the whole invoice on each change.</p>
<p><strong>The perfect solution</strong><br />
Ideally a change of one or more parts of the invoice would be changed, a new revision would be created. There are several issues in actually creating this those. Detecting the change of multiple parts of the invoice at once, generating a single revision, would mean we need to know if the actions are done within the same transaction. Unfortunately there is a connection_id(), but no transaction_id() function in MySQL. Also, the <a rel="nofollow" target="_blank" href="http://dev.mysql.com/doc/refman/5.1/en/error-messages-server.html#error_er_cant_update_used_table_in_sf_or_trg">query would fail</a> when a query inserts or updates a record in the child table, using the parent table. We need to come up with something else.</p>
<p>In the implementation we currently have in production, we version the rows in the parent as well in the child tables. For each version of the parent row, we register which versions of the child rows ware set. This however has really complicated the trigger code and tends to need a lot of checking an querying slowing the write process down. Since nobody ever looks at the versions of the child rows, the application forces a new version of the parent row. The benefits of versioning both are therefor minimal.</p>
<p><strong>Only versioning the parent</strong><br />
For this new (simplified) implementation, we will only have one revision number across all tables of the record. Changing data from the parent table, will trigger a new version. This will not only copy the parent row to the revisioning table, but also the rows of the children.</p>
<p>Writing to the child will not trigger a new version, instead it will update the data in the revisioning table. This means that when changing the record, you need to write to the parent table, before writing to the child tables. To force a new version without changing values use</p> <div><div><pre><span>UPDATE</span> mytable <span>SET</span> _revision=<span>NULL</span> <span>WHERE</span> id=$id</pre></div></div> <p><span></span></p>
<p>The parent and child tables are defined as</p> <div><div><pre><span>CREATE</span> <span>TABLE</span> <span>`mytable`</span> <span>&#40;</span> <span>`id`</span> int<span>&#40;</span><span>10</span><span>&#41;</span> <span>UNSIGNED</span> <span>NOT</span> <span>NULL</span> <span>AUTO_INCREMENT</span>, <span>`name`</span> varchar<span>&#40;</span><span>255</span><span>&#41;</span> <span>NOT</span> <span>NULL</span> <span>DEFAULT</span> <span>''</span>, <span>`description`</span> text, <span>PRIMARY</span> <span>KEY</span> <span>&#40;</span><span>`id`</span><span>&#41;</span>, <span>UNIQUE</span> <span>KEY</span> <span>`name`</span> <span>&#40;</span><span>`name`</span><span>&#41;</span>
<span>&#41;</span> ENGINE=InnoDB
&nbsp;
<span>CREATE</span> <span>TABLE</span> <span>`mychild`</span> <span>&#40;</span> <span>`id`</span> int<span>&#40;</span><span>10</span><span>&#41;</span> <span>UNSIGNED</span> <span>NOT</span> <span>NULL</span> <span>AUTO_INCREMENT</span>, <span>`mytable_id`</span> int<span>&#40;</span><span>10</span><span>&#41;</span> <span>UNSIGNED</span> <span>NOT</span> <span>NULL</span> <span>DEFAULT</span> <span>'0'</span>, <span>`title`</span> varchar<span>&#40;</span><span>255</span><span>&#41;</span> <span>NOT</span> <span>NULL</span> <span>DEFAULT</span> <span>''</span>, <span>PRIMARY</span> <span>KEY</span> <span>&#40;</span><span>`id`</span><span>&#41;</span>, <span>KEY</span> <span>`mytable_id`</span> <span>&#40;</span><span>`mytable_id`</span><span>&#41;</span>, CONSTRAINT <span>`mychild_ibfk_1`</span> <span>FOREIGN</span> <span>KEY</span> <span>&#40;</span><span>`mytable_id`</span><span>&#41;</span> <span>REFERENCES</span> <span>`mytable`</span> <span>&#40;</span><span>`id`</span><span>&#41;</span> <span>ON</span> <span>DELETE</span> CASCADE
<span>&#41;</span> ENGINE=InnoDB</pre></div></div> <p>Note that we are using InnoDB tables here. MyISAM doesn&#8217;t have foreign key constraints, therefor it&#8217;s not possible to define a parent-child relationship.</p>
<p><strong>Insert, update and delete</strong><br />
In the parent trigger, to different things happen concerning the child rows. When a new version is created, the data of `mychild` is copied to the revisioning table. On a revision switch, data will be copied from the revisioning table into `mychild`. The &#8220;`_revision_action` IS NULL&#8221; condition, means that `_revision_mytable` is only updated when a new revision is created.</p> <div><div><pre><span>CREATE</span> <span>TRIGGER</span> <span>`mytable-afterupdate`</span> AFTER <span>UPDATE</span> <span>ON</span> <span>`mytable`</span> <span>FOR</span> EACH ROW BEGIN DECLARE <span>`newrev`</span> <span>BOOLEAN</span>;
&nbsp; <span>UPDATE</span> <span>`_revision_mytable`</span> <span>SET</span> <span>`id`</span> = NEW.<span>`id`</span>, <span>`name`</span> = NEW.<span>`name`</span>, <span>`description`</span> = NEW.<span>`description`</span>, <span>`_revision_action`</span>=<span>'update'</span> <span>WHERE</span> <span>`_revision`</span>=NEW.<span>`_revision`</span> <span>AND</span> <span>`_revision_action`</span> <span>IS</span> <span>NULL</span>; <span>SET</span> newrev = <span>&#40;</span>ROW_COUNT<span>&#40;</span><span>&#41;</span> &gt; <span>0</span><span>&#41;</span>; <span>INSERT</span> <span>INTO</span> <span>`_revhistory_mytable`</span> <span>VALUES</span> <span>&#40;</span>NEW.<span>`id`</span>, NEW.<span>`_revision`</span>, @auth_uid, NOW<span>&#40;</span><span>&#41;</span><span>&#41;</span>;
&nbsp; <span>IF</span> newrev THEN <span>INSERT</span> <span>INTO</span> <span>`_revision_mychild`</span> <span>SELECT</span> *, NEW.<span>`_revision`</span> <span>FROM</span> <span>`mychild`</span> <span>WHERE</span> <span>`mytable_id`</span> = NEW.<span>`id`</span>; ELSE <span>DELETE</span> <span>`t`</span>.* <span>FROM</span> <span>`mychild`</span> <span>AS</span> <span>`t`</span> <span>LEFT</span> <span>JOIN</span> <span>`_revision_mychild`</span> <span>AS</span> <span>`r`</span> <span>ON</span> <span>0</span>=<span>1</span> <span>WHERE</span> <span>`t`</span>.<span>`mytable_id`</span> = NEW.<span>`id`</span>; <span>INSERT</span> <span>INTO</span> <span>`mychild`</span> <span>SELECT</span> <span>`id`</span>, <span>`mytable_id`</span>, <span>`title`</span> <span>FROM</span> <span>`_revision_mychild`</span> <span>WHERE</span> <span>`_revision`</span> = NEW.<span>`_revision`</span>; END <span>IF</span>; END
&nbsp;
<span>CREATE</span> <span>TRIGGER</span> <span>`mychild-afterinsert`</span> AFTER <span>INSERT</span> <span>ON</span> <span>`mychild`</span> <span>FOR</span> EACH ROW BEGIN DECLARE CONTINUE HANDLER <span>FOR</span> <span>1442</span> BEGIN END; <span>INSERT</span> <span>IGNORE</span> <span>INTO</span> <span>`_revision_mychild`</span> <span>&#40;</span><span>`id`</span>, <span>`mytable_id`</span>, <span>`title`</span>, <span>`_revision`</span><span>&#41;</span> <span>SELECT</span> NEW.<span>`id`</span>, NEW.<span>`mytable_id`</span>, NEW.<span>`title`</span>, <span>`_revision`</span> <span>FROM</span> <span>`mytable`</span> <span>AS</span> <span>`p`</span> <span>WHERE</span> <span>`p`</span>.<span>`id`</span>=NEW.<span>`mytable_id`</span>; END
&nbsp;
<span>CREATE</span> <span>TRIGGER</span> <span>`mychild-afterupdate`</span> AFTER <span>UPDATE</span> <span>ON</span> <span>`mychild`</span> <span>FOR</span> EACH ROW BEGIN <span>REPLACE</span> <span>INTO</span> <span>`_revision_mychild`</span> <span>&#40;</span><span>`id`</span>, <span>`mytable_id`</span>, <span>`title`</span>, <span>`_revision`</span><span>&#41;</span> <span>SELECT</span> NEW.<span>`id`</span>, NEW.<span>`mytable_id`</span>, NEW.<span>`title`</span>, <span>`_revision`</span> <span>FROM</span> <span>`mytable`</span> <span>AS</span> <span>`p`</span> <span>WHERE</span> <span>`p`</span>.<span>`id`</span>=NEW.<span>`mytable_id`</span>; END
&nbsp;
<span>CREATE</span> <span>TRIGGER</span> <span>`mychild-afterdelete`</span> AFTER <span>DELETE</span> <span>ON</span> <span>`mychild`</span> <span>FOR</span> EACH ROW BEGIN DECLARE CONTINUE HANDLER <span>FOR</span> <span>1442</span> BEGIN END; <span>DELETE</span> <span>`r`</span>.* <span>FROM</span> <span>`_revision_mychild`</span> <span>AS</span> <span>`r`</span> <span>INNER</span> <span>JOIN</span> <span>`mytable`</span> <span>AS</span> <span>`p`</span> <span>ON</span> <span>`r`</span>.<span>`_revision`</span> = <span>`p`</span>.<span>`_revision`</span> <span>WHERE</span> <span>`r`</span>.<span>`id`</span> = OLD.<span>`id`</span>; END</pre></div></div> <p>Changing data in table `mychild` simply updates the data in the revisioning table. The revision number is grabbed from the field in the parent table.</p>
<p>Switching the revision can only be done through the parent table. This will also automatically change the data in the child tables. We simply delete all rows of the record and replace them with data from the revisioning table. This would however trigger the deletion of the data in `_revision_child` on which the insert has nothing to do. To prevent this, we can abuse that fact that a trigger can&#8217;t update data of a table using in the insert/update/delete query. This causes <a rel="nofollow" target="_blank" href="http://dev.mysql.com/doc/refman/5.1/en/error-messages-server.html#error_er_cant_update_used_table_in_sf_or_trg">error 1442</a>. With a continue handler we can ignore this silently.</p>
<p>The InnoDB constraints will handle the cascading delete. Deleting child data <a rel="nofollow" target="_blank" href="http://dev.mysql.com/doc/refman/5.4/en/innodb-foreign-key-constraints.html">won&#8217;t activate the deletion trigger</a>, which is all the better in this case.</p>
<p><strong>Without a primary key</strong><br />
A primary key is not required for the child table, since versioning is done purely based on the id of `mytable`.</p> <div><div><pre><span>CREATE</span> <span>TABLE</span> <span>`mypart`</span> <span>&#40;</span> <span>`mytable_id`</span> int<span>&#40;</span><span>10</span><span>&#41;</span> <span>UNSIGNED</span> <span>NOT</span> <span>NULL</span>, <span>`reference`</span> varchar<span>&#40;</span><span>255</span><span>&#41;</span> <span>NOT</span> <span>NULL</span>, <span>KEY</span> <span>`mytable_id`</span> <span>&#40;</span><span>`mytable_id`</span><span>&#41;</span>, CONSTRAINT <span>`mypart_ibfk_1`</span> <span>FOREIGN</span> <span>KEY</span> <span>&#40;</span><span>`mytable_id`</span><span>&#41;</span> <span>REFERENCES</span> <span>`mytable`</span> <span>&#40;</span><span>`id`</span><span>&#41;</span> <span>ON</span> <span>DELETE</span> CASCADE
<span>&#41;</span> ENGINE=InnoDB</pre></div></div> <p>This does cause an issue for the update and delete triggers of the child table. It can&#8217;t use the primary to id to locate the current version of the modified/removed row. This can be solved by a trick I got from PhpMyAdmin. We can simply locate the record by comparing the old values of all fields. There is no constraint for the table enforcing the uniqueness of a row, so we could be targeting multiple identical rows. Since they are identical, it doesn&#8217;t matter which one we target, as long as we limit to 1 row.</p> <div><div><pre><span>CREATE</span> <span>TRIGGER</span> <span>`mypart-afterupdate`</span> AFTER <span>UPDATE</span> <span>ON</span> <span>`mypart`</span> <span>FOR</span> EACH ROW BEGIN <span>DELETE</span> <span>FROM</span> <span>`_revision_mypart`</span> <span>WHERE</span> <span>`_revision`</span> <span>IN</span> <span>&#40;</span><span>SELECT</span> <span>`_revision`</span> <span>FROM</span> <span>`mytable`</span> <span>WHERE</span> <span>`id`</span> = OLD.<span>`mytable_id`</span><span>&#41;</span> <span>AND</span> <span>`mytable_id`</span> = OLD.<span>`mytable_id`</span> <span>AND</span> <span>`reference`</span> = OLD.<span>`reference`</span> <span>LIMIT</span> <span>1</span>; <span>INSERT</span> <span>INTO</span> <span>`_revision_mypart`</span> <span>&#40;</span><span>`mytable_id`</span>, <span>`reference`</span>, <span>`_revision`</span><span>&#41;</span> <span>SELECT</span> NEW.<span>`mytable_id`</span>, NEW.<span>`reference`</span>, <span>`_revision`</span> <span>FROM</span> <span>`mytable`</span> <span>AS</span> <span>`p`</span> <span>WHERE</span> <span>`p`</span>.<span>`id`</span>=NEW.<span>`mytable_id`</span>; END
&nbsp;
<span>CREATE</span> <span>TRIGGER</span> <span>`mypart-afterdelete`</span> AFTER <span>DELETE</span> <span>ON</span> <span>`mypart`</span> <span>FOR</span> EACH ROW BEGIN DECLARE CONTINUE HANDLER <span>FOR</span> <span>1442</span> BEGIN END; <span>DELETE</span> <span>FROM</span> <span>`_revision_mypart`</span> <span>WHERE</span> <span>`_revision`</span> <span>IN</span> <span>&#40;</span><span>SELECT</span> <span>`_revision`</span> <span>FROM</span> <span>`mytable`</span> <span>WHERE</span> <span>`id`</span> = OLD.<span>`mytable_id`</span><span>&#41;</span> <span>AND</span> <span>`mytable_id`</span> = OLD.<span>`mytable_id`</span> <span>AND</span> <span>`reference`</span> = OLD.<span>`reference`</span> <span>LIMIT</span> <span>1</span>; END</pre></div></div> <p><strong>Unique keys</strong><br />
The revisioning table has multiple versions of a record. Unique indexes from the original table should be converted to non-unique indexes in the revisioning table. This information can be fetched using INFORMATION_SCHEMA.</p> <div><div><pre><span>SELECT</span> c.CONSTRAINT_NAME, GROUP_CONCAT<span>&#40;</span>CONCAT<span>&#40;</span><span>'`'</span>, k.COLUMN_NAME, <span>'`'</span><span>&#41;</span><span>&#41;</span> <span>AS</span> cols <span>FROM</span> INFORMATION_SCHEMA.TABLE_CONSTRAINTS <span>AS</span> <span>`c`</span> <span>INNER</span> <span>JOIN</span> INFORMATION_SCHEMA.KEY_COLUMN_USAGE <span>AS</span> <span>`k`</span> <span>ON</span> c.TABLE_SCHEMA=k.TABLE_SCHEMA <span>AND</span> c.TABLE_NAME=k.TABLE_NAME <span>AND</span> c.CONSTRAINT_NAME=k.CONSTRAINT_NAME <span>WHERE</span> c.TABLE_SCHEMA=<span>DATABASE</span><span>&#40;</span><span>&#41;</span> <span>AND</span> c.TABLE_NAME=<span>'mytable'</span> <span>AND</span> c.CONSTRAINT_TYPE=<span>'UNIQUE'</span> <span>AND</span> c.CONSTRAINT_NAME != <span>'_revision'</span> <span>GROUP</span> <span>BY</span> c.CONSTRAINT_NAME</pre></div></div> <p><strong>Revisioning and replication</strong><br />
Baron Schwartz pointed out a <a rel="nofollow" target="_blank" href="http://www.mysqlperformanceblog.com/2008/09/29/why-audit-logging-with-triggers-in-mysql-is-bad-for-replication/">race condition</a> when relying on auto-increment keys in triggers with replication. Actions carried out through triggers on a master are <a rel="nofollow" target="_blank" href="http://dev.mysql.com/doc/refman/5.0/en/faqs-triggers.html#qandaitem-22-5-1-12">not replicated to a slave server</a>. Instead, <a rel="nofollow" target="_blank" href="http://dev.mysql.com/doc/refman/5.0/en/faqs-triggers.html#qandaitem-22-5-1-10">triggers on the slave will be invoked</a>, which should do the same action as on the master.</p>
<p>It probably isn&#8217;t needed to have a copy of the revisioning tables on the slave. This would mean that we could simply omit the triggers. Unfortunately this causes problems when changing the revision. In that case we are forced to move switching of a revision out of the database. Instead the application needs to select the data from all revisioning tables and write that to the original tables. Any other thoughts on solving this issue are welcome.</p>
<p><strong>Download</strong><br />
<a rel="nofollow" target="_blank" href="http://www.adaniels.nl/wp-content/uploads/mysql-revisioning.zip">&#8211;&gt; Download mysql-revisioning script &lt;--</a></p>
<div>&nbsp;</div><br/>PlanetMySQL Voting: <a rel="nofollow" target="_blank" href="http://planet.mysql.com/entry/vote/?entry_id=22412&vote=1&apivote=1">Vote UP</a> / <a rel="nofollow" target="_blank" href="http://planet.mysql.com/entry/vote/?entry_id=22412&vote=-1&apivote=1">Vote DOWN</a>]]></content:encoded>
      </item>
      <item>
         <title>OpenSQLCamp Lightning Talk Videos</title>
         <link>http://www.pythian.com/news/5807/opensqlcamp-lightning-talk-videos/</link>
         <description>OpenSQLCamp was a huge success! Not many folks have blogged about what they learned there&amp;#8230;.if you missed it, all is not lost. We did take videos of most of the sessions (we only had 3 video cameras, and 4 rooms, and 2 sessions were not recorded).
All the videos have been processed, and I am working on uploading them to YouTube and filling in details for the video descriptions. Not all the videos are up right now&amp;#8230;.right now all the lightning talks are up. All the lightning talks belowin one continuous video (just over 54 minutes total)
The Graph Engine (Antony Curtis)
Cluster/J, a new set of Java APIs to MySQL Cluster 5.1 (John David Duncan)
Sphinx, the fulltext storage engine (Peter Zaitsev)
iiBench, the Indexed Insertion Benchmark (Bradley Kuszmaul of Tokutek)
JJtree in Coco
Integrating OSS with Windows (Tom Hanrahan of Microsoft)
Trainwreck, an agent for MySQL replication (Domas Mituzas)
Column Stores (David Lutz of Infobright)
I Play With Data, about doing real statistical calculations of data, when SQL gets in the way (Joseph di Paolantonio)
Your Guide to NoSQL (Brian Aker)
PL/Parrot The call for PL/Parrot in Postgres is put out there (Jonathan &amp;#8220;Duke&amp;#8221; Leto) I am working on uploading the rest of the sessions, so look for them next week, though I may have one or two up very soon.</description>
         <guid isPermaLink="false">http://www.pythian.com/news/?p=5807</guid>
         <pubDate>Wed, 25 Nov 2009 09:34:10 -0800</pubDate>
         <content:encoded><![CDATA[<p><a rel="nofollow" target="_blank" href="http://www.opensqlcamp.org">OpenSQLCamp</a> was a huge success! Not many folks have blogged about what they learned there&#8230;.if you missed it, all is not lost. We did take videos of most of the sessions (we only had 3 video cameras, and 4 rooms, and 2 sessions were not recorded).</p>
<p>All the videos have been processed, and I am working on uploading them to YouTube and filling in details for the video descriptions. Not all the videos are up right now&#8230;.right now all the lightning talks are up.<br />
<ul><br />
<li><a rel="nofollow" target="_blank" href="http://www.youtube.com/watch?v=kziX_aAFYrM">All the lightning talks below</a>in one continuous video (just over 54 minutes total)<br />
<li><a rel="nofollow" target="_blank" href="http://www.youtube.com/watch?v=Tg1UI4DXfM4">The Graph Engine</a> (Antony Curtis)</li><br />
<li><a rel="nofollow" target="_blank" href="http://www.youtube.com/watch?v=M6SXWvWD5oU">Cluster/J</a>, a new set of Java APIs to MySQL Cluster 5.1 (John David Duncan)</li><br />
<li><a rel="nofollow" target="_blank" href="http://www.youtube.com/watch?v=H1qkxPzdMM8">Sphinx</a>, the fulltext storage engine (Peter Zaitsev)</li><br />
<li><a rel="nofollow" target="_blank" href="http://www.youtube.com/watch?v=IL8qPKan-x0">iiBench</a>, the Indexed Insertion Benchmark (Bradley Kuszmaul of <a rel="nofollow" target="_blank" href="http://www.tokutek.com">Tokutek</a>)</li><br />
<li><a rel="nofollow" target="_blank" href="http://www.youtube.com/watch?v=gTbGwYFsjPE">JJtree in Coco</a></li><br />
<li><a rel="nofollow" target="_blank" href="http://www.youtube.com/watch?v=58JxyX8j2t8">Integrating OSS with Windows</a> (Tom Hanrahan of Microsoft)</li><br />
<li><a rel="nofollow" target="_blank" href="http://www.youtube.com/watch?v=q5-w72JtU4E">Trainwreck</a>, an agent for MySQL replication (Domas Mituzas)</li><br />
<li><a rel="nofollow" target="_blank" href="http://www.youtube.com/watch?v=y6Unbwbyvxk">Column Stores</a> (David Lutz of <a rel="nofollow" target="_blank" href="http://www.infobright.com">Infobright</a>)</li><br />
<li><a rel="nofollow" target="_blank" href="http://www.youtube.com/watch?v=_XoWh6xR2wY">I Play With Data</a>, about doing real statistical calculations of data, when SQL gets in the way (Joseph di Paolantonio)</li><br />
<li><a rel="nofollow" target="_blank" href="http://www.youtube.com/watch?v=LhnGarRsKnA">Your Guide to NoSQL</a> (Brian Aker)</li><br />
<li><a rel="nofollow" target="_blank" href="http://www.youtube.com/watch?v=WFbTAVleQ7s">PL/Parrot</a> The call for PL/Parrot in Postgres is put out there (Jonathan &#8220;Duke&#8221; Leto)</li><br />
</ul><br />
<span></span><br />
I am working on uploading the rest of the sessions, so look for them next week, though I may have one or two up very soon.</p><br/>PlanetMySQL Voting: <a rel="nofollow" target="_blank" href="http://planet.mysql.com/entry/vote/?entry_id=22407&vote=1&apivote=1">Vote UP</a> / <a rel="nofollow" target="_blank" href="http://planet.mysql.com/entry/vote/?entry_id=22407&vote=-1&apivote=1">Vote DOWN</a>]]></content:encoded>
      </item>
      <item>
         <title>Is your database schema in sync?</title>
         <link>http://ronaldbradford.com/blog/is-your-database-schema-in-sync-2009-11-25/</link>
         <description>If you have more then a single MySQL database for your production environment, e.g. a development and test environment, or a MySQL replication topology, ensuring your schema&amp;#8217;s are in sync can be task that requires some time if not managed correctly.
There is a tool I do use for MySQL environments called Schema Sync &amp;#8211; a MySQL Schema Versioning and Migration Utility. There are many reasons why schema&amp;#8217;s get out of sync. Developers may not ensure their changes are reflected in any software to be deployed, and when not tested you could end up with broken functionality. A DBA might try some different index strategies on a slave, but not the master, and never implement or revert.
While some people want the quick and hidden just sync version akin to Rails, I really like this product as it produces proper patch and revert SQL scripts. As a DBA I really want to know what&amp;#8217;s going to be applied to my schema, I want to be able to review it before it&amp;#8217;s just magically applied. The revert process is also ideal, in time critical situations when you may need to rollback a change, either it&amp;#8217;s taking to long to deploy.
This is definitely a tool to use over time, especially if you have multiple developers, DBA&amp;#8217;s and more then one MySQL environment that should be the same.</description>
         <guid isPermaLink="false">http://ronaldbradford.com/blog/?p=2270</guid>
         <pubDate>Wed, 25 Nov 2009 09:04:57 -0800</pubDate>
         <content:encoded><![CDATA[<p>If you have more then a single MySQL database for your production environment, e.g. a development and test environment, or a MySQL replication topology, ensuring your schema&#8217;s are in sync can be task that requires some time if not managed correctly.</p>
<p>There is a tool I do use for MySQL environments called <a rel="nofollow" target="_blank" href="http://schemasync.org/">Schema Sync &#8211; a MySQL Schema Versioning and Migration Utility</a>. There are many reasons why schema&#8217;s get out of sync. Developers may not ensure their changes are reflected in any software to be deployed, and when not tested you could end up with broken functionality. A DBA might try some different index strategies on a slave, but not the master, and never implement or revert.</p>
<p>While some people want the quick and hidden just sync version akin to Rails, I really like this product as it produces proper patch and revert SQL scripts. As a DBA I really want to know what&#8217;s going to be applied to my schema, I want to be able to review it before it&#8217;s just magically applied. The revert process is also ideal, in time critical situations when you may need to rollback a change, either it&#8217;s taking to long to deploy.</p>
<p>This is definitely a tool to use over time, especially if you have multiple developers, DBA&#8217;s and more then one MySQL environment that should be the same.</p><br/>PlanetMySQL Voting: <a rel="nofollow" target="_blank" href="http://planet.mysql.com/entry/vote/?entry_id=22405&vote=1&apivote=1">Vote UP</a> / <a rel="nofollow" target="_blank" href="http://planet.mysql.com/entry/vote/?entry_id=22405&vote=-1&apivote=1">Vote DOWN</a>]]></content:encoded>
      </item>
      <item>
         <title>Monitoring MySQL with MONyog</title>
         <link>http://ronaldbradford.com/blog/monitoring-mysql-with-monyog-2009-11-25/</link>
         <description>It just works. In absence of any MySQL monitoring for your site, I have found no solution that gets you operational as quickly and easily. MONyog can be deployed in 60 seconds, and configured in another 60 seconds. Within 5 minutes you can have visual monitoring of your MySQL environment.
MONyog is an agentless process, which is an advantage for easy install, but does not provide for monitoring redundancy in the capture of information due to agentless nature. It&amp;#8217;s a static standalone executable which is great if you need something to work out of the box. You can easily configure multiple servers in a replication topology, or different servers in your environment. You get the ability to monitor all the usual information, with a dashboard and detailed graphs. While MONyog does provide customizations of rules for the graphs and presentation order, that&amp;#8217;s about it. You can&amp;#8217;t at this time for example change the colors, what&amp;#8217;s on graphs except for what MONyog monitors or the security of certain options in the GUI to different users, however I hope they offer these suggestions in future releases. MONyog includes some nice features that are overlooked in other products. You have the ability to monitor the MySQL error log (if configured appropriately) which is a common complaint of end users. You can also see the process list, and when configured you can also perform query gathering and analysis.
MONyog is a well priced commercial product with a free trial download without registration requirements which gives no barrier to access and evaluate. As a solution and ease of deployment, there is no excuse not to evaluate this product. If you have no monitoring, you can now quickly and easily. I find a number of clients that simply have no monitoring. There really is no excuse as it&amp;#8217;s critical information you need to have and record for a successful business.
You can get it from www.webyog.com.</description>
         <guid isPermaLink="false">http://ronaldbradford.com/blog/?p=2260</guid>
         <pubDate>Wed, 25 Nov 2009 08:52:34 -0800</pubDate>
         <content:encoded><![CDATA[<p>It just works. In absence of any MySQL monitoring for your site, I have found no solution that gets you operational as quickly and easily. <a rel="nofollow" target="_blank" href="http://www.webyog.com/">MONyog</a> can be deployed in 60 seconds, and configured in another 60 seconds. Within 5 minutes you can have visual monitoring of your MySQL environment.</p>
<p>MONyog is an agentless process, which is an advantage for easy install, but does not provide for monitoring redundancy in the capture of information due to agentless nature. It&#8217;s a static standalone executable which is great if you need something to work out of the box. You can easily configure multiple servers in a replication topology, or different servers in your environment. You get the ability to monitor all the usual information, with a dashboard and detailed graphs. While MONyog does provide customizations of rules for the graphs and presentation order, that&#8217;s about it. You can&#8217;t at this time for example change the colors, what&#8217;s on graphs except for what MONyog monitors or the security of certain options in the GUI to different users, however I hope they offer these suggestions in future releases. </p>
<p>MONyog includes some nice features that are overlooked in other products. You have the ability to monitor the MySQL error log (if configured appropriately) which is a common complaint of end users. You can also see the process list, and when configured you can also perform query gathering and analysis.</p>
<p>MONyog is a well priced commercial product with a free trial download without registration requirements which gives no barrier to access and evaluate. As a solution and ease of deployment, there is no excuse not to evaluate this product. If you have no monitoring, you can now quickly and easily. I find a number of clients that simply have no monitoring. There really is no excuse as it&#8217;s critical information you need to have and record for a successful business.</p>
<p>You can get it from <a rel="nofollow" target="_blank" href="http://www.webyog.com">www.webyog.com</a>.</p><br/>PlanetMySQL Voting: <a rel="nofollow" target="_blank" href="http://planet.mysql.com/entry/vote/?entry_id=22406&vote=1&apivote=1">Vote UP</a> / <a rel="nofollow" target="_blank" href="http://planet.mysql.com/entry/vote/?entry_id=22406&vote=-1&apivote=1">Vote DOWN</a>]]></content:encoded>
      </item>
      <item>
         <title>Running MySQL Cluster on Mac: working around a ndb_mgmd bug</title>
         <link>http://blog.some-abstract-type.com/2009/11/running-mysql-cluster-on-mac-working.html</link>
         <description>A week ago we found a workaround for a bug in MySQL Cluster making it impossible to run a management node on MacOS X. Until the bug is fixed, you should use the --nodaemon option for the ndb_mgmd executable. Both MySQL Cluster v6.3 and v7.0 are affected.Currently, I'm starting the management node like this: ( cd /opt/mysql/mysql ; ./libexec/ndb_mgmd -f /opt/mysql/config.ini &amp;#92; --nodaemon 2&amp;gt;/dev/null 1&amp;gt;&amp;2 &amp;lt;/dev/null &amp; )Obviously, you'll want to change the paths.Eventually, the bug will get fixed, but until then you got no excuse to not try MySQL Cluster on Mac!</description>
         <guid isPermaLink="false">tag:blogger.com,1999:blog-5702936365231918674.post-7529522761969872939</guid>
         <pubDate>Wed, 25 Nov 2009 07:32:00 -0800</pubDate>
         <content:encoded><![CDATA[<p>A week ago we found a workaround for <a rel="nofollow" target="_blank" href="http://bugs.mysql.com/bug.php?id=47214">a bug</a> in <a rel="nofollow" target="_blank" href="http://dev.mysql.com/downloads/select.php?id=14">MySQL Cluster</a> making it impossible to run a management node on MacOS X. Until the bug is fixed, you should use the <tt>--nodaemon</tt> option for the <tt>ndb_mgmd</tt> executable. Both MySQL Cluster v6.3 and v7.0 are affected.</p><p>Currently, I'm starting the management node like this:</p><pre><br /> (<br /> cd /opt/mysql/mysql ; <br /> ./libexec/ndb_mgmd -f /opt/mysql/config.ini &#92;<br /> --nodaemon 2&gt;/dev/null 1&gt;&2 &lt;/dev/null &<br /> )<br /></pre><p>Obviously, you'll want to change the paths.</p><p>Eventually, the bug will get fixed, but until then you got no excuse to not try MySQL Cluster on Mac!</p><div><img width="1" height="1" src="https://blogger.googleusercontent.com/tracker/5702936365231918674-7529522761969872939?l=blog.some-abstract-type.com" alt=""/></div><br/>PlanetMySQL Voting: <a rel="nofollow" target="_blank" href="http://planet.mysql.com/entry/vote/?entry_id=22404&vote=1&apivote=1">Vote UP</a> / <a rel="nofollow" target="_blank" href="http://planet.mysql.com/entry/vote/?entry_id=22404&vote=-1&apivote=1">Vote DOWN</a>]]></content:encoded>
      </item>
      <item>
         <title>A Laptop for Developers without paying The Windows Tax</title>
         <link>http://jpipes.com/index.php?/archives/313-A-Laptop-for-Developers-without-paying-The-Windows-Tax.html</link>
         <description>I find it amazing that the U.S. Department of Justice can continue to cover its eyes and ears while Microsoft is allowed to exert its monopolistic power over all hardware manufacturers. About 20 months ago, I was able to purchase a Lenovo Thinkpad T61 from the lenovo.com website without an operating system installed. Today, I went to purchase a new Lenovo Thinkpad laptop, again without having to pay the Windows Tax. Turns out Lenovo has stopped offering this option. What a complete PILE OF SHIT. Somebody in Microsoft's &quot;Business Development&quot; or &quot;Partners&quot; team must have told Lenovo to stop offering its customers a simple choice of not having to pay the OEM license fees for Windows. And there's nothing anyone can do about it. Microsoft is just too big and too pervasive for anybody to have a damn effect on them. Frankly, it's anti-choice, anti-competition, anti-innovation behaviour from Microsoft. And its ridiculous. Does anyone out there know how to get a decent laptop any more without having to fork over my money to a software giant that continues to bully all competition out of the market? Your suggestions are most welcome. P.S. Mac is not an option for me. Sorry. P.P.S The only thing this post has to do with MySQL is the general discussion on the acquisition of Sun by Oracle, and the pending investigation into possibly monopoly concerns by the EC...but of course I can't comment on that directly...grr. UPDATE: Seems DELL offers laptops with Ubuntu installed instead of Windows, at least according to search results from their website. Yeah! &amp;#92;o/ Of course, now I have to just figure out how to get to that customization option. When I've gone through the customization screens, no option other than Windows is available. UPDATE 2: The DELL representative on their online chat program was quite helpful and offered this link to laptops they offer with no Windows Tax.</description>
         <guid isPermaLink="false">http://jpipes.com/index.php?/archives/313-guid.html</guid>
         <pubDate>Tue, 24 Nov 2009 10:39:56 -0800</pubDate>
         <content:encoded><![CDATA[<p>
I find it amazing that the U.S. Department of Justice can continue to cover its eyes and ears while Microsoft is allowed to exert its monopolistic power over all hardware manufacturers.
</p>
<p>
About 20 months ago, I was able to purchase a Lenovo Thinkpad T61 from the lenovo.com website without an operating system installed. Today, I went to purchase a new Lenovo Thinkpad laptop, again without having to pay the Windows Tax. Turns out <a rel="nofollow" target="_blank" href="http://lwn.net/Articles/298101/" title="Lenovo no longer offering Linux laptops">Lenovo has stopped offering this option</a>. What a complete PILE OF SHIT. Somebody in Microsoft's "Business Development" or "Partners" team must have told Lenovo to stop offering its customers a simple choice of not having to pay the OEM license fees for Windows. And there's nothing anyone can do about it. Microsoft is just too big and too pervasive for anybody to have a damn effect on them.
</p>
<p>
Frankly, it's anti-choice, anti-competition, anti-innovation behaviour from Microsoft.
</p>
<p>
And its ridiculous.
</p>
<p>
Does anyone out there know how to get a decent laptop any more without having to fork over my money to a software giant that continues to bully all competition out of the market? Your suggestions are most welcome.
</p>
<p>
P.S. Mac is not an option for me. Sorry.
</p>
<p>
P.P.S The only thing this post has to do with MySQL is the general discussion on the acquisition of Sun by Oracle, and the pending investigation into possibly monopoly concerns by the EC...but of course I can't comment on that directly...grr.
</p>
<p>
<strong>UPDATE</strong>:
<br />
Seems DELL offers laptops with Ubuntu installed instead of Windows, at least according to search results from their website. Yeah! &#92;o/ Of course, now I have to just figure out how to get to that customization option. When I've gone through the customization screens, no option other than Windows is available. <img src="http://jpipes.com/templates/default/img/emoticons/sad.png" alt=":-(" style="display:inline;vertical-align:bottom;" class="emoticon"/>
</p>
<p>
<strong>UPDATE 2</strong>:
<br />
The DELL representative on their online chat program was quite helpful and offered this link to <a rel="nofollow" target="_blank" href="http://www.dell.com/business/laptops#subcats=&amp;navla=80770~0~1791343&amp;navidc=LT:%20Operating%20System&amp;navValc=FreeDOS%20and%20Linux&amp;a=80770~0~1791343&amp;page=1">laptops they offer with no Windows Tax</a>. </p><br/>PlanetMySQL Voting: <a rel="nofollow" target="_blank" href="http://planet.mysql.com/entry/vote/?entry_id=22374&vote=1&apivote=1">Vote UP</a> / <a rel="nofollow" target="_blank" href="http://planet.mysql.com/entry/vote/?entry_id=22374&vote=-1&apivote=1">Vote DOWN</a>]]></content:encoded>
      </item>
      <item>
         <title>Software preview MySQL Scriptable Replication</title>
         <link>http://www.clusterdb.com/mysql-replication/software-preview-mysql-scriptable-replication/</link>
         <description>Fig. 1 MySQL per-row replication filtering
A MySQL Software preview is available which allows you to write Lua scripts to control replication on a statement-by-statement basis. Note that this is prototype functionality and is not supported but feedback on its usefulness would be gratefully received.The final version would allow much greater functionality but this preview allows you to implement filters on either the master or slave to examine the statements being replicated and decide whether to continue processing each one or not.
After reading this article, you may be interested in trying this out for yourself and want to create your own script(s). You can get more information on the functionality and download the special version of MySQL from http://forge.mysql.com/wiki/ReplicationFeatures/ScriptableReplication
To understand how this feature works, you first need to understand the very basics about how MySQL replication works. Changes that are made to the &amp;#8216;Master&amp;#8217; MySQL Server are written to a binary log. Any slave MySQL Servers that subscribe to this master are sent the data from the master&amp;#8217;s binary log; the slave(s) then copy this data to their own relay log(s). The slave(s) will then work through all of the updates in their relay logs and apply them to their local database(s). The implementation is a little more complex when using MySQL Cluster as the master&amp;#8217;s updates may come through multiple MySQL Servers or directly from an application through the NDB API but all of the changes will still make it into the binary log.
MySQL Replication supports both statement and row based replication (as well as mixed) but this software preview is restricted to statement based replication. As MySQL Cluster must use row based replication this preview cannot be used with Cluster but the final implementation should work with all storage engines.
As show in Fig. 1 there are 4 points where you can choose to filter statements being replicated: Before the update is written to the binary log
After the update has been read from the binary log
Before the update is written to the relay log
After the update has been read from the relay log The final 2 interest me most as it allows us to have multiple slaves which apply different filters &amp;#8211; this article includes a worked example of how that could be exploited.
Fig. 2 Details for each filtering point
The filters are written as Lua scripts. The names of the script file, module name and function names vary depending on which of these filtering points is to be used. Fig. 2 shows these differences. In all cases, the scripts are stored in the following folder: &amp;#8220;&amp;lt;mysql-base-directory&amp;gt;/ext/replication&amp;#8221;.
This article creates 2 different scripts &amp;#8211; one for each of 2 slave servers. In both cases the filter script is executed after an update is read from the relay log. One slave will discard any statement of the form &amp;#8220;INSERT INTO &amp;lt;table-name&amp;gt; SET sub_id = 401, &amp;#8230;&amp;#8221; by searching for the sub string &amp;#8220;sub_id = X&amp;#8221; where X is even while the second slave will discard any where X is odd. Any statement that doesn&amp;#8217;t include this pattern will be allowed through.
Fig. 3 Implementation of odd/even sharded replication
If a script returns TRUE then the statement is discarded, if it returns FALSE then the replication process continues. Fig. 3 shows the architecture and pseudo code for the odd/even replication sharding. The actual code for the two slaves is included here:
slave-odd: &amp;lt;mysql-base-directory&amp;gt;/ext/replication/relay_log.lua
function after_read(event) local m = event.query if m then id = string.match(m, &quot;sub_id = (%d+)&quot;) if id then if id %2 == 0 then return true else return false end else id = string.match(m, &quot;sub_id=(%d+)&quot;) if id then if id %2 == 0 then return true else return false end else return false end end else return false end
end
slave-even: &amp;lt;mysql-base-directory&amp;gt;/ext/replication/relay_log.lua
function after_read(event) local m = event.query if m then id = string.match(m, &quot;sub_id = (%d+)&quot;) if id then if id %2 == 1 then return true else return false end else id = string.match(m, &quot;sub_id=(%d+)&quot;) if id then if id %2 == 1 then return true else return false end else return false end end else return false end
end
Replication can then be set-up as normal as described in Setting up MySQL Asynchronous Replication for High Availability with the exception that we use 2 slaves rather than 1.
Once replication has been started on both of the slaves, the database and tables should be created; note that for some reason, the creation of the tables isn&amp;#8217;t replicated to the slaves when using this preview load and so the tables actually need to be created 3 times:
mysql-master&amp;gt; CREATE DATABASE clusterdb; mysql-master&amp;gt; USE clusterdb; mysql-master&amp;gt; CREATE TABLE sys1 (code INT NOT NULL PRIMARY KEY, country VARCHAR (30)) engine=innodb; mysql-master&amp;gt; CREATE TABLE subs1 (sub_id INT NOT NULL PRIMARY KEY, code INT) engine=innodb;
mysql-slave-odd&amp;gt; USE clusterdb; mysql-slave-odd&amp;gt; CREATE TABLE sys1 (code INT NOT NULL PRIMARY KEY, country VARCHAR (30)) engine=innodb; mysql-slave-odd&amp;gt; create table subs1 (sub_id INT NOT NULL PRIMARY KEY, code INT) engine=innodb;
mysql-slave-even&amp;gt; USE clusterdb; mysql-slave-even&amp;gt; CREATE TABLE sys1 (code INT NOT NULL PRIMARY KEY, country VARCHAR (30)) engine=innodb; mysql-slave-even&amp;gt; CREATE TABLE subs1 (sub_id INT NOT NULL PRIMARY KEY, code INT) engine=innodb;
The data can then be added to the master and then the 2 slaves can be checked to validate that it behaved as expected:
mysql-master&amp;gt; INSERT INTO sys1 SET area_code=33, country=&quot;France&quot;;
mysql-master&amp;gt; INSERT INTO sys1 SET area_code=44, country=&quot;UK&quot;;
mysql-master&amp;gt; INSERT INTO subs1 SET sub_id=401, code=44;
mysql-master&amp;gt; INSERT INTO subs1 SET sub_id=402, code=33;
mysql-master&amp;gt; INSERT INTO subs1 SET sub_id=976, code=33;
mysql-master&amp;gt; INSERT INTO subs1 SET sub_id=981, code=44;
mysql-slave-odd&amp;gt; SELECT * FROM sys1;
+------+---------+
| code | country |
+------+---------+
| 33 | France |
| 44 | UK |
+------+---------+ mysql-slave-odd&amp;gt; SELECT * FROM subs1;
+--------+------+
| sub_id | code |
+--------+------+
| 401 | 44 |
| 981 | 44 |
+--------+------+
Fig. 4 Results of partitioned replication
mysql-slave-even&amp;gt; SELECT * FROM sys1;
+------+---------+
| code | country |
+------+---------+
| 33 | France |
| 44 | UK |
+------+---------+
mysql-slave-even&amp;gt; SELECT * FROM subs1;
+--------+------+
| sub_id | code |
+--------+------+
| 402 | 33 |
| 976 | 33 |
+--------+------+
Fig. 4 illustrates this splitting of data between the 2 slaves &amp;#8211; all rows from the system table are stored in both databases (as well as in the master) while the data in the subscriber table (and it would work for multiple subscriber tables too) are partitioned between the 2 databases &amp;#8211; odd values in one, even in the other. Obviously, this could be extended to more slaves by changing the checks in the scripts.
As an illustration of how this example could be useful, all administrative data could be provisioned into and maintained by the master &amp;#8211; both system and subscriber data. Each slave could then serve a subset of the subscribers, providing read-access to the administrative data andread/write access for the more volatile subscriber data (which is mastered on the &amp;#8217;slave&amp;#8217;). In this way, there can be a central point to manage the administrative data while being able to scale out to multiple, databases to provide maximum capacity and performance to the applications. For example, in a telco environment, you may filter rows by comparing a subscriber&amp;#8217;s phone number to a set of area codes so that the local subscribers are accessed from the local database &amp;#8211; minimising latency.
From a data integrity perspective, this approach is safe if (and only if) the partitioning rules ensures that all related rows are on the same slave (in our example, all rows from all tables for a particular subscriber will be on the same slave &amp;#8211; so as long as we don&amp;#8217;t need transactional consistency between different subscribers then this should be safe). Fig. 5 Partitioned replication for MySQL Cluster
As mentioned previously this software preview doesn&amp;#8217;t work with MySQL Cluster but looking forward to when it does, the example could be extended by having each of the slave servers be part of the same Cluster. In this case, the partitioned data will be consolidated back into a single database (for this scenario, you would likely configure just one server to act as the slave for the system data). On the face of it, this would be a futile exercise but in cases where the performance bottlenecks on the throughput of a single slave server, this might be a way to horizontally scale the replication performance for applications which make massive numbers of database writes.</description>
         <guid isPermaLink="false">http://www.clusterdb.com/?p=694</guid>
         <pubDate>Tue, 24 Nov 2009 08:14:39 -0800</pubDate>
         <content:encoded><![CDATA[<div><a rel="nofollow" target="_blank" href="http://www.clusterdb.com/wp-content/uploads/2009/11/slave_filter.jpg"><img class="size-medium wp-image-689" title="MySQL per-row replication filtering" src="http://www.clusterdb.com/wp-content/uploads/2009/11/slave_filter-300x153.jpg" alt="Fig. 1 MySQL per-row replication filtering" width="300" height="153"/></a><p>Fig. 1 MySQL per-row replication filtering</p></div>
<p>A MySQL Software preview is available which allows you to write Lua scripts to control replication on a statement-by-statement basis. <strong>Note that this is prototype functionality and is not supported but feedback on its usefulness would be gratefully received.</strong>The final version would allow much greater functionality but this preview allows you to implement filters on either the master or slave to examine the statements being replicated and decide whether to continue processing each one or not.</p>
<p>After reading this article, you may be interested in trying this out for yourself and want to create your own script(s). You can get more information on the functionality and download the special version of MySQL from <a rel="nofollow" target="_blank" href="http://forge.mysql.com/wiki/ReplicationFeatures/ScriptableReplication">http://forge.mysql.com/wiki/ReplicationFeatures/ScriptableReplication</a></p>
<p>To understand how this feature works, you first need to understand the very basics about how MySQL replication works. Changes that are made to the &#8216;Master&#8217; MySQL Server are written to a binary log. Any slave MySQL Servers that subscribe to this master are sent the data from the master&#8217;s binary log; the slave(s) then copy this data to their own relay log(s). The slave(s) will then work through all of the updates in their relay logs and apply them to their local database(s). The implementation is a little more complex when using MySQL Cluster as the master&#8217;s updates may come through multiple MySQL Servers or directly from an application through the NDB API but all of the changes will still make it into the binary log.</p>
<p>MySQL Replication supports both statement and row based replication (as well as mixed) but this software preview is restricted to statement based replication. As MySQL Cluster must use row based replication this preview cannot be used with Cluster but the final implementation should work with all storage engines.</p>
<p>As show in Fig. 1 there are 4 points where you can choose to filter statements being replicated:</p>
<ol>
<li>Before the update is written to the binary log</li>
<li>After the update has been read from the binary log</li>
<li>Before the update is written to the relay log</li>
<li>After the update has been read from the relay log</li>
</ol>
<p>The final 2 interest me most as it allows us to have multiple slaves which apply different filters &#8211; this article includes a worked example of how that could be exploited.</p>
<div><a rel="nofollow" target="_blank" href="http://www.clusterdb.com/wp-content/uploads/2009/11/slave_filter_lua_modules1.jpg"><img class="size-medium wp-image-692 " title="Details for each filtering point" src="http://www.clusterdb.com/wp-content/uploads/2009/11/slave_filter_lua_modules1.jpg" alt="Fig. 2 Details for each filtering point" width="300" height="140"/></a><p>Fig. 2 Details for each filtering point</p></div>
<p>The filters are written as Lua scripts. The names of the script file, module name and function names vary depending on which of these filtering points is to be used. Fig. 2 shows these differences. In all cases, the scripts are stored in the following folder: &#8220;&lt;mysql-base-directory&gt;/ext/replication&#8221;.</p>
<p>This article creates 2 different scripts &#8211; one for each of 2 slave servers. In both cases the filter script is executed after an update is read from the relay log. One slave will discard any statement of the form &#8220;INSERT INTO &lt;table-name&gt; SET sub_id = 401, &#8230;&#8221; by searching for the sub string &#8220;sub_id = X&#8221; where X is even while the second slave will discard any where X is odd. Any statement that doesn&#8217;t include this pattern will be allowed through.</p>
<div><a rel="nofollow" target="_blank" href="http://www.clusterdb.com/wp-content/uploads/2009/11/slave_filter_lua_code.jpg"><img class="size-medium wp-image-691" title="Implementation of odd/even sharded replication" src="http://www.clusterdb.com/wp-content/uploads/2009/11/slave_filter_lua_code-300x229.jpg" alt="Fig. 3 Implementation of odd/even sharded replication" width="300" height="229"/></a><p>Fig. 3 Implementation of odd/even sharded replication</p></div>
<p>If a script returns TRUE then the statement is discarded, if it returns FALSE then the replication process continues. Fig. 3 shows the architecture and pseudo code for the odd/even replication sharding.</p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p>The actual code for the two slaves is included here:</p>
<pre>slave-odd: &lt;mysql-base-directory&gt;/ext/replication/relay_log.lua</pre>
<pre>function after_read(event) local m = event.query if m then id = string.match(m, "sub_id = (%d+)") if id then if id %2 == 0 then return true else return false end else id = string.match(m, "sub_id=(%d+)") if id then if id %2 == 0 then return true else return false end else return false end end else return false end
end</pre>
<pre>slave-even: &lt;mysql-base-directory&gt;/ext/replication/relay_log.lua</pre>
<pre>function after_read(event) local m = event.query if m then id = string.match(m, "sub_id = (%d+)") if id then if id %2 == 1 then return true else return false end else id = string.match(m, "sub_id=(%d+)") if id then if id %2 == 1 then return true else return false end else return false end end else return false end
end</pre>
<p>Replication can then be set-up as normal as described in <a rel="nofollow" target="_blank" href="http://www.clusterdb.com/mysql-cluster/setting-up-mysql-asynchronous-replication-for-high-availability/">Setting up MySQL Asynchronous Replication for High Availability</a> with the exception that we use 2 slaves rather than 1.</p>
<p>Once replication has been started on both of the slaves, the database and tables should be created; note that for some reason, the creation of the tables isn&#8217;t replicated to the slaves when using this preview load and so the tables actually need to be created 3 times:</p>
<pre>mysql-master&gt; CREATE DATABASE clusterdb; mysql-master&gt; USE clusterdb; mysql-master&gt; CREATE TABLE sys1 (code INT NOT NULL PRIMARY KEY, country VARCHAR (30)) engine=innodb; mysql-master&gt; CREATE TABLE subs1 (sub_id INT NOT NULL PRIMARY KEY, code INT) engine=innodb;</pre>
<pre>mysql-slave-odd&gt; USE clusterdb; mysql-slave-odd&gt; CREATE TABLE sys1 (code INT NOT NULL PRIMARY KEY, country VARCHAR (30)) engine=innodb; mysql-slave-odd&gt; create table subs1 (sub_id INT NOT NULL PRIMARY KEY, code INT) engine=innodb;</pre>
<pre>mysql-slave-even&gt; USE clusterdb; mysql-slave-even&gt; CREATE TABLE sys1 (code INT NOT NULL PRIMARY KEY, country VARCHAR (30)) engine=innodb; mysql-slave-even&gt; CREATE TABLE subs1 (sub_id INT NOT NULL PRIMARY KEY, code INT) engine=innodb;</pre>
<p>The data can then be added to the master and then the 2 slaves can be checked to validate that it behaved as expected:</p>
<pre>mysql-master&gt; INSERT INTO sys1 SET area_code=33, country="France";
mysql-master&gt; INSERT INTO sys1 SET area_code=44, country="UK";
mysql-master&gt; INSERT INTO subs1 SET sub_id=401, code=44;
mysql-master&gt; INSERT INTO subs1 SET sub_id=402, code=33;
mysql-master&gt; INSERT INTO subs1 SET sub_id=976, code=33;
mysql-master&gt; INSERT INTO subs1 SET sub_id=981, code=44;</pre>
<pre>mysql-slave-odd&gt; SELECT * FROM sys1;
+------+---------+
| code | country |
+------+---------+
| 33 | France |
| 44 | UK |
+------+---------+ mysql-slave-odd&gt; SELECT * FROM subs1;
+--------+------+
| sub_id | code |
+--------+------+
| 401 | 44 |
| 981 | 44 |
+--------+------+</pre>
<div><a rel="nofollow" target="_blank" href="http://www.clusterdb.com/wp-content/uploads/2009/11/slave_filter_results.jpg"><img class="size-medium wp-image-693" title="Results of partitioned replication" src="http://www.clusterdb.com/wp-content/uploads/2009/11/slave_filter_results-300x191.jpg" alt="Fig. 4 Results of partitioned replication" width="300" height="191"/></a><p>Fig. 4 Results of partitioned replication</p></div>
<pre>mysql-slave-even&gt; SELECT * FROM sys1;
+------+---------+
| code | country |
+------+---------+
| 33 | France |
| 44 | UK |
+------+---------+
mysql-slave-even&gt; SELECT * FROM subs1;
+--------+------+
| sub_id | code |
+--------+------+
| 402 | 33 |
| 976 | 33 |
+--------+------+</pre>
<p>Fig. 4 illustrates this splitting of data between the 2 slaves &#8211; all rows from the system table are stored in both databases (as well as in the master) while the data in the subscriber table (and it would work for multiple subscriber tables too) are partitioned between the 2 databases &#8211; odd values in one, even in the other. Obviously, this could be extended to more slaves by changing the checks in the scripts.</p>
<p>As an illustration of how this example could be useful, all administrative data could be provisioned into and maintained by the master &#8211; both system and subscriber data. Each slave could then serve a subset of the subscribers, providing read-access to the administrative data <strong>and</strong>read/write access for the more volatile subscriber data (which is mastered on the &#8217;slave&#8217;). In this way, there can be a central point to manage the administrative data while being able to scale out to multiple, databases to provide maximum capacity and performance to the applications. For example, in a telco environment, you may filter rows by comparing a subscriber&#8217;s phone number to a set of area codes so that the local subscribers are accessed from the local database &#8211; minimising latency.</p>
<p>From a data integrity perspective, this approach is safe if (and only if) the partitioning rules ensures that all related rows are on the same slave (in our example, all rows from all tables for a particular subscriber will be on the same slave &#8211; so as long as we don&#8217;t need transactional consistency between different subscribers then this should be safe).</p>
<div><a rel="nofollow" target="_blank" href="http://www.clusterdb.com/wp-content/uploads/2009/11/slave_filter_cluster.jpg"><img class="size-medium wp-image-690" title="Partioned replication for MySQL Cluster" src="http://www.clusterdb.com/wp-content/uploads/2009/11/slave_filter_cluster-300x156.jpg" alt="Fig. 5 Partioned replication for MySQL Cluster" width="300" height="156"/></a> <p>Fig. 5 Partitioned replication for MySQL Cluster</p></div>
<p>As mentioned previously this software preview doesn&#8217;t work with MySQL Cluster but looking forward to when it does, the example could be extended by having each of the slave servers be part of the same Cluster. In this case, the partitioned data will be consolidated back into a single database (for this scenario, you would likely configure just one server to act as the slave for the system data). On the face of it, this would be a futile exercise but in cases where the performance bottlenecks on the throughput of a single slave server, this might be a way to horizontally scale the replication performance for applications which make massive numbers of database writes.</p><br/>PlanetMySQL Voting: <a rel="nofollow" target="_blank" href="http://planet.mysql.com/entry/vote/?entry_id=22371&vote=1&apivote=1">Vote UP</a> / <a rel="nofollow" target="_blank" href="http://planet.mysql.com/entry/vote/?entry_id=22371&vote=-1&apivote=1">Vote DOWN</a>]]></content:encoded>
      </item>
   </channel>
</rss>
<!-- fe4.pipes.sp1.yahoo.com uncompressed/chunked Sun Nov 29 18:36:08 PST 2009 -->
