Friday, March 29, 2019

dbdeployer cookbook - usability by example

When I designed dbdeployer, I wanted to eliminate most of the issues that the old MySQL-Sandbox had:

  • dependencies during installation
  • mistaken tarballs
  • clarity of syntax
  • features (un)awareness.


Dependencies during installation did go away right from the start, as the dbdeployer executable is ready to be used without additional components. The only dependency is to have a host that can run MySQL. There is little dbdeployer can do about detecting whether or not your system can run MySQL. It depends on which version and flavor of MySQL you are running. It should not be a big deal as I assume that anyone in need of dbdeployer has already the necessary knowledge about MySQL prerequisites. This is not always the case, but the issue goes beyond dbdeployer’s scope.


Mistaken tarballs are a common problem for users who have never seen a binaries tarball. Here dbdeployer can help, up to a point, to guide the user. It recognizes most cases where you are trying to use a source tarball or the wrong operating system tarball. It still does not recognize when you try to run a sandbox for a Linux 64bits out of a 32bit tarball, but to be honest I haven’t tried to solve this problem yet. There are still cases where users are a great risk of picking the wrong tarball (Percona Server download page is a minefield and the one for MariaDB is not picnic either) but I feel that I have given dbdeployer users a big help on this count.


Clarity of syntax is probably the biggest issue with the previous tool. It’s a consequence of the tool being developed over several years, slowly adapting to changing circumstances. I made dbdeployer clearer from the beginning, when I decided to avoid piling up many actions as the consequence of the same command. In dbdeployer, you need to unpack the tarball explicitly before running a deployment, and this gives dbdeployer users the visibility on the architecture that eluded many MySQL-Sandbox users. The architecture of dbdeployer is such that adding new features, commands, and options is easy and fits within an easily detectable paradigm. Thus, the operations are easier to spot and use.


Features awareness is still a problem. There is a lengthy description of everything dbdeployer can do, but, as often happens with even the best tools, users don’t read manuals.

The biggest obstacle about reading manuals is that dbdeployer executable is installed without any need to take the README file along. If you need it, you should go online and read it, and given that dbdeployer is built to be used mostly without internet connection, there are cases when you want to know how to do something, but you can’t get the manual right away.

There is the tool integrated help (dbdeployer [command] -h), which gives you a lot of information, but this tells you how to do something that you know already exists, not what you can do in general.

To help on this count, I added a collection of samples (the cookbook) that were initially released in a directory of the GitHub project, but then the sample scripts suffered of the same invisibility that plagues the README file. There is one more problem: when you tried using the generic cookbook scripts (now removed) you had to use the same environment as I did when defining them, or they would fail.

The current solution is to include cookbook files right within the tool, using templates (same as for regular sandbox scripts) with the double advantage that the scripts are available anywhere the dbdeployer executable is, and the scripts are adapted to the environment, since dbdeployer knows how to search for available binaries and can suggest the best parameters for the scripts.


Introducing dbdeployer cookbook

The command dbdeployer cookbook (with aliases recipes or samples) has three subcommands:

  • list displays a list of available samples.
  • show displays the contents of a recipe.
  • create (with alias make) will build the recipe script.

We should try the list first:

$ dbdeployer cookbook list
.----------------------------------.-------------------------------------.--------------------------------------------------------------------.--------.
|              recipe              |             script name             |                            description                             | needed |
|                                  |                                     |                                                                    | flavor |
+----------------------------------+-------------------------------------+--------------------------------------------------------------------+--------+
| all-masters                      | all-masters-deployment.sh           | Creation of an all-masters replication sandbox                     | mysql  |
| delete                           | delete-sandboxes.sh                 | Delete all deployed sandboxes                                      |        |
| fan-in                           | fan-in-deployment.sh                | Creation of a fan-in (many masters, one slave) replication sandbox | mysql  |
| group-multi                      | group-multi-primary-deployment.sh   | Creation of a multi-primary group replication sandbox              | mysql  |
| group-single                     | group-single-primary-deployment.sh  | Creation of a single-primary group replication sandbox             | mysql  |
| master-slave                     | master-slave-deployment.sh          | Creation of a master/slave replication sandbox                     |        |
| ndb                              | ndb-deployment.sh                   | Shows deployment with ndb                                          | ndb    |
| prerequisites                    | prerequisites.sh                    | Shows dbdeployer prerequisites and how to make them                |        |
| pxc                              | pxc-deployment.sh                   | Shows deployment with pxc                                          | pxc    |
| remote                           | remote.sh                           | Shows how to get a remote MySQL tarball                            |        |
| replication-restart              | repl-operations-restart.sh          | Show how to restart sandboxes with custom options                  |        |
| replication-operations           | repl-operations.sh                  | Show how to run operations in a replication sandbox                |        |
| replication_between_groups       | replication-between-groups.sh       | Shows how to run replication between two group replications        | mysql  |
| replication_between_master_slave | replication-between-master-slave.sh | Shows how to run replication between two master/slave replications |        |
| replication_between_ndb          | replication-between-ndb.sh          | Shows how to run replication between two NDB clusters              | ndb    |
| show                             | show-sandboxes.sh                   | Show deployed sandboxes                                            |        |
| single                           | single-deployment.sh                | Creation of a single sandbox                                       |        |
| single-reinstall                 | single-reinstall.sh                 | Re-installs a single sandbox                                       |        |
| tidb                             | tidb-deployment.sh                  | Shows deployment and some operations with TiDB                     | tidb   |
| upgrade                          | upgrade.sh                          | Shows a complete upgrade example from 5.5 to 8.0                   | mysql  |
'----------------------------------'-------------------------------------'--------------------------------------------------------------------'--------'

The recipe that seems to be the simplest one is single. We can try to see what is in there:


$ dbdeployer  cookbook show single
#!/bin/bash

[...]
# Generated by dbdeployer 1.26.0 using template single on Fri Mar 29 12:27:53 UTC 2019
cd $(dirname $0)
source cookbook_include.sh

version=$1
[ -z "$version" ] && version=NOTFOUND_mysql
check_version $version

if [ -n "$(dbdeployer sandboxes | grep 'single\s*'$version)" ]
then
    echo "single version $version is already installed"
else
    header "Deploying a single sandbox for version $version"
    run dbdeployer deploy single $version
fi

What looks odd is the line that says NOTFOUND. If we try creating that script and then running it, it won’t work, and rightfully so.


There is a recipe named prerequisites that could probably help us.

$ dbdeployer cookbook create prerequisites
recipes/prerequisites.sh created

So, now, we have a starting point. Let’s run it:


$ ./recipes/prerequisites.sh

# ****************************************************************************
# Creating Sandbox binary directory ($HOME/opt/mysql)
# ****************************************************************************

## HOW TO GET  binaries for dbdeployer
# FOR REGULAR MYSQL
# run the commands:

1. dbdeployer remote list
2. dbdeployer remote get mysql-5.7.25
3. dbdeployer unpack mysql-5.7.25.tar.xz

4. dbdeployer versions

# FOR MySQL forks, MySQL Cluster, PXC:

# 1. Get the binaries from the maker download pages
# 2. run the command
 dbdeployer unpack FlavorName-X.X.XX-OS.tar.gz  --prefix=FlavorName
3. dbdeployer versions

The first thing we see is that the sandbox binary directory was created, and then we see a series of steps to fill it in.

Let’s try:

$ dbdeployer remote list
Files available in https://raw.githubusercontent.com/datacharmer/mysql-docker-minimal/master/dbdata/available.json
5.5 -> [mysql-5.5.61 mysql-5.5.62]
5.6 -> [mysql-5.6.41 mysql-5.6.43]
5.7 -> [mysql-5.7.24 mysql-5.7.25]
8.0 -> [mysql-8.0.13 mysql-8.0.15]
4.1 -> [mysql-4.1.22]
5.0 -> [mysql-5.0.15 mysql-5.0.96]
5.1 -> [mysql-5.1.72]

$ dbdeployer remote get mysql-5.7.25
File /home/msandbox/mysql-5.7.25.tar.xz downloaded

$ dbdeployer unpack mysql-5.7.25.tar.xz
Unpacking tarball mysql-5.7.25.tar.xz to $HOME/opt/mysql/5.7.25
[...]
Renaming directory $HOME/opt/mysql/mysql-5.7.25 to $HOME/opt/mysql/5.7.25

$ dbdeployer versions
Basedir: /home/msandbox/opt/mysql
5.7.25

If we repeat the show command now, we get a different result:


$ dbdeployer  cookbook show single
#!/bin/bash
[...]
# Generated by dbdeployer 1.26.0 using template single on Fri Mar 29 12:37:26 UTC 2019
cd $(dirname $0)
source cookbook_include.sh

version=$1
[ -z "$version" ] && version=5.7.25
check_version $version

if [ -n "$(dbdeployer sandboxes | grep 'single\s*'$version)" ]
then
    echo "single version $version is already installed"
else
    header "Deploying a single sandbox for version $version"
    run dbdeployer deploy single $version
fi

There! instead of the NOTFOUND we saw before, it now shows the version that we just downloaded. If we repeat the same procedure (remote list, remote get, unpack) for MySQL 8.0.15, we would see 8.0.15 as the recommended version.


Now we can create the single recipe. Or even better, since we want to try several ones, we can create all of them.


$ dbdeployer  cookbook create all
recipes/replication-between-master-slave.sh created
recipes/single-reinstall.sh created
recipes/fan-in-deployment.sh created
recipes/group-multi-primary-deployment.sh created
recipes/repl-operations.sh created
recipes/tidb-deployment.sh created
recipes/remote.sh created
recipes/upgrade.sh created
recipes/ndb-deployment.sh created
recipes/cookbook_include.sh created
recipes/master-slave-deployment.sh created
recipes/prerequisites.sh created
recipes/replication-between-groups.sh created
recipes/replication-between-ndb.sh created
recipes/pxc-deployment.sh created
recipes/single-deployment.sh created
recipes/show-sandboxes.sh created
recipes/delete-sandboxes.sh created
recipes/all-masters-deployment.sh created
recipes/group-single-primary-deployment.sh created
recipes/repl-operations-restart.sh created

Now it’s time to try one:


msandbox@505969e46289:~$ ./recipes/single-deployment.sh

# ****************************************************************************
# Deploying a single sandbox for version 5.7.25
# ****************************************************************************
+ dbdeployer deploy single 5.7.25
Creating directory /home/msandbox/sandboxes
Database installed in $HOME/sandboxes/msb_5_7_25
run 'dbdeployer usage single' for basic instructions'
. sandbox server started
0

Looking at the list, we see a single-reinstall recipe. If we run it, we will get a mini tutorial on how to use a single sandbox:


$ ./recipes/single-deployment.sh

# ****************************************************************************
# Deploying a single sandbox for version 5.7.25
# ****************************************************************************
+ dbdeployer deploy single 5.7.25
Creating directory $HOME/sandboxes
Database installed in $HOME/sandboxes/msb_5_7_25
run 'dbdeployer usage single' for basic instructions'
. sandbox server started
0
msandbox@505969e46289:~$ ./recipes/show-sandboxes.sh
+ dbdeployer sandboxes --full-info
.------------.--------.---------.---------.--------.-------.--------.
|    name    |  type  | version |  ports  | flavor | nodes | locked |
+------------+--------+---------+---------+--------+-------+--------+
| msb_5_7_25 | single | 5.7.25  | [5725 ] | mysql  |     0 |        |
'------------'--------'---------'---------'--------'-------'--------'
0
msandbox@505969e46289:~$ ./recipes/single-reinstall.sh

# ****************************************************************************
# Deploying the same sandbox again, with different parameters
# We need to use --force, as we are overwriting an existing sandbox
# Incidentally, the new deployment will run a query before and after the grants
# ****************************************************************************
+ dbdeployer deploy single 5.7.25 '--pre-grants-sql=select host, user from mysql.user' '--post-grants-sql=select host, user from mysql.user' --force
Overwriting directory $HOME/sandboxes/msb_5_7_25
stop $HOME/sandboxes/msb_5_7_25
Database installed in $HOME/sandboxes/msb_5_7_25
run 'dbdeployer usage single' for basic instructions'
. sandbox server started
+-----------+---------------+
| host      | user          |
+-----------+---------------+
| localhost | mysql.session |
| localhost | mysql.sys     |
| localhost | root          |
+-----------+---------------+
+-----------+---------------+
| host      | user          |
+-----------+---------------+
| 127.%     | msandbox      |
| 127.%     | msandbox_ro   |
| 127.%     | msandbox_rw   |
| 127.%     | rsandbox      |
| localhost | msandbox      |
| localhost | msandbox_ro   |
| localhost | msandbox_rw   |
| localhost | mysql.session |
| localhost | mysql.sys     |
| localhost | root          |
+-----------+---------------+

# ****************************************************************************
# Deploying the same sandbox with a different directory.
# No --force is necessary, as dbdeployer will choose a different port
# ****************************************************************************
+ dbdeployer deploy single 5.7.25 --sandbox-directory=msb_5_7_25_new
Database installed in $HOME/sandboxes/msb_5_7_25_new
run 'dbdeployer usage single' for basic instructions'
. sandbox server started
0

+ dbdeployer sandboxes --header
            name               type    version    ports
---------------------------- -------- --------- ---------
 msb_5_7_25               :   single   5.7.25    [5725 ]
 msb_5_7_25_new           :   single   5.7.25    [5726 ]
0

# ****************************************************************************
# Removing the second sandbox
# ****************************************************************************
+ dbdeployer delete msb_5_7_25_new
List of deployed sandboxes:
$HOME/sandboxes/msb_5_7_25_new
Running $HOME/sandboxes/msb_5_7_25_new/stop
stop $HOME/sandboxes/msb_5_7_25_new
Running rm -rf $HOME/sandboxes/msb_5_7_25_new
Directory $HOME/sandboxes/msb_5_7_25_new deleted
0

This script teaches us the basics of starting and restarting a sandbox, with useful twists as running an SQL command before granting privileges.


There is a similar tutorial for replication operations, but we’ll have a look at a slightly different one.


$ ./recipes/master-slave-deployment.sh
+ dbdeployer deploy replication 5.7.25 --concurrent
$HOME/sandboxes/rsandbox_5_7_25/initialize_slaves
initializing slave 1
initializing slave 2
Replication directory installed in $HOME/sandboxes/rsandbox_5_7_25
run 'dbdeployer usage multiple' for basic instructions'

$ ./recipes/repl-operations.sh

# ****************************************************************************
# Running a simple command with the master in the sandbox.
# Notice the usage of the '-e', as if we were using the 'mysql' client
# ****************************************************************************
+ $HOME/sandboxes/rsandbox_5_7_25/m -e 'SHOW MASTER STATUS'
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000001 |     4089 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+

# ****************************************************************************
# Creating a table in the master
# ****************************************************************************
+ $HOME/sandboxes/rsandbox_5_7_25/m -e 'DROP TABLE IF EXISTS test.t1'
+ $HOME/sandboxes/rsandbox_5_7_25/m -e 'CREATE TABLE test.t1(id int not null primary key)'

# ****************************************************************************
# Inserting 3 lines into the new table
# ****************************************************************************
+ $HOME/sandboxes/rsandbox_5_7_25/m -e 'INSERT INTO test.t1 VALUES(1)'
+ $HOME/sandboxes/rsandbox_5_7_25/m -e 'INSERT INTO test.t1 VALUES(2)'
+ $HOME/sandboxes/rsandbox_5_7_25/m -e 'INSERT INTO test.t1 VALUES(3)'

# ****************************************************************************
# Getting the table contents from one slave
# ****************************************************************************
+ $HOME/sandboxes/rsandbox_5_7_25/s1 -e 'SELECT * FROM test.t1'
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
+----+

# ****************************************************************************
# Getting the table count from all nodes (NOTE: no '-e' is needed)
# $HOME/sandboxes/rsandbox_5_7_25/use_all 'SELECT COUNT(*) FROM test.t1'
# master
COUNT(*)
3
# server: 1
COUNT(*)
3
# server: 2
COUNT(*)
3

# ****************************************************************************
# Checking the status of all slaves
# ****************************************************************************
+ $HOME/sandboxes/rsandbox_5_7_25/check_slaves
master
port 19226 - server_id 100
             File: mysql-bin.000001
         Position: 5213
Executed_Gtid_Set:
slave1
port 19227 - server_id 200
              Master_Log_File: mysql-bin.000001
          Read_Master_Log_Pos: 5213
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
          Exec_Master_Log_Pos: 5213
           Retrieved_Gtid_Set:
            Executed_Gtid_Set:
                Auto_Position: 0
slave2
port 19228 - server_id 300
              Master_Log_File: mysql-bin.000001
          Read_Master_Log_Pos: 5213
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
          Exec_Master_Log_Pos: 5213
           Retrieved_Gtid_Set:
            Executed_Gtid_Set:
                Auto_Position: 0
0

# ****************************************************************************
# Running a multiple query in all slaves
# ****************************************************************************
+ $HOME/sandboxes/rsandbox_5_7_25/use_all_slaves 'STOP SLAVE; SET GLOBAL slave_parallel_workers=3; START SLAVE;show processlist '
# server: 1
Id User Host db Command Time State Info
11 msandbox localhost NULL Query 0 starting show processlist
12 system user  NULL Connect 0 Checking master version NULL
13 system user  NULL Connect 0 System lock NULL
14 system user  NULL Connect 0 Waiting for an event from Coordinator NULL
15 system user  NULL Connect 0 Waiting for an event from Coordinator NULL
16 system user  NULL Connect 0 Waiting for an event from Coordinator NULL
# server: 2
Id User Host db Command Time State Info
10 msandbox localhost NULL Query 0 starting show processlist
11 system user  NULL Connect 0 Checking master version NULL
12 system user  NULL Connect 0 System lock NULL
13 system user  NULL Connect 0 Waiting for an event from Coordinator NULL
14 system user  NULL Connect 0 Waiting for an event from Coordinator NULL
15 system user  NULL Connect 0 Waiting for an event from Coordinator NULL


By studying the commands mentioned in these samples, you will become proficient in dbdeployer components, allowing you to use it for advanced testing operations.

No comments: