gizzmo 0.10.0 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.10.0
1
+ 0.10.1
data/gizzmo.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{gizzmo}
8
- s.version = "0.10.0"
8
+ s.version = "0.10.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Kyle Maxwell"]
12
- s.date = %q{2010-11-02}
12
+ s.date = %q{2011-01-05}
13
13
  s.default_executable = %q{gizzmo}
14
14
  s.description = %q{Gizzmo is a command-line client for managing gizzard clusters.}
15
15
  s.email = %q{kmaxwell@twitter.com}
@@ -19,8 +19,7 @@ Gem::Specification.new do |s|
19
19
  "README.rdoc"
20
20
  ]
21
21
  s.files = [
22
- ".document",
23
- ".gitignore",
22
+ ".gitignore",
24
23
  "LICENSE",
25
24
  "README.rdoc",
26
25
  "Rakefile",
@@ -1,5 +1,6 @@
1
1
  require "pp"
2
2
  require "digest/md5"
3
+
3
4
  module Gizzard
4
5
  class Command
5
6
  include Thrift
@@ -17,7 +18,7 @@ module Gizzard
17
18
  end
18
19
 
19
20
  def self.classify(string)
20
- string.split(/\W+/).map{|s| s.capitalize }.join("")
21
+ string.split(/\W+/).map { |s| s.capitalize }.join("")
21
22
  end
22
23
 
23
24
  attr_reader :service, :global_options, :argv, :command_options
@@ -41,13 +42,13 @@ module Gizzard
41
42
  end
42
43
  end
43
44
  end
44
-
45
+
45
46
  class RetryProxy
46
47
  def initialize(retries, object)
47
48
  @inner = object
48
49
  @retries_left = retries
49
50
  end
50
-
51
+
51
52
  def method_missing(*args)
52
53
  @inner.send(*args)
53
54
  rescue
@@ -63,15 +64,15 @@ module Gizzard
63
64
 
64
65
  class ShardCommand < Command
65
66
  def self.make_service(global_options, log)
66
- RetryProxy.new global_options.retry.to_i,
67
- Gizzard::Thrift::ShardManager.new(global_options.host, global_options.port, log, global_options.dry)
67
+ RetryProxy.new global_options.retry.to_i,
68
+ Gizzard::Thrift::ShardManager.new(global_options.host, global_options.port, log, global_options.framed, global_options.dry)
68
69
  end
69
70
  end
70
71
 
71
72
  class JobCommand < Command
72
73
  def self.make_service(global_options, log)
73
74
  RetryProxy.new global_options.retry.to_i ,
74
- Gizzard::Thrift::JobManager.new(global_options.host, global_options.port + 2, log, global_options.dry)
75
+ Gizzard::Thrift::JobManager.new(global_options.host, global_options.port + 2, log, global_options.framed, global_options.dry)
75
76
  end
76
77
  end
77
78
 
@@ -147,7 +148,18 @@ module Gizzard
147
148
  class ReloadCommand < ShardCommand
148
149
  def run
149
150
  if global_options.force || ask
150
- service.reload_forwardings
151
+ if @argv
152
+ # allow hosts to be given on the command line
153
+ @argv.each do |hostname|
154
+ output hostname
155
+ opts = global_options.dup
156
+ opts.host = hostname
157
+ s = self.class.make_service(opts, global_options.log || "./gizzmo.log")
158
+ s.reload_forwardings
159
+ end
160
+ else
161
+ service.reload_forwardings
162
+ end
151
163
  else
152
164
  STDERR.puts "aborted"
153
165
  end
@@ -201,7 +213,10 @@ module Gizzard
201
213
  upward_links = service.list_upward_links(shard_id)
202
214
  downward_links = service.list_downward_links(shard_id)
203
215
 
204
- help! "Shard must not be a root or leaf" if upward_links.length == 0 or downward_links.length == 0
216
+ if upward_links.length == 0 or downward_links.length == 0
217
+ STDERR.puts "Shard #{shard_id_string} must not be a root or leaf"
218
+ next
219
+ end
205
220
 
206
221
  upward_links.each do |uplink|
207
222
  downward_links.each do |downlink|
@@ -239,11 +254,15 @@ module Gizzard
239
254
  shard_ids.each do |shard_id_text|
240
255
  shard_id = ShardId.parse(shard_id_text)
241
256
  next if !shard_id
242
- service.list_upward_links(shard_id).each do |link_info|
243
- output link_info.to_unix
257
+ unless command_options.down
258
+ service.list_upward_links(shard_id).each do |link_info|
259
+ output command_options.ids ? link_info.up_id.to_unix : link_info.to_unix
260
+ end
244
261
  end
245
- service.list_downward_links(shard_id).each do |link_info|
246
- output link_info.to_unix
262
+ unless command_options.up
263
+ service.list_downward_links(shard_id).each do |link_info|
264
+ output command_options.ids ? link_info.down_id.to_unix : link_info.to_unix
265
+ end
247
266
  end
248
267
  end
249
268
  end
@@ -406,7 +425,7 @@ module Gizzard
406
425
 
407
426
  puts "gizzmo create #{shard_info.class_name} -s '#{shard_info.source_type}' -d '#{shard_info.destination_type}' #{new_shards.join(" ")}"
408
427
  puts "gizzmo wrap #{command_options.write_only_shard} #{new_shards.join(" ")}"
409
- shards.map {|(old, new)| puts "gizzmo copy #{old} #{new}" }
428
+ shards.map { |(old, new)| puts "gizzmo copy #{old} #{new}" }
410
429
  end
411
430
  end
412
431
 
@@ -433,16 +452,16 @@ module Gizzard
433
452
 
434
453
  overlaps = {}
435
454
  ids_by_table.values.each do |arr|
436
- key = arr.map{|id| id.hostname }.sort
455
+ key = arr.map { |id| id.hostname }.sort
437
456
  overlaps[key] ||= 0
438
- overlaps[key] += 1
457
+ overlaps[key] += 1
439
458
  end
440
459
 
441
460
  displayed = {}
442
- overlaps.sort_by{|hosts, count| count }.reverse.each do |(host_a, host_b), count|
461
+ overlaps.sort_by { |hosts, count| count }.reverse.each do |(host_a, host_b), count|
443
462
  next if !host_a || !host_b || displayed[host_a] || displayed[host_b]
444
- id_a = ids_by_host[host_a].find{|id| service.list_upward_links(id).size > 0 }
445
- id_b = ids_by_host[host_b].find{|id| service.list_upward_links(id).size > 0 }
463
+ id_a = ids_by_host[host_a].find { |id| service.list_upward_links(id).size > 0 }
464
+ id_b = ids_by_host[host_b].find { |id| service.list_upward_links(id).size > 0 }
446
465
  next unless id_a && id_b
447
466
  weight_a = service.list_upward_links(id_a).first.weight
448
467
  weight_b = service.list_upward_links(id_b).first.weight
@@ -466,7 +485,6 @@ module Gizzard
466
485
 
467
486
  class ReportCommand < ShardCommand
468
487
  def run
469
-
470
488
  things = @argv.map do |shard|
471
489
  parse(down(ShardId.parse(shard))).join("\n")
472
490
  end
@@ -492,20 +510,20 @@ module Gizzard
492
510
  m[e] ||= []
493
511
  m[e] << e
494
512
  m
495
- end.to_a.sort_by{|k, v| v.length}.reverse
513
+ end.to_a.sort_by { |k, v| v.length }.reverse
496
514
  end
497
515
 
498
516
  def parse(obj, id = nil, depth = 0, sub = true)
499
517
  case obj
500
518
  when Hash
501
519
  id, prefix = parse(obj.keys.first, id, depth, sub)
502
- [prefix] + parse(obj.values.first, id, depth + 1, sub)
520
+ [ prefix ] + parse(obj.values.first, id, depth + 1, sub)
503
521
  when String
504
522
  host, prefix = obj.split("/")
505
523
  host = "db" if host != "localhost" && sub
506
- id ||= prefix[/(\w+ward_)?\d+_\d+(_\w+ward)?/]
524
+ id ||= prefix[/(\w+ward_)?n?\d+_\d+(_\w+ward)?/]
507
525
  prefix = (" " * depth) + host + "/" + ((sub && id) ? prefix.sub(id, "[ID]") : prefix)
508
- [id, prefix]
526
+ [ id, prefix ]
509
527
  when Array
510
528
  obj.map do |e|
511
529
  parse e, id, depth, sub
@@ -517,7 +535,7 @@ module Gizzard
517
535
  vals = service.list_downward_links(id).map do |link|
518
536
  down(link.down_id)
519
537
  end
520
- {id.to_unix => vals}
538
+ { id.to_unix => vals }
521
539
  end
522
540
  end
523
541
 
@@ -575,6 +593,60 @@ module Gizzard
575
593
  end
576
594
  end
577
595
 
596
+ class SetupReplicaCommand < ShardCommand
597
+ def run
598
+ from_shard_id_string, to_shard_id_string = @argv
599
+ help!("Requires source & destination shard id") unless from_shard_id_string && to_shard_id_string
600
+ from_shard_id = ShardId.parse(from_shard_id_string)
601
+ to_shard_id = ShardId.parse(to_shard_id_string)
602
+
603
+ if service.list_upward_links(to_shard_id).size > 0
604
+ STDERR.puts "Destination shard #{to_shard_id} has links to it."
605
+ exit 1
606
+ end
607
+
608
+ link = service.list_upward_links(from_shard_id)[0]
609
+ replica_shard_id = link.up_id
610
+ weight = link.weight
611
+ write_only_shard_id = ShardId.new("localhost", "#{to_shard_id.table_prefix}_copy_write_only")
612
+ service.create_shard(ShardInfo.new(write_only_shard_id, "WriteOnlyShard", "", "", 0))
613
+ service.add_link(replica_shard_id, write_only_shard_id, weight)
614
+ service.add_link(write_only_shard_id, to_shard_id, 1)
615
+ output to_shard_id.to_unix
616
+ end
617
+ end
618
+
619
+ class FinishReplicaCommand < ShardCommand
620
+ def run
621
+ from_shard_id_string, to_shard_id_string = @argv
622
+ help!("Requires source & destination shard id") unless from_shard_id_string && to_shard_id_string
623
+ from_shard_id = ShardId.parse(from_shard_id_string)
624
+ to_shard_id = ShardId.parse(to_shard_id_string)
625
+
626
+ write_only_shard_id = ShardId.new("localhost", "#{to_shard_id.table_prefix}_copy_write_only")
627
+ link = service.list_upward_links(write_only_shard_id)[0]
628
+ replica_shard_id = link.up_id
629
+ weight = link.weight
630
+
631
+ # careful. need to validate some basic assumptions.
632
+ unless global_options.force
633
+ if service.list_upward_links(from_shard_id).map { |link| link.up_id }.to_a != [ replica_shard_id ]
634
+ STDERR.puts "Uplink from #{from_shard_id} is not a migration replica."
635
+ exit 1
636
+ end
637
+ if service.list_upward_links(to_shard_id).map { |link| link.up_id }.to_a != [ write_only_shard_id ]
638
+ STDERR.puts "Uplink from #{to_shard_id} is not a write-only barrier."
639
+ exit 1
640
+ end
641
+ end
642
+
643
+ service.remove_link(write_only_shard_id, to_shard_id)
644
+ service.remove_link(replica_shard_id, write_only_shard_id)
645
+ service.add_link(replica_shard_id, to_shard_id, weight)
646
+ service.delete_shard(write_only_shard_id)
647
+ end
648
+ end
649
+
578
650
  class SetupMigrateCommand < ShardCommand
579
651
  def run
580
652
  from_shard_id_string, to_shard_id_string = @argv
@@ -52,7 +52,7 @@ module Gizzard
52
52
  end
53
53
 
54
54
  def to_unix
55
- [id.to_unix, class_name, busy? ? "busy" : "unbusy"].join("\t")
55
+ [id.to_unix, class_name, busy? ? "busy" : "ok"].join("\t")
56
56
  end
57
57
  end
58
58
 
@@ -91,8 +91,8 @@ module Gizzard
91
91
  end
92
92
 
93
93
  class GizzmoService < T::ThriftService
94
- def initialize(host, port, log_path, dry_run = false)
95
- super(host, port)
94
+ def initialize(host, port, log_path, framed, dry_run = false)
95
+ super(host, port, framed)
96
96
  @dry = dry_run
97
97
  begin
98
98
  @log = File.open(log_path, "a")
data/lib/gizzmo.rb CHANGED
@@ -7,11 +7,25 @@ require "gizzard"
7
7
  require "yaml"
8
8
 
9
9
  DOC_STRINGS = {
10
- "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.",
11
- "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.",
10
+ "addforwarding" => "Add a forwarding from a graph_id / base_source_id to a given shard.",
11
+ "addlink" => "Add a relationship link between two shards.",
12
+ "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.",
13
+ "drill" => "Show shard trees for replicas of a given structure signature (from 'report').",
14
+ "find" => "Show all shards with a given hostname.",
15
+ "finish-replica" => "Remove the write-only barrier in front of a shard that's finished being copied after 'setup-replica'.",
16
+ "flush" => "Flush error queue for a given priority.",
17
+ "forwardings" => "Get a list of all forwardings.",
18
+ "hosts" => "List hosts used in shard names in the forwarding table and replicas.",
19
+ "info" => "Show id/class/busy for shards.",
12
20
  "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.",
21
+ "links" => "List parent & child links for shards.",
13
22
  "lookup" => "Lookup the shard id that holds the record for a given table / source_id.",
14
- "flush" => "Flush error queue for a given priority."
23
+ "markbusy" => "Mark a shard as busy.",
24
+ "pair" => "Report the replica pairing structure for a list of hosts.",
25
+ "reload" => "Instruct an appserver to reload its nameserver state.",
26
+ "report" => "Show each unique replica structure for a given list of shards. Usually this shard list comes from << gizzmo forwardings | awk '{ print $3 }' >>.",
27
+ "setup-replica" => "Add a replica to be parallel to an existing replica, in write-only mode, ready to be copied to.",
28
+ "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.",
15
29
  }
16
30
 
17
31
  ORIGINAL_ARGV = ARGV.dup
@@ -20,6 +34,7 @@ zero = File.basename($0)
20
34
  # Container for parsed options
21
35
  global_options = OpenStruct.new
22
36
  global_options.render = []
37
+ global_options.framed = false
23
38
  subcommand_options = OpenStruct.new
24
39
 
25
40
  # Leftover arguments
@@ -61,6 +76,12 @@ def separators(opts, string)
61
76
  opts.separator("")
62
77
  end
63
78
 
79
+ def load_config(options, filename)
80
+ YAML.load(File.open(filename)).each do |k, v|
81
+ options.send("#{k}=", v)
82
+ end
83
+ end
84
+
64
85
  subcommands = {
65
86
  'create' => OptionParser.new do |opts|
66
87
  opts.banner = "Usage: #{zero} create [options] CLASS_NAME SHARD_ID [MORE SHARD_IDS...]"
@@ -163,6 +184,16 @@ subcommands = {
163
184
  'links' => OptionParser.new do |opts|
164
185
  opts.banner = "Usage: #{zero} links SHARD_ID [MORE SHARD_IDS...]"
165
186
  separators(opts, DOC_STRINGS["links"])
187
+
188
+ opts.on("--ids", "Show shard ids only") do
189
+ subcommand_options.ids = true
190
+ end
191
+ opts.on("--up", "Show uplinks only") do
192
+ subcommand_options.up = true
193
+ end
194
+ opts.on("--down", "show downlinks only") do
195
+ subcommand_options.down = true
196
+ end
166
197
  end,
167
198
  'info' => OptionParser.new do |opts|
168
199
  opts.banner = "Usage: #{zero} info SHARD_ID [MORE SHARD_IDS...]"
@@ -209,6 +240,14 @@ subcommands = {
209
240
  opts.banner = "Usage: #{zero} busy"
210
241
  separators(opts, DOC_STRINGS["busy"])
211
242
  end,
243
+ 'setup-replica' => OptionParser.new do |opts|
244
+ opts.banner = "Usage: #{zero} setup-replica SOURCE_SHARD_ID DESTINATION_SHARD_ID"
245
+ separators(opts, DOC_STRINGS["setup-replica"])
246
+ end,
247
+ 'finish-replica' => OptionParser.new do |opts|
248
+ opts.banner = "Usage: #{zero} finish-replica SOURCE_SHARD_ID DESTINATION_SHARD_ID"
249
+ separators(opts, DOC_STRINGS["finish-replica"])
250
+ end,
212
251
  'setup-migrate' => OptionParser.new do |opts|
213
252
  opts.banner = "Usage: #{zero} setup-migrate SOURCE_SHARD_ID DESTINATION_SHARD_ID"
214
253
  separators(opts, DOC_STRINGS["setup-migrate"])
@@ -231,6 +270,10 @@ subcommands = {
231
270
  end
232
271
  }
233
272
 
273
+ if ENV['GIZZMORC']
274
+ load_config(global_options, ENV['GIZZMORC'])
275
+ end
276
+
234
277
  global = OptionParser.new do |opts|
235
278
  opts.banner = "Usage: #{zero} [global-options] SUBCOMMAND [subcommand-options]"
236
279
  opts.separator ""
@@ -243,7 +286,7 @@ global = OptionParser.new do |opts|
243
286
  opts.separator ""
244
287
  opts.separator "You may find it useful to create a ~/.gizzmorc file, which is simply YAML"
245
288
  opts.separator "key/value pairs corresponding to options you want by default. A common .gizzmorc"
246
- opts.separator "simply contain:"
289
+ opts.separator "simply contains:"
247
290
  opts.separator ""
248
291
  opts.separator " host: localhost"
249
292
  opts.separator " port: 7917"
@@ -270,6 +313,10 @@ global = OptionParser.new do |opts|
270
313
  global_options.port = port.to_i
271
314
  end
272
315
 
316
+ opts.on("-F", "--framed", "use the thrift framed transport") do |framed|
317
+ global_options.framed = true
318
+ end
319
+
273
320
  opts.on("-r", "--retry=TIMES", "TIMES to retry the command") do |r|
274
321
  global_options.retry = r
275
322
  end
@@ -290,10 +337,8 @@ global = OptionParser.new do |opts|
290
337
  global_options.dry = true
291
338
  end
292
339
 
293
- opts.on("-C", "--config=YAML_FILE", "YAML_FILE of option key/values") do |file|
294
- YAML.load(File.open(file)).each do |k, v|
295
- global_options.send("#{k}=", v)
296
- end
340
+ opts.on("-C", "--config=YAML_FILE", "YAML_FILE of option key/values") do |filename|
341
+ load_config(global_options, filename)
297
342
  end
298
343
 
299
344
  opts.on("-L", "--log=LOG_FILE", "Path to LOG_FILE") do |file|
@@ -370,7 +415,7 @@ def custom_timeout(seconds)
370
415
  end
371
416
  end
372
417
 
373
- begin
418
+ begin
374
419
  custom_timeout(global_options.timeout) do
375
420
  Gizzard::Command.run(subcommand_name, global_options, argv, subcommand_options, log)
376
421
  end
@@ -87,8 +87,14 @@ class ThriftClient
87
87
  end
88
88
  end
89
89
 
90
- def pack_request(method_name, arg_struct, request_id=0)
91
- [ VERSION_1, CALL, method_name.to_s.size, method_name.to_s, request_id, arg_struct._pack ].pack("nnNa*Na*")
90
+ def pack_request(method_name, arg_struct, framed, request_id=0)
91
+ msg = [ VERSION_1, CALL, method_name.to_s.size, method_name.to_s, request_id, arg_struct._pack ].pack("nnNa*Na*")
92
+ if framed
93
+ frame = [ msg.length ].pack("N")
94
+ frame + msg
95
+ else
96
+ msg
97
+ end
92
98
  end
93
99
 
94
100
  def read_value(s, type)
@@ -169,8 +175,13 @@ class ThriftClient
169
175
  end
170
176
  end
171
177
  end
172
-
173
- def read_response(s, rv_class)
178
+
179
+ def read_response(s, rv_class, framed)
180
+ if framed
181
+ # unwrap frame size. dont use for now
182
+ framesize = s.read(4).unpack("N")
183
+ end
184
+
174
185
  version, message_type, method_name_len = s.read(8).unpack("nnN")
175
186
  method_name = s.read(method_name_len)
176
187
  seq_id = s.read(4).unpack("N").first
@@ -280,9 +291,10 @@ class ThriftClient
280
291
  UnknownStruct = make_struct(:Unknown)
281
292
 
282
293
  class ThriftService
283
- def initialize(host, port)
294
+ def initialize(host, port, framed = false)
284
295
  @host = host
285
296
  @port = port
297
+ @framed = framed
286
298
  end
287
299
 
288
300
  def self._arg_structs
@@ -310,8 +322,8 @@ class ThriftClient
310
322
  arg_class, rv_class = cls._arg_structs[method_name.to_sym]
311
323
  arg_struct = arg_class.new(*args)
312
324
  sock = TCPSocket.new(@host, @port)
313
- sock.write(ThriftClient::Simple.pack_request(method_name, arg_struct))
314
- rv = ThriftClient::Simple.read_response(sock, rv_class)
325
+ sock.write(ThriftClient::Simple.pack_request(method_name, arg_struct, @framed))
326
+ rv = ThriftClient::Simple.read_response(sock, rv_class, @framed)
315
327
  sock.close
316
328
  rv[2]
317
329
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 10
8
- - 0
9
- version: 0.10.0
8
+ - 1
9
+ version: 0.10.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Kyle Maxwell
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-11-02 00:00:00 -07:00
17
+ date: 2011-01-05 00:00:00 -08:00
18
18
  default_executable: gizzmo
19
19
  dependencies: []
20
20
 
@@ -28,7 +28,6 @@ extra_rdoc_files:
28
28
  - LICENSE
29
29
  - README.rdoc
30
30
  files:
31
- - .document
32
31
  - .gitignore
33
32
  - LICENSE
34
33
  - README.rdoc
data/.document DELETED
@@ -1,5 +0,0 @@
1
- README.rdoc
2
- lib/**/*.rb
3
- bin/*
4
- features/**/*.feature
5
- LICENSE