Testing Xeround’s database as a service

So while I was at the MySQL UC, The Xeround database came to my attention.  It bills itself as database as a service for MySQL systems and a seamless replacement for standard MySQL.

Of course, since I am a QA Engineer, I could not resist the urge to try to break it >:)  As my friend and former MySQL colleage, Kostja says, “QA Engineers are a unique breed…they like to push all the buttons” : )  I would say that the QA mindset goes a bit further than that, but it is something I will delve into in another post.  I will only say that there is a reason that Microsoft recognizes QA software engineering as a distinct and specialized discipline.

So, let’s get back to Xeround.  It was the first database as a service that caught my eye and I just had to test it!  They are currently offering a free beta.  It is remarkably easy and fast to get set up with a test database and the web-based dashboard they provide is pretty interesting and offers some good information (though some of it is confusing…more on that in a bit)

It was my intent to run a small handful of tests with the mighty, mighty randgen!

My tests were as follows:

  1. outer_join grammar – creates seriously nasty JOIN queries that can use up to 20 tables
  2. transactional grammar – we have a grammar that creates a variety of transactions.  Some good, some bad, with lots of ROLLBACKs and SAVEPOINTs sprinkled in for spice.
  3. subqueries – the nastiest grammar I have created and as I have mentioned elsewhere, it is also part of why we are just now seeing optimizer features like index condition pushdown (ICP) being reintroduced to MySQL >: )

My thoughts were that these could be quickly executed and point out any serious problems in basic functionality.  MySQL and Drizzle both use these grammars as part of their testing.  Drizzle must survive these tests on every push to trunk, so these seem like reasonable stressors for a new engine >: )

It should be noted that I had to modify the test grammars to accomodate some Xeround limitations, the modified randgen branch I used is here.  It can be branched via bzr branch lp:~patrick-crews/randgen/randgen_drizzle_exp

Each grammar would be run with the randgen’s –debug option.  This is because the user is presented with a nice report at the end of the run which indicates:  query_count:row_count (ie how many queries returned how many rows):

# 2011-04-27T20:40:18 Rows returned:
$VAR1 = {
‘    0’ => 59,
‘    1’ => 2,
‘    4’ => 1,
‘    9’ => 1,
‘   -1’ => 35,
‘>100’ => 1
};

I would use this as a comparison point against MySQL 5.1.  Granted, I could use the –Validator=ResultsetComparatorSimplify option, but then I would have an actual bug report that I would feel compelled to file and this would feel less like fun and more like work ; )  However, I have been in contact with engineers from Xeround and have shared my findings with them.

For the transactional grammar, I would run the grammar on each system and then do a diff of mysqldump files from each database.  As Xeround is a MySQL engine, this could cause some differences, but the data in the tables should be consistent.

Before I get into the testing results, I’ll provide some overall impressions:
As I said, the web interface is pretty nice and provides you with a lot of useful information.  It allows you to easily create a new named database instance and provides you with data such as status, scale, uptime, cpu utilization, memory utilization, number of connections, ops/sec, and message count.  Scale refers to the autoscale capabilities that Xeround advertises.  For the beta, you are allowed to scale from 3 to 4 servers.  3 servers is considered 100%, adding the extra server (when certain user-specified CPU or Memory limits are hit) equates to 133% .  Interestingly enough, I observed that there were always 6 active connections when the database was idle (probably some of the Xeround ‘secret sauce‘ working…).

The control panel also allows the user to set the CPU, memory, and connections limits that will trigger scale up (and possibly scale down).  In my experiments, I never seemed to tax memory or connections, but CPU limits were hit and auto-scale did trigger, though I will admit that I didn’t observe any noticeable change in the test execution.

There are also tabs for backup (not available in the free beta, though mysqldump does work against a Xeround instance), general monitoring which provides real-time information about cpu, memory and connections, and an events (messages tab).  The one thing I noted about the events tab was that I received a number of warning messages about the health of my database during times I wasn’t using it.  However, it is a beta service for general evaluation and certain oddities are to be expected.

Here is what I found with my tests:
1)  Xeround is a MySQL engine.  They do advertise this, but the main reason I noticed that all of my created test tables were now ‘Engine=Xeround’ was that I was unable to create a varchar_1024 indexed column.  Xeround is limited to 255 characters max:

# 2011-04-27T19:50:27 key (`col_char_1024_key` ))  failed: 1074 Column length too big for column 'col_char_1024' (max = 255); use BLOB or TEXT instead

This limitation required modification of the randgen grammars and gendata files to limit char columns to 255.  As noted above, you can find the modified version of the randgen here.

2)  Tables with an ENGINE=$engine_name argument are processed without an issue (ie you should be able to use a dumpfile without problems) and are converted to Xeround tables.  One thing to note is that dumpfiles *from* Xeround have ENGINE=Xeround for the CREATE TABLE statements


create table t1 (a int not null auto_increment, primary key(a)) engine=innodb;
Query OK, 0 rows affected, 2 warnings (0.702761 sec)
drizzle> show create table t1;
+-------+---------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                          |
+-------+---------------------------------------------------------------------------------------------------------------------------------------+
| t1    | CREATE TABLE `t1` (
`a` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`a`)
) ENGINE=Xeround DEFAULT CHARSET=utf8 COLLATE=utf8_bin |
+-------+---------------------------------------------------------------------------------------------------------------------------------------+

3)  outer_join grammar:
I used the following command line:

./gentest.pl --gendata=conf/drizzle/outer_join_drizzle.zz --grammar=conf/drizzle/outer_join_drizzle.yy --queries=100 --threads=1 --dsn=dbi:mysql:host=00.00.00.00:port=9999:user=USER:password=PASSWORD:database=test --sqltrace --debug

The test is designed to generate queries with large numbers of tables (up to ~20).  The test ran without much incident.  The Xeround server monitor indicated that the CPU was hovering near 80% for most of the time, but again…beta test setup, so I’ll give them some leeway.

The big trouble is what follows.  Remember those randgen summary reports I mentioned earlier?  Below is a comparison of Xeround vs. MySQL for the same command line.  The values are row_count’ => number_of_queries_returning_said_row_count.  What this means is that for the same set of queries, Xeround and MySQL do not always return the same result sets.  I did not note any differences in query failures, so this simply indicates that results processing is differing somewhere : (  To elaborate, Xeround had 56 queries that returned 0 rows, for the same workload, MySQL only had 39.  A row count of -1 indicates that there was an error with the query, such as referencing a table or column that doesn’t exist.  Somehow, Xeround hit fewer errors than MySQL, though that is also worrisome – why do they register errors differently?
Xeround:

# 2011-04-27T20:11:05 Rows returned:
$VAR1 = {
'    0' => 56,
'    1' => 16,
'    2' => 6,
'    3' => 2,
'    5' => 1,
'    6' => 1,
'    7' => 1,
'    8' => 1,
'   -1' => 13,
'   10' => 2,
'>10' => 1
};

MySQL 5.1

$VAR1 = {
'    0' => 39,
'    1' => 15,
'    2' => 2,
'    3' => 2,
'    4' => 1,
'    7' => 2,
'    8' => 1,
'   -1' => 32,
'   10' => 1,
'>10' => 5
};

4)  transactional grammar:
I used the following command line:

./gentest.pl --gendata=conf/drizzle/translog_drizzle.zz --grammar=conf/drizzle/translog_concurrent1.yy --queries=100 --threads=1 --dsn=dbi:mysql:host=00.00.00.00:port=9999:user=USER:password=PASSWORD:database=test --sqltrace --debug

This grammar generates a variety of transactions and standalone queries.  The queries generated consist of both good and invalid SQL with lots of ROLLBACK’s and SAVEPOINT’s here and there.  Unfortunately, I noticed a large number of differences.  We’ll start with the easiest one:

< DROP TABLE IF EXISTS `A`;
< CREATE TABLE `A` (
---
> DROP TABLE IF EXISTS `a`;
> CREATE TABLE `a` (
50c50
< ) ENGINE='InnoDB' AUTO_INCREMENT=105 COLLATE='utf8_general_ci';
---
> ) ENGINE='Xeround' COLLATE='utf8_bin';

It isn’t huge, but Xeround apparently auto-converts tables names to lower-case.  The randgen attempts to create table `A`, but it is stored as table `a`.  This could be an issue for some people, but Xeround does say that the beta is for people to evaluate the system’s suitability for their purposes.

