Hierarchical Replication Support in Drizzle

I took a look at how you would go about setting up a replication topology such as A->B->C (Server A replicating to Server B, Server B replicating to Server C). Specifically how to handle failures in the chain, choose a new master etc. Heres a example using the current replication implementation:

Setup for Server A

drizzled –datadir=/home/jdaly/drizzle/repos/inno_repl/tests/serverA –innodb.replication-log –-server-id=1 –drizzle-protocol.port=9307 –mysql-protocol.port=3307

Setup for Server B

drizzled –datadir=/home/jdaly/drizzle/repos/inno_repl/tests/serverB –server-id=2 –innodb.replication-log –plugin-add=slave –slave.config-file=/home/jdaly/drizzle/repos/inno_repl/tests/serverB/slave.config –drizzle-protocol.port=9308 –mysql-protocol.port=3308

slave.config

master-host=127.0.0.1
master-port=9307
master-user=test
master-pass=me
max-reconnects=1000
seconds-between-reconnects=1

Setup for Server C

drizzled –datadir=/home/jdaly/drizzle/repos/inno_repl/tests/serverC –server-id=3 –innodb.replication-log –plugin-add=slave –slave.config-file=/home/jdaly/drizzle/repos/inno_repl/tests/serverC/slave.config –drizzle-protocol.port=9309 –mysql-protocol.port=3309

slave.config

master-host=127.0.0.1
master-port=9308
master-user=test
master-pass=me
max-reconnects=1000
seconds-between-reconnects=1

Pretty easy to setup, now lets look at the data on Server A compared to Server C in the replication tables.

Server A

drizzle> create database serverA;
drizzle> select ID,  SEGID, COMMIT_ID, END_TIMESTAMP from DATA_DICTIONARY.SYS_REPLICATION_LOG;
+------+-------+-----------+------------------+
| ID   | SEGID | COMMIT_ID | END_TIMESTAMP    |
+------+-------+-----------+------------------+
|  772 |     1 |         1 | 1300842502563167 |
|  818 |     1 |         2 | 1300842824136908 |
+------+-------+-----------+------------------+

Server C

drizzle> select ID,  SEGID, COMMIT_ID, END_TIMESTAMP from DATA_DICTIONARY.SYS_REPLICATION_LOG;
+------+-------+-----------+------------------+
| ID   | SEGID | COMMIT_ID | END_TIMESTAMP    |
+------+-------+-----------+------------------+
|  772 |     1 |         1 | 1300842759914337 |
|  819 |     1 |         2 | 1300842830416566 |
+------+-------+-----------+------------------+

This looks pretty straightforward if Server B were to fail, to point Server C at Server A. They both have two entries and both have a commit_id of 2. However this only works in this very simple example. If a backup was restored and replication enabled, or if Server C was stopped and restarted the entries would not be ordered so nicely. Both these events would throw the two tables out of sync. So you would be back to looking at the actual SQL statement that was last to execute on Server C to find where it matched in Server A.

To remedy this problem I added two new columns to the SYS_REPLICATION_LOG table, ORIGINATING_SERVER_ID and ORIGINATING_ID. These are the ID and SERVER_ID for the server that originally applied the changes. The table now looks like:

drizzle> describe DATA_DICTIONARY.SYS_REPLICATION_LOG;
+-----------------------+---------+------+---------+-----------------+-----------+
| Field                 | Type    | Null | Default | Default_is_NULL | On_Update |
+-----------------------+---------+------+---------+-----------------+-----------+
| ID                    | BIGINT  | YES  |         | NO              |           |
| SEGID                 | INTEGER | YES  |         | NO              |           |
| COMMIT_ID             | BIGINT  | YES  |         | NO              |           |
| END_TIMESTAMP         | BIGINT  | YES  |         | NO              |           |
| ORIGINATING_SERVER_ID | INTEGER | YES  |         | NO              |           |
| ORIGINATING_ID        | BIGINT  | YES  |         | NO              |           |
| MESSAGE_LEN           | INTEGER | YES  |         | NO              |           |
| MESSAGE               | BLOB    | YES  |         | NO              |           |
+-----------------------+---------+------+---------+-----------------+-----------+

On Server A this looks like:

drizzle> create database serverA;
drizzle> select ID,  SEGID, COMMIT_ID, END_TIMESTAMP, ORIGINATING_SERVER_ID,  ORIGINATING_ID from DATA_DICTIONARY.SYS_REPLICATION_LOG;
+------+-------+-----------+------------------+-----------------------+----------------+
| ID   | SEGID | COMMIT_ID | END_TIMESTAMP    | ORIGINATING_SERVER_ID | ORIGINATING_ID |
+------+-------+-----------+------------------+-----------------------+----------------+
|  772 |     1 |         1 | 1300839966870903 |                     1 |            772 |
|  861 |     1 |         2 | 1300840709527015 |                     1 |            861 |
+------+-------+-----------+------------------+-----------------------+----------------+

On Server B this looks like:

drizzle> select ID,  SEGID, COMMIT_ID, END_TIMESTAMP, ORIGINATING_SERVER_ID,  ORIGINATING_ID from DATA_DICTIONARY.SYS_REPLICATION_LOG;
+------+-------+-----------+------------------+-----------------------+----------------+
| ID   | SEGID | COMMIT_ID | END_TIMESTAMP    | ORIGINATING_SERVER_ID | ORIGINATING_ID |
+------+-------+-----------+------------------+-----------------------+----------------+
|  772 |     1 |         1 | 1300840469433946 |                     2 |            772 |
|  855 |     1 |         2 | 1300840715001766 |                     1 |            861 |
+------+-------+-----------+------------------+-----------------------+----------------+

On Server C this looks like:

drizzle>  select ID,  SEGID, COMMIT_ID, END_TIMESTAMP, ORIGINATING_SERVER_ID,  ORIGINATING_ID from DATA_DICTIONARY.SYS_REPLICATION_LOG;
+------+-------+-----------+------------------+-----------------------+----------------+
| ID   | SEGID | COMMIT_ID | END_TIMESTAMP    | ORIGINATING_SERVER_ID | ORIGINATING_ID |
+------+-------+-----------+------------------+-----------------------+----------------+
|  772 |     1 |         1 | 1300840574916708 |                     3 |            772 |
|  833 |     1 |         2 | 1300840715511684 |                     1 |            861 |
+------+-------+-----------+------------------+-----------------------+----------------+

The last entry in each SYS_REPLICATION_LOG on all three servers has the same values for ORIGINATING_SERVER_ID and ORIGINATING_ID. Note: The first entry is a startup event and is unique to each server. How is this helpful? If Server B were to fail and Server C needed to be pointed to a new master it would be as simple as running this query on Server A:

select commit_id FROM DATA_DICTIONARY.SYS_REPLICATION_LOG WHERE originating_id= 861;

Then restarting Server C with that COMMIT_ID position.

This is still in design heavily, walking through this example it may be better to have the ORIGINATING_COMMIT_ID rather then ORIGINATING_ID in the table. I think that would then allow just reconfiguring the slave to point at the new master. The example also illustrates the need for more on the fly configuration such as configuring a slave to point to a different master without restarting it. The code for this is at:

lp:~skinny.moey/drizzle/hierarch_repl

Comments and design suggestions are more then welcome!

Drizzle7 Beta Release!!

This is really exciting. Ive been working on Drizzle since about May 2009 and followed it closely before that time. Its nice to see something go from being very alpha to being quite stable and usable. It has many cool features, more can be read about it on the official drizzle blog.

I plan to continue work on the transaction log as well as fix any problems that may arise with the statistics plugin Ive added. I think it may be time to resurrect my Gearman replication plugin that has been in hibernation for a bit.

I think its time to update this site to use the Beta version of Drizzle as well, its currently using a older Drizzle release.

Drizzle Statistics Scoreboard (logging_stats plugin)

Drizzle has a Scoreboard for collecting statistics (logging_stats plugin) I introduced this about 6 months ago with little user documentation unless you are into reading header files which have a descriptive writeup. Below is a detailed explanation of whats included from the user perspective and if your really interested some design details. A good portion of the user level statistics was inspired by the google v2 patch for MySQL.

Tables

drizzle> use data_dictionary;
drizzle> describe CUMULATIVE_USER_STATS;
+---------------------+---------+-------+---------+-----------------+-----------+
| Field               | Type    | Null  | Default | Default_is_NULL | On_Update |
+---------------------+---------+-------+---------+-----------------+-----------+
| USER                | VARCHAR | FALSE |         | FALSE           |           |
| BYTES_RECEIVED      | VARCHAR | FALSE |         | FALSE           |           |
| BYTES_SENT          | VARCHAR | FALSE |         | FALSE           |           |
| DENIED_CONNECTIONS  | VARCHAR | FALSE |         | FALSE           |           |
| LOST_CONNECTIONS    | VARCHAR | FALSE |         | FALSE           |           |
| ACCESS_DENIED       | VARCHAR | FALSE |         | FALSE           |           |
| CONNECTED_TIME_SEC  | VARCHAR | FALSE |         | FALSE           |           |
| EXECUTION_TIME_NSEC | VARCHAR | FALSE |         | FALSE           |           |
| ROWS_FETCHED        | VARCHAR | FALSE |         | FALSE           |           |
| ROWS_UPDATED        | VARCHAR | FALSE |         | FALSE           |           |
| ROWS_DELETED        | VARCHAR | FALSE |         | FALSE           |           |
| ROWS_INSERTED       | VARCHAR | FALSE |         | FALSE           |           |
+---------------------+---------+-------+---------+-----------------+-----------+
drizzle> describe CUMULATIVE_SQL_COMMANDS;
+----------------+---------+-------+---------+-----------------+-----------+
| Field          | Type    | Null  | Default | Default_is_NULL | On_Update |
+----------------+---------+-------+---------+-----------------+-----------+
| USER           | VARCHAR | FALSE |         | FALSE           |           |
| COUNT_SELECT   | BIGINT  | FALSE |         | FALSE           |           |
| COUNT_DELETE   | BIGINT  | FALSE |         | FALSE           |           |
| COUNT_UPDATE   | BIGINT  | FALSE |         | FALSE           |           |
| COUNT_INSERT   | BIGINT  | FALSE |         | FALSE           |           |
| COUNT_ROLLBACK | BIGINT  | FALSE |         | FALSE           |           |
| COUNT_COMMIT   | BIGINT  | FALSE |         | FALSE           |           |
| COUNT_CREATE   | BIGINT  | FALSE |         | FALSE           |           |
| COUNT_ALTER    | BIGINT  | FALSE |         | FALSE           |           |
| COUNT_DROP     | BIGINT  | FALSE |         | FALSE           |           |
| COUNT_ADMIN    | BIGINT  | FALSE |         | FALSE           |           |
+----------------+---------+-------+---------+-----------------+-----------+

In addition the functionality of “show status” and “show global status” resides in the GLOBAL_STATUS, and SESSION_STATUS tables which are constructed from the Scoreboard. The SESSION_STATEMENTS, and GLOBAL_STATEMENTS tables also reside in the plugin.

Desgin Overview

The Scoreboard is a pre-allocated vector of vectors of ScoreboardSlots. It can be thought of as a vector of buckets where each bucket contains pre-allocated ScoreboardSlots. To determine which bucket gets used for recording statistics the modulus operator is used on the session_id. This will result in a bucket to search for a unused ScoreboardSlot.

Startup Options

logging_stats_max_user_count – this is the maximum number of users that statistics will be aggregated for, if this is exceeded new users will not be logged. This should be allocated sufficiently large enough to not reach the maximum, its memory usage is minimal.

logging_stats_bucket_count – the number of buckets to allocate, see Design Overview on bucket description.

logging_stats_scoreboard_size – the number of ScoreboardSlots to pre-allocate, this represents approximately the number of concurrent sessions the system can have. If the system were to exceed this statistics would not be logged for a particular session. It should be allocated to be sufficiently larger then the maximum number of concurrent sessions that is anticipated.

Future Options

This is to be determined based on what users want, the infrastructure is there now to make adding and aggregating new statistics fairly easy.

Drizzle, WordPress, Amazon EC2

After some work I finally got Drizzle, WordPress and Amazon EC2 playing nicely with each other. The backend server for this WordPress site is running Drizzle, and the front end client uses the MySQL libraries for connecting, this is needed as there currently is not a fully functional PHP interface that uses the Drizzle client interface. WordPress required a few changes as Drizzle did not have support for all the same data types.

Why?

Well theres a saying “Eat your own dog food” which sums it up.

Heres a patch for WordPress which contains the schema modifications necessary for running wordpress with Drizzle.

schema.php patch

Ill be working with WordPress to see if they would like these changes incorporated into their source tree.