<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>code.openark.org &#187; scripts</title>
	<atom:link href="http://code.openark.org/blog/tag/scripts/feed" rel="self" type="application/rss+xml" />
	<link>http://code.openark.org/blog</link>
	<description>Blog by Shlomi Noach</description>
	<lastBuildDate>Wed, 01 Feb 2012 08:19:12 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<item>
		<title>MySQL command line vs. visual editors</title>
		<link>http://code.openark.org/blog/mysql/mysql-command-line-vs-visual-editors</link>
		<comments>http://code.openark.org/blog/mysql/mysql-command-line-vs-visual-editors#comments</comments>
		<pubDate>Mon, 30 Jan 2012 15:04:34 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[command line]]></category>
		<category><![CDATA[Opinions]]></category>
		<category><![CDATA[scripts]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=4639</guid>
		<description><![CDATA[Students in my training classes usually prefer to use some kind of visual editor for MySQL. Typically this would be the software they're using at work. Sometimes they just bring over their laptops with the software installed. Or they would use MySQL Workbench, which is what I usually have pre-installed on their desktops. I see [...]]]></description>
			<content:encoded><![CDATA[<p>Students in my training classes usually prefer to use some kind of visual editor for MySQL. Typically this would be the software they're using at work. Sometimes they just bring over their laptops with the software installed. Or they would use MySQL Workbench, which is what I usually have pre-installed on their desktops.</p>
<p>I see MySQL Workbench, SQLyog, Toad for MySQL, or several more.</p>
<p>I always humbly suggest they <em>close down their software and open up a command line</em>.</p>
<p>It isn't fancy. It may not even be convenient (especially on Windows, in my opinion). And repeating your last command with a minor modification requires a lot of key stroking. Or you would copy+paste from some text editor. Most students will give it a shot, then go back to their favorite editor.</p>
<p>Well, again and again I reach the same conclusion:</p>
<h4>Visual editors are not as trustworthy as the command line.</h4>
<p>Time and again students show me something on their editor. Behavior seems strange to me. Opening up a console shows a completely different picture.</p>
<p>Things like:<span id="more-4639"></span></p>
<ul>
<li>The visual editor would open a new connection for every new query (oh, so the <strong>@user_defined_variable</strong> I've just assigned turns <strong>NULL</strong>, or the <strong>TEMPORARY TABLE</strong> disappears).</li>
<li>The visual editor will only show <strong>1,000</strong> results, via <strong>LIMIT 0,1000</strong>. "But the same query runs <em>so much faster</em> on my machine!". Well, sure, a <strong>filesort</strong> of <strong>1,000,000</strong> rows that can satisfy the first <strong>1,000</strong> will quit early!</li>
<li>The visual editor shows table definition graphically. "I didn't realize the index did(n't) cover this and that columns. I didn't realize it only covered first <strong>n</strong> characters of my <strong>VARCHAR</strong>.". That's because you can't beat <strong>SHOW CREATE TABLE</strong>, the definite table structure description.</li>
<li>The visual editor allows for export/import/copy/transfer of tables and rows with just one click! "Why is it so complicated in the command line to purge <strong>1,000,000</strong> rows from a table?". Ummm, did you realize the visual editor would typically use a naive approach of doing everything in one huge transaction?</li>
<li>The visual editor is smart. But sometimes you don't want smart. You just <a href="http://www.devart.com/blogs/dbforge/index.php/mind-data-modifications-via-data-editor-in-dbforge-studio-for-mysql.html">assume simple</a>. I personally take great precaution with <em>smart</em> solutions. Luckily, with scripts you have so much greater control (i.e. command line options, "dry-run" mode, etc.) that I have greater confidence in them.</li>
</ul>
<p>I do like it when a visual editor plays it both <em>smart</em> and <em>safe</em>, in such way that before doing its smart work it actually presents you with <em>the query it's going to issue</em>. Which is why I always considered MySQL Query Browser (now replaced by Workbench) to be the visual editor of choice in my classes.</p>
<p>But, at the end of the day, I strongly believe: if you don't know how to do it with command line, you can't really know how it's done.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/mysql-command-line-vs-visual-editors/feed</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>More MySQL foreach()</title>
		<link>http://code.openark.org/blog/mysql/more-mysql-foreach</link>
		<comments>http://code.openark.org/blog/mysql/more-mysql-foreach#comments</comments>
		<pubDate>Fri, 02 Dec 2011 13:55:32 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[common_schema]]></category>
		<category><![CDATA[Hack]]></category>
		<category><![CDATA[INFORMATION_SCHEMA]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[scripts]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=4171</guid>
		<description><![CDATA[In my previous post I've shown several generic use cases for foreach(), a new scripting functionality introduced in common_schema. In this part I present DBA's handy syntax for schema and table operations and maintenance. Confession: while I love INFORMATION_SCHEMA's power, I just hate writing queries against it. It's just so much typing! Just getting the [...]]]></description>
			<content:encoded><![CDATA[<p>In my <a href="http://code.openark.org/blog/mysql/mysql-foreach">previous post</a> I've shown several generic use cases for <a href="http://common-schema.googlecode.com/svn/trunk/common_schema/doc/html/foreach.html"><em>foreach()</em></a>, a new scripting functionality introduced in <a href="http://code.google.com/p/common-schema/" rel="nofollow">common_schema</a>.</p>
<p>In this part I present DBA's handy syntax for schema and table operations and maintenance.</p>
<p>Confession: while I love <strong>INFORMATION_SCHEMA</strong>'s power, I just <em>hate</em> writing queries against it. It's just so much typing! Just getting the list of tables in a schema makes for this heavy duty query:</p>
<blockquote>
<pre>SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='sakila' AND TABLE_TYPE='BASE TABLE';</pre>
</blockquote>
<p>When a join is involved this really becomes a nightmare. I think it's cumbersome, and as result, many do not remember the names and meaning of columns, making for <em>"oh, I need to read the manual all over again just to get that query right"</em>. Anyway, that's my opinion.</p>
<p>A <strong>SHOW TABLES</strong> statement is easier to type, but cannot be integrated into a <strong>SELECT</strong> query (though <a href="http://code.openark.org/blog/mysql/reading-results-of-show-statements-on-server-side">we have a partial solution</a> for that, too), and besides, when filtering out the views, the <strong>SHOW</strong> statement becomes almost as cumbersome as the one on <strong>INFORMATION_SCHEMA</strong>.</p>
<p>Which is why <em>foreach()</em> offers handy shortcuts to common iterations on schemata and tables, as follows:</p>
<h4>Use case: iterate all databases</h4>
<blockquote>
<pre>call foreach(<span style="color: #808000;">'schema'</span>, <span style="color: #003366;">'CREATE TABLE ${schema}.event(event_id INT, msg VARCHAR(128))'</span>);</pre>
</blockquote>
<p>In the above we execute a query on each database. Hmmm, maybe not such a good idea to perform this operation on all databases? Let's filter them:</p>
<h4>Use case: iterate databases by name match</h4>
<blockquote>
<pre>call foreach(<span style="color: #808000;">'schema like wordpress_%'</span>, <span style="color: #003366;">'ALTER TABLE ${schema}.wp_posts MODIFY COLUMN comment_author VARCHAR(96) NOT NULL'</span>);</pre>
</blockquote>
<p>The above will only iterate my WordPress databases (I have several of these), performing an <strong>ALTER</strong> on <strong>wp_posts</strong> for each of those databases.<span id="more-4171"></span></p>
<p>I don't have to quote the <em>like</em> expression, but I can, if I wish to.</p>
<p>I can also use a regular expression match:</p>
<blockquote>
<pre>call foreach(<span style="color: #808000;">'schema ~ /^wordpress_[0-9]+$/'</span>, <span style="color: #003366;">'ALTER TABLE ${schema}.wp_posts MODIFY COLUMN comment_author VARCHAR(96) NOT NULL'</span>);</pre>
</blockquote>
<h4>Use case: iterate tables in a specific schema</h4>
<p>Time to upgrade our <strong>sakila</strong> tables to InnoDB's compressed format. We use <strong>$()</strong>, a synonym for <em>foreach()</em>.</p>
<blockquote>
<pre>call $(<span style="color: #808000;">'table in sakila'</span>, <span style="color: #003366;">'ALTER TABLE ${schema}.${table} ENGINE=InnoDB ROW_FORMAT=COMPRESSED'</span>);</pre>
</blockquote>
<p>The above will iterate on tables in <strong>sakila</strong>. I say <em>tables</em>, since it will avoid iterating views (there is still no specific syntax for views iteration). This is done on purpose, as my experience shows there is very little in common between tables and views when it comes to maintenance and operations.</p>
<h4>Use case: iterate tables by name match</h4>
<p>Here's a interesting scenario: you wish to work on all tables matching some name. The naive approach would be to:</p>
<blockquote>
<pre>SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'wp_posts' AND TABLE_TYPE = 'BASE TABLE'</pre>
</blockquote>
<p><em><strong>Wait!</strong></em> Are you aware this may bring your server down? This query will open all databases at once, opening all <strong>.frm</strong> files (though thankfully not data files, since we only check for name and type).</p>
<p>Here's a better approach:</p>
<blockquote>
<pre>call foreach(<span style="color: #808000;">'table like wp_posts'</span>, <span style="color: #003366;">'ALTER TABLE ${schema}.${table} ENGINE=InnoDB'</span>);</pre>
</blockquote>
<p>(There's now FULLTEXT to InnoDB, so the above can make sense in the near future!)</p>
<p>The good part is that <em>foreach()</em> will look for matching tables <em>one database at a time</em>. It will iterate the list of database, then look for matching tables per database, thereby optimizing the query on <strong>INFORMATION_SCHEMA</strong>.</p>
<p>Here, too, I can use regular expressions:</p>
<blockquote>
<pre>call $(<span style="color: #808000;">'table ~ /^wp_.*$/'</span>, <span style="color: #003366;">'ALTER TABLE ${schema}.${table} ENGINE=InnoDB'</span>);</pre>
</blockquote>
<h4>Conclusion</h4>
<p>This is work in the making, but, as someone who maintains a few productions servers, I've already put it to work.</p>
<p>I'm hoping the syntax is easy to comprehend. I know that since I developed it it must be far more intuitive to myself than to others. I've tried to keep close on common syntax and concepts from various programming languages.</p>
<p>I would like to get as much feedback as possible. I have further ideas and thoughts on the direction <a href="http://code.google.com/p/common-schema/">common_schema</a> is taking, but wish take it in small steps. Your feedback is appreciated!</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/more-mysql-foreach/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>MySQL foreach()</title>
		<link>http://code.openark.org/blog/mysql/mysql-foreach</link>
		<comments>http://code.openark.org/blog/mysql/mysql-foreach#comments</comments>
		<pubDate>Fri, 02 Dec 2011 04:59:03 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[common_schema]]></category>
		<category><![CDATA[Hack]]></category>
		<category><![CDATA[scripts]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=4002</guid>
		<description><![CDATA[A new routine is now available in common_schema, which makes for an easier execution syntax for some operations: foreach(collection_to_iterate_over, queries_to_execute_per_iteration_step); To illustrate what it can do, consider: call foreach('table in sakila', 'ALTER TABLE ${schema}.${table} ENGINE=InnoDB ROW_FORMAT=COMPACT'); call $('schema like shard_%', 'CREATE TABLE ${schema}.messages (id INT)'); call $('2000:2009', 'INSERT IGNORE INTO report (report_year) VALUES (${1})'); $() [...]]]></description>
			<content:encoded><![CDATA[<p>A new routine is now available in <a href="http://code.google.com/p/common-schema/" rel="nofollow">common_schema</a>, which makes for an easier execution syntax for some operations:</p>
<blockquote>
<pre>foreach(<span style="color: #808000;"><em>collection_to_iterate_over</em></span>, <span style="color: #003366;"><em>queries_to_execute_per_iteration_step</em></span>);</pre>
</blockquote>
<p>To illustrate what it can do, consider:</p>
<blockquote>
<pre>call foreach(<span style="color: #808000;">'table in sakila'</span>, <span style="color: #000080;">'ALTER TABLE <strong>${schema}</strong>.<strong>${table}</strong> ENGINE=InnoDB ROW_FORMAT=COMPACT'</span>);

call $(<span style="color: #808000;">'schema like shard_%</span>', <span style="color: #000080;">'CREATE TABLE <strong>${schema}</strong>.messages (id INT)'</span>);

call $(<span style="color: #808000;">'2000:2009'</span>, <span style="color: #000080;">'INSERT IGNORE INTO report (report_year) VALUES (<strong>${1}</strong>)'</span>);</pre>
</blockquote>
<p><strong>$()</strong> stands as a synonym to <em>foreach()</em>. I suspect it should look familiar to web programmers.</p>
<p>The idea for <em>foreach()</em> was introduced by Giuseppe Maxia during a correspondence. At first I was skeptic: this isn't <a href="http://api.jquery.com/jQuery.each/">jQuery</a>; this is SQL. Why would I want to use <em>foreach()</em>?</p>
<p>Then Giuseppe provided some use cases, and as I started thinking about it, I found more and more cases where such a tool might considerably reduce scripting overhead and avoid requiring SQL-fu skills. In fact, I have been using it myself for the past few weeks</p>
<p>I provide examples which I hope will convince the reader as for the simplicity of using such syntax. Showing off the types of input <em>foreach()</em> accepts (query, table search, schema search, set of constants, single or double numbers range), and the types of queries it can execute (single, multiple, using placeholders).</p>
<p>I stress that this is not a replacement for common queries (i.e. does <em>not</em> come to replace your common <strong>SELECT</strong>, <strong>UPDATE</strong>, <strong>DELETE</strong>), but more for working out administrative tasks. Nevertheless, the last example in this post does provide with an interesting insight on possible "normal" use.<span id="more-4002"></span></p>
<h4>Use case: using values from query</h4>
<p>Let's kill all queries running for over <strong>20</strong> seconds:</p>
<blockquote>
<pre>call foreach(<span style="color: #808000;">'SELECT id FROM INFORMATION_SCHEMA.PROCESSLIST WHERE time &gt; 20'</span>, <span style="color: #000080;">'KILL QUERY <strong>${1}</strong>'</span>);</pre>
</blockquote>
<p>The thing I like most about <em>foreach()</em> is that it's self explanatory. Nevertheless, I note:</p>
<ul>
<li>The <strong>KILL</strong> command is executed for each process running for more than <strong>20</strong> seconds (I did round up corners, since I didn't check for sleeping processes, for simplicity).</li>
<li>I also use the <strong>${1}</strong> placeholder: much like in <em>awk</em>, this will get the first column in the result set. In our case, it is the single column, <strong>id</strong>.</li>
<li>I chose to invoke a single query/command per iteration step.</li>
</ul>
<p>Compare the above with another solution to the same problem, using <a href="http://code.openark.org/blog/mysql/mysql-eval">eval()</a>:</p>
<blockquote>
<pre>call eval('SELECT CONCAT(\'KILL QUERY \',id) FROM INFORMATION_SCHEMA.PROCESSLIST WHERE TIME &gt; 20');</pre>
</blockquote>
<p>They both get the same thing done. But <em>foreach()</em> is just a bit more friendly to write (and read).</p>
<p>Let's move to a more complicated example.</p>
<h4>Use case: using multiple values from a query, invoking multiple commands</h4>
<p>Let's kill some queries, as above, but also write down a log entry so that we know what happened:</p>
<blockquote>
<pre>call foreach(
  <span style="color: #808000;">'SELECT id, user FROM INFORMATION_SCHEMA.PROCESSLIST WHERE time &gt; 20'</span>,
  <span style="color: #000080;">'KILL QUERY <strong>${1}</strong>; INSERT INTO my_log VALUES (\'have just killed query <strong>${1}</strong>, executed by <strong>${2}</strong>\');'</span>)
;</pre>
</blockquote>
<p>In the above, for each long running process, we:</p>
<ul>
<li>Kill the process' query. <strong>id</strong> being the first column, is referenced by <strong>${1}</strong>.</li>
<li>INSERT to my_log that said process has been killed. We note both <strong>id</strong> and <strong>user</strong> using placeholders <strong>${1}</strong> and <strong>${2}</strong>, respectively.</li>
</ul>
<p>It's possible to invoke as many queries/commands per iteration step. It is possible to use placeholders <strong>${1}</strong> through <strong>${9}</strong>, as well as <strong>${NR}</strong>, which works as in <em>awk</em>: it is a row-counter, <strong>1</strong>-based.</p>
<p>This example can still be written with <em>eval()</em>, but in much uglier form. I can't just first <strong>KILL</strong> the processes, then log about them, since by the time I want to log, the queries will not be running; the commands <em>must be coupled</em>. This is naturally done with <em>foreach()</em>.</p>
<h4>Use case: iterating constant values, invoking DDL</h4>
<p>The commands invoked by <em>foreach()</em> can take the form of DML (<strong>INSERT</strong>/<strong>UPDATE</strong>/...), DDL (<strong>CREATE</strong>/<strong>ALTER</strong>/...) or other (<strong>KILL</strong>/<strong>SET</strong>/...). The placeholders can be used anywhere within the text.</p>
<p>Take an installation where different schemata have the same exact table structure. We want to refactor a table on all schemata:</p>
<blockquote>
<pre>call $(<span style="color: #808000;">'<strong>{USA, UK, Japan, NZ}</strong>'</span>, <span style="color: #000080;">'ALTER TABLE db_region_<strong>${1}</strong>.customer ADD COLUMN personal_notes VARCHAR(4096) CHARSET utf8'</span>);</pre>
</blockquote>
<p>The above translates to the following commands:</p>
<blockquote>
<pre>ALTER TABLE <strong>db_region_USA</strong>.customer ADD COLUMN personal_notes VARCHAR(4096) CHARSET utf8;
ALTER TABLE <strong>db_region_UK</strong>.customer ADD COLUMN personal_notes VARCHAR(4096) CHARSET utf8;
ALTER TABLE <strong>db_region_Japan</strong>.customer ADD COLUMN personal_notes VARCHAR(4096) CHARSET utf8;
ALTER TABLE <strong>db_region_NZ</strong>.customer ADD COLUMN personal_notes VARCHAR(4096) CHARSET utf8;</pre>
</blockquote>
<p>In the above, we:</p>
<ul>
<li>Provide a list of constant values. These can be strings, numbers, whatever. They are space delimited.</li>
<li>Use the <strong>${1}</strong> place holder. We can also use <strong>${NR}</strong>.</li>
</ul>
<h4>Use case: loop through number sequence</h4>
<p>We wish to populate a table with values:</p>
<blockquote>
<pre>call foreach(<span style="color: #808000;">'<strong>1970:2038</strong>'</span>, <span style="color: #003366;">'INSERT INTO test.test_dates (dt) VALUES (DATE(\'<strong>${1}</strong>-01-01\'))'</span>);</pre>
</blockquote>
<p>The above results with:</p>
<blockquote>
<pre>mysql&gt; SELECT dt FROM test_dates;
+------------+
| dt         |
+------------+
| 1970-01-01 |
| 1971-01-01 |
| 1972-01-01 |
| 1973-01-01 |
| 1974-01-01 |
...
| 2036-01-01 |
| 2037-01-01 |
| 2038-01-01 |
+------------+</pre>
</blockquote>
<p>With numbers range:</p>
<ul>
<li>Integers are assumed</li>
<li>Range is indicated by low and high values, both inclusive</li>
<li>Negatives allowed (e.g. <strong>'-5:5'</strong>, resulting with <strong>11</strong> steps)</li>
<li>Placeholders <strong>${1}</strong> and <strong>${NR}</strong> are allowed.</li>
</ul>
<h4>Use case: iterating through two dimensional numbers range:</h4>
<p>We use <strong>3</strong> template tables; we create <strong>15</strong> schemata; in each we create <strong>3</strong> tables based on the template tables:</p>
<blockquote>
<pre>call foreach(<span style="color: #808000;"><strong> '1:15,1:3'</strong></span>,
  <span style="color: #003366;">'CREATE DATABASE IF NOT EXISTS db_test_${1}; CREATE TABLE db_test_${1}.tbl_${2} LIKE db_template.my_table_${2};'</span>
);</pre>
</blockquote>
<p>Notes:</p>
<ul>
<li>Each of the number ranges has the same restrictions and properties as listed above (integers, inclusive, ascending)</li>
<li>We can now use <strong>${1}</strong> and <strong>${2}</strong> placeholders, noting the first and second numbers range, respectively.</li>
<li>We may also use <strong>${NR}</strong>, which, in this case, will run <strong>1</strong> through <strong>45</strong> (<strong>15</strong> times <strong>3</strong>).</li>
<li>We use multiple queries per iteration step.</li>
</ul>
<h4>Use case: overcoming MySQL limitations</h4>
<p>MySQL does not support <strong>ORDER BY</strong> &amp; <strong>LIMIT</strong> in multi-table <strong>UPDATE</strong> and <strong>DELETE</strong> statements (as noted <a href="http://code.openark.org/blog/mysql/three-wishes-for-a-new-year">last year</a>). So we <em>cannot</em>:</p>
<blockquote>
<pre>DELETE FROM t1 USING t1 JOIN t2 ON (...) JOIN t3 ON (..) WHERE x = 7 ORDER BY ts LIMIT 100;</pre>
</blockquote>
<p>However, we <em>can</em>:</p>
<blockquote>
<pre>call foreach(
  <span style="color: #808000;">'SELECT t1.id FROM t1 JOIN t2 ON (...) JOIN t3 ON (..) WHERE x = 7 ORDER BY ts LIMIT 100'</span>,
  <span style="color: #003366;">'DELETE FROM t1 WHERE id = ${1}'</span>
);</pre>
</blockquote>
<p>Of course, it will do a lot of single row <strong>DELETE</strong>s. There are further MySQL limitations which complicate things if I want to overcome this. Perhaps at a later blog post.</p>
<h4>Acknowledgements</h4>
<p>I hit a weird <a href="http://bugs.mysql.com/bug.php?id=62406">bug</a> which prevented me from releasing this earlier on. Actually it's a duplicate of <a href="http://bugs.mysql.com/bug.php?id=12257">this bug</a>, which makes it <strong>6</strong> years old. Hurray.</p>
<p>To the rescue came <a href="http://rpbouman.blogspot.com/">Roland Bouman</a>, who suggested an idea so crazy even I was skeptic: to parse and modify the original query so as to rename column names according to my scheme. And of course he made it happen, along with some additional very useful stuff. It's really a <em>super-ultra-meta-meta-sql-fu</em> magic he does there.</p>
<p>So, thanks, Roland, for joining the ride, and thanks, Giuseppe, for testing and helping out to shape this functionality. It's great fun working with other people on open-source -- a new experience for me.</p>
<h4>Continued</h4>
<p>In this post I've covered the general-purpose iterations. There are also more specific types of iterations with <em>foreach()</em>. <a href="http://code.openark.org/blog/mysql/more-mysql-foreach">Continued next</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/mysql-foreach/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Test-driven SQL development</title>
		<link>http://code.openark.org/blog/mysql/test-driven-sql-development</link>
		<comments>http://code.openark.org/blog/mysql/test-driven-sql-development#comments</comments>
		<pubDate>Thu, 20 Oct 2011 17:55:04 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Ant]]></category>
		<category><![CDATA[common_schema]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[scripts]]></category>
		<category><![CDATA[Stored routines]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=4036</guid>
		<description><![CDATA[I'm having a lot of fun writing common_schema, an SQL project which includes views, tables and stored routines. As the project grows (and it's taking some interesting directions, in my opinion) more dependencies are being introduced, and a change to one routine or view may affect many others. This is why I've turned the development [...]]]></description>
			<content:encoded><![CDATA[<p>I'm having a lot of fun writing <a href="http://code.google.com/p/common-schema/" rel="nofollow">common_schema</a>, an SQL project which includes views, tables and stored routines.</p>
<p>As the project grows (and it's taking some interesting directions, in my opinion) more dependencies are being introduced, and a change to one routine or view may affect many others. This is why I've turned the development on <em>common_schema</em> to be <em>test driven</em>.</p>
<p>Now, just how do you test drive an SQL project?</p>
<p>Well, much like the way you test any other project in your favorite programming language. If its functions you're testing, that's all too familiar: functions get some input and provide some output. Hmmm, they might be changing SQL data during that time. With procedures it's slightly more complex, since they do not directly return output but result sets.</p>
<p>Here's the testing scheme I use:<span id="more-4036"></span></p>
<ul>
<li>Tests are divided to families. For example, there is a family of tests for the <em>eval()</em> function.</li>
<li>Each test in a family is responsible for checking the simplest, most "atomic" issue. This means many small tests.</li>
<li>Each test can have a <em>"pre-test"</em> step, which prepares the ground (for example, create a table and populate it)</li>
<li>Likewise, a test can have a <em>"post-test"</em> step, which is typically just cleanup code (since the test is already complete by the time the post step is invoked).</li>
<li>Each test is an SQL file: a set of commands to be executed.</li>
<li>A test may have an <em>"expected output"</em> file.</li>
</ul>
<ul>
<li>If no explicit <em>expected</em> exists, the test is expected to return <strong>"1"</strong> (just as the most basic <em>JUnit</em> test assumes an "assert true")</li>
<li>A test family may also have <em>pre-</em> and <em>post-</em> steps.</li>
<li>Any failure in any step fails the entire process. Failures may include:
<ul>
<li>Failure to prepare the grounds for a test or family of tests</li>
<li>Failure in executing the test</li>
<li>Mismatch between test's output and expected result.</li>
<li>Failure in executing the <em>post-</em> step (may indicate yet invalid test result not intercepted by the test)</li>
</ul>
</li>
</ul>
<h4>An example</h4>
<p>The following image presents a single test family: the <em>eval</em> family, testing the <em>eval()</em> routine.</p>
<blockquote><p><a href="http://code.openark.org/blog/wp-content/uploads/2011/10/test-driven-sql-development-01.png"><img class="size-full wp-image-4205 alignnone" title="test-driven-sql-development-01" src="http://code.openark.org/blog/wp-content/uploads/2011/10/test-driven-sql-development-01.png" alt="Test driven SQL development - sample" width="198" height="258" /></a></p></blockquote>
<ul>
<li>In this family, there are two tests.</li>
<li>In both tests, we have a <em>pre-test</em> step, and a test.</li>
<li>We have no <em>post-test</em> here.</li>
<li>Nor do we have an <em>expected-output</em> sample, which means the tests expect to return with <strong>"1"</strong>.</li>
</ul>
<h4>Implementation</h4>
<p>But how are tests conducted? Via <em>mysql</em>, of course. While tests are plain SQL text file, they are being executed against a running MySQL server using the <em>mysql</em> client. It is given the test files as input, and its output is directed to file as well.</p>
<p>This makes it very easy to code the test using a simple shell script. It takes a small measure of file iteration, process invocation, exit code check, and <em>diff</em> execution.</p>
<p>For example, to test <em>eval()</em>'s <strong>01</strong> test, we first execute <em>mysql</em> with <strong>01/pre.sql</strong> as input. Assuming success, we execute <em>mysql</em> again, this time with <strong>01/test.sql</strong>. We capture the output of this execution, and compare it with <em>expected-output</em>, or with <strong>"1"</strong> when no <em>expected-output</em> specified.</p>
<h4>Tests pass, or no code!</h4>
<p>Some <strong>12</strong> years ago, I worked with a less-known version system called <a href="http://aegis.sourceforge.net/documents.html">aegis</a>. The thing I remember most from <em>aegis</em> was that it had a good tests infrastructure. Long before "test-driven development" was coined, or was even commonly practiced, <em>aegis</em> supported tests right into your version control. "Right into", in the sense that <em>you could not merge your code back to the baseline</em> if it didn't pass all of the tests.</p>
<p>I work with SVN for <em>common_schema</em>, and I do not know of such an option in SVN. But I also use <em>ant</em> to build this project, and the dependency is clear there: <strong>ant dist</strong>, my target which creates the distribution files, is dependent on <strong>ant test</strong>, the target which works out the tests.</p>
<p>That is, you cannot generate the distribution files when tests fail.</p>
<h4>Further notes</h4>
<p>Since I'm now retroactively patching tests for already existing functionality, calling it <em>test-driven</em> development is an overstatement; nevertheless new tests are already proving invaluable when I keep changing and improving existing code. Suddenly dependent functionality no longer works as expected. What fun!</p>
<p><a href="http://code.google.com/p/common-schema/source/browse/trunk/common_schema/tests/test_all.sh">The code</a> for the testing suite is actually much shorter than this blog post.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/test-driven-sql-development/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Useful sed / awk liners for MySQL</title>
		<link>http://code.openark.org/blog/mysql/useful-sed-awk-liners-for-mysql</link>
		<comments>http://code.openark.org/blog/mysql/useful-sed-awk-liners-for-mysql#comments</comments>
		<pubDate>Wed, 06 Jul 2011 06:41:00 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Configuration]]></category>
		<category><![CDATA[InnoDB]]></category>
		<category><![CDATA[mysqldump]]></category>
		<category><![CDATA[scripts]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=3685</guid>
		<description><![CDATA[Listing some useful sed / awk liners to use with MySQL. I use these on occasion. sed, awk &#38; grep have many overlapping features. Some simple tasks can be performed by either. For example, stripping empty lines can be performed by either: grep '.' awk '/./' sed '/./!d' grep -v '^$' awk '!/^$/' sed '/^$/d' [...]]]></description>
			<content:encoded><![CDATA[<p>Listing some useful <strong>sed</strong> / <strong>awk</strong> liners to use with MySQL. I use these on occasion.</p>
<p><strong>sed</strong>, <strong>awk</strong> &amp; <strong>grep</strong> have many overlapping features. Some simple tasks can be performed by either. For example, stripping empty lines can be performed by either:</p>
<blockquote>
<pre><strong>grep</strong> '.'
<strong>awk</strong> '/./'
<strong>sed</strong> '/./!d'
<strong>grep</strong> -v '^$'
<strong>awk</strong> '!/^$/'
<strong>sed</strong> '/^$/d'</pre>
</blockquote>
<p>It's a matter of taste &amp; convention which tool and variation to use. So for any script I suggest, there may be many variations, possibly cleaner, shorter; feel free to comment.</p>
<h4>mysqldump</h4>
<p>The output of <em>mysqldump</em> is in particular useful when one wishes to make transformation on data or metadata.<span id="more-3685"></span></p>
<ul>
<li>Convert MyISAM tables to InnoDB:</li>
</ul>
<blockquote>
<pre>mysqldump | sed -e 's/^) ENGINE=MyISAM/) ENGINE=InnoDB/'</pre>
</blockquote>
<p>I've had several occasion when people said this type of conversion assumes no <strong>'ENGINE=MyISAM'</strong> snippet exists within row data. This is not so. The <strong>'^) ENGINE=MyISAM/'</strong> pattern strictly requires that this text is outside row data. No row data begins with a <strong>')'</strong>. This is a safe conversion.</p>
<ul>
<li>Convert InnoDB to InnoDB plugin, compressed tables:</li>
</ul>
<blockquote>
<pre>mysqldump | sed -e 's/^) ENGINE=InnoDB/) ENGINE=InnoDB ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8/'</pre>
</blockquote>
<ul>
<li>Slice out a specific database (assumes existence of the <strong>USE</strong> statement):</li>
</ul>
<blockquote>
<pre>sed -n "/^USE \`employees\`/,/^USE \`/p"</pre>
</blockquote>
<ul>
<li>Slice out a specific table:</li>
</ul>
<blockquote>
<pre>sed -n "/^-- Table structure for table \`departments\`/,/^-- Table structure for table/p"</pre>
</blockquote>
<ul>
<li>Combine the above two statements to slice a specific table from a specific database:</li>
</ul>
<blockquote>
<pre>sed -n "/^USE \`employees\`/,/^USE \`/p" | sed -n "/^-- Table structure for table \`departments\`/,/^-- Table structure for table/p"</pre>
</blockquote>
<p>See also <a rel="bookmark" href="http://code.openark.org/blog/mysql/on-restoring-a-single-table-from-mysqldump">On restoring a single table from mysqldump</a>.</p>
<h4>my.cnf</h4>
<p>Some <em>my.cnf</em> files are just a mess to read. Here's some normalizing scripts:</p>
<ul>
<li>Strip a <em>my.cnf</em> file from comments, remove blank lines, normalize spaces:</li>
</ul>
<blockquote>
<pre>cat my.sandbox.cnf | sed '/^#/d' | sed '/^$/d' | sed -e 's/[ \t]\+//g'</pre>
</blockquote>
<ul>
<li>Same, but only present <strong>[mysqld]</strong> section parameters:</li>
</ul>
<blockquote>
<pre>cat my.sandbox.cnf | sed -n '/^\[mysqld\]/,/^\[/p' | sed '/^\[/d' | sed '/^#/d' | sed '/^$/d' | sed -e 's/[ \t]\+//g'</pre>
</blockquote>
<ul>
<li>Only present <strong>[mysqld]</strong> section parameters, tab delimited (this is useful in exporting and comparing instance parameters):</li>
</ul>
<blockquote>
<pre>cat my.sandbox.cnf | sed -n '/^\[mysqld\]/,/^\[/p' | sed '/^\[/d' | sed '/^#/d' | sed '/^$/d' | sed -e 's/[ \t]\+//g' | sed -e 's/=/\t/'</pre>
</blockquote>
<ul>
<li>Multi-word parameters in <em>my.cnf</em> can be written with either hyphens or underscores. <strong>innodb_file_per_</strong>table is the same as <strong>innodb-file-per-table</strong>, as well as <strong>innodb_file-per_table</strong>. The following normalizes the parameter names to using underscores only, keeping from changing values (e.g. <strong>'mysql-bin' </strong>parameter value should not change). It isn't pretty!</li>
</ul>
<blockquote>
<pre>cat my.sandbox.cnf | awk -F "=" 'NF &lt; 2 {print} sub("=", "=~placeholder~=") {print}' | awk -F "=~placeholder~=" 'NF &lt; 2 {gsub("-", "_", $0); print} NF==2 {gsub("-", "_", $1); print $1 "=" $2}'</pre>
</blockquote>
<div id="_mcePaste" class="mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">grep "."<br />
awk '/./'<br />
sed '/./!d'<br />
grep -v '^$'<br />
awk '!/^$/'<br />
sed '/^$/d'</div>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/useful-sed-awk-liners-for-mysql/feed</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>oak-hook-general-log: your poor man&#039;s Query Analyzer</title>
		<link>http://code.openark.org/blog/mysql/oak-hook-general-log-your-poor-mans-query-analyzer</link>
		<comments>http://code.openark.org/blog/mysql/oak-hook-general-log-your-poor-mans-query-analyzer#comments</comments>
		<pubDate>Wed, 15 Dec 2010 17:46:06 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Analysis]]></category>
		<category><![CDATA[logs]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[openark kit]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[scripts]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=3032</guid>
		<description><![CDATA[The latest release of openark kit introduces oak-hook-general-log, a handy tool which allows for some analysis of executing queries. Initially I just intended for the tool to be able to dump the general log to standard output, from any machine capable to connect to MySQL. Quick enough, I realized the power it brings. With this [...]]]></description>
			<content:encoded><![CDATA[<p>The latest release of <a href="http://code.openark.org/forge/openark-kit">openark kit</a> introduces <a href="http://openarkkit.googlecode.com/svn/trunk/openarkkit/doc/html/oak-hook-general-log.html">oak-hook-general-log</a>, a handy tool which allows for some analysis of executing queries.</p>
<p>Initially I just intended for the tool to be able to dump the general log to standard output, from any machine capable to connect to MySQL. Quick enough, I realized the power it brings.</p>
<p>With this tool, one can dump to standard output all queries using temporary tables; or using a specific index; or doing a full index scan; or just follow up on connections; or... For example, the following execution will only log queries which make for filesort:</p>
<blockquote>
<pre>oak-hook-general-log --user=root --host=localhost --password=123456 --filter-explain-filesort</pre>
</blockquote>
<h4>The problem with using the standard logs</h4>
<p>So you have the <em>general log</em>, which you don't often enable, since it tends to grow huge within moments. You then have the <em>slow log</em>. Slow log is great, and is among the top tools for MySQL diagnosis.</p>
<p>The slow log allows for <strong>log-queries-not-using-indexes</strong>, which is yet another nice feature. Not only should you log any query running for over <strong>X</strong> seconds, but also log any query which does not use an index.</p>
<p>Wait. This logs all single-row tables (no single row table will use an index), as well as very small tables (a common <strong>20</strong> rows lookup table will most often be scanned). These are OK scans. This makes for some noise in the slow log.</p>
<p>And how about queries which do use an index, but do so poorly? They use an index, but retrieve some <strong>12,500,000</strong> rows, <em>using temporary</em> table &amp; <em>filesort</em>?</p>
<h4>What oak-hook-general-log does for you</h4>
<p>This tool streams out the general log, and filters out queries based on their <em>role</em> or on their <em>execution plan</em>.</p>
<p>To work at all, it must enable the general log. Moreover, it directs the general log to log table. Mind that this makes for a performance impact, which is why the tool auto-terminates and restores original log settings (default is <strong>1</strong> minute, configurable). It's really not a tool you should keep running for days. But during the few moments it runs, it will:</p>
<ul>
<li>Routinely rotate the <strong>mysql.general_log</strong> table so that it doesn't fill up</li>
<li>Examine entries found in the general log</li>
<li>Cross reference entries to the PROCESSLIST so as to deduce database context (<a href="http://bugs.mysql.com/bug.php?id=52554">bug #52554</a>)</li>
<li>If required and appropriate, evaluate a query's execution plan</li>
<li>Decide whether to dump each entry based on filtering rules</li>
</ul>
<h4>Filtering rules</h4>
<p>Filtering rules are passed as command line options. At current, only one filtering rule applies (if more than one specified only one is used, so no point in passing more than one). Some of the rules are:<span id="more-3032"></span></p>
<ul>
<li><strong>filter-connection</strong>: only log connect/quit entries</li>
<li><strong>filter-explain-fullscan</strong>: only log full table scans</li>
<li><strong>filter-explain-temporary</strong>: only log queries which create implicit temporary tables</li>
<li><strong>filter-explain-rows-exceed</strong>: only log queries where more than <strong>X</strong> number of rows are being accessed on some table (estimated)</li>
<li><strong>filter-explain-total-rows-exceed</strong>: only log queries where more than <strong>X</strong> number of rows are accessed on all tables combined (estimated, with possibly incorrect numbers on some queries)</li>
<li><strong>filter-explain-key</strong>: only log queries using a specific index. This feature somewhat overlaps with Maatkit's <em>mk-index-usage</em> (read <a href="http://www.mysqlperformanceblog.com/2010/11/11/advanced-index-analysis-with-mk-index-usage/">announcement</a>).</li>
<li><strong>filter-explain-contains</strong>: a general purpose <em>grep</em> on the execution plan. Log queries where the execution plan contains <em>some text</em>.</li>
</ul>
<p>There are other filters, and I will possibly add more in due time.</p>
<p>Here are a couple cases I used <em>oak-hook-general-log</em> for:</p>
<h4>Use case: temporary tables</h4>
<p>I have a server with this alarming chart (courtesy <a href="http://code.openark.org/forge/mycheckpoint">mycheckpoint</a>) of temporary tables:</p>
<blockquote>
<pre><img class="alignnone" title="Created tmp tables per second" src="http://chart.apis.google.com/chart?cht=lc&amp;chs=370x180&amp;chts=303030,12&amp;chtt=Latest+24+hours:+Dec+9,+06:30++-++Dec+10,+06:30&amp;chf=c,s,ffffff&amp;chdl=created_tmp_tables_psec|created_tmp_disk_tables_psec&amp;chdlp=b&amp;chco=ff8c00,4682b4&amp;chd=s:yzzy02zzz100zzz0rv9zz0zyzyz0yy2xz1t11xzztz0xr1xt2tz07vwzz100100z31z111yz1vzzzzz1zs80r902s1111010y20z03z11487zz011z11011002w0q5rxxz0y00z0s02xy1yy0,gggfghggfgggghhgYekhhghhhhhghfjghhdihfhgdghgZhgcicihpcehhhhhhhifkigjihghjehgiigjgYqiYqgiaihiifkhekhfijgiihhggggggggggfhgghffZoYgggggggggdihfggghg&amp;chxt=x,y&amp;chxr=1,0,35.060000&amp;chxl=0:||08:00||+||12:00||+||16:00||+||20:00||+||00:00||+||04:00||+|&amp;chxs=0,505050,10,0,lt&amp;chg=4.17,25,1,2,2.08,0&amp;chxp=0,2.08,6.25,10.42,14.59,18.76,22.93,27.10,31.27,35.44,39.61,43.78,47.95,52.12,56.29,60.46,64.63,68.80,72.97,77.14,81.31,85.48,89.65,93.82,97.99&amp;tsstart=2010-12-09+06:30:00&amp;tsstep=600" alt="" width="370" height="180" />
</pre>
</blockquote>
<p>What could possibly create <strong>30</strong> temporary tables per second on average?</p>
<p>The slow log produced nothing helpful, even with <strong>log-queries-not-using-indexes</strong> enabled. There were a lot of queries not using indexes there, but nothing at these numbers. With:</p>
<blockquote>
<pre>oak-hook-general-log --filter-explain-temporary</pre>
</blockquote>
<p>enabled for <strong>1</strong> minute, nothing came out. Weird. Enabled for <strong>5</strong> minutes, I got one entry. Turned out a scheduled script, acting once per <strong>5</strong> minutes, was making a single complicated query involving many nested views, which accounted for some <em>hundreds</em> of temporary tables created. All of them very small, query time was very fast. There is no temporary tables problem with this server, case closed.</p>
<h4>Use case: connections</h4>
<p>A server had issues with some exceptions being thrown on the client side. There was a large number of new connections created per second although the client was using a connection pool. Suspecting the pool didn't work well, I issued:</p>
<blockquote>
<pre>oak-hook-general-log --filter-connect</pre>
</blockquote>
<p>The pool was working well, all right. No entries for that client were recorder in <strong>1</strong> minute of testing. However, it turned out some old script was flooding the MySQL server with requests, every second. The log showed root@somehost, and sure enough, the script was disabled. Exceptions were due to another reason; it was good to eliminate a suspect.</p>
<p>Some of the tool's use case is relatively easy to solve with tail, grep &amp; awk; others are not. I am using it more and more often, and find it to make significant shortcuts in tracking down queries.</p>
<h4>Get it</h4>
<p>Download the tool as part of <em>openark kit</em>: access the <a href="http://code.google.com/p/openarkkit/">openark kit project page</a>.</p>
<p>Or get the <a href="http://openarkkit.googlecode.com/svn/trunk/openarkkit/src/oak/oak-hook-general-log.py">source code</a> directly.</p>
<p>Feedback is most welcome.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/oak-hook-general-log-your-poor-mans-query-analyzer/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>openark-kit (rev. 170): new tools, new functionality</title>
		<link>http://code.openark.org/blog/mysql/openark-kit-rev-170-new-tools-new-functionality</link>
		<comments>http://code.openark.org/blog/mysql/openark-kit-rev-170-new-tools-new-functionality#comments</comments>
		<pubDate>Wed, 15 Dec 2010 06:31:24 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Analysis]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[openark kit]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[Replication]]></category>
		<category><![CDATA[scripts]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=3124</guid>
		<description><![CDATA[I'm pleased to announce a new release of the openark kit. There's a lot of new functionality inside; following is a brief overview. The openark kit is a set of utilities for MySQL. They solve everyday maintenance tasks, which may be complicated or time consuming to work by hand. It's been a while since the [...]]]></description>
			<content:encoded><![CDATA[<p>I'm pleased to announce a new release of the <a href="http://code.openark.org/forge/openark-kit">openark kit</a>. There's a lot of new functionality inside; following is a brief overview.</p>
<p>The <em>openark kit</em> is a set of utilities for MySQL. They  solve everyday maintenance tasks, which may be complicated or time  consuming to work by hand.</p>
<p>It's been a while since the last announced release. Most of my attention was on <a href="http://code.openark.org/forge/mycheckpoint">mycheckpoint</a>, building new features, writing documentation etc. However my own use of <em>openark kit</em> has only increased in the past few months, and there's new useful solutions to common problems that have been developed.</p>
<p>I've used and improved many tools over this time, but doing the final cut, along with proper documentation, took some time. Anyway, here are the highlights:</p>
<h4>New tool: oak-hook-general-log</h4>
<p><em>oak-hook-general-log</em> hooks up a MySQL server and dumps the general log based on filtering rules, applying to query role or execution plan. It is possible to only dump connect/disconnect entries, queries which make a full table scan, or use temporary tables, or scan more than X number of rows, or...</p>
<p>I'll write more on this tool shortly.</p>
<h4>New tool: oak-prepare-shutdown</h4>
<p>This tool makes for an orderly and faster shutdown by safely stopping replication, and flushing InnoDB pages to disk prior to shutting down (keeping server available for connections even while attempting to flush dirty pages to disk). A typical use case would be:</p>
<blockquote>
<pre>oak-prepare-shutdown --user=root --ask-pass --socket=/tmp/mysql.sock &amp;&amp; /etc/init.d/mysql stop</pre>
</blockquote>
<h4>New tool: oak-repeat query</h4>
<p><em>oak-repeat-query</em> repeats executing a given query until some condition holds. The condition can be:</p>
<ul>
<li>Number of given iterations has been reached</li>
<li>Given time has elapsed</li>
<li>No rows have been affected by query</li>
</ul>
<p>The tool comes in handy for cleanup jobs, warming up caches, etc.<span id="more-3124"></span></p>
<h4>New tool: oak-get-slave-lag</h4>
<p>This simple tool just returns the number of seconds a slave is behind master. But it also returns with an appropriate exit code, based on a given threshold: <strong>0</strong> when lag is good, <strong>1</strong> (error exit code) when lag is too great or slave fails to replicate.</p>
<p>This tool has been used by 3rd party applications, such as a load balancer, to determine whether a slave should be accessed.</p>
<h4>Updated tool: oak-chunk-update</h4>
<p>This extremely useful utility breaks down very long queries into smaller chunks. These could be queries which should affect a huge amount of rows, or queries which cannot utilize an index.</p>
<p>Updates to the tool include limiting the range of rows the tool scans, by specifying start and stop position (either by providing constant values or by SELECT query). Also added is auto-termination when no rows are found to be affected. Last, it is possible to override INFORMATION_SCHEMA lookup by explicitly specifying chunking key.</p>
<p>This tool works great for your daily/weekly/monthly batch jobs; in creating DWH tables; populating new columns; purging old entries; clearing data based on non-indexed values; generating summary tables; and more.</p>
<h4>Frozen tool: oak-apply-ri</h4>
<p>I haven't been using this tool for a while. The main work down by this tool can be done with <em>oak-chunk-update</em>. There are some additional safety checks <em>oak-apply-ri</em> provides; I'm thinking over if they justify the tool's existence.</p>
<h4>Frozen tool: oak-online-alter-table</h4>
<p>With the appearance of Facebook’s <a href="http://www.facebook.com/note.php?note_id=430801045932">Online Schema Change</a> (OSC) tool, which derives from <em>oak-online-alter-table</em>, I'm not sure I will continue developing the tool. I intend to wait for general feedback on OSC before making a decision.</p>
<h4>Documentation</h4>
<p><a href="http://openarkkit.googlecode.com/svn/trunk/openarkkit/doc/html/introduction.html">Documentation</a> is now part of <em>openark kit</em>'s SVN repository.</p>
<h4>Download</h4>
<p>The <em>openark kit</em> project is currently hosted by Google Code.  Downloads are available at the Google Code <a href="http://code.google.com/p/openarkkit/">openark kit project page</a>.</p>
<p>Downloads are available in the following packaging formats:</p>
<ul>
<li><strong>.deb</strong> package, to be installed on <em>debian</em>, <em>ubuntu</em> and otherwise debian based distributions.</li>
<li><strong>.rpm</strong> package, architecture free (<em>noarch</em>), for RPM supporting Linux distributions such as <em>RedHat</em>, <em>Fedora</em>, <em>CentOS</em> etc.</li>
<li><strong>.tar.gz</strong> using python's distutils installer.</li>
<li><strong>source</strong>, directly retrieved from SVN or from above python package.</li>
<li>Some distribution specific <a href="http://software.opensuse.org/search?baseproject=ALL&amp;p=1&amp;q=openark-kit">RPM packages</a>, courtesy Lenz Grimmer.</li>
</ul>
<h4>Feedback</h4>
<p>Your feedback is welcome! I may not always respond promptly; and I confess that some bugs were left open for more than I would have liked them to. I hope to make for good quality of code, and bug reporting is one major factor you can control.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/openark-kit-rev-170-new-tools-new-functionality/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>mycheckpoint (rev 208): aggregation tables, enhanced charting, RPM distribution</title>
		<link>http://code.openark.org/blog/mysql/mycheckpoint-rev-208-aggregation-tables-enhanced-charting-rpm-distribution</link>
		<comments>http://code.openark.org/blog/mysql/mycheckpoint-rev-208-aggregation-tables-enhanced-charting-rpm-distribution#comments</comments>
		<pubDate>Mon, 08 Nov 2010 10:45:45 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Monitoring]]></category>
		<category><![CDATA[mycheckpoint]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[scripts]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=3066</guid>
		<description><![CDATA[Revision 208 of mycheckpoint, a MySQL monitoring solution, has been released. New and updated in this revision: Aggregation tables: aggregated data makes for fast reports on previously slow queries. Enhanced charting: interactive charts now present time stamps dynamically (see demo); "Zoom in" charts are available (see demo) on mycheckpoint's HTTP server. RPM distribution: a "noarch" [...]]]></description>
			<content:encoded><![CDATA[<p>Revision <strong>208</strong> of <a href="../../forge/mycheckpoint">mycheckpoint</a>, a MySQL monitoring solution, has  been released. New and updated in this revision:</p>
<ul>
<li><strong>Aggregation tables</strong>: aggregated data makes for fast reports on previously slow queries.</li>
<li><strong>Enhanced charting</strong>: interactive charts now present time stamps dynamically (see <a href="http://mycheckpoint.googlecode.com/svn/trunk/doc/html/sample/http/mcp_sql00/sv_report_html_brief"><strong>demo</strong></a>); "Zoom in" charts are available (see <a href="http://mycheckpoint.googlecode.com/svn/trunk/doc/html/sample/http/mcp_sql00/zoom/questions"><strong>demo</strong></a>) on <em>mycheckpoint</em>'s HTTP server.</li>
<li><strong>RPM distribution</strong>: a "noarch" RPM <em>mycheckpoint</em> build is now available.</li>
<li>Initial work on formalizing test environment</li>
</ul>
<p><em>mycheckpoint</em> celebrates one year of existence!</p>
<h4>Aggregation tables</h4>
<p>I really wanted to avoid using these: everything was so more beautiful with one single dataset and dozens of supporting views (OK, the views themselves are hardly "beautiful").</p>
<p>However it was impossible (for my level of expertise) to optimize query performance what with all those views on per-hour and per-day aggregation. The GROUP BYs and the JOINs did not make it possible for condition pushdown (i.e. using MERGE algorithm) where desired.</p>
<p>As result, mycheckpoint now manages aggregation tables: per-hour and per-day. The impact on sample taking is neglect able (making for two additional fast queries), but the impact on reading aggregated data is overwhelming. Generating a HTML full report could take a few minutes to complete. It now returns in no time. This makes charting more attractive, and allows for enhanced charting, such as zooming in on charts, as described following.</p>
<p>Aggregation tables will automatically be created and retroactively populated upon using revision 208. There's nothing special to do; be advised that for one single execution of <em>mycheckpoint</em>, many INSERT queries are going to be executed. Shouldn't take more than a couple minutes on commodity hardware and a few months of history.</p>
<p>It is possible to disable aggregation tables, or make for a complete rebuild of tables; by default, though, aggregation is ON.</p>
<h4>Enhanced charting</h4>
<p>Two enhancements here:<span id="more-3066"></span></p>
<ol>
<li>The interactive line charts already know how to update legend data as mouse hovers over them. Now they also present accurate date &amp; time. This provides with fully informative charts.</li>
<li>As with other monitoring tools, it is possible to "zoom in" on a chart: zooming in will present any chart in "last 24 hours", "last 10 days" and "complete history" views, magnified on screen. See <a href="http://mycheckpoint.googlecode.com/svn/trunk/doc/html/sample/http/mcp_sql00/zoom/questions"><strong>demo</strong></a> here.</li>
</ol>
<h4>RPM distribution</h4>
<p>No excuse for this being so late, I know. But RPM distribution is now <a href="http://code.google.com/p/mycheckpoint/">available</a>. Yeepee!</p>
<p>This is a <em>noarch</em> distribution, courtesy of Python's <a href="http://docs.python.org/distutils/">distutils</a>; you should be able to install the package on any RPM supporting platform. I have only tested in on CentOS; feedback is welcome.</p>
<h4>Future plans</h4>
<p>Work is going on. These are the non-scheduled future tasks I see:</p>
<ul>
<li>Monitoring InnoDB Plugin &amp; XtraDB status.</li>
<li>A proper <em>man</em> page.</li>
<li>Anything else that interests me &amp; the users.</li>
</ul>
<h4>Try it out</h4>
<p>Try out <em>mycheckpoint</em>. It’s a different kind of monitoring    solution. Simple monitoring (charting) is immediate. For more  interesting results you will need basic SQL skills, and in return you’ll  get a lot   of power under your hands.</p>
<ul>
<li>Download mycheckpoint <a href="https://code.google.com/p/mycheckpoint/">here</a></li>
<li>Visit the project’s <a href="../../forge/mycheckpoint">homepage</a></li>
<li>Browse the <a href="../../forge/mycheckpoint/documentation">documentation</a></li>
<li>Report <a href="https://code.google.com/p/mycheckpoint/issues/list">bugs</a></li>
</ul>
<p><em>mycheckpoint</em> is released under the <a href="http://www.opensource.org/licenses/bsd-license.php">New BSD  License</a>.</p>
<p>Umm, I'll repeat this last one: <em>mycheckpoint</em> is released under the <a href="http://www.opensource.org/licenses/bsd-license.php">New BSD  License</a>. Still, and will continue to be. Thanks for the <a href="http://code.openark.org/blog/mysql/openark-kit-facebook-online-schema-change-and-thoughts-on-open-source-licenses#comments">good advice</a> by Lenz, Domas and others.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/mycheckpoint-rev-208-aggregation-tables-enhanced-charting-rpm-distribution/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Thoughts and ideas for Online Schema Change</title>
		<link>http://code.openark.org/blog/mysql/thoughts-and-ideas-for-online-schema-change</link>
		<comments>http://code.openark.org/blog/mysql/thoughts-and-ideas-for-online-schema-change#comments</comments>
		<pubDate>Thu, 07 Oct 2010 08:29:10 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Indexing]]></category>
		<category><![CDATA[INFORMATION_SCHEMA]]></category>
		<category><![CDATA[InnoDB]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[openark kit]]></category>
		<category><![CDATA[Opinions]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[Schema]]></category>
		<category><![CDATA[scripts]]></category>
		<category><![CDATA[Triggers]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=3005</guid>
		<description><![CDATA[Here's a few thoughts on current status and further possibilities for Facebook's Online Schema Change (OSC) tool. I've had these thoughts for months now, pondering over improving oak-online-alter-table but haven't got around to implement them nor even write them down. Better late than never. The tool has some limitations. Some cannot be lifted, some could. [...]]]></description>
			<content:encoded><![CDATA[<p>Here's a few thoughts on current status and further possibilities for Facebook's <a href="http://www.facebook.com/note.php?note_id=430801045932">Online Schema Change</a> (OSC) tool. I've had these thoughts for months now, pondering over improving <a href="../../forge/openark-kit/oak-online-alter-table">oak-online-alter-table</a> but haven't got around to implement them nor even write them down. Better late than never.</p>
<p>The tool has some limitations. Some cannot be lifted, some could. Quoting from the <a href="http://www.facebook.com/notes/mysql-at-facebook/online-schema-change-for-mysql/430801045932">announcement</a> and looking at the code, I add a few comments. I conclude with a general opinion on the tool's abilities.</p>
<h4>"The original table must have PK. Otherwise an error is returned."</h4>
<p>This restriction could be lifted: it's enough that the table has a UNIQUE KEY. My original <em>oak-online-alter-table</em> handled that particular case. As far as I see from their code, the Facebook code would work just as well with any unique key.</p>
<p>However, this restriction is of no real interest. As we're mostly interested in InnoDB tables, and since any InnoDB table <em>should have</em> a PRIMARY KEY, we shouldn't care too much.</p>
<h4>"No foreign keys should exist. Otherwise an error is returned."</h4>
<p>Tricky stuff. With <em>oak-online-alter-table</em>, changes to the original table were immediately reflected in the <em>ghost</em> table. With InnoDB tables, that meant same transaction. And although I never got to update the text and code, there shouldn't be a reason for not using child-side foreign keys (the child-side is the table on which the FK constraint is defined).</p>
<p>The Facebook patch works differently: it captures changes and writes them to a <strong>delta</strong> table,  to be later (asynchronously) analyzed and make for a <em>replay</em> of actions on the <em>ghost</em> table.<span id="more-3005"></span></p>
<p>So in the Facebook code, some cases will lead to undesired behavior. Consider two tables, <strong>country</strong> and <strong>city</strong>, with city holding a RESTRICT/NO ACTION foreign key on <strong>country</strong>'s id. Now consider the scenario:</p>
<ol>
<li>Rows from <strong>city</strong> are DELETEd, where the country Id is Spain's.
<ul>
<li><strong>city</strong>'s ghost table is still unaffected, Spain's cities are still there.</li>
<li>A change is written to the delta table to mark these rows for deletion.</li>
</ul>
</li>
<li>A DELETE is issued on <strong>country</strong>'s Spain record.
<ul>
<li>The DELETE should work, from the user's perspective</li>
<li>But it will fail: city's ghost table has not received the changes yet. There's still matching rows. The NO ACTION constraint will fail the DELETE statement.</li>
</ul>
</li>
</ol>
<p>Now, this does not lead to corruption, just to seemingly unreasonable behavior on the database part. This behavior is probably undesired. NO ACTION constraint won't do.</p>
<p>However, with CASCADE or SET NULL options, there is less of an issue: operations on the parent table (e.g. <strong>country</strong>) cannot fail. We must make sure operations on the ghost table make it consistent with the original table (e.g. <strong>city</strong>).</p>
<p>Consider the following scenario:</p>
<ol>
<li>A new country is created, called "Sleepyland". An INSERT is made to <strong>country</strong>.
<ul>
<li>Both <strong>city</strong> and <strong>city</strong>'s ghost are immediately aware of it.</li>
</ul>
</li>
<li>A new town is created and INSERTed to <strong>city</strong>. The town is called "Naphaven".
<ul>
<li>The change takes time to propagate to <strong>city</strong>'s ghost table.</li>
</ul>
</li>
<li>Meanwhile, we realized we made a mistake. We've been had. There's no such city nor country.
<ol>
<li>We DELETE "Naphaven" from <strong>city</strong>.</li>
<li>We DELETE "Sleepyland" from <strong>country</strong>.</li>
</ol>
<ul>
<li>Note that <strong>city</strong>'s ghost table still hasn't caught up with the changes.</li>
</ul>
</li>
<li>Eventually, the INSERT statement for "Naphaven" reaches <strong>city</strong>'s ghost table.
<ul>
<li>What should happen now? The INSERT cannot succeed.</li>
<li>Will this fail the entire process?</li>
</ul>
</li>
</ol>
<p>Looking at the PHP code, I see that changes written on the <strong>delta</strong> table are blindly replayed on the ghost table.</p>
<p>Since the process is asynchronous, this should not be the case. We can solve the above if we use INSERT IGNORE instead of INSERT. The statement will fail without failing anything else. The row cannot exist, and that's because the original row does not exist anymore.</p>
<p>Unlike a replication corruption, this does not lead to accumulation mistakes. The <strong>replay</strong> is static, somewhat like in <em>binary log format</em>. Changes are <em>just written</em>, regardless of existing data.</p>
<p>I have given this considerable thought, and I can't say I've covered all the possible scenario. However I believe that with proper use of INSERT IGNORE and REPLACE INTO (two statements I heavily relied on with <em>oak-online-alter-table</em>), correctness can be achieved.</p>
<p>There's the small pain of re-generating the foreign key definition on the "ghost" table (<strong>CREATE TABLE LIKE ...</strong> does not copy FK definitions). And since foreign key names are unique, a new name must be picked up. Not pretty, but perfectly doable.</p>
<h4>"No AFTER_{INSERT/UPDATE/DELETE} triggers must exist."</h4>
<p>It would be nicer if MySQL had an ALTER TRIGGER statement. There isn't such statement. If there were such an atomic statement, then we would be able to rewrite the trigger, so as to add our own code to the <em>end of the trigger's code</em>. Yuck. Would be even nicer if we were <a href="http://code.openark.org/blog/mysql/triggers-use-case-compilation-part-ii">allowed to have multiple triggers</a> of same event.</p>
<p>So, we are left with DROP and CREATE triggers. Alas, this makes for a short period where the trigger does not exist. Bad. The easy solution would be to LOCK WRITE the table, but apparently you can't DROP the trigger (*) when the table is locked. Sigh.</p>
<p>(*) Happened to me, apparently to Facebook too; With latest 5.1 (5.1.51) version this actually works. With 5.0 it didn't use to; this needs more checking.</p>
<h4>Use of INFORMATION_SCHEMA</h4>
<p>As with oak-online-alter-table, the OSC checks for triggers, indexes, column by searching on the INFORMATION_SCHEMA tables. This makes for nice SQL for getting the exact listing and types of PRIMARY KEY columns, whether or not AFTER triggers exist, and so on.</p>
<p>I've always considered this to be the weak part of <a href="http://code.openark.org/forge/openark-kit">openark-kit</a>, that it relies on INFORMATION_SCHEMA so much. It's easier, it's cleaner, it's even <em>more correct</em> to work that way -- but it just puts too much locks. I think Baron Schwartz (and now Daniel Nichter) did amazing work on analyzing table schemata by parsing the SHOW CREATE TABLE and other SHOW commands regex-wise with <a href="http://www.maatkit.org/">Maatkit</a>. It's a crazy work! Had I written <em>openark-kit</em> in Perl, I would have just import their code. But I'm too <span style="text-decoration: line-through;">lazy</span> busy to do the conversion from Perl to Python, and rewrite that code, what with all the debugging.</p>
<p>OSC is written in PHP. Again, much conversion work. I think performance-wise this is an important step to make.</p>
<h4>A word for the critics</h4>
<p>Finally, a word for the critics. I've read some Facebook/MySQL bashing comments and wish to relate.</p>
<p>In his <a href="http://www.theregister.co.uk/2010/09/21/facebook_online_schema_change_for_mysql/">interview to The Register</a>, Mark Callaghan gave the example that "Open Schema Change lets the company update indexes without user downtime, according to Callaghan".</p>
<p>PostgreSQL was mentioned for being able to add index with only read locks taken, or being able to do the work with no locks using CREATE INDEX CONCURRENTLY. I wish MySQL had that feature! Yes, MySQL has a lot to improve upon, and the latest PostgreSQL 9.0 brings valuable new features. (Did I make it clear I have no intention of bashing PostgreSQL? If not, please re-read this paragraph until convinced).</p>
<p>Bashing related to the notion of MySQL being so poor that Facebook used an even poorer mechanism to work out the ALTER TABLE.</p>
<p>Well, allow me to add a few words: the CREATE INDEX is by far not the only thing you can achieve with OSC (although it may be Facebook's major concern). You should be able to:</p>
<ul>
<li>Add columns</li>
<li>Drop columns</li>
<li>Convert character sets</li>
<li>Modify column types</li>
<li>Add partitioning</li>
<li>Reorganize partitioning</li>
<li>Compress the table</li>
<li>Otherwise changing table format</li>
<li>Heck, you could even modify the storage engine! (To other transactional engine)</li>
</ul>
<p>These are giant steps. How easy would it be to write these down into the database? It only takes a few weeks time to work out a working solution with reasonable limitations, just using the resources the MySQL server provides you with. The <a href="http://www.facebook.com/MySQLatFacebook">MySQL@Facebook team</a> should be given credit for that.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/thoughts-and-ideas-for-online-schema-change/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>mycheckpoint (rev. 190): HTTP server; interactive charts</title>
		<link>http://code.openark.org/blog/mysql/mycheckpoint-rev-190-http-server-interactive-charts</link>
		<comments>http://code.openark.org/blog/mysql/mycheckpoint-rev-190-http-server-interactive-charts#comments</comments>
		<pubDate>Tue, 07 Sep 2010 05:53:01 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Monitoring]]></category>
		<category><![CDATA[mycheckpoint]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[scripts]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=2866</guid>
		<description><![CDATA[Revision 190 of mycheckpoint, a MySQL monitoring solution, has been released. New and updated in this revision: HTTP server: mycheckpoint can now act as a web server. Point your browser and start browsing through HTML reports. See mock up demo. Interactive charts: HTML line charts are now interactive, presenting with accurate data as you move [...]]]></description>
			<content:encoded><![CDATA[<p>Revision <strong>190</strong> of <a href="../../forge/mycheckpoint">mycheckpoint</a>, a MySQL monitoring solution, has  been released. New and updated in this revision:</p>
<ul>
<li><strong>HTTP server</strong>: <em>mycheckpoint</em> can now act as a web server. Point your browser and start browsing through HTML reports. See mock up <a href="http://code.openark.org/forge/wp-content/uploads/2010/09/r190/mcp_sql00/sv_report_html_brief.html"><strong>demo</strong></a>.</li>
<li><strong>Interactive charts</strong>: HTML line charts are now interactive, presenting with accurate data as you move over them. See <a href="http://code.openark.org/forge/wp-content/uploads/2010/09/r190/mcp_sql00_samples/sv_report_html_brief.html"><strong>sample</strong></a>.</li>
<li><strong>Enhanced auto-deploy</strong>: now auto-recognizing failed upgrades.</li>
<li><strong>Reduced footprint</strong>: much code taken out of the views, leading to faster loading times.</li>
<li><strong>Better configuration file use</strong>: now supporting all command line options in config file.</li>
<li><strong>Remote host monitoring accessibility</strong>: now supporting complete configurable accessibility details.</li>
<li><strong>Bug fixes</strong>: thanks to the bug reporters!</li>
</ul>
<p><em>mycheckpoint</em> is free, simple, easy to use (now easier with HTTP server) and <strong>useful</strong>. I encourage you to try it out: even compared with other existing and emerging monitoring tools, I believe you will find it a breeze; it's low impact and lightness appealing; it's alerts mechanism assuring; its geeky SQL-based nature with ability to drill down to fine details -- geeky-kind-of-attractive.</p>
<p>&lt;/encouragement&gt;</p>
<h4>HTTP server</h4>
<p>You can now run <em>mycheckpoint</em> in <em>http</em> mode:</p>
<blockquote>
<pre>bash$ <strong>mycheckpoint http</strong></pre>
</blockquote>
<p><em>mycheckpoint</em> will listen on port <strong>12306</strong>, and will present you with easy browsing through the reports of your <em>mycheckpoint</em> databases.<span id="more-2866"></span></p>
<p>The <em>http</em> server automatically detects those schemata used by mycheckpoint, and utilizes the existing HTML views, integrating them into the greater web framework.</p>
<p>While in <em>http</em> mode, mycheckpoint does nothing besides serving web pages. It does not actively exercise monitoring: you must still use the usual cron jobs or other scheduled tasks by which you invoke <em>mycheckpoint</em> for monitoring.</p>
<p>The http server is directed at a single MySQL server, as with the following example:</p>
<blockquote>
<pre>bash$ <strong>mycheckpoint --host=slave1.localdomain --port=3306 --http-port=12306 http</strong></pre>
</blockquote>
<p>It is assumed that this server has the monitoring schemata.</p>
<p>See mock up <a href="http://code.openark.org/forge/wp-content/uploads/2010/09/r190/mcp_sql00/sv_report_html_brief.html"><strong>demo</strong></a>. The demo uses presents with real output from a mycheckpoint HTTP server; I haven't got the means to put up a live demo.</p>
<h4>Interactive charts</h4>
<p>The <em>openark line charts</em>, used in the HTML reports, are now interactive. As you scroll over, the legend presents you with series values.</p>
<p>No more <em>"I have this huge spike once every 4 hours, which reduces all other values to something that looks like zero but is actually NOT"</em>. Hover, and see the real values.</p>
<p>See <a href="http://code.openark.org/forge/wp-content/uploads/2010/09/r190/mcp_sql00_samples/sv_report_html_brief.html"><strong>sample</strong></a>.</p>
<h4>Enhanced auto-deploy</h4>
<p>The idea with mycheckpoint is that it should know how to self upgrade the schema on version upgrade (much like automatic WordPress upgrades). mycheckpoint does bookkeeping of installed versions within the database, and upgrades by simple comparison.</p>
<p>It now, following a couple of reported bugs, also recognizes failure of partial, failed upgrades. This adds to the automation of <em>mycheckpoint</em>'s installation.</p>
<h4>Reduced footprint</h4>
<p>Some of <em>mycheckpoint</em>'s views are complicated, and lead to a large amount of code in view declaration. This leads to increased table definition size (large <strong>.frm</strong> files). There has been some work to reduce this size where possible. Work is still ongoing, but some 30% has been taken off already. This leads to faster table (view) load time.</p>
<h4>Better configuration file use</h4>
<p>Any argument supported on the command line is now also supported in the config style. Much like is handled with MySQL. For example, one can issue:</p>
<blockquote>
<pre>mycheckpoint --monitored-host=sql02.mydb.com  --monitored-user=monitor --monitored-password=123456</pre>
</blockquote>
<p>But now also:</p>
<blockquote>
<pre>mycheckpoint</pre>
</blockquote>
<p>With the following in <strong>/etc/mycheckpoint.cnf</strong>:</p>
<blockquote>
<pre>[mycheckpoint]
monitored_host     = sql02.mydb.com
monitored_user     = monitor
monitored_password = 123456
</pre>
</blockquote>
<p>Rules are:</p>
<ul>
<li>If an option is specified on command line, it takes precedence over anything else.</li>
<li>Otherwise, if it's specified in the configuration file, value is read from file.</li>
<li>Otherwise use default value is used.</li>
<li>On command line, option format is<strong> xxx-yyy-zzz</strong>: words split with dash/minus character.</li>
<li>On configuration file, option format is <strong>xxx_yyy_zzz</strong>: words split with underscore. Unlike MySQL configuration format, dashes cannot be used.</li>
<li>If an option is specified multiple times on configuration file -- well -- I have the answer, but I won't tell. Just don't do it. It's bad for your health.</li>
</ul>
<h4>Future plans</h4>
<p>Work is going on. These are the non-scheduled future tasks I see:</p>
<ul>
<li>Monitoring InnoDB Plugin &amp; XtraDB status.</li>
<li>A proper <em>man</em> page.</li>
<li>Anything else that interests me &amp; the users.</li>
</ul>
<h4>Try it out</h4>
<p>Try out <em>mycheckpoint</em>. It’s a different kind of monitoring   solution. Simple monitoring (charting) is immediate. For more interesting results you will need basic SQL skills, and in return you’ll get a lot   of power under your hands.</p>
<ul>
<li>Download mycheckpoint <a href="https://code.google.com/p/mycheckpoint/">here</a></li>
<li>Visit the project’s <a href="../../forge/mycheckpoint">homepage</a></li>
<li>Browse the <a href="../../forge/mycheckpoint/documentation">documentation</a></li>
<li>Report <a href="https://code.google.com/p/mycheckpoint/issues/list">bugs</a></li>
</ul>
<p><em>mycheckpoint</em> is released under the <a href="http://www.opensource.org/licenses/bsd-license.php">New BSD  License</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/mycheckpoint-rev-190-http-server-interactive-charts/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