The big issue is that Xeround appears to not have registered a lot of the transactions issued by the randgen.  The Xeround dumpfile only contained the original 10 rows from table `a`, while the MySQL 5.1 version I ran locally had 94 rows by the end of the randgen run : (

Further research of the randgen logs indicate the following issue:

# 2011-04-27T20:06:56 Query:  INSERT INTO `d` ( `col_char_10` , `col_char_10_key` , `col_char_10_not_null` , `col_char_10_not_null_key` , `col_char_255` , `col_char_255_key` , `col_char_255_not_null` , `col_char_255_not_null_key` , `col_int` , `col_int_key` , `col_int_not_null` , `col_int_not_null_key` , `col_bigint` , `col_bigint_key` , `col_bigint_not_null` , `col_bigint_not_null_key` , `col_enum` , `col_enum_key` , `col_enum_not_null` , `col_enum_not_null_key` , `col_text` , `col_text_key` , `col_text_not_null` , `col_text_not_null_key` ) SELECT `col_char_10` , `col_char_10_key` , `col_char_10_not_null` , `col_char_10_not_null_key` , `col_char_255` , `col_char_255_key` , `col_char_255_not_null` , `col_char_255_not_null_key` , `col_int` , `col_int_key` , `col_int_not_null` , `col_int_not_null_key` , `col_bigint` , `col_bigint_key` , `col_bigint_not_null` , `col_bigint_not_null_key` , `col_enum` , `col_enum_key` , `col_enum_not_null` , `col_enum_not_null_key` , `col_text` , `col_text_key` , `col_text_not_null` , `col_text_not_null_key` FROM `bb`  ORDER BY `col_bigint`,`col_bigint_key`,`col_bigint_not_null`,`col_bigint_not_null_key`,`col_char_10`,`col_char_10_key`,`col_char_10_not_null`,`col_char_10_not_null_key`,`col_char_255`,`col_char_255_key`,`col_char_255_not_null`,`col_char_255_not_null_key`,`col_enum`,`col_enum_key`,`col_enum_not_null`,`col_enum_not_null_key`,`col_int`,`col_int_key`,`col_int_not_null`,`col_int_not_null_key`,`col_text`,`col_text_key`,`col_text_not_null`,`col_text_not_null_key`,`pk` LIMIT 50 /*Generated by THREAD_ID 1*/  failed: 1038 Out of sort memory; increase server sort buffer size

So, it would appear that transactions are failing for some reason or another.  However, I repeat the disclaimer about this being a beta and not a production deployment.  It could have something to do with the resources allocated for each beta user.

5)  Subquery grammar
This was the initial test I ran, but I have saved it for last.  First of all, the command line:

./gentest.pl --gendata=conf/drizzle/drizzle.zz --grammar=conf/drizzle/optimizer_subquery_drizzle.yy --queries=100 --threads=1 --dsn=dbi:mysql:host=00.00.00.00:port=9999:user=USER:password=PASSWORD:database=test --sqltrace --debug

This test generates some very nasty subquery-laded queries (see below).  The first thing I noticed on the single-threaded run was that Xeround seemed to not like this query very much at all:

SELECT    table2 . `col_int` AS field1 FROM ( CC AS table1 STRAIGHT_JOIN ( ( CC AS table2 STRAIGHT_JOIN CC AS table3 ON (table3 . `col_bigint_key` = table2 . `col_int_not_null_key`  ) ) ) ON (table3 . `col_text_not_null_key` = table2 . `col_char_10_key`  ) ) WHERE (  table1 . `col_int` NOT IN ( SELECT   SUBQUERY1_t1 . `col_int_not_null_key` AS SUBQUERY1_field1 FROM ( BB AS SUBQUERY1_t1 INNER JOIN ( CC AS SUBQUERY1_t2 INNER JOIN BB AS SUBQUERY1_t3 ON (SUBQUERY1_t3 . `col_char_10_key` = SUBQUERY1_t2 . `col_char_10_key`  ) ) ON (SUBQUERY1_t3 . `col_char_10_not_null_key` = SUBQUERY1_t2 . `col_char_10`  ) ) WHERE SUBQUERY1_t2 . `col_bigint` != table1 . `pk` OR SUBQUERY1_t2 . `pk` >= table2 . `pk` ) ) OR ( table1 . `col_int_key`  BETWEEN 48 AND ( 48 + 183 ) OR table1 . `pk`  BETWEEN 48 AND ( 48 + 104 ) )  GROUP BY field1  ;

Now it is quite nasty, but standard MySQL executes it with a minimum of fuss (though it does take a moment to handle this monster as well).

The other thing is that Xeround took an exceedingly long time to execute this workload.  While the other grammars executed in moderate amounts of time (my testing was from a hotel room in Santa Clara while the instance is in Chicago), the subquery test was noticeably slow.  I was able to walk down to the lobby, buy something, and return to my room while it was dealing with the nasty query above : (  For some context, running the same command line on my laptop took 8 seconds, Xeround took 14 minutes, but again…beta test setup and hardware, so YMMV.

Finally, we have the dreaded row count report:
Xeround:

# 2011-04-27T20:45:19 Rows returned:
$VAR1 = {
'    0' => 59,
'    1' => 2,
'    4' => 1,
'   -1' => 35,
'>10' => 1,
'>100' => 1
};

MySQL 5.1:

# 2011-04-27T20:40:18 Rows returned:
$VAR1 = {
'    0' => 59,
'    1' => 2,
'    4' => 1,
'    9' => 1,
'   -1' => 35,
'>100' => 1
};

As we can see, there is 1 query out of the 100 issued where result sets differed (returning 9 rows in MySQL vs. >10 rows in Xeround).

I also tried using –threads=10 to really stress the Xeround system (I didn’t bother with MySQL, it handles 10 threads of nasty subqueries like a champ…incidentally, so does Drizzle) ; ) Xeround was able to handle the workload and did so in 27 minutes. Since single-threaded took 14 minutes, perhaps Xeround doesn’t really begin to shine until we start hitting large numbers of concurrent connections?

So what can I say from the results of these informal tests?  Personally, I would hesitate to say that Xeround is a drop-in replacement.  The limitations on column sizes, changes in table naming, and differing result sets are a bit worrisome.  However, I will say that the Xeround engineers I met at the UC were very engaged and interested in my findings and have made significant strides in subquery processing since my initial tests.  I believe that with time these issues will be fixed and that not every customer will run into them (I know I’m beating this into the ground, but I was using a beta test system).  Behavior may be different on a production machine and not every MySQL user will generate such workloads and every customer should perform their own careful testing and evaluation before making any changes to their systems.

My personal interest ends here.  The UC introduced me to a number of interesting new storage engines and I was mainly curious about ways of evaluating them.  This was a quick and dirty bit of testing just to see if I could produce any interesting pyrotechnics ; )  Go go randgen!

I really want this picture to be shown when anyone searches for 'randgen' ; )

In all seriousness, I highly recommend adoption of the random query generator.  It offers a modular and customizable system for creating evaluation tools (like result set comparison, execution time comparison, replication validation, etc, etc) and has been used in production-level testing for MySQL, MariaDB and Drizzle for some time.  It also plays with Postgresql and Java DB (kind of scary that 40% of that list is owned by Oracle…), so please give it a spin and see what kinds of pretty explosions you can make…who knows, testing might actually become fun for non-QA folks >; )

Additionally, these tests only took me about half an hour to setup and execute.  Granted, I have been using the tool for some time, but 30 minutes to identify a number of potential problem areas seems pretty awesome to me, but then again, I am a QA Engineer and we live for such things.

More on kewpie (the query probulator)

My presentation from the MySQL UC didn’t give a lot of detail on the actual tool I have hacked up, nor did it go into how to play with it / try it out.  I figured I should rectify that (at least one person seemed interested in trying it out <g>)

To begin with, you should have the random query generator installed (see the docs for handling that).  Besides being *the* cutting edge, production-ready testing tool in the open-source dbms world, it comes with a handy data generator.

One of the key features of kewpie, is that it can easily generate test queries against any test bed.  A standard randgen practice is to develop grammars and gendata files (which generates a user-specified test-bed) that are designed to work together.  By knowing which tables and columns will be available to the randgen at runtime, the grammar writer can tune the randgen to produce a higher percentage of valid queries.

It is possible to just use the built in _field element, which will randomly retrieve some available field, however, being able to match columns by data type (for both joins and comparisons) results in much more interesting queries >:)  At some point, the randgen will likely be improved to overcome this, but it is a sad fact of qa that we often spend more time producing tests, than working on beefing up our testing infrastructure.

At any rate, the kewpie demos are designed to work with the random data generator.  It is a very cool tool, and one can also use it with –start-and-exit to have a populated test server.

Requirements:

  • randgen
  • dbd::drizzle (see randgen docs)
  • MySQLDB (Drizzle speaks the MySQL protocol.  MySQLDB enables us to play well with SQLAlchemy too!)
  • the demo branch from launchpad: bzr branch lp:~patrick-crews/drizzle/dbqp_kewpie_demo

It is important to remember that kewpie is more of a science project than something you’ll use for testing any time soon.  It is meant to help illustrate the power behind large-scale automated query generation and analysis, but it pales in comparison to the mighty, mighty randgen.  However, if you are interested, please read on : )

Config files

kewpie runs are controlled via a .cnf file.  Python has a very nice ConfigParser library and it seems like a solid way of organizing this information and getting at it.  Also, the very well-designed drizzle-automation uses similar files.  I’ll just digress a bit here to give big props to Jay Pipes of the fu for his work here.  It has informed a lot of the infrastructure work I’ve been doing for Drizzle. : )

test_info section:


[test_info]
comment = basic test of kewpie seeding
test_schema = test
init_query_count = 2
init_table_count = 3
init_column_count = 4
init_where_count = 0
# limits for various values
max_table_count = 10
max_column_count = 25
max_where_count = 10
max_mutate_count = 3

This section seeds the initial query population.  In the example above, we produce 2 queries that each have 4 columns and use 2 tables (and no WHERE clause).  It is an eventual dream to have more fine-grained control over such things, but this was a proof-of-concept as much as anything.

Next we have limits.  We don’t want to go over 10 tables, 25 columns (in the SELECT clause), or 10 conditions in the WHERE clause.  We also set max_mutate_count so that only 3 mutant queries will ever be produced from a seed.  Setting it higher = more variants that are possible from each query.

mutators section:


[mutators]
add_table = 5
add_column = 1
add_where = 3

At the moment, kewpie only has 3 ways to manipulate a query – add_table, add_column, and add_where.  These should be fairly self-explanatory ; )  The vision is that these will eventually have a variety of parameters that can be set, so that we can one day ask that we only add conditions to the WHERE clause that use an integer column, for example.  The numeric values following each mutator name is how we stack the deck in favor of one mutator over another.  When we evaluate this section, we create a python list object that contains N occurances of each mutator name, when it comes time to mutate a query, we randomly choose one mutator from the list and then call that method against the query.

test_servers section:


[test_servers]
servers = [[--innodb.replication-log]]

As we do in other dbqp tests, we provide a list of python lists.  Each sublist represents the server options we want to use for the test server.  At present, there is no need to start more than 1 server, though there may be value in altering certain options.

evaluators section:


[evaluators]
row_count = True
explain_output = False

Currently, we only have the row_count evaluator.  This ensures that at least one row of data was returned for a given query.  It is surprising how valuable just this tiny filter can be.  In Microsoft’s research, they found that purely random systems only produced valid queries 50% of the time.  The remainder tended to short out at the parser level.  The evaluator is what helps us produce useful queries, the mutators are what help the system hit its evaluation targets (whatever they may be).

Future evaluators can measure code-coverage, server variable effect, log file effect, pretty much anything.  We want testing to be flexible and have borrowed heavily from the modular Validator and Reporter design of the randgen.

Now to see it in action!

We are going to take our join.cnf file and seed it so we create 2 initial queries, with 4 columns and 3 tables each.  We run this in conjunction with the conf/drizzle/drizzle.zz gendata file (sort of our go-to test bed for the randgen).


./dbqp --mode=kewpie --randgen-path=$RANDGEN_PATH --gendata=$RANDGEN_PATH/conf/drizzle/drizzle.zz join --verbose
Setting --no-secure-file-priv=True for randgen mode...
21 Apr 2011 11:38:59 VERBOSE: Initializing system manager...
21 Apr 2011 11:38:59 VERBOSE: Processing source tree under test...
21 Apr 2011 11:38:59 INFO: Using Drizzle source tree:
21 Apr 2011 11:38:59 INFO: basedir: /home/user/repos/kewpie_demo
<snip>
21 Apr 2011 11:39:00 INFO: Taking clean db snapshot...
21 Apr 2011 11:39:00 VERBOSE: Starting executor: bot0
21 Apr 2011 11:39:00 VERBOSE: Executor: bot0 beginning test execution...
21 Apr 2011 11:39:00 VERBOSE: Restoring from db snapshot
21 Apr 2011 11:39:00 VERBOSE: Starting server: bot0.s0
21 Apr 2011 11:39:00 INFO: bot0 server:
21 Apr 2011 11:39:00 INFO: NAME: s0
21 Apr 2011 11:39:00 INFO: MASTER_PORT: 9306
21 Apr 2011 11:39:00 INFO: DRIZZLE_TCP_PORT: 9307
21 Apr 2011 11:39:00 INFO: MC_PORT: 9308
21 Apr 2011 11:39:00 INFO: PBMS_PORT: 9309
21 Apr 2011 11:39:00 INFO: RABBITMQ_NODE_PORT: 9310
21 Apr 2011 11:39:00 INFO: VARDIR: /home/user/repos/kewpie_demo/tests/workdir/bot0/s0/var
21 Apr 2011 11:39:00 INFO: STATUS: 1
# 2011-04-21T11:39:00 Default schema: test
# 2011-04-21T11:39:00 Executor initialized, id GenTest::Executor::Drizzle 2011.03.14.2269 ()
# 2011-04-21T11:39:00 # Creating Drizzle table: test.A; engine: ; rows: 0 .
# 2011-04-21T11:39:00 # Creating Drizzle table: test.B; engine: ; rows: 0 .
# 2011-04-21T11:39:00 # Creating Drizzle table: test.C; engine: ; rows: 1 .
# 2011-04-21T11:39:00 # Creating Drizzle table: test.D; engine: ; rows: 1 .
# 2011-04-21T11:39:00 # Creating Drizzle table: test.AA; engine: ; rows: 10 .
# 2011-04-21T11:39:00 # Creating Drizzle table: test.BB; engine: ; rows: 10 .
# 2011-04-21T11:39:00 # Creating Drizzle table: test.CC; engine: ; rows: 100 .
# 2011-04-21T11:39:00 # Creating Drizzle table: test.DD; engine: ; rows: 100 .
21 Apr 2011 11:39:01 INFO: Executing query: SELECT table_1.col_char_1024_not_null_key AS column_1, table_3.col_char_1024 AS column_2, table_3.col_enum AS column_3, table_1.pk AS column_4 FROM AA AS table_1 RIGHT JOIN D AS table_2 ON table_1.col_char_1024_not_null_key = table_2.col_char_10_not_null_key LEFT OUTER JOIN D AS table_3 ON table_2.col_text_key = table_3.col_text
21 Apr 2011 11:39:01 INFO: EVALUATOR: row_count STATUS: True EXTRA: 1
21 Apr 2011 11:39:01 VERBOSE: ORIG QUERY:  SELECT table_1.col_char_1024_not_null_key AS column_1, table_3.col_char_1024 AS column_2, table_3.col_enum AS column_3, table_1.pk AS column_4 FROM AA AS table_1 RIGHT JOIN D AS table_2 ON table_1.col_char_1024_not_null_key = table_2.col_char_10_not_null_key LEFT OUTER JOIN D AS table_3 ON table_2.col_text_key = table_3.col_text
21 Apr 2011 11:39:01 VERBOSE: USING ADD_TABLE mutation
21 Apr 2011 11:39:01 VERBOSE: MUTANT QUERY: SELECT table_1.col_char_1024_not_null_key AS column_1, table_3.col_char_1024 AS column_2, table_3.col_enum AS column_3, table_1.pk AS column_4 FROM AA AS table_1 RIGHT JOIN D AS table_2 ON table_1.col_char_1024_not_null_key = table_2.col_char_10_not_null_key LEFT OUTER JOIN D AS table_3 ON table_2.col_text_key = table_3.col_text RIGHT JOIN B AS table_4 ON table_3.col_text_key = table_4.col_text_not_null
<snip>

From this output we can see how the query was executed, evaluated, and mutated.  As we wanted, we have 4 columns and 3 tables in the original query and we add extra tables to queries that evaluate well.

Now let’s see what happens when we use a different gendata file.  We’ll use one called varchar_drizzle.zz which, surprisingly enough, only uses varchars:


./dbqp --mode=kewpie --randgen-path=$RANDGEN_PATH --gendata=$RANDGEN_PATH/conf/drizzle/varchar_drizzle.zz join --verbose
Setting --no-secure-file-priv=True for randgen mode...
21 Apr 2011 11:44:20 VERBOSE: Initializing system manager...
21 Apr 2011 11:44:20 VERBOSE: Processing source tree under test...
21 Apr 2011 11:44:20 INFO: Using Drizzle source tree:
21 Apr 2011 11:44:20 INFO: basedir: /home/user/repos/kewpie_demo
<snip>
21 Apr 2011 11:44:20 INFO: Taking clean db snapshot...
21 Apr 2011 11:44:20 VERBOSE: Starting executor: bot0
21 Apr 2011 11:44:20 VERBOSE: Executor: bot0 beginning test execution...
21 Apr 2011 11:44:20 VERBOSE: Restoring from db snapshot
21 Apr 2011 11:44:20 VERBOSE: Starting server: bot0.s0
21 Apr 2011 11:44:20 INFO: bot0 server:
21 Apr 2011 11:44:20 INFO: NAME: s0
21 Apr 2011 11:44:20 INFO: MASTER_PORT: 9306
21 Apr 2011 11:44:20 INFO: DRIZZLE_TCP_PORT: 9307
21 Apr 2011 11:44:20 INFO: MC_PORT: 9308
21 Apr 2011 11:44:20 INFO: PBMS_PORT: 9309
21 Apr 2011 11:44:20 INFO: RABBITMQ_NODE_PORT: 9310
21 Apr 2011 11:44:20 INFO: VARDIR: /home/user/repos/kewpie_demo/tests/workdir/bot0/s0/var
21 Apr 2011 11:44:20 INFO: STATUS: 1
# 2011-04-21T11:44:20 Default schema: test
# 2011-04-21T11:44:20 Executor initialized, id GenTest::Executor::Drizzle 2011.03.14.2269 ()
# 2011-04-21T11:44:20 # Creating Drizzle table: test.table0_varchar_150_not_null; engine: ; rows: 0 .
# 2011-04-21T11:44:20 # Creating Drizzle table: test.table1_varchar_150_not_null; engine: ; rows: 1 .
# 2011-04-21T11:44:20 # Creating Drizzle table: test.table2_varchar_150_not_null; engine: ; rows: 2 .
# 2011-04-21T11:44:20 # Creating Drizzle table: test.table10_varchar_150_not_null; engine: ; rows: 10 .
# 2011-04-21T11:44:20 # Creating Drizzle table: test.table100_varchar_150_not_null; engine: ; rows: 100 .
21 Apr 2011 11:44:20 INFO: Executing query: SELECT table_1.col_varchar_1024 AS column_1, table_3.pk AS column_2, table_2.col_varchar_1024_key AS column_3, table_3.col_varchar_1024_not_null AS column_4 FROM table10_varchar_150_not_null AS table_1 RIGHT OUTER JOIN table2_varchar_150_not_null AS table_2 ON table_1.col_varchar_1024 = table_2.col_varchar_1024_not_null LEFT JOIN table10_varchar_150_not_null AS table_3 ON table_1.col_varchar_1024_key = table_3.pk
21 Apr 2011 11:44:20 INFO: EVALUATOR: row_count STATUS: True EXTRA: 2
21 Apr 2011 11:44:20 VERBOSE: ORIG QUERY:  SELECT table_1.col_varchar_1024_not_null_key AS column_1, table_3.pk AS column_2, table_3.col_varchar_1024_not_null_key AS column_3, table_1.col_varchar_1024 AS column_4 FROM table0_varchar_150_not_null AS table_1 RIGHT JOIN table2_varchar_150_not_null AS table_2 ON table_1.col_varchar_1024_not_null_key = table_2.col_varchar_1024 LEFT OUTER JOIN table1_varchar_150_not_null AS table_3 ON table_2.col_varchar_1024_not_null_key = table_3.col_varchar_1024_not_null
21 Apr 2011 11:44:20 VERBOSE: USING ADD_TABLE mutation
21 Apr 2011 11:44:21 VERBOSE: MUTANT QUERY: SELECT table_1.col_varchar_1024_not_null_key AS column_1, table_3.pk AS column_2, table_3.col_varchar_1024_not_null_key AS column_3, table_1.col_varchar_1024 AS column_4 FROM table0_varchar_150_not_null AS table_1 RIGHT JOIN table2_varchar_150_not_null AS table_2 ON table_1.col_varchar_1024_not_null_key = table_2.col_varchar_1024 LEFT OUTER JOIN table1_varchar_150_not_null AS table_3 ON table_2.col_varchar_1024_not_null_key = table_3.col_varchar_1024_not_null RIGHT JOIN table100_varchar_150_not_null AS table_4 ON table_3.col_varchar_1024_key = table_4.col_varchar_1024_not_null

As you can see, the testbed (created / populated tables have changed).  As a result, the generated queries have changed as well.  Allowing this kind of flexibility will allow qa engineers to not only look for good queries, but also for interesting query / test bed combinations (sometimes optimizations and code paths executed rely on both) in an easy and automated manner.

Next, we’ll take a look at how to add other things into the mix.  Suppose you want to also add WHERE conditions to your generated queries – it is as simple as tweaking the following line in join.cnf from:

[mutators]
add_table = 5
add_column = 0
add_where = 0

to


[mutators]
add_table = 2
add_column = 0
add_where = 4

We are now twice as likely to add a WHERE condition as we are to add a table to a query.  Let’s see what happens from the exact same command line:


./dbqp --mode=kewpie --randgen-path=$RANDGEN_PATH --gendata=$RANDGEN_PATH/conf/drizzle/varchar_drizzle.zz join --verbose
Setting --no-secure-file-priv=True for randgen mode...
21 Apr 2011 11:50:16 VERBOSE: Initializing system manager...
21 Apr 2011 11:50:16 VERBOSE: Processing source tree under test...
21 Apr 2011 11:50:16 INFO: Using Drizzle source tree:
21 Apr 2011 11:50:16 INFO: basedir: /home/user/repos/kewpie_demo
<snip>
21 Apr 2011 11:50:16 INFO: Taking clean db snapshot...
21 Apr 2011 11:50:16 VERBOSE: Starting executor: bot0
21 Apr 2011 11:50:16 VERBOSE: Executor: bot0 beginning test execution...
21 Apr 2011 11:50:16 VERBOSE: Restoring from db snapshot
21 Apr 2011 11:50:16 VERBOSE: Starting server: bot0.s0
21 Apr 2011 11:50:16 INFO: bot0 server:
21 Apr 2011 11:50:16 INFO: NAME: s0
21 Apr 2011 11:50:16 INFO: MASTER_PORT: 9306
21 Apr 2011 11:50:16 INFO: DRIZZLE_TCP_PORT: 9307
21 Apr 2011 11:50:16 INFO: MC_PORT: 9308
21 Apr 2011 11:50:16 INFO: PBMS_PORT: 9309
21 Apr 2011 11:50:16 INFO: RABBITMQ_NODE_PORT: 9310
21 Apr 2011 11:50:16 INFO: VARDIR: /home/user/repos/kewpie_demo/tests/workdir/bot0/s0/var
21 Apr 2011 11:50:16 INFO: STATUS: 1
# 2011-04-21T11:50:16 Default schema: test
# 2011-04-21T11:50:16 Executor initialized, id GenTest::Executor::Drizzle 2011.03.14.2269 ()
# 2011-04-21T11:50:16 # Creating Drizzle table: test.table0_varchar_150_not_null; engine: ; rows: 0 .
# 2011-04-21T11:50:16 # Creating Drizzle table: test.table1_varchar_150_not_null; engine: ; rows: 1 .
# 2011-04-21T11:50:16 # Creating Drizzle table: test.table2_varchar_150_not_null; engine: ; rows: 2 .
# 2011-04-21T11:50:16 # Creating Drizzle table: test.table10_varchar_150_not_null; engine: ; rows: 10 .
# 2011-04-21T11:50:16 # Creating Drizzle table: test.table100_varchar_150_not_null; engine: ; rows: 100 .
<snip>
21 Apr 2011 11:50:17 INFO: Executing query: SELECT table_1.col_varchar_1024 AS column_1, table_3.pk AS column_2, table_2.col_varchar_1024_key AS column_3, table_3.col_varchar_1024_not_null AS column_4 FROM table10_varchar_150_not_null AS table_1 RIGHT OUTER JOIN table2_varchar_150_not_null AS table_2 ON table_1.col_varchar_1024 = table_2.col_varchar_1024_not_null LEFT JOIN table10_varchar_150_not_null AS table_3 ON table_1.col_varchar_1024_key = table_3.pk
21 Apr 2011 11:50:17 INFO: EVALUATOR: row_count STATUS: True EXTRA: 2
21 Apr 2011 11:50:17 VERBOSE: ORIG QUERY:  SELECT table_1.col_varchar_1024_not_null_key AS column_1, table_3.pk AS column_2, table_3.col_varchar_1024_not_null_key AS column_3, table_1.col_varchar_1024 AS column_4 FROM table0_varchar_150_not_null AS table_1 RIGHT JOIN table2_varchar_150_not_null AS table_2 ON table_1.col_varchar_1024_not_null_key = table_2.col_varchar_1024 LEFT OUTER JOIN table1_varchar_150_not_null AS table_3 ON table_2.col_varchar_1024_not_null_key = table_3.col_varchar_1024_not_null
21 Apr 2011 11:50:17 VERBOSE: USING ADD_WHERE mutation
21 Apr 2011 11:50:17 VERBOSE: MUTANT QUERY: SELECT table_1.col_varchar_1024_not_null_key AS column_1, table_3.pk AS column_2, table_3.col_varchar_1024_not_null_key AS column_3, table_1.col_varchar_1024 AS column_4 FROM table0_varchar_150_not_null AS table_1 RIGHT JOIN table2_varchar_150_not_null AS table_2 ON table_1.col_varchar_1024_not_null_key = table_2.col_varchar_1024 LEFT OUTER JOIN table1_varchar_150_not_null AS table_3 ON table_2.col_varchar_1024_not_null_key = table_3.col_varchar_1024_not_null WHERE table_1.pk >= 'W'

As I said, it is still beta software ; )  However, in all seriousness, we want to be able to generate ‘bad’ queries, but to have the option of not using them and filtering them out of a test if they serve no purpose.

Hopefully, this will give anyone that is interested a better idea of how to play with the code.  Development will likely continue, but this is still more of a prototype of how things could be.  If you *really* want to test a database, I still highly recommend the amazing random query generator – it is good for blowing things up!

I really want this picture to be shown when anyone searches for 'randgen' ; )

MySQL conference slides are up

My presentation slides are now up here.

The testing tool is still very early days, but it is based on some very cool research from Microsoft’s SQL Server team.

Say what you will about MS (and I have plenty to say), their QA is quite amazing and they are one of the only resources for information related to database QA.  In case you didn’t know, the amazing randgen is based on research from their team as well.

I’ll be posting more about how to play with the tool later and it requires a bit of explanation, so I won’t go too deeply into it.  If someone is just dying to try the new hawtness in dbms testing, you can contact me and I’ll see about getting you sorted.

I’ll just close with an explanation of the source of the name:  kewpie = query probulator.  Futurama for the win!  That and it is every QA Engineer’s dream to have tools that leave the system under test feeling like this:

taken from http://slurmed.com/wall/143/143_fry-probulator_0800.png

Many thanks to Javier for the use of the picture.

 

Testing Drizzle’s multi-master replication, part I

So, the amazing Mr. Shrewsbury, master of all things Drizzle-replication, has created a beta tree with multi-master replication!

As his blog explains, it is still very early days, but as it currently stands, it can provide some long-hoped-for functionality for people with particular replication needs.  This feature will definitely be made more robust in the future, but it is still quite useful in its present form.

As I think this would be cool to see in trunk, I have started testing the tree.  No progress has been made on the multi-master side of things…yet, but I am happy to report that all of our existing replication tests (including crash and restore) are passing just fine.

What this means is that the code could make it into trunk without breaking anything for anyone and also providing access to a very cool new feature.  As time permits, we will be expanding the test cases for this feature, but in the interim, I hope that you will enjoy testing Dave’s awesome work ; )

Crash-testing the innodb transaction log!

So, back when we released our first beta in September, one of the many responses was this

The comments about the reliability / durability of the log definitely struck me as testing we needed.

It’s taken a while (we had this GA thing we were working on…), but we finally have crash and recover testing of the innodb transaction log and the slave plugin.

Here is what happens for the innodb-based log:

  • Set up the test servers and start the randgen with the trx_log grammars.  I’ll point you to my superhuman colleague, Andrew Hutchings for a summary of what they do.
  • Some time into the test (after several rounds of queries have run), `kill -9 $pid` is issued against the master server
  • The master server is restarted
  • The transaction_reader utility is called to generate SQL from the contents of the log
  • A validator server is populated with the log’s SQL
  • Drizzledump is called against the master and validation servers
  • A diff is taken of the dump files – if all is well, they should match

For the slave plugin, everything is basically the same except that we wait and make sure the slave and master are synched, then dumpfiles are compared.

With this testing we can say that:
* The innodb-based rpl log will provide an accurate representation of the database’s state even after a crash.
* The slave plugin will provide an accurate representation of the master server even after a crash and restart.

Many iterations of these tests have been run so far, using the standard randgen data and queries as well as making use of –seed=time.  When we do this, we randomize the data and queries generated so it can cover more ground than simply running the same 1000 transactions over and over.  As it is a well designed tool, any runs can easily be repeated as the same seed *always* produces the same data and queries…repeatability is one of a qa engineer’s favorite words : )

So without further ado, here is some output from the tests.  They are located in the innodb_trx_log and slave_plugin suites for the randgen, executable via dbqp:
To run them:
./dbqp –mode=randgen –randgen-path=/path/to/randgen –suite=innodb_trx_log,slave_plugin multi_thread1_crash_recover

NOTE that the output doesn’t normally include the ps output, just putting it in here to show off the magic ; )


<snip>
# 2011-03-24T17:00:03 Query:  SELECT * FROM `C` AS X WHERE X . `col_bigint_key` BETWEEN 211 AND 2673999872 LIMIT 5 FOR UPDATE /*Generated by THREAD_ID 1*/  failed: 1213 Deadlock found when trying to get lock; try restarting transaction
# 2011-03-24T17:00:03 Query:  ROLLBACK TO SAVEPOINT A /*Generated by THREAD_ID 1*/  failed: 1305 SAVEPOINT %s does not exist
# 2011-03-24T17:00:03 Query:  ROLLBACK TO SAVEPOINT A /*Generated by THREAD_ID 1*/  failed: 1305 SAVEPOINT %s does not exist
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
0 S  1000 13090   491  0  80   0 -  9317 -      pts/6    00:00:00 python
0 S  1000 13229     1 99  80   0 - 164210 -     pts/6    00:01:31 lt-drizzled
0 S  1000 13260     1  0  80   0 - 104483 -     pts/6    00:00:00 lt-drizzled
0 S  1000 13290 13090  0  80   0 -  1038 -      pts/6    00:00:00 sh
0 S  1000 13291 13290  0  80   0 - 18502 -      pts/6    00:00:00 gentest.pl
1 S  1000 13298 13291  0  80   0 - 18502 -      pts/6    00:00:00 gentest.pl
1 S  1000 13299 13291  0  80   0 - 18502 -      pts/6    00:00:00 gentest.pl
1 S  1000 13300 13291  5  80   0 - 20378 -      pts/6    00:00:02 gentest.pl
1 S  1000 13302 13291  5  80   0 - 20355 -      pts/6    00:00:02 gentest.pl
1 S  1000 13304 13291  5  80   0 - 20324 -      pts/6    00:00:02 gentest.pl
1 S  1000 13306 13291  5  80   0 - 20406 -      pts/6    00:00:02 gentest.pl
0 R  1000 13343 13299  0  80   0 -  1651 -      pts/6    00:00:00 ps
# 2011-03-24T17:00:03 0
# 2011-03-24T17:00:03 Sending kill -9 to server pid 13229 in order to force a recovery.
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
0 S  1000 13090   491  0  80   0 -  9317 -      pts/6    00:00:00 python
0 Z  1000 13229     1 99  80   0 -     0 ?      pts/6    00:01:31 lt-drizzled <defunct>
0 S  1000 13260     1  0  80   0 - 104483 -     pts/6    00:00:00 lt-drizzled
0 S  1000 13290 13090  0  80   0 -  1038 -      pts/6    00:00:00 sh
0 S  1000 13291 13290  0  80   0 - 18502 -      pts/6    00:00:00 gentest.pl
1 S  1000 13298 13291  0  80   0 - 18502 -      pts/6    00:00:00 gentest.pl
1 S  1000 13299 13291  0  80   0 - 18502 -      pts/6    00:00:00 gentest.pl
1 S  1000 13300 13291  5  80   0 - 20378 -      pts/6    00:00:02 gentest.pl
1 S  1000 13302 13291  5  80   0 - 20355 -      pts/6    00:00:02 gentest.pl
1 S  1000 13304 13291  5  80   0 - 20324 -      pts/6    00:00:02 gentest.pl
1 S  1000 13306 13291  5  80   0 - 20406 -      pts/6    00:00:02 gentest.pl
0 R  1000 13344 13299  0  80   0 -  1651 -      pts/6    00:00:00 ps
# 2011-03-24T17:00:03 0
# 2011-03-24T17:00:03 Killing child process with pid 13300...
# 2011-03-24T17:00:03 Killing child process with pid 13306...
# 2011-03-24T17:00:03 Killing child process with pid 13302...
# 2011-03-24T17:00:03 Killing child process with pid 13304...
# 2011-03-24T17:00:03 Kill GenTest::ErrorFilter(13298)
# 2011-03-24T17:00:03 Attempting database recovery using the server ...
# 2011-03-24T17:00:03 Executing drizzle/drizzled/drizzled --no-defaults --core-file --datadir="drizzle/tests/workdir/bot0/s0/var/master-data" --basedir="drizzle" --plugin-add=shutdown_function --mysql-protocol.port=9306 2>&1 .
# 2011-03-24T17:00:03 13345
# 2011-03-24T17:00:08 transaction_log output file:  /tmp//translog_13291_.sql
# 2011-03-24T17:00:08 drizzle/plugin/transaction_log/utilities/transaction_reader -uroot --use-innodb-replication-log -p 9306 --ignore-events > /tmp//translog_13291_.sql
# 2011-03-24T17:00:09 Replicating from transaction_log output...
# 2011-03-24T17:00:09 drizzle/client/drizzle --host=127.0.0.1 --port=9311 --user=root test <  /tmp//translog_13291_.sql
# 2011-03-24T17:00:16 Validating replication via dumpfile compare...
# 2011-03-24T17:00:16 /tmp//translog_rpl_dump_13291_9306.sql
# 2011-03-24T17:00:16 drizzle/client/drizzledump --compact --skip-extended-insert --host=127.0.0.1 --port=9306 --user=root test >/tmp//translog_rpl_dump_13291_9306.sql
# 2011-03-24T17:00:17 /tmp//translog_rpl_dump_13291_9311.sql
# 2011-03-24T17:00:17 drizzle/client/drizzledump --compact --skip-extended-insert --host=127.0.0.1 --port=9311 --user=root test >/tmp//translog_rpl_dump_13291_9311.sql
# 2011-03-24T17:00:17 Executing diff --unified /tmp//translog_rpl_dump_13291_9306.sql /tmp//translog_rpl_dump_13291_9311.sql
# 2011-03-24T17:00:17 Cleaning up validation server...
# 2011-03-24T17:00:17 Resetting validation server...
# 2011-03-24T17:00:18 0
# 2011-03-24T17:00:18 Test completed successfully.

The randgen code is in lp:randgen and the updated dbqp tests should be merged to trunk very soon.  Docs on running the randgen are here, dbqp are here.

At this point, I’d also like to shine the spotlight on David Shrewsbury for all of his hard work on the replication system.  He’s shepherded this code from a file-based log with limited testing all the way to a functional (and highly flexible) replication solution.  It was Dave who helped me with the often frustrating task of rooting out early bugs so that subsequent code could have a good foundation.  Big props should also go to Jay Pipes (master of the fu!) for his design work on the initial transaction log code.  Good design, good coding, and lots of love = some pretty cool stuff.  Of course, plenty of other people have helped…I just wanted to personally thank Dave for not trying to kill me when I was bombarding him with painful bugs early on ; )

As always, we hope to hear from you guys via IRC, emails and launchpad.  Also Drizzle Developer Day !  Sign up for it ; )  Hope to see you guys at the 2011 MySQL User’s Conference

Drizzle7 GA – testing summary

With the HUGE news of Drizzle’s first GA, I thought it appropriate that I spend some time discussing the testing that has gone into this release.

I tend to agree with Stewart’s assessment of our quality – it is solid and I think that you will find yourself pleasantly surprised and not at all angry when using it, but it is always in the eye of the user ; )  With that said, I did want to highlight some areas of interest.

To begin with, as we are a fork of MySQL, the bulk of our test suite comes directly from there as well.  Most of the standard mysql-test-run tests that are used to validate MySQL are also used for Drizzle.  All of the basics like creating tables, inserting data, etc – all tested just the same : )  Of course, we have also added significant numbers of new tests as development has progressed.

On top of this, we utilize a variety of other testing and benchmarking tools.  It seems appropriate to describe the life of a Drizzle patch really quickly:

Once a patch is approved for trunk, we merge it into our build branch.  This kicks off a set of Jenkins jobs on a variety of platforms that make sure the code will build and pass the main test suite.  If it fails, we usually ask the person who submitted the patch to sort things out.  We also provide for param-builds so that people can test their branches directly before proposing a merge – it is an invaluable tool.

Once the patch passes build, we move it into staging.  Staging consists of deeper tests and benchmarks.  We take lcov numbers, we run sysbench and dbt2, we utilize a variety of randgen tests to try to crash the server and to validate functionality like drizzledump and the transaction log.  In short, we really put our code through the wringer before we ever allow it into trunk.

For this release, my efforts were focused on a few particular areas:

Additional testing was provided by the community (many thanks to everyone who helped us out with feedback!) and by the individual developers.  We generally create test cases for our main suite.

We have a lot more interesting things planned for the future.  There are several intriguing new features on the horizon and we are also doing some cool things with our testing tools as well.

dbqp / randgen docs are up

As I mentioned earlier, dbqp now has a randgen mode.  This allows us to run test cases for the randgen in much the same way as we execute drizzletest cases.  At present, all of our randgen tests are defined as dbqp cases and live in drizzle/tests/randgen_tests.  They are organized into suites (much like drizzletest cases).  You can also use it as a quick means of creating an ad hoc replication setup!

 

Anyway, we now have proper documentation up.  Instructions and prereqs for the randgen are included in the docs.  If anyone runs into any issues, please write a bug and I’ll get on it.  Thanks and have fun testing! : )

 

We have lots more cool stuff planned for dbqp and drizzle.  Ultimately, the goal is to make it fast, easy and efficient for anyone to make sure the server is behaving as they expect it to – we want happy users!  With that said –   GA release tomorrow!  It is a huge deal for us and lots of blood, sweat and tears have gone into the software.  We are really excited and hope that everyone else will wonder how they lived without Drizzle for so long (I know I have) : )

 

dbqp / randgen integration…huzzah!

What is the big deal, you may ask?  Well, read on and all shall be revealed, intrepid reader ; )
As I mentioned an earlier post, our new test-runner – dbqp – allows us to define testing ‘modes’ which all utilize the same system and server management code.

One only has to define a testManager (what does a test look like / how to organize tests) and a testExecutor (how to execute / evaluate a test).  The aim is for dbqp to be a one-stop shop for test execution and to provide a clean and simple way to manage and expand this.

I have just added –mode=randgen to the test-runner.  The random query generator is a significant part of Drizzle’s testing strategy and we use a large number of tests with the tool.  Currently, they are executed and managed by drizzle-automation / Jenkins in our build system, but there has not been an easy way for users to execute these tests (outside of installing / learning drizzle automation or the randgen)…until now >; )

Documentation including requirements and setup instructions for the randgen can be found here
For the cost of about 300 lines of code (the new manager/executor files), we can now execute our randgen tests much the same way as we do our drizzletest suite!

./dbqp –mode=randgen –randgen-path=/home/user/repos/randgen
<snip>
25 Feb 2011 12:14:55 INFO: Using testing mode: randgen
25 Feb 2011 12:14:55 INFO: Processing test suites…
25 Feb 2011 12:14:55 INFO: Found 18 test(s) for execution
25 Feb 2011 12:14:55 INFO: Creating 1 testbot(s)
25 Feb 2011 12:14:55 INFO: Taking clean db snapshot…
25 Feb 2011 12:14:55 INFO: testbot0 server:
25 Feb 2011 12:14:55 INFO: NAME: server0
25 Feb 2011 12:14:55 INFO: MASTER_PORT: 9306
25 Feb 2011 12:14:55 INFO: DRIZZLE_TCP_PORT: 9307
25 Feb 2011 12:14:55 INFO: MC_PORT: 9308
25 Feb 2011 12:14:55 INFO: PBMS_PORT: 9309
25 Feb 2011 12:14:55 INFO: RABBITMQ_NODE_PORT: 9310
25 Feb 2011 12:14:55 INFO: VARDIR: drizzle/tests/workdir/testbot0/server0/var
25 Feb 2011 12:14:55 INFO: STATUS: 1
25 Feb 2011 12:15:00 : ================================================================================
25 Feb 2011 12:15:00 : TEST NAME                                               [ RESULT ]    TIME (ms)
25 Feb 2011 12:15:00 : ================================================================================
25 Feb 2011 12:15:00 : main.basic                                              [ pass ]         4376
25 Feb 2011 12:15:08 : main.blob                                               [ pass ]         7829
25 Feb 2011 12:15:12 : main.collations                                         [ pass ]         3989
25 Feb 2011 12:15:16 : main.combinations                                       [ pass ]         4169
25 Feb 2011 12:15:19 : main.create_drop                                        [ pass ]         3387
25 Feb 2011 12:15:34 : main.drizzledump_restore                                [ pass ]        14726
25 Feb 2011 12:15:42 : main.drizzledump_restore_rand                           [ pass ]         8289
25 Feb 2011 12:15:46 : main.limit_compare                                      [ pass ]         3535
25 Feb 2011 12:15:48 : main.many_indexes                                       [ pass ]         1725
25 Feb 2011 12:15:56 : main.optimizer_subquery                                 [ pass ]         8659
25 Feb 2011 12:16:03 : main.outer_join                                         [ pass ]         6464
25 Feb 2011 12:16:09 : main.outer_join_portable                                [ pass ]         6535
25 Feb 2011 12:16:12 : main.repeatable_read                                    [ pass ]         2906
25 Feb 2011 12:17:42 : main.select_stability_validator                         [ pass ]        90206
25 Feb 2011 12:17:47 : main.subquery                                           [ pass ]         4344
25 Feb 2011 12:19:29 : main.subquery_semijoin                                  [ pass ]       102766
25 Feb 2011 12:19:39 : main.subquery_semijoin_nested                           [ pass ]         9195
25 Feb 2011 12:19:42 : main.varchar                                            [ pass ]         3225
25 Feb 2011 12:19:42 : ================================================================================
25 Feb 2011 12:19:42 INFO: Test execution complete in 286 seconds
25 Feb 2011 12:19:42 INFO: Summary report:
25 Feb 2011 12:19:42 INFO: Executed 18/18 test cases, 100.00 percent
25 Feb 2011 12:19:42 INFO: STATUS: PASS, 18/18 test cases, 100.00 percent executed
25 Feb 2011 12:19:42 INFO: Spent 286 / 286 seconds on: TEST(s)
25 Feb 2011 12:19:42 INFO: Test execution complete
25 Feb 2011 12:19:42 INFO: Stopping all running servers…

Each testcase is a simple .cnf file that defines a few crucial elements:

[test_info]
comment = starts up a master-slave setup.  Still needs some validation work
[test_command]
command = ./gentest.pl –gendata=conf/drizzle/drizzle.zz –grammar=conf/drizzle/optimizer_subquery_drizzle.yy –queries=10 –threads=1
[test_servers]
servers = [[–innodb.replication-log=true],[–plugin-add=slave –slave.config-file=$MASTER_SERVER_SLAVE_CONFIG]]

The test_command is the command line that is passed to the randgen tool itself.  We have created most of these tests and have put together the command lines that serve our testing needs.  By organizing our tests in this fashion, it is now easy for anyone to use the tool to perform meaningful tests.

The test_servers section is interesting – it is a simple list of python lists.  Each sublist contains a set of server options that a server will need.  Servers are started in-order with the first defined server being considered the ‘master_server’.  As we can see in the example to test the new slave plugin, the files are amazingly simple and clean.

Some other added benefits are that dbqp’s –valgrind and –gdb options work the same with randgen tests as they would with drizzletest cases.  Addtionally, we will be able to incorporate the code coverage provided by the randgen in our gcov reports.

One final little feature worth noting is the –gendata option.

The randgen provides a feature called the random data generator / gendata.pl
By passing it a configuration file that defines a set of test tables (see the forge documentation for further details), it will connect to a server, create the test tables, and populate them according to specifications.

While I have other plans for this, one cool use is for ad-hoc testing.  By passing a simple command line + the use of –start-and-exit, one can have a nicely populated test database to play with:

./dbqp –mode=randgen –randgen-path=/home/user/repos/randgen –gendata=/home/user/repos/randgen/conf/drizzle/drizzle.zz –start-and-exit
Setting –no-secure-file-priv=True for randgen mode…
<snip>
25 Feb 2011 13:28:23 INFO: Using testing mode: randgen
25 Feb 2011 13:28:23 INFO: Processing test suites…
25 Feb 2011 13:28:23 INFO: Found 18 test(s) for execution
25 Feb 2011 13:28:23 INFO: Creating 1 testbot(s)
25 Feb 2011 13:28:23 INFO: Taking clean db snapshot…
25 Feb 2011 13:28:23 INFO: testbot0 server:
25 Feb 2011 13:28:23 INFO: NAME: server0
25 Feb 2011 13:28:23 INFO: MASTER_PORT: 9306
25 Feb 2011 13:28:23 INFO: DRIZZLE_TCP_PORT: 9307
25 Feb 2011 13:28:23 INFO: MC_PORT: 9308
25 Feb 2011 13:28:23 INFO: PBMS_PORT: 9309
25 Feb 2011 13:28:23 INFO: RABBITMQ_NODE_PORT: 9310
25 Feb 2011 13:28:23 INFO: VARDIR: drizzle/tests/workdir/testbot0/server0/var
25 Feb 2011 13:28:23 INFO: STATUS: 1
# 2011-02-25T13:28:23 Default schema: test
# 2011-02-25T13:28:23 Executor initialized, id GenTest::Executor::Drizzle 2011.02.2198 ()
# 2011-02-25T13:28:23 # Creating Drizzle table: test.A; engine: ; rows: 0 .
# 2011-02-25T13:28:23 # Creating Drizzle table: test.B; engine: ; rows: 0 .
# 2011-02-25T13:28:23 # Creating Drizzle table: test.C; engine: ; rows: 1 .
# 2011-02-25T13:28:23 # Creating Drizzle table: test.D; engine: ; rows: 1 .
# 2011-02-25T13:28:23 # Creating Drizzle table: test.AA; engine: ; rows: 10 .
# 2011-02-25T13:28:23 # Creating Drizzle table: test.BB; engine: ; rows: 10 .
# 2011-02-25T13:28:24 # Creating Drizzle table: test.CC; engine: ; rows: 100 .
# 2011-02-25T13:28:24 # Creating Drizzle table: test.DD; engine: ; rows: 100 .
25 Feb 2011 13:28:24 INFO: User specified –start-and-exit.  dbqp.py exiting and leaving servers running…
../client/drizzle -uroot -p9306 test
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Welcome to the Drizzle client..  Commands end with ; or \g.
Your Drizzle connection id is 3
Connection protocol: mysql
Server version: 2011.02.2198 Source distribution (dbqp_randgen_updates)
Type ‘help;’ or ‘\h’ for help. Type ‘\c’ to clear the buffer.
drizzle> show tables;
+—————-+
| Tables_in_test |
+—————-+
| A              |
| AA             |
| B              |
| BB             |
| C              |
| CC             |
| D              |
| DD             |
+—————-+
8 rows in set (0.001614 sec)
drizzle> select count(*) from dd;
+———-+
| count(*) |
+———-+
|      100 |
+———-+
1 row in set (0.000614 sec)

Please give it a try if you feel so inclined.  We will be working to integrate this better into our build and test systems (Jenkins / make targets / etc).  And remember ./dbqp –mode=cleanup for quick and easy cleanup when you are done playing! ; )

Drizzle’s slave plugin is working!

These are exciting times for the Drizzle team.  We just released our first RC and things are finally coming together into some awesome new features.  I’m excited to bring you latest news from the replication front:

Where to begin?  Well, many moons ago, Brian sent David Shrewsbury and myself out on the task of making the transaction_log plugin rock solid.  This plugin provides a file-based log that captures the server’s state via protobuf messages.  After much blood, sweat, and tears (and *many* bugs), we accomplished our task with *plenty* of help from everyone on the team.  With this task accomplished, we could say that any replication solution using the log would have an accurate representation of the server’s state.  However, this was a long way from actually replicating server-server.

During this time, we were also working on storing the transaction log in an innodb table rather than a file.  Initial sysbench tests show a significant performance gain using this code versus the file based log.  Thanks to the initial work on the file-based log, getting this code up and running wasn’t too painful and it also passes all of our tests with flying colors.  Special mention should be given to Joe Daly, Stewart Smith, and Andrew Hutchings for hacking on this.

Having the transaction log in a database table provides other advantages beyond speed (such as easy, standardized access by other servers), but I’ll leave that to the hackers to discuss (I’d really recommend catching Dave’s UC talk if you are interested!).  The gist is that it has allowed the amazing Mr. Shrewsbury to cook up the slave plugin!

This is a plugin that allows a server to replicate from another server that is using the innodb-trx log.  It is amazingly simple:
master options:  –innodb.replication-log=true
slave options: –plugin-add=slave –slave.config-file=XYZ

The config file may contain the following options in option=value format:
master-host – hostname/ip of the master host
master-port – port used by the master server
master-user – username
master-pass – password
max-reconnects – max # of reconnect attempts if the master disappears
seconds-between-reconnects – how long to wait between reconnect attempts

The code hasn’t yet been merged to trunk, but can be checked out from lp:~dshrews/drizzle/slave
Currently, the plugin is able to replicate *anything* we throw at the master, which is HUGE!

Additionally, our experimental test-runner, dbqp, is sporting randgen integration!  I’ll write more about this in an upcoming post, but I mention it here as you can use the new randgen-mode + –start-and-exit to have yourself a handy-dandy replication setup for ad-hoc testing.  Observe:

./dbqp –mode=randgen –start-and-exit –suite=slave_plugin
Setting –no-secure-file-priv=True for randgen mode…
<snip>
24 Feb 2011 12:25:11 INFO: Using testing mode: randgen
24 Feb 2011 12:25:11 INFO: Processing test suites…
24 Feb 2011 12:25:11 INFO: Found 1 test(s) for execution
24 Feb 2011 12:25:11 INFO: Creating 1 testbot(s)
24 Feb 2011 12:25:11 INFO: Taking clean db snapshot…
24 Feb 2011 12:25:12 INFO: Taking clean db snapshot…
24 Feb 2011 12:25:13 INFO: testbot0 server:
24 Feb 2011 12:25:13 INFO: NAME: server0
24 Feb 2011 12:25:13 INFO: MASTER_PORT: 9316
24 Feb 2011 12:25:13 INFO: DRIZZLE_TCP_PORT: 9317
24 Feb 2011 12:25:13 INFO: MC_PORT: 9318
24 Feb 2011 12:25:13 INFO: PBMS_PORT: 9319
24 Feb 2011 12:25:13 INFO: RABBITMQ_NODE_PORT: 9320
24 Feb 2011 12:25:13 INFO: VARDIR: drizzle/tests/workdir/testbot0/server0/var
24 Feb 2011 12:25:13 INFO: STATUS: 1
24 Feb 2011 12:25:13 INFO: testbot0 server:
24 Feb 2011 12:25:13 INFO: NAME: server1
24 Feb 2011 12:25:13 INFO: MASTER_PORT: 9321
24 Feb 2011 12:25:13 INFO: DRIZZLE_TCP_PORT: 9322
24 Feb 2011 12:25:13 INFO: MC_PORT: 9323
24 Feb 2011 12:25:13 INFO: PBMS_PORT: 9324
24 Feb 2011 12:25:13 INFO: RABBITMQ_NODE_PORT: 9325
24 Feb 2011 12:25:13 INFO: VARDIR: drizzle/tests/workdir/testbot0/server1/var
24 Feb 2011 12:25:13 INFO: STATUS: 1
24 Feb 2011 12:25:13 INFO: User specified –start-and-exit.  dbqp.py exiting and leaving servers running…

We now have two servers running, a master on port 9316 and a slave on port 9321

user@mahmachine:~drizzle/tests$ ../client/drizzle -uroot -p9316 test

Reading table information for completion of table and column names

You can turn off this feature to get a quicker startup with -A

Welcome to the Drizzle client..  Commands end with ; or \g.
Your Drizzle connection id is 4
Connection protocol: mysql
Server version: 2011.02.2197 Source distribution (drizzle)

Type ‘help;’ or ‘\h’ for help. Type ‘\c’ to clear the buffer.

drizzle> create table t1 (a int auto_increment not null, primary key(a));
Query OK, 0 rows affected (0.001652 sec)

drizzle> insert into t1 values (),(),();
Query OK, 3 rows affected (0.001182 sec)
Records: 3  Duplicates: 0  Warnings: 0

drizzle> select * from t1;
+—+
| a |
+—+
| 1 |
| 2 |
| 3 |
+—+
3 rows in set (0.000538 sec)

drizzle> exit
Bye
user@mahmachine:~drizzle/tests$ ../client/drizzle -uroot -p9321 test
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the Drizzle client..  Commands end with ; or \g.
Your Drizzle connection id is 82
Connection protocol: mysql
Server version: 2011.02.2197 Source distribution (drizzle)

Type ‘help;’ or ‘\h’ for help. Type ‘\c’ to clear the buffer.

drizzle> show tables;
+—————-+
| Tables_in_test |
+—————-+
| t1             |
+—————-+
1 row in set (0.000983 sec)

drizzle> select * from t1;
+—+
| a |
+—+
| 1 |
| 2 |
| 3 |
+—+
3 rows in set (0.00058 sec)

You will likely need to merge with trunk to use this feature in the slave branch, but the actual plugin should be merged soon.

When you are done playing, you can take advantage of a new quality of life feature – cleanup mode.
A pet peeve of mine is needing to clean up / shutdown any servers I may have started during testing.  With dbqp –mode=cleanup, the tool will now kill any server pids it detects in its workdir.  Nothing fancy, but useful, quick, and easy:

./dbqp –mode=cleanup
Setting –start-dirty=True for cleanup mode…
24 Feb 2011 12:32:55 INFO: Using Drizzle source tree:
24 Feb 2011 12:32:55 INFO: basedir: drizzle
24 Feb 2011 12:32:55 INFO: clientbindir: drizzle/client
24 Feb 2011 12:32:55 INFO: testdir: drizzle/tests
24 Feb 2011 12:32:55 INFO: server_version: 2011.02.2197
24 Feb 2011 12:32:55 INFO: server_compile_os: unknown-linux-gnu
24 Feb 2011 12:32:55 INFO: server_platform: x86_64
24 Feb 2011 12:32:55 INFO: server_comment: (Source distribution (drizzle))
24 Feb 2011 12:32:55 INFO: Using –start-dirty, not attempting to touch directories
24 Feb 2011 12:32:55 INFO: Using default-storage-engine: innodb
24 Feb 2011 12:32:55 INFO: Using testing mode: cleanup
24 Feb 2011 12:32:55 INFO: Killing pid 24416 from drizzle/tests/workdir/testbot0/server1/var/run/server1.pid
24 Feb 2011 12:32:55 INFO: Killing pid 24385 from drizzle/tests/workdir/testbot0/server0/var/run/server0.pid
24 Feb 2011 12:32:55 INFO: Stopping all running servers…

Please give it a whirl and help us make the code better by filing any bugs you detect!

Introducing dbqp.py aka project_steve_austin

So, what is all this dbqp / steve_austin mess?
Short answer – it is a nascent test runner.  Currently it duplicates the functionality of test-run.pl, but it is implemented in what I hope to be a more adaptable and reusable manner.  Please note that I said ‘duplicate’.  We haven’t touched test-run.pl or changed anything about how that tool works.  We don’t want to shoot ourselves in the foot by breaking a currently useful tool.

steve_austin…really?
Yes, it is test-run.pl, but better…stronger…faster ; )

dbqp?
database quality platform.  The idea is that this isn’t meant to just run a single type of test.  The code is intended to facilitate the execution of other tests.

Why?
Let me give you an example – any test of a database that I can think of requires starting up one or more servers (not much to test otherwise).  However, no project provides a standardized way of allocating and managing servers for tests (until now).  test-run.pl has a start-and-exit option (so does dbqp), but it seems crazy to have to call an entirely separate tool just to start a server (and such a method provides no management or cleanup – it is on the individual test to deal with that).  To run the randgen on Drizzle, one must start a server with ./test-run –start-and-exit, then call the randgen, then shutdown the server when finished : (  The situation is slightly better for MySQL / MariaDB, but the ./runall script the randgen uses still calls mysql-test-run.  Other tools define their own startup and management code, but such code has never seemed easily re-used to me.  This isn’t meant to be a knock on anyone or anything.  It is simply my belief that ‘things can be better‘ ™.

With dbqp, I have separated server allocation / management, system management (setting environment vars, finding files, etc), and test managment.  By structuring things in this way, we can define different testing modes.  Each mode uses some combination of adjusting / searching the system, starting and managing servers, doing things to the servers, then evaluating the effect of these ‘things’.  At present we only have a ‘dtr’ mode which runs our standard test suite, but further modes can be defined by simply writing a custom testManager (what does a testcase look like and how to package the test info) and testExecutor (how to execute / evaluate a testcase).  This will allow us to do things like create a ‘randgen’ mode which will facilitate executing our randgen transaction log tests (which currently involve too many manual steps outside of our automated build system) and do other cool things.

It should be noted that this is what the current test-run.pl basically does – it looks for testcases, starts the servers required by the test, packages the data and calls drizzletest.cc to actually execute the test (same for mysql-test-run.pl and mysqltest.cc).  It just makes sense to redo this in a way that will allow us to do the same thing for other types of testing tools : )

How can I play with it?
It is in the tree.  There is a make target – ‘make test-dbqp’.  I have tested it on Ubuntu and Mac OS X and it takes ~5 minutes to run our tests (all 551 of them!)
The manual page is here.

Please note that it is still in development.  It is reasonably solid and is beta quality, but test-run.pl is still in the tree for a reason ; )  All tests currently pass and one can run a variety of test / suite combinations as well as record tests.  Other utility features such as gdb and valgrind are still being polished.  Rely on test-run.pl for now, play with dbqp.
I’m also still adding and improving things as I write this – many, many thanks to Lee Bieber and Vijay Samuel for being early guinea pigs.

Anything else?
I’d like to give credit where credit is due and thank Monty Taylor for proposing the GSOC project that got me started on this path.  He gets at least part of the credit for any goodness this may bring (all responsibility for anything FAIL is on my shoulders).

There are some interesting things to come – we’re also moving on to the next phase of replication testing now that our trx log is rock solid