lib/gizzmo.rb in gizzmo-0.12.1 vs lib/gizzmo.rb in gizzmo-0.13.0
- old
+ new
@@ -2,37 +2,60 @@
$: << File.dirname(__FILE__)
class HelpNeededError < RuntimeError; end
require "optparse"
require "ostruct"
require "gizzard"
+require "shellwords"
require "yaml"
DOC_STRINGS = {
+ "add-host" => "Add a remote cluster host to replicate to. Format: cluster:host:port.",
"addforwarding" => "Add a forwarding from a graph_id / base_source_id to a given shard.",
"addlink" => "Add a relationship link between two shards.",
+ "add-partition" => "Rebalance the cluster by appending new partitions to the current topology.",
+ "busy" => "List any shards with a busy flag set.",
+ "copy" => "Copy between the given list of shards. Given a set of shards, it will copy and repair to ensure that all shards have the latest data.",
"create" => "Create shard(s) of a given Java/Scala class. If you don't know the list of available classes, you can just try a bogus class, and the exception will include a list of valid classes.",
+ "create-table" => "Create tables in an existing cluster.",
+ "delete" => "", # TODO: Undocumented
+ "deleteforwarding" => "", # TODO: Undocumented
+ "diff-shards" => "Log differences between n shards",
"drill" => "Show shard trees for replicas of a given structure signature (from 'report').",
"dump" => "Show shard trees for given table ids.",
"find" => "Show all shards with a given hostname.",
+ "finish-migrate" => "", # TODO: Undocumented
"finish-replica" => "Remove the write-only barrier in front of a shard that's finished being copied after 'setup-replica'.",
"flush" => "Flush error queue for a given priority.",
"forwardings" => "Get a list of all forwardings.",
"hosts" => "List hosts used in shard names in the forwarding table and replicas.",
"info" => "Show id/class/busy for shards.",
"inject" => "Inject jobs (as literal json) into the server. Jobs can be linefeed-terminated from stdin, or passed as arguments. Priority is server-defined, but typically lower numbers (like 1) are lower priority.",
- "links" => "List parent & child links for shards.",
+ "links" => "List parent and child links for shards.",
+ "list-hosts" => "List remote cluster hosts being replicated to.",
"lookup" => "Lookup the shard id that holds the record for a given table / source_id.",
- "markbusy" => "Mark a shard as busy.",
+ "markbusy" => "Mark a list of shards as busy.",
+ "markunbusy" => "Mark a list of shards as not busy.",
"pair" => "Report the replica pairing structure for a list of hosts.",
+ "rebalance" => "Restructure and move shards to reflect a new list of tree structures.",
"reload" => "Instruct application servers to reload the nameserver state.",
- "repair-shards" => "Reconcile n shards by detecting differences and rescheduling them",
- "diff-shards" => "Log differences between n shards",
+ "remove-host" => "Remove a remote cluster host being replicate to.",
+ "remove-partition" => "Rebalance the cluster by removing the provided partitions from the current topology.",
+ "repair-tables" => "Reconcile all the shards in the given tables (supplied with -T) by detecting differences and writing them back to shards as needed.",
"report" => "Show each unique replica structure for a given list of shards. Usually this shard list comes from << gizzmo forwardings | awk '{ print $3 }' >>.",
+ "setup-migrate" => "", # TODO: Undocumented
"setup-replica" => "Add a replica to be parallel to an existing replica, in write-only mode, ready to be copied to.",
+ "subtree" => "Show the subtree of replicas given a shard id.",
+ "tables" => "List the table IDs known by this nameserver.",
+ "topology" => "List the full topologies known for the table IDs provided.",
+ "transform" => "Transform from one topology to another.",
+ "transform-tree" => "Transforms given forwardings to the corresponding given tree structure.",
+ "unlink" => "Remove a link from one shard to another.",
+ "unwrap" => "Remove a wrapper created with wrap.",
"wrap" => "Wrapping creates a new (virtual, e.g. blocking, replicating, etc.) shard, and relinks SHARD_ID_TO_WRAP's parent links to run through the new shard.",
}
+
ORIGINAL_ARGV = ARGV.dup
zero = File.basename($0)
# Container for parsed options
global_options = OpenStruct.new
@@ -44,15 +67,16 @@
subcommand_options = OpenStruct.new
# Leftover arguments
argv = nil
+
GIZZMO_VERSION = File.read(File.dirname(__FILE__) + "/../VERSION") rescue "unable to read version file"
begin
YAML.load_file(File.join(ENV["HOME"], ".gizzmorc")).each do |k, v|
- global_options.send("#{k}=", v)
+ #global_options.send("#{k}=", v)
end
rescue Errno::ENOENT
# Do nothing...
rescue => e
abort "Unknown error loading ~/.gizzmorc: #{e.message}"
@@ -84,10 +108,17 @@
def load_config(options, filename)
YAML.load(File.open(filename)).each do |k, v|
k = "hosts" if k == "host"
v = v.split(",").map {|h| h.strip } if k == "hosts"
+ if k == "template_options"
+ opts = {}
+ v.each do |k1, v1|
+ opts[k1.to_sym] = v1
+ end
+ v = opts
+ end
options.send("#{k}=", v)
end
end
def add_scheduler_opts(subcommand_options, opts)
@@ -98,18 +129,42 @@
(subcommand_options.scheduler_options ||= {})[:copies_per_host] = c.to_i
end
opts.on("--poll-interval=SECONDS", "Sleep SECONDS between polling for copy status") do |c|
(subcommand_options.scheduler_options ||= {})[:poll_interval] = c.to_i
end
- opts.on("--copy-wrapper=TYPE", "Wrap copy destination shards with TYPE. default WriteOnlyShard") do |t|
+ opts.on("--copy-wrapper=SHARD_TYPE", "Wrap copy destination shards with SHARD_TYPE. default BlockedShard") do |t|
(subcommand_options.scheduler_options ||= {})[:copy_wrapper] = t
end
+ opts.on("--skip-copies", "Do transformation without copying. WARNING: This is VERY DANGEROUS if you don't know what you're doing!") do
+ (subcommand_options.scheduler_options ||= {})[:skip_copies] = true
+ end
opts.on("--no-progress", "Do not show progress bar at bottom.") do
(subcommand_options.scheduler_options ||= {})[:no_progress] = true
end
+ opts.on("--batch-finish", "Wait until all copies are complete before cleaning up unneeded links and shards") do
+ (subcommand_options.scheduler_options ||= {})[:batch_finish] = true
+ end
end
+def add_template_opts(subcommand_options, opts)
+ opts.on("--virtual=SHARD_TYPE", "Concrete shards will exist behind a virtual shard of this SHARD_TYPE (default ReplicatingShard)") do |t|
+ (subcommand_options.template_options ||= {})[:replicating] = t
+ end
+
+ opts.on("-c", "--concrete=SHARD_TYPE", "Concrete shards will be this SHARD_TYPE (REQUIRED when using --simple)") do |t|
+ (subcommand_options.template_options ||= {})[:concrete] = t
+ end
+
+ opts.on("--source-type=DATA_TYPE", "The data type for the source column. (REQUIRED when using --simple)") do |t|
+ (subcommand_options.template_options ||= {})[:source_type] = t
+ end
+
+ opts.on("--dest-type=DATA_TYPE", "The data type for the destination column. (REQUIRED when using --simple)") do |t|
+ (subcommand_options.template_options ||= {})[:dest_type] = t
+ end
+end
+
subcommands = {
'create' => OptionParser.new do |opts|
opts.banner = "Usage: #{zero} create [options] CLASS_NAME SHARD_ID [MORE SHARD_IDS...]"
separators(opts, DOC_STRINGS["create"])
@@ -243,25 +298,20 @@
opts.on("--fnv", "Use FNV1A_64 hash on source") do
subcommand_options.hash_function = :fnv
end
end,
'copy' => OptionParser.new do |opts|
- opts.banner = "Usage: #{zero} copy SOURCE_SHARD_ID DESTINATION_SHARD_ID"
+ opts.banner = "Usage: #{zero} copy SHARD_IDS..."
separators(opts, DOC_STRINGS["copy"])
end,
- 'repair-shards' => OptionParser.new do |opts|
- opts.banner = "Usage: #{zero} repair-shards SHARD_IDS..."
- separators(opts, DOC_STRINGS["repair-shards"])
+ 'repair-tables' => OptionParser.new do |opts|
+ opts.banner = "Usage: #{zero} -T TABLE,... repair-tables [options]"
+ separators(opts, DOC_STRINGS["repair-tables"])
+ opts.on("--max-copies=COUNT", "Limit max simultaneous copies to COUNT.") do |c|
+ subcommand_options.num_copies = c.to_i
+ end
end,
- 'diff-shards' => OptionParser.new do |opts|
- opts.banner = "Usage: #{zero} diff-shards SHARD_IDS..."
- separators(opts, DOC_STRINGS["diff-shards"])
- end,
- 'diff-shards' => OptionParser.new do |opts|
- opts.banner = "Usage: #{zero} diff-shards SOURCE_SHARD_ID DESTINATION_SHARD_ID"
- separators(opts, DOC_STRINGS["diff-shards"])
- end,
'busy' => OptionParser.new do |opts|
opts.banner = "Usage: #{zero} busy"
separators(opts, DOC_STRINGS["busy"])
end,
'setup-replica' => OptionParser.new do |opts|
@@ -315,52 +365,77 @@
opts.on("--shards", "Show topology by root shard ids instead of counts") do
subcommand_options.root_shards = true
end
end,
'transform-tree' => OptionParser.new do |opts|
- opts.banner = "Usage: #{zero} transform-tree [options] TEMPLATE ROOT_SHARD_ID"
+ opts.banner = "Usage: #{zero} transform-tree [options] TEMPLATE ROOT_SHARD_ID ..."
separators(opts, DOC_STRINGS['transform-tree'])
add_scheduler_opts subcommand_options, opts
+ add_template_opts subcommand_options, opts
opts.on("-q", "--quiet", "Do not display transformation info (only valid with --force)") do
subcommand_options.quiet = true
end
end,
'transform' => OptionParser.new do |opts|
opts.banner = "Usage: #{zero} transform [options] FROM_TEMPLATE TO_TEMPLATE ..."
separators(opts, DOC_STRINGS['transform'])
add_scheduler_opts subcommand_options, opts
+ add_template_opts subcommand_options, opts
opts.on("-q", "--quiet", "Do not display transformation info (only valid with --force)") do
subcommand_options.quiet = true
end
end,
'rebalance' => OptionParser.new do |opts|
opts.banner = "Usage: #{zero} rebalance [options] WEIGHT TO_TEMPLATE ..."
separators(opts, DOC_STRINGS["rebalance"])
add_scheduler_opts subcommand_options, opts
+ add_template_opts subcommand_options, opts
opts.on("-q", "--quiet", "Do not display transformation info (only valid with --force)") do
subcommand_options.quiet = true
end
end,
+ 'add-partition' => OptionParser.new do |opts|
+ opts.banner = "Usage: #{zero} add-partition [options] TEMPLATE ..."
+ separators(opts, DOC_STRINGS["add-partition"])
+
+ add_scheduler_opts subcommand_options, opts
+
+ opts.on("-q", "--quiet", "Do not display transformation info (only valid with --force)") do
+ subcommand_options.quiet = true
+ end
+ end,
+ 'remove-partition' => OptionParser.new do |opts|
+ opts.banner = "Usage: #{zero} remove-partition [options] TEMPLATE ..."
+ separators(opts, DOC_STRINGS["remove-partition"])
+
+ add_scheduler_opts subcommand_options, opts
+
+ opts.on("-q", "--quiet", "Do not display transformation info (only valid with --force)") do
+ subcommand_options.quiet = true
+ end
+ end,
'create-table' => OptionParser.new do |opts|
opts.banner = "Usage: #{zero} create-table [options] WEIGHT TEMPLATE ..."
separators(opts, DOC_STRINGS["create-table"])
+ add_template_opts subcommand_options, opts
+
opts.on("--shards=COUNT", "Create COUNT shards for each table.") do |count|
subcommand_options.shards = count.to_i
end
- opts.on("--min-id=NUM", "Set lower bound on the id space to NUM (default min signed long: -1 * 2^63)") do |min_id|
+ opts.on("--min-id=NUM", "Set lower bound on the id space to NUM (default 0)") do |min_id|
subcommand_options.min_id = min_id.to_i
end
- opts.on("--max-id=NUM", "Set upper bound on the id space to NUM (default max signed long: 2^63 - 1)") do |max_id|
+ opts.on("--max-id=NUM", "Set upper bound on the id space to NUM (default 2^60 - 1)") do |max_id|
subcommand_options.max_id = max_id.to_i
end
opts.on("--base-name=NAME", "Use NAME as the base prefix for each shard's table prefix (default 'shard')") do |base_name|
subcommand_options.base_name = base_name
@@ -385,10 +460,12 @@
opts.separator ""
opts.separator "You can type `#{zero} help SUBCOMMAND` for help on a specific subcommand. It's"
opts.separator "also useful to remember that global options come *before* the subcommand, while"
opts.separator "subcommand options come *after* the subcommand."
opts.separator ""
+ opts.separator "You can find explanations and example usage on the wiki (go/gizzmo)."
+ opts.separator ""
opts.separator "You may find it useful to create a ~/.gizzmorc file, which is simply YAML"
opts.separator "key/value pairs corresponding to options you want by default. A common .gizzmorc"
opts.separator "simply contains:"
opts.separator ""
opts.separator " hosts: localhost"
@@ -406,27 +483,27 @@
opts.separator base
end
opts.separator ""
opts.separator ""
opts.separator "Global options:"
- opts.on("-H", "--hosts=HOST[,HOST,...]", "HOSTS of application servers") do |hosts|
+ opts.on("-H", "--hosts=HOST[,HOST,...]", "Comma-delimited list of application servers") do |hosts|
global_options.hosts = hosts.split(",").map {|h| h.strip }
end
- opts.on("-P", "--port=PORT", "PORT of remote manager service. default 7920") do |port|
+ opts.on("-P", "--port=PORT", "PORT of remote manager service (default 7920)") do |port|
global_options.port = port.to_i
end
- opts.on("-I", "--injector=PORT", "PORT of remote job injector service. default 7921") do |port|
+ opts.on("-I", "--injector=PORT", "PORT of remote job injector service (default 7921)") do |port|
global_options.injector_port = port.to_i
end
opts.on("-T", "--tables=TABLE[,TABLE,...]", "TABLE ids of forwardings to affect") do |tables|
global_options.tables = tables.split(",").map {|t| t.to_i }
end
- opts.on("-F", "--framed", "use the thrift framed transport") do |framed|
+ opts.on("-F", "--framed", "Use the thrift framed transport") do |framed|
global_options.framed = true
end
opts.on("-r", "--retry=TIMES", "TIMES to retry the command") do |r|
global_options.retry = r.to_i
@@ -446,10 +523,14 @@
opts.on("-D", "--dry-run", "") do
global_options.dry = true
end
+ opts.on("-s", "--simple", "Represent shard templates in a simple format") do
+ (global_options.template_options ||= {})[:simple] = true #This is a temporary setting until the nameserver design changes match the simpler format
+ end
+
opts.on("-C", "--config=YAML_FILE", "YAML_FILE of option key/values") do |filename|
load_config(global_options, filename)
end
opts.on("-L", "--log=LOG_FILE", "Path to LOG_FILE") do |file|
@@ -457,10 +538,14 @@
end
opts.on("-f", "--force", "Don't display confirmation dialogs") do |force|
global_options.force = force
end
+
+ opts.on("--argv=FILE", "Put the contents of FILE onto the command line") do |f|
+ ARGV.push *Shellwords.shellwords(File.read(f))
+ end
opts.on_tail("-v", "--version", "Show version") do
puts GIZZMO_VERSION
exit
end
@@ -470,11 +555,10 @@
if ARGV.length == 0
STDERR.puts global
exit 1
end
-# This
def process_nested_parsers(global, subcommands)
begin
global.order!(ARGV) do |subcommand_name|
# puts args.inspect
subcommand = subcommands[subcommand_name]
@@ -526,9 +610,10 @@
end
end
begin
custom_timeout(global_options.timeout) do
+ Gizzard::ShardTemplate.configure((global_options.template_options || {}).merge(subcommand_options.template_options || {}))
Gizzard::Command.run(subcommand_name, global_options, argv, subcommand_options, log)
end
rescue HelpNeededError => e
if e.class.name != e.message
STDERR.puts("=" * 80)