jetpants 0.7.8 → 0.7.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,16 +8,16 @@
8
8
 
9
9
  == MOTIVATION:
10
10
 
11
- \Jetpants was created by {Tumblr}[http://www.tumblr.com/] to help manage our database infrastructure. It handles automation tasks for our entire database topology, which as of June 2012 consists of approximately:
11
+ \Jetpants was created by {Tumblr}[http://www.tumblr.com/] to help manage our database infrastructure. It handles automation tasks for our entire database topology, which as of October 2012 consists of approximately:
12
12
  * 200 dedicated database servers
13
- * 12 global (unsharded) functional pools
14
- * 45 shard pools
15
- * 21 terabytes total of unique relational data on masters
16
- * 60 billion total unique relational rows on masters
13
+ * 5 global (unsharded) functional pools
14
+ * 58 shard pools
15
+ * 28 terabytes total of unique relational data on masters
16
+ * 100 billion total unique relational rows on masters
17
17
 
18
18
  One of the primary requirements for \Jetpants was speed. On our hardware, <b>\Jetpants can divide a 750GB, billion-row shard in half in about six hours</b> -- or even faster if you're diving into thirds or fourths. It can also <b>clone slaves at line speed on gigabit ethernet</b>, including to multiple destinations at once, using a novel "chained copy" approach.
19
19
 
20
- For more background on the initial motivations behind \Jetpants, please see {Evan Elias's presentation at Velocity Europe 2011}[https://github.com/tumblr/jetpants/blob/master/doc/VelocityEurope2011Presentation.pdf?raw=true].
20
+ For more background on the initial motivations behind \Jetpants, please see {Evan Elias's presentation at Percona Live NYC 2012}[https://github.com/tumblr/jetpants/blob/master/doc/PerconaLiveNYC2012Presentation.pdf?raw=true].
21
21
 
22
22
  == COMMAND SUITE FEATURES:
23
23
 
@@ -64,6 +64,8 @@ It is highly recommended that you tie \Jetpants into your site's asset tracker /
64
64
 
65
65
  Other recommended uses of plugins include integration with your site's monitoring system, trending system, query killers, and environment-specific overrides to various core methods.
66
66
 
67
+ If you are using \Collins for asset management, \Jetpants now ships with a plugin that offers integration. Please see doc/jetpants_collins.rdoc ({view on GitHub}[https://github.com/tumblr/jetpants/blob/master/doc/jetpants_collins.rdoc]) for usage.
68
+
67
69
  For more information on how to write plugins and use the Jetpants::CallbackHandler system, please see doc/plugins.rdoc ({view on GitHub}[https://github.com/tumblr/jetpants/blob/master/doc/plugins.rdoc])
68
70
 
69
71
  == FREQUENTLY ASKED QUESTIONS:
@@ -170,10 +170,10 @@ module Jetpants
170
170
  def pools_compact
171
171
  puts
172
172
  Jetpants.shards.each do |s|
173
- puts "[%-12s] %8s to %-11s = %3s GB" % [s.ip, s.min_id, s.max_id, s.data_set_size(true)]
173
+ puts "[%-15s] %8s to %-11s = %3s GB" % [s.ip, s.min_id, s.max_id, s.data_set_size(true)]
174
174
  end
175
175
  Jetpants.functional_partitions.each do |p|
176
- puts "[%-12s] %-23s = %3s GB" % [p.ip, p.name, p.data_set_size(true)]
176
+ puts "[%-15s] %-23s = %3s GB" % [p.ip, p.name, p.data_set_size(true)]
177
177
  end
178
178
  puts
179
179
  end
@@ -12,9 +12,9 @@ Here's a more thorough description of the commands, grouped by function:
12
12
 
13
13
  \Jetpants copies data sets by shutting down the MySQL daemon on the source node and then copying all MySQL files. This is the fastest way to clone MySQL data sets, and is part of the reason why we recommend having 2 standby slaves per pool for high availability.
14
14
 
15
- The copy method in \Jetpants uses a combination of tar, netcat (nc), and pigz. It does not use encryption; we assume you are transferring over a secure local network. When copying to multiple destinations, \Jetpants creates a "copy chain" using tee and a fifo. For more information on this technique, please see {our post on the Tumblr engineering blog}[http://engineering.tumblr.com/post/7658008285/efficiently-copying-files-to-multiple-destinations].
15
+ The copy method in \Jetpants uses a combination of tar, netcat (nc), and whichever compression binary you have specified in your Jetpants configuration file (if any). It does not use encryption; we assume you are transferring over a secure local network. When copying to multiple destinations, \Jetpants creates a "copy chain" using tee and a fifo. For more information on this technique, please see {our post on the Tumblr engineering blog}[http://engineering.tumblr.com/post/7658008285/efficiently-copying-files-to-multiple-destinations].
16
16
 
17
- This command does not require an asset tracker plugin, but DOES require that all your database nodes have pigz (a parallel gzip tool) installed. This may become configurable in a future release.
17
+ This command does not require an asset tracker plugin, but DOES require that all your database nodes have installed whichever compression binary you specified in the Jetpants config file.
18
18
 
19
19
 
20
20
  == Master/slave state changes
@@ -73,7 +73,7 @@ Or if you're deploying a brand new pool in an existing topology:
73
73
  5. Stop replicating writes from the parent shard, and then take the parent pool offline entirely.
74
74
  6. Remove rows that replicated to the wrong child shard. This data will be sparse, since it's only the writes that were made since the shard split process started.
75
75
 
76
- For more information, including diagrams of each step, please see {our presentation at Velocity Europe 2011}[https://github.com/tumblr/jetpants/blob/master/doc/VelocityEurope2011Presentation.pdf?raw=true].
76
+ For more information, including diagrams of each step, please see {our presentation at Percona Live NYC 2012}[https://github.com/tumblr/jetpants/blob/master/doc/PerconaLiveNYC2012Presentation.pdf?raw=true].
77
77
 
78
78
  Separately, \Jetpants also allows you to alter the range of the last shard in your topology. In a range-based sharding scheme, the last shard has a range of X to infinity; eventually this will be too large of a range, so you need to truncate that shard range and create a "new" last shard after it. We call this process "shard cutover".
79
79
 
@@ -20,8 +20,48 @@ mysql_repl_password:: mysql password for replication (mandatory)
20
20
  mysql_root_password:: mysql root password (default: false, indicating that \Jetpants should use /root/.my.cnf instead)
21
21
  mysql_grant_ips:: mysql user manipulations are applied to these IPs (array; mandatory)
22
22
  mysql_grant_privs:: mysql user manipulations grant this set of privileges by default (array; default: \['ALL'])
23
+ compress_with:: command line to perform compression during large file copy operations; see below (default: false)
24
+ decompress_with:: command line to perform decompression during large file copy operations; see below (default: false)
23
25
  export_location:: directory to use for data dumping (default: '/tmp')
24
26
  verify_replication:: raise exception if the actual replication topology differs from Jetpants' understanding of it (ie, disagreement between asset tracker and probed state), or if MySQL's two replication threads are in different states (one running and the other stopped) on a DB node. (default: true. master promotion tool ignores this, since the demoted master may legitimately be dead/offline)
25
27
  plugins:: hash of plugin name => arbitrary plugin data, usually a nested hash of settings (default: \{})
26
28
  ssh_keys:: array of SSH private key file locations, if not using standard id_dsa or id_rsa. Passed directly to Net::SSH.start's :keys parameter (default: nil)
27
29
  sharded_tables:: array of name => \{sharding_key=>X, chunks=>Y} hashes, describing all tables on shards. Required by shard split/rebuild processes (default: \[])
30
+
31
+ == Compression
32
+
33
+ \Jetpants has the ability to use compression during large file copy operations, which are performed by commands "jetpants clone_slave" and "jetpants shard_split". Compression is disabled by default in \Jetpants unless you specify a compression program to use via the <tt>compress_with</tt> and <tt>decompress_with</tt> config options. It is highly recommended that you do so, in order to speed up these operations when working with large data sets.
34
+
35
+ The command lines that you specify should accept input from STDIN and supply output to STDOUT, because they will be used in the middle of a series of piped commands. The binary specified should be in root's PATH on all database nodes. We recommend use of a parallel compression tool, to take advantage of multiple cores.
36
+
37
+ You will need to do some profiling to determine the best tool to use for your hardware and data set; there's no universal best choice of compression algorithm or settings.
38
+
39
+ Some example values of these parameters are as follows:
40
+
41
+ === Disable compression (default)
42
+
43
+ compress_with: false
44
+ decompress_with: false
45
+
46
+ === pigz
47
+ pigz is an open-source parallel gzip tool by Mark Adler. It is available as a package in several Linux distros. It performs well, but is very CPU intensive. More information: http://zlib.net/pigz/
48
+
49
+ compress_with: pigz
50
+ decompress_with: pigz -d
51
+
52
+ === qpress
53
+ qpress is a multi-threaded portable file archiver using QuickLZ. A prebuilt package is not available for most Linux distros due to licensing reasons, but a binary can be downloaded from http://www.quicklz.com/. It performs extremely well, especially once tuned.
54
+
55
+ In order to read from STDIN and write to STDOUT, use <tt>qpress -io</tt>. In this case qpress still requires a filename during compression, even though it is unused. Decompression does not have the same requirement.
56
+
57
+ The example below uses 4 threads and a block size of 32768KB.
58
+
59
+ compress_with: qpress -ioT4K32768 dummyfilename
60
+ decompress_with: qpress -dioT4
61
+
62
+ === lzop
63
+ lzop is a less CPU-intensive compressor. lzop is still single-threaded in v1.x, so its performance may not be ideal for the \Jetpants use-case. Multithreading is planned for v2.x. More information: http://www.lzop.org/
64
+
65
+ compress_with: lzop
66
+ decompress_with: lzop -d
67
+
@@ -72,7 +72,11 @@ The main downside to the range-based approach is lack of even distribution of "h
72
72
 
73
73
  \Jetpants clones slaves by stopping replication, shutting down the MySQL daemon, and then copying the raw files to the destination(s). This is the fastest way to get a consistent clone of a data set in MySQL. After the copy operation is complete, we start MySQL back up on the source and destinations, and then make the destination instances start slaving at the appropriate binlog coordinates.
74
74
 
75
- We perform the copy operation using a combination of tar (for archiving), pigz (for fast compression), and nc (for transferring the data over the network). If there are multiple destinations, we create a serial "copy chain" using tee and a fifo.
75
+ We perform the copy operation using a combination of:
76
+ * <tt>tar</tt>, for archiving
77
+ * a compression tool, if specified in your \Jetpants config file; we recommend <tt>qpress</tt> or <tt>pigz</tt>
78
+ * <tt>nc</tt> (netcat), for transferring the data over the network
79
+ * If there are multiple destinations, we create a serial "copy chain" using <tt>tee</tt> and a FIFO.
76
80
 
77
81
  Please note that we don't encrypt the data in this process, so we assume you are using it on a private LAN or over a VPN tunnel.
78
82
 
@@ -115,7 +119,7 @@ For any given operation that requires an asset tracker, there's one of two reaso
115
119
 
116
120
  * The operation inherently involves generating a new configuration for your application -- for example, setting a shard to read-only or promoting a standby slave to an active slave. These operations are meaningless outside of your application, since MySQL has no notion of "standby slave" or "degraded shard". \Jetpants has a notion of these things, but needs to persist the information somewhere, and it makes more sense to have \Jetpants relay this information to an external hardware management tool rather than maintain a separate (and potentially conflicting) source of truth.
117
121
 
118
- If you have enough servers to be using a sharded architecture, you hopefully already have some sort of hardware management / asset tracker system in place. \Jetpants is designed to be integrated with this system, but since every site runs something different, this requires that you write some custom plugin code to achieve.
122
+ If you have enough servers to be using a sharded architecture, you hopefully already have some sort of hardware management / asset tracker system in place. \Jetpants is designed to be integrated with this system, but since every site runs something different, this requires that you write some custom plugin code to achieve. (Unless you use \Collins for tracking hardware, in which case you can use the bundled jetpants_collins plugin.)
119
123
 
120
124
 
121
125
  == Can I use \Jetpants with PostgreSQL?
@@ -0,0 +1,95 @@
1
+ = jetpants_collins
2
+
3
+ == OVERVIEW:
4
+
5
+ This \Jetpants plugin offers integration with the \Collins hardware asset tracking system. This allows \Jetpants to automatically query the list of pools, shards, hosts, and databases in your topology at start-up time. Furthermore, every change you make to your topology using \Jetpants (master promotions, shard splits, new slaves cloned, etc) will automatically be reflected in \Collins immediately.
6
+
7
+ == CONFIGURATION:
8
+
9
+ This plugin has a number of configuration options, some of which are mandatory.
10
+
11
+ user:: \Collins account username for \Jetpants to use (required)
12
+ password:: \Collins account password (required)
13
+ url:: \Collins URL (required)
14
+ timeout:: \Collins client timeout, in seconds (default: 30)
15
+ datacenter:: \Collins data center name that we're running \Jetpants in the context of (required if multi-datacenter, omit otherwise)
16
+ remote_lookup:: Supply "remoteLookup" parameter for \Collins requests, to search multiple datacenters (default: false)
17
+
18
+ To enable this plugin, add it to your \Jetpants configuration file (either <tt>/etc/jetpants.yaml</tt> or <tt>~/.jetpants.yaml</tt>). For example, in a single-datacenter environment, you configuration might look like this:
19
+
20
+ # ... rest of Jetpants config here
21
+
22
+ plugins:
23
+ jetpants_collins:
24
+ user: jetpants
25
+ password: xxx
26
+ url: http://collins.yourdomain.com:8080
27
+
28
+ # ... other plugins configured here
29
+
30
+ == ASSUMPTIONS AND REQUIREMENTS:
31
+
32
+ Use of this plugin assumes that you already have \Collins set up, and have performed hardware intake for all your servers already.
33
+
34
+ This plugin also makes some assumptions about the way in which you use \Collins, namely:
35
+
36
+ * All Linux servers have a TYPE of SERVER_NODE.
37
+ * All MySQL database server hosts will have a PRIMARY_ROLE of DATABASE.
38
+ * All MySQL database server hosts that are in-use will have a STATUS of either ALLOCATED or MAINTENANCE.
39
+ * All MySQL database server hosts that are in-use will have a POOL set matching the name of their pool/shard, and a SECONDARY_ROLE set matching their \Jetpants role within the pool (MASTER, ACTIVE_SLAVE, STANDBY_SLAVE, or BACKUP_SLAVE).
40
+ * You can initially assign PRIMARY_ROLE, STATUS, POOL, and SECONDARY_ROLE to database servers somewhat automatically; see GETTING STARTED, below.
41
+ * All database server hosts that are "spares" (not yet in use, but ready for use in shard splits, shard cutover, or slave cloning) need to have a STATUS of PROVISIONED. These nodes must meet the requirements of spares as defined by the REQUIREMENTS doc that comes with \Jetpants. They should NOT have a POOL set, but they may have a ROLE set to either MASTER or STANDBY_SLAVE. The role will be used to select spare nodes for shard splits and shard cutover.
42
+ * Database server hosts may optionally have an attribute called SLAVE_WEIGHT. The default weight, if omitted, is 100. This field has no effect in \Jetpants, but can be used by your custom configuration generator as needed, if your application supports a notion of different weights for slave selection.
43
+ * Arbitrary metadata regarding pools and shards will be stored in assets with a TYPE of CONFIGURATION. These assets will have a POOL matching the pool's name, a TAG matching the pool's name but prefixed with 'mysql-', a STATUS reflecting the pool's state, and a PRIMARY_ROLE of either MYSQL_POOL or MYSQL_SHARD depending on the type of pool. You can make jetpants_collins create these automatically; see GETTING STARTED, below.
44
+
45
+ Please note that jetpants_collins does not generate application configuration files, because every web app/framework uses a different format. You will need to write a custom plugin to generate a configuration file for your application as needed, by overriding the Topology#write_config method.
46
+
47
+ == GETTING STARTED:
48
+
49
+ Once you've met all of the requirements listed in the previous section, the next step is to tell \Jetpants about your existing pools/shards via <tt>jetpants console</tt>. You only need to do this process once.
50
+
51
+ Adding functional partitions (global / unsharded pools):
52
+
53
+ # Create the pool object, specifying pool name and IP of current master
54
+ p = Pool.new('my-pool-name', '10.42.3.4')
55
+
56
+ # Tell Jetpants about IPs of any existing active slaves (read slaves), if any.
57
+ # For example, say this pool has 2 active slaves and 2 standby slaves. \Jetpants
58
+ # can automatically figure out which slaves exist, but won't automatically know
59
+ # which ones are active for reads, so you need to tell it.
60
+ p.has_active_slave('10.42.3.30')
61
+ p.has_active_slave('10.42.3.32')
62
+
63
+ # Sync the information to Collins
64
+ p.sync_configuration
65
+
66
+ Repeat this process for each functional partition, if you have more than one.
67
+
68
+ Adding shard pools:
69
+
70
+ # Create and sync each shard object, specifying ID range and IP of current master
71
+ Shard.new( 1, 1000000, '10.42.4.10' ).sync_configuration
72
+ Shard.new(1000001, 2000000, '10.42.3.112').sync_configuration
73
+ Shard.new(2000001, 4000000, '10.42.3.45' ).sync_configuration
74
+ Shard.new(4000001, 'INFINITY', '10.42.3.26' ).sync_configuration
75
+
76
+ The max ID of the last shard must be 'INFINITY' in order for <tt>jetpants shard_cutover</tt> to work.
77
+
78
+
79
+ == MULTI-DATACENTER SUPPORT:
80
+
81
+ This plugin offers preliminary support for multi-datacenter \Collins deployments. The assumed topology is:
82
+ * Each datacenter has its own copy of \Collins, and they're configured to talk to each other
83
+ * Each datacenter has a node to run \Jetpants from, with the jetpants_collins configuration options differing between datacenters
84
+ * Every database pool has only one true, writable master. This is located in any datacenter.
85
+ * The true master may have several slaves in its own datacenter.
86
+ * The true master may have slaves in other datacenters, but should only have <i>one direct slave per remote datacenter</i>. These remote slaves should have a SECONDARY_ROLE of MASTER in their datacenter's copy of \Collins, and they may have additional slaves of their own (tiered replication).
87
+ * In other words, each datacenter -- and hence each copy of \Collins -- still has at most one MASTER per database pool. However only one of these nodes is the true, writable master; the others are actually slaves of master, and are read-only.
88
+
89
+ Also, jetpants_collins currently enforces several restrictions on interacting with databases in remote datacenters, to simplify handling of tiered replication:
90
+
91
+ * jetpants_collins won't change Collins attributes on remote server node assets. If you need to manipulate those assets, do it from the copy of \Jetpants and copy of \Collins in that datacenter.
92
+ * If a local slave node has a master in a remote datacenter, it is ignored/hidden by jetpants_collins. In other words, each datacenter's master is viewed as a "real" master, even if it's actually slaving from another remote master.
93
+ * If a local master node has a slave in a remote datacenter, it's treated as a backup_slave, in order to prevent cross-datacenter master promotions. If any of these remote slaves have slaves of their own, they're ignored/hidden by jetpants_collins.
94
+
95
+ Due to the nature of this implementation, it works best for setups with 1 active datacenter and 1 or more passive datacenters. This support will be expanded in future releases to better capture the tiered replication roles and support active/active topologies. At that time, these restrictions/simplifications will be lifted wherever possible.
@@ -8,9 +8,9 @@ It is highly recommended that you tie \Jetpants into your site's asset tracker (
8
8
 
9
9
  Other recommended uses of plugins include integration with your site's monitoring system, trending system, query killers, and environment-specific overrides to various core methods.
10
10
 
11
- == Asset tracker
11
+ == Asset tracker examples
12
12
 
13
- === Example
13
+ === simple_tracker
14
14
 
15
15
  We supply a sample plugin called simple_tracker, demonstrating how to go about writing a very basic asset-tracking plugin. This plugin just uses an internal JSON file to keep track of database topology/state, and separately writes app configuration to a YAML file. This isn't actually suitable for production use, but should provide a reasonable starting point for learning the plugin system and building a real asset tracker.
16
16
 
@@ -24,8 +24,11 @@ When you first start using simple_tracker, there will be no pools, shards, or sp
24
24
  * <tt>jetpants add_shard</tt>
25
25
  * <tt>jetpants add_spare</tt>
26
26
 
27
+ === jetpants_collins
27
28
 
28
- === Methods to override
29
+ We also supply a plugin called jetpants_collins, offering integration with the Collins asset management software. This is fully intended for production use, and powers our automation at Tumblr. For more information on jetpants_collins, please see jetpants_collins.rdoc ({view on GitHub}[https://github.com/tumblr/jetpants/blob/master/doc/jetpants_collins.rdoc]).
30
+
31
+ === Rolling your own
29
32
 
30
33
  If you're writing your own asset-tracker plugin, you will need to override the following methods:
31
34
 
@@ -58,7 +61,7 @@ You may also want to override or implement these, though it's not strictly manda
58
61
 
59
62
  === Name and location
60
63
 
61
- If you define a plugin named "foo" in your \Jetpants config file, on startup \Jetpants will first attempt to require 'foo/foo', and failing that simply 'foo'.
64
+ If you define a plugin named "foo" in your \Jetpants config file, on startup \Jetpants will first attempt to require 'foo/foo' (for loading bundled plugins in <tt>jetpants/plugins/<>tt>), if that fails then simply 'foo' (for loading a stand-alone gem).
62
65
 
63
66
  Plugins may be located anywhere on your Ruby load path, so packing them as standard gems should work perfectly. You may want to prefix the gem name with "\jetpants_" to avoid conflicts with other gems. \Jetpants also adds its plugins directory to its load path automatically, so that any pre-bundled plugins (like simple_tracker) can be loaded easily.
64
67
 
@@ -16,7 +16,7 @@ Plugins may freely override these assumptions, and upstream patches are very wel
16
16
  * Required Linux binaries that must be installed and in root's PATH on all of your database machines:
17
17
  * <tt>service</tt>, a wrapper around init scripts, supporting syntax <tt>service mysql start</tt>, <tt>service mysql status</tt>, etc. Some distros include this by default (typically as /sbin/service or /usr/sbin/service) while others offer it as a package. Implementation varies slightly between distros; currently \Jetpants expects <tt>service mysql status</tt> output to include either "not running" (RHEL/Centos) or "stop/waiting" (Ubuntu) in the output if the MySQL server is not running.
18
18
  * <tt>nc</tt>, also known as netcat, a tool for piping data to or from a socket.
19
- * <tt>pigz</tt>, an open-source single-binary parallel gzip tool by Mark Adler. A future version of \Jetpants will allow pluggable compression tools, but at present we strictly use <tt>pigz</tt> for compression in all file copy operations.
19
+ * Whichever compression tool you've specified in the \Jetpants config file for the <tt>compress_with</tt> and <tt>decompress_with</tt> options, if any. (if omitted, compression will not be used for file copy operations.)
20
20
  * InnoDB / Percona XtraDB for storage engine. \Jetpants has not been tested with MyISAM, since \Jetpants is geared towards huge tables, and MyISAM is generally a bad fit.
21
21
  * All MySQL instances run on port 3306, with only one instance per logical machine.
22
22
  * A plugin could override this easily, but would require you to use the --report-host option on all slaves running MySQL 5.1, so that crawling the replication topology is possible. It would also have to override various methods that specify the MySQL init script location, config file location, data directory, etc.
@@ -64,4 +64,4 @@ By default, the <tt>standby_slaves_per_pool</tt> config option is set to 2. This
64
64
 
65
65
  A "spare" machine in \Jetpants should be in a clean-slate state: MySQL should be installed and have the proper grants and root password, but there should be no data on these machines, and they should not be slaving. \Jetpants will set up replication appropriately when it assigns the nodes to their appropriate pools.
66
66
 
67
- For more information on the shard split process implemented by \Jetpants, including diagrams of each stage of the process, please see {Evan Elias's presentation at Velocity Europe 2011}[https://github.com/tumblr/jetpants/blob/master/doc/VelocityEurope2011Presentation.pdf?raw=true], starting at slide 19.
67
+ For more information on the shard split process implemented by \Jetpants, including diagrams of each stage of the process, please see {Evan Elias's presentation at Percona Live NYC 2012}[https://github.com/tumblr/jetpants/blob/master/doc/PerconaLiveNYC2012Presentation.pdf?raw=true], starting at slide 20.
@@ -27,6 +27,11 @@ mysql_grant_privs:
27
27
  # has higher capacity anyway.
28
28
  export_location: /some/path/on/root/partition
29
29
 
30
+ # If you want to speed up large copy operations in Jetpants, supply relevant
31
+ # command lines here. See configuration.rdoc for more information on formatting.
32
+ compress_with: false
33
+ decompress_with: false
34
+
30
35
  # List all tables defined on the shards, along with what column name corresponds
31
36
  # to your app's sharding key (to determine which shard a given row lives on),
32
37
  # and how many "chunks" to split the data set into when doing an import or
@@ -36,6 +36,8 @@ module Jetpants
36
36
  'plugins' => {}, # hash of plugin name => arbitrary plugin data (usually a nested hash of settings)
37
37
  'ssh_keys' => nil, # array of SSH key file locations
38
38
  'sharded_tables' => [], # array of name => {sharding_key=>X, chunks=>Y} hashes
39
+ 'compress_with' => false, # command line to use for compression in large file transfers
40
+ 'decompress_with' => false, # command line to use for decompression in large file transfers
39
41
  }
40
42
  %w(/etc/jetpants.yaml ~/.jetpants.yml ~/.jetpants.yaml).each do |path|
41
43
  overrides = YAML.load_file(File.expand_path path) rescue {}
@@ -105,7 +105,15 @@ module Jetpants
105
105
  sleep(interval)
106
106
  global_status[:Connections].to_i - conn_counter > threshold
107
107
  end
108
-
108
+
109
+ # Confirms the binlog of this node has not moved during a duration
110
+ # of [interval] seconds.
111
+ def taking_writes?(interval=5.0)
112
+ coords = binlog_coordinates
113
+ sleep(interval)
114
+ coords != binlog_coordinates
115
+ end
116
+
109
117
  # Returns true if this instance appears to be a standby slave,
110
118
  # false otherwise. Note that "standby" in this case is based
111
119
  # on whether the slave is actively receiving connections, not
@@ -249,9 +257,17 @@ module Jetpants
249
257
  @slaves = []
250
258
  slaves_mutex = Mutex.new
251
259
  processes = mysql_root_cmd("SHOW PROCESSLIST", :terminator => ';').split("\n")
252
- processes.grep(/Binlog Dump/).concurrent_each do |p|
260
+
261
+ # We have to de-dupe the output, since it's possible in weird edge cases for
262
+ # the same slave to be listed twice
263
+ ips = {}
264
+ processes.grep(/Binlog Dump/).each do |p|
253
265
  tokens = p.split
254
266
  ip, dummy = tokens[2].split ':'
267
+ ips[ip] = true
268
+ end
269
+
270
+ ips.keys.concurrent_each do |ip|
255
271
  db = ip.to_db
256
272
  db.probe
257
273
  slaves_mutex.synchronize {@slaves << db if db.master == self}
@@ -192,7 +192,6 @@ module Jetpants
192
192
  ###### Directory Copying / Listing / Comparison methods ####################
193
193
 
194
194
  # Quickly and efficiently recursively copies a directory to one or more target hosts.
195
- # Requires that pigz is installed on source (self) and all targets.
196
195
  # base_dir:: is base directory to copy from the source (self). Also the default destination base
197
196
  # directory on the targets, if not supplied via next param.
198
197
  # targets:: is one of the following:
@@ -225,6 +224,14 @@ module Jetpants
225
224
  file_list = filenames.join ' '
226
225
  port = (options[:port] || 7000).to_i
227
226
 
227
+ if Jetpants.compress_with || Jetpants.decompress_with
228
+ comp_bin = Jetpants.compress_with.split(' ')[0]
229
+ confirm_installed comp_bin
230
+ output "Using #{comp_bin} for compression"
231
+ else
232
+ output "Compression disabled -- no compression method specified in Jetpants config file"
233
+ end
234
+
228
235
  # On each destination host, do any initial setup (and optional validation/erasing),
229
236
  # and then listen for new files. If there are multiple destination hosts, all of them
230
237
  # except the last will use tee to "chain" the copy along to the next machine.
@@ -233,7 +240,10 @@ module Jetpants
233
240
  dir = destinations[t]
234
241
  raise "Directory #{t}:#{dir} looks suspicious" if dir.include?('..') || dir.include?('./') || dir == '/' || dir == ''
235
242
 
236
- t.confirm_installed 'pigz'
243
+ if Jetpants.compress_with || Jetpants.decompress_with
244
+ decomp_bin = Jetpants.decompress_with.split(' ')[0]
245
+ t.confirm_installed decomp_bin
246
+ end
237
247
  t.ssh_cmd "mkdir -p #{dir}"
238
248
 
239
249
  # Check if contents already exist / non-empty.
@@ -244,8 +254,9 @@ module Jetpants
244
254
  dirlist.each {|name, size| raise "File #{name} exists on destination and has nonzero size!" if size.to_i > 0}
245
255
  end
246
256
 
257
+ decompression_pipe = Jetpants.decompress_with ? "| #{Jetpants.decompress_with}" : ''
247
258
  if i == 0
248
- workers << Thread.new { t.ssh_cmd "cd #{dir} && nc -l #{port} | pigz -d | tar xvf -" }
259
+ workers << Thread.new { t.ssh_cmd "cd #{dir} && nc -l #{port} #{decompression_pipe} | tar xv" }
249
260
  t.confirm_listening_on_port port
250
261
  t.output "Listening with netcat."
251
262
  else
@@ -254,16 +265,16 @@ module Jetpants
254
265
  workers << Thread.new { t.ssh_cmd "cd #{dir} && mkfifo #{fifo} && nc #{tt.ip} #{port} <#{fifo} && rm #{fifo}" }
255
266
  checker_th = Thread.new { t.ssh_cmd "while [ ! -p #{dir}/#{fifo} ] ; do sleep 1; done" }
256
267
  raise "FIFO not found on #{t} after 10 tries" unless checker_th.join(10)
257
- workers << Thread.new { t.ssh_cmd "cd #{dir} && nc -l #{port} | tee #{fifo} | pigz -d | tar xvf -" }
268
+ workers << Thread.new { t.ssh_cmd "cd #{dir} && nc -l #{port} | tee #{fifo} #{decompression_pipe} | tar xv" }
258
269
  t.confirm_listening_on_port port
259
270
  t.output "Listening with netcat, and chaining to #{tt}."
260
271
  end
261
272
  end
262
273
 
263
274
  # Start the copy chain.
264
- confirm_installed 'pigz'
265
275
  output "Sending files over to #{targets[0]}: #{file_list}"
266
- ssh_cmd "cd #{base_dir} && tar vc #{file_list} | pigz | nc #{targets[0].ip} #{port}"
276
+ compression_pipe = Jetpants.compress_with ? "| #{Jetpants.compress_with}" : ''
277
+ ssh_cmd "cd #{base_dir} && tar vc #{file_list} #{compression_pipe} | nc #{targets[0].ip} #{port}"
267
278
  workers.each {|th| th.join}
268
279
  output "File copy complete."
269
280
 
@@ -17,6 +17,32 @@ module Enumerable
17
17
  def concurrent_each_with_index(&block)
18
18
  each_with_index.concurrent_each(&block)
19
19
  end
20
+
21
+ # Alternative for concurrent_map which also has the ability to limit how
22
+ # many threads are used. Much less elegant :(
23
+ def limited_concurrent_map(thread_limit=40)
24
+ lock = Mutex.new
25
+ group = ThreadGroup.new
26
+ items = to_a
27
+ results = []
28
+ pos = 0
29
+
30
+ # Number of concurrent threads is the lowest of: self length, supplied thread limit, global concurrency limit
31
+ [items.length, thread_limit, Jetpants.max_concurrency].min.times do
32
+ th = Thread.new do
33
+ while true do
34
+ my_pos = nil
35
+ lock.synchronize { my_pos = pos; pos += 1}
36
+ break unless my_pos < items.length
37
+ my_result = yield items[my_pos]
38
+ lock.synchronize { results[my_pos] = my_result }
39
+ end
40
+ end
41
+ group.add th
42
+ end
43
+ group.list.each {|th| th.join}
44
+ results
45
+ end
20
46
  end
21
47
 
22
48
  # Add Jetpants-specific conversion methods to Object.
@@ -177,14 +177,14 @@ module Jetpants
177
177
  end
178
178
 
179
179
  binlog_pos = extended_info ? details[@master][:coordinates].join(':') : ''
180
- print "\tmaster = %-13s %-30s %s\n" % [@master.ip, @master.hostname, binlog_pos]
180
+ print "\tmaster = %-15s %-30s %s\n" % [@master.ip, @master.hostname, binlog_pos]
181
181
 
182
182
  [:active, :standby, :backup].each do |type|
183
183
  slave_list = slaves(type)
184
184
  slave_list.sort.each_with_index do |s, i|
185
185
  binlog_pos = extended_info ? details[s][:coordinates].join(':') : ''
186
186
  slave_lag = extended_info ? "lag=#{details[s][:lag]}" : ''
187
- print "\t%-7s slave #{i + 1} = %-13s %-30s %-26s %s\n" % [type, s.ip, s.hostname, binlog_pos, slave_lag]
187
+ print "\t%-7s slave #{i + 1} = %-15s %-30s %-26s %s\n" % [type, s.ip, s.hostname, binlog_pos, slave_lag]
188
188
  end
189
189
  end
190
190
  true