flare-tools 0.1.4 → 0.4.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. data/.gemtest +0 -0
  2. data/Flare-tools.txt +0 -0
  3. data/History.txt +114 -2
  4. data/LICENSE +21 -0
  5. data/Manifest.txt +65 -8
  6. data/README.txt +356 -0
  7. data/Rakefile +90 -25
  8. data/Tutorial.txt +370 -0
  9. data/bin/flare-admin +6 -0
  10. data/bin/flare-argv0 +6 -0
  11. data/bin/flare-deploy +6 -0
  12. data/bin/flare-keychecker +6 -0
  13. data/bin/flare-part +6 -0
  14. data/bin/flare-ping +6 -0
  15. data/bin/flare-stats +4 -10
  16. data/bin/flare-zkadmin +6 -0
  17. data/lib/flare/net/connection.rb +98 -0
  18. data/lib/flare/test/cluster.rb +140 -0
  19. data/lib/flare/test/daemon.rb +144 -0
  20. data/lib/flare/test/node.rb +62 -0
  21. data/lib/flare/tools.rb +18 -16
  22. data/lib/flare/tools/cli.rb +32 -0
  23. data/lib/flare/tools/cli/activate.rb +106 -0
  24. data/lib/flare/tools/cli/balance.rb +83 -0
  25. data/lib/flare/tools/cli/cli_util.rb +77 -0
  26. data/lib/flare/tools/cli/deploy.rb +170 -0
  27. data/lib/flare/tools/cli/down.rb +85 -0
  28. data/lib/flare/tools/cli/dump.rb +219 -0
  29. data/lib/flare/tools/cli/dumpkey.rb +117 -0
  30. data/lib/flare/tools/cli/flare_admin.rb +81 -0
  31. data/lib/flare/tools/cli/flare_argv0.rb +60 -0
  32. data/lib/flare/tools/cli/flare_keychecker.rb +106 -0
  33. data/lib/flare/tools/cli/flare_zkadmin.rb +226 -0
  34. data/lib/flare/tools/cli/index.rb +54 -0
  35. data/lib/flare/tools/cli/list.rb +93 -0
  36. data/lib/flare/tools/cli/master.rb +143 -0
  37. data/lib/flare/tools/cli/part.rb +100 -0
  38. data/lib/flare/tools/cli/ping.rb +81 -0
  39. data/lib/flare/tools/cli/reconstruct.rb +164 -0
  40. data/lib/flare/tools/cli/remove.rb +119 -0
  41. data/lib/flare/tools/cli/restore.rb +180 -0
  42. data/lib/flare/tools/cli/slave.rb +125 -0
  43. data/lib/flare/tools/cli/stats.rb +229 -122
  44. data/lib/flare/tools/cli/sub_command.rb +73 -0
  45. data/lib/flare/tools/cli/summary.rb +97 -0
  46. data/lib/flare/tools/cli/threads.rb +78 -0
  47. data/lib/flare/tools/cli/verify.rb +202 -0
  48. data/lib/flare/tools/client.rb +267 -0
  49. data/lib/flare/tools/cluster.rb +319 -0
  50. data/lib/flare/tools/common.rb +196 -0
  51. data/lib/flare/tools/index_server.rb +51 -0
  52. data/lib/flare/tools/node.rb +162 -0
  53. data/lib/flare/tools/stats.rb +75 -0
  54. data/lib/flare/tools/zk_util.rb +28 -0
  55. data/lib/flare/util.rb +34 -0
  56. data/lib/flare/util/bwlimit.rb +132 -0
  57. data/lib/flare/util/command_line.rb +79 -0
  58. data/lib/flare/util/conf.rb +71 -0
  59. data/lib/flare/util/constant.rb +25 -0
  60. data/lib/flare/util/conversion.rb +26 -0
  61. data/lib/flare/util/default_logger.rb +52 -0
  62. data/lib/flare/util/exception.rb +19 -0
  63. data/lib/flare/util/filesystem.rb +30 -0
  64. data/lib/flare/util/flared_conf.rb +33 -0
  65. data/lib/flare/util/flarei_conf.rb +32 -0
  66. data/lib/flare/util/hash_function.rb +32 -0
  67. data/lib/flare/util/interruption.rb +70 -0
  68. data/lib/flare/util/key_resolver.rb +67 -0
  69. data/lib/flare/util/log4r_logger.rb +79 -0
  70. data/lib/flare/util/logger.rb +40 -0
  71. data/lib/flare/util/logging.rb +84 -0
  72. data/lib/flare/util/result.rb +53 -0
  73. data/test/test/experimental/cache_test.rb +113 -0
  74. data/test/test/experimental/key_distribution_test.rb +38 -0
  75. data/test/test/experimental/keychecker_test.rb +60 -0
  76. data/test/test/experimental/list_test.rb +108 -0
  77. data/test/test/extra/replication_test.rb +184 -0
  78. data/test/test/integration/cli_test.rb +348 -0
  79. data/test/test/integration/dump_expired_test.rb +103 -0
  80. data/test/test/integration/dump_test.rb +128 -0
  81. data/test/test/integration/index_server_test.rb +35 -0
  82. data/test/test/integration/node_test.rb +78 -0
  83. data/test/test/integration/partition_test.rb +235 -0
  84. data/test/test/integration/proxy_test.rb +54 -0
  85. data/test/test/integration/stats_test.rb +79 -0
  86. data/test/test/system/flare_admin_test.rb +191 -0
  87. data/test/test/unit/bwlimit_test.rb +52 -0
  88. data/test/test/unit/cluster_test.rb +96 -0
  89. data/test/test/unit/daemon_test.rb +30 -0
  90. data/test/test/unit/logger_test.rb +46 -0
  91. data/test/test/unit/tools_test.rb +25 -0
  92. data/test/test/unit/util_test.rb +70 -0
  93. metadata +239 -84
  94. data/README.rdoc +0 -83
  95. data/bin/flare-partition-setting +0 -12
  96. data/lib/flare/tools/cli/partition_setting.rb +0 -86
  97. data/lib/flare/tools/core.rb +0 -189
  98. data/lib/flare/tools/logger.rb +0 -31
  99. data/test/test_flare-tools.rb +0 -11
  100. data/test/test_helper.rb +0 -3
@@ -0,0 +1,53 @@
1
+ # -*- coding: utf-8; -*-
2
+ # Authors:: Kiyoshi Ikehara <kiyoshi.ikehara@gree.net>
3
+ # Copyright:: Copyright (C) GREE, Inc. 2011.
4
+ # License:: MIT-style
5
+
6
+ #
7
+ module Flare
8
+ module Util
9
+
10
+ # == Description
11
+ # Result is a class for handling result code.
12
+ module Result
13
+ None = nil
14
+ Ok = :OK
15
+ End = :END
16
+ Stored = :STORED
17
+ NotStored = :NOT_STORED
18
+ Exists = :EXISTS
19
+ NotFound= :NOT_FOUND
20
+ Deleted = :DELETED
21
+ Found = :FOUND
22
+ Error = :ERROR
23
+ ClientError = :CLIENT_ERROR
24
+ ServerError = :SERVER_ERROR
25
+
26
+ # Converts a result code to its string representation.
27
+ def self.string_of_result(result)
28
+ case result
29
+ when None
30
+ ""
31
+ when Ok,End,Stored,NotStored,Exists,NotFound,Deleted,Found,Error,ClientError,ServerError
32
+ result.to_s
33
+ else
34
+ raise "Invalid argument '"+result.to_s+"'"
35
+ end
36
+ end
37
+
38
+ # Converts a string representation of a request result to its result code.
39
+ def self.result_of_string(string)
40
+ case string
41
+ when ""
42
+ None
43
+ else
44
+ [Ok,End,Stored,NotStored,Exists,NotFound,Deleted,Found,Error,ClientError,ServerError].each do |x|
45
+ return x if x.to_s == string
46
+ end
47
+ raise "Invalid arugument '"+string.to_s+"'"
48
+ end
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/ruby
2
+ # -*- coding: utf-8; -*-
3
+
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__)+"/../lib")
5
+
6
+ require 'test/unit'
7
+ require 'flare/tools'
8
+ require 'flare/tools/cli'
9
+ require 'flare/test/cluster'
10
+ require 'subcommands'
11
+
12
+ class CacheTest < Test::Unit::TestCase
13
+ include Flare::Tools::Common
14
+ include Subcommands
15
+
16
+ S_OK = 0
17
+ S_NG = 1
18
+
19
+ def setup
20
+ @flare_cluster = Flare::Test::Cluster.new('test')
21
+ sleep 1 # XXX
22
+ @node_servers = ['localmaster1', 'localproxy1', 'remoteproxy1'].map {|name|
23
+ @flare_cluster.create_node(name, { 'proxy-cache-size' => 1000, 'proxy-cache-expire' => 60 }, "/usr/local/proxy_cache/bin/flared")
24
+ }
25
+ sleep 1 # XXX
26
+ @flare_cluster.wait_for_ready
27
+ @config = {
28
+ :command => 'dummy',
29
+ :index_server_hostname => @flare_cluster.indexname,
30
+ :index_server_port => @flare_cluster.indexport,
31
+ :dry_run => false,
32
+ :timeout => 10
33
+ }
34
+ @nodes = @node_servers.map do |node|
35
+ Flare::Tools::Node.open(node.hostname, node.port, 10)
36
+ end
37
+ @localmaster1, @localproxy1, @remoteproxy1 = @nodes
38
+ end
39
+
40
+ def teardown
41
+ @nodes.map {|n| n.close}
42
+ end
43
+
44
+ def test_proxy_gets_from_various_routes
45
+ @flare_cluster.prepare_master_and_slaves(@node_servers[0..0])
46
+ key = "hoge"
47
+ @localmaster1.set(key, "v1")
48
+ value, version = @localmaster1.gets(key)
49
+ assert_equal(1, version);
50
+ assert_equal("v1", value);
51
+ @localproxy1.set(key, "v2")
52
+ value, version = @localmaster1.gets(key)
53
+ assert_equal(2, version);
54
+ assert_equal("v2", value);
55
+ @remoteproxy1.set(key, "v3")
56
+ value, version = @localmaster1.gets(key)
57
+ assert_equal(3, version);
58
+ assert_equal("v3", value);
59
+ end
60
+
61
+ def test_proxy_cas_to_master
62
+ @flare_cluster.prepare_master_and_slaves(@node_servers[0..0])
63
+ key = "hoge"
64
+ @localmaster1.set(key, "v1")
65
+ value, version = @localmaster1.gets(key)
66
+ assert_equal(1, version);
67
+ @localmaster1.cas(key, "v2", version)
68
+ value, version = @localmaster1.gets(key)
69
+ assert_equal(2, version);
70
+ @localproxy1.cas(key, "v3", version)
71
+ value, version = @localproxy1.gets(key)
72
+ assert_equal(3, version);
73
+ end
74
+
75
+ class Client
76
+ def initialize(node)
77
+ @node = node
78
+ end
79
+
80
+ def gets(*keys)
81
+ @node.gets(*keys)
82
+ end
83
+
84
+ def cas(*keys)
85
+ @node.cas(*keys)
86
+ end
87
+ end
88
+
89
+ def test_proxy_gets_and_cas
90
+ @flare_cluster.prepare_master_and_slaves(@node_servers[0..0])
91
+ key = "hoge"
92
+
93
+ @localmaster1.set(key, "initial")
94
+
95
+ c0 = Client.new(@localmaster1)
96
+ c1 = Client.new(@remoteproxy1)
97
+ c2 = Client.new(@remoteproxy1)
98
+
99
+ v1,v1version = c1.gets(key)
100
+ v0,v0version = c0.gets(key)
101
+ v2,v2version = c2.gets(key)
102
+
103
+ r0 = c0.cas(key, "client0", v0version)
104
+ r1 = c1.cas(key, "client1", v1version)
105
+ r2 = c2.cas(key, "client2", v2version)
106
+
107
+ assert_equal(true, r0)
108
+ assert_equal(false, r1)
109
+ assert_equal(false, r2)
110
+ end
111
+
112
+ end
113
+
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/ruby
2
+ # -*- coding: utf-8; -*-
3
+
4
+ $LOAD_PATH.unshift File.dirname(__FILE__)+"/../lib"
5
+
6
+ require 'flare/util/key_resolver'
7
+ require 'flare/util/hash_function'
8
+ require 'shell'
9
+ require 'test/unit'
10
+
11
+ class KeyDistributionTest < Test::Unit::TestCase
12
+ include Flare::Util::HashFunction
13
+
14
+ def setup
15
+ @resolver = Flare::Util::KeyResolver.new
16
+ end
17
+
18
+ def test_key_distribution1
19
+ proxy_concurrency = 8
20
+ partition_size = 4
21
+ prefix = "test::key::distribution"
22
+ bin = []
23
+ (0...proxy_concurrency).each do |i|
24
+ bin[i] = 0
25
+ end
26
+ (1..10000).each do |i|
27
+ key = "#{prefix}::#{i}"
28
+ hash_of_key = get_key_hash_value(key, :bitshift, 32)
29
+ target_thread = hash_of_key % proxy_concurrency
30
+ target_partition = @resolver.resolve(get_key_hash_value(key, :simple, 32), partition_size)
31
+ if target_partition == 0
32
+ bin[target_thread] += 1
33
+ end
34
+ end
35
+ p bin
36
+ end
37
+
38
+ end
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/ruby
2
+ # -*- coding: utf-8; -*-
3
+
4
+ $LOAD_PATH.unshift File.dirname(__FILE__)+"/../lib"
5
+
6
+ require 'shell'
7
+ require 'test/unit'
8
+
9
+ Shell.verbose = false
10
+ Shell.def_system_command "kc", "../../bin/flare-keychecker"
11
+
12
+ class KeycheckerTest < Test::Unit::TestCase
13
+ KeyTxt = "keychecker_test.key.txt"
14
+ ResultCsv = "keychecker_test.result.csv"
15
+
16
+ def setup
17
+ @basedir = File.dirname(__FILE__)+"/work"
18
+ @sh = Shell.new
19
+ @sh.cd(@basedir)
20
+ @sh.transact {
21
+ unless exist? KeyTxt
22
+ keys = (0...1000).inject([]) {|r,v| r << "prefix::#{v}"}
23
+ echo(*keys) >> KeyTxt
24
+ end
25
+ }
26
+ end
27
+
28
+ def test_help1
29
+ @sh.transact {
30
+ kc("--help")
31
+ }
32
+ end
33
+
34
+ def hash(type)
35
+ @sh.transact {
36
+ (cat(KeyTxt) | kc("--hash=#{type}")) > ResultCsv
37
+ }
38
+ end
39
+
40
+ def test_hash1
41
+ hash("simple")
42
+ hash("bitshift")
43
+ hash("crc32")
44
+ end
45
+
46
+ def delimiter(delimiter)
47
+ @sh.transact {
48
+ echo(*@keys) >> KeyTxt
49
+ (cat(KeyTxt) | kc("--delimiter=#{delimiter}")) > ResultCsv
50
+ }
51
+ end
52
+
53
+ def test_delimiter1
54
+ delimiter("::")
55
+ end
56
+
57
+ end
58
+
59
+
60
+
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/ruby
2
+ # -*- coding: utf-8; -*-
3
+
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__)+"/../lib")
5
+
6
+ require 'test/unit'
7
+ require 'flare/tools'
8
+ require 'flare/tools/cli'
9
+ require 'flare/test/cluster'
10
+ require 'subcommands'
11
+
12
+ class ListTest < Test::Unit::TestCase
13
+ include Flare::Tools::Common
14
+ include Subcommands
15
+
16
+ S_OK = 0
17
+ S_NG = 1
18
+
19
+ def setup
20
+ @flare_cluster = Flare::Test::Cluster.new('test')
21
+ sleep 1 # XXX
22
+ @node_servers = ['node1', 'node2', 'node3'].map {|name| @flare_cluster.create_node(name)}
23
+ sleep 1 # XXX
24
+ @flare_cluster.wait_for_ready
25
+ @config = {
26
+ :command => 'dummy',
27
+ :index_server_hostname => @flare_cluster.indexname,
28
+ :index_server_port => @flare_cluster.indexport,
29
+ :dry_run => false,
30
+ :timeout => 10
31
+ }
32
+ @nodes = @node_servers.map do |node|
33
+ Flare::Tools::Node.open(node.hostname, node.port, 10)
34
+ end
35
+ end
36
+
37
+ def teardown
38
+ @nodes.map {|n| n.close}
39
+ end
40
+
41
+ def push_and_get(npush, nget)
42
+ list if defined? DEBUG
43
+ key = "k"
44
+ (0...100).each do |i|
45
+ value = "v#{i}"
46
+ npush.x_list_push(key, value)
47
+ sleep 0.001
48
+ count = 0
49
+ nget.x_list_get(key, i, i+1) do |v, k, rel, abs, flag, len, version, expire|
50
+ assert_equal(key, k)
51
+ assert_equal(value, v)
52
+ count += 1
53
+ end
54
+ assert_equal(1, count)
55
+ end
56
+ end
57
+
58
+ def push_and_range_get(npush, nget)
59
+ list if defined? DEBUG
60
+ key = "k"
61
+ size = 100
62
+ range = (0...size)
63
+ expected = range.map {|i| "v#{i}"}
64
+ range.each do |i|
65
+ npush.x_list_push(key, expected[i])
66
+ end
67
+ count = 0
68
+ nget.x_list_get(key, 0, size) do |v, k, rel, abs, f|
69
+ value = "v#{count}"
70
+ assert_equal(key, k)
71
+ assert_equal(value, v)
72
+ count += 1
73
+ end
74
+ assert_equal(size, count)
75
+ end
76
+
77
+ def push_and_shift(npush, nshift)
78
+ list if defined? DEBUG
79
+ key = "k"
80
+ range = (0...100)
81
+ expected = range.map {|i| "v#{i}"}
82
+ range.each do |i|
83
+ npush.x_list_push(key, expected[i])
84
+ end
85
+ values = range.map {|i| nshift.x_list_shift(key)}
86
+ assert_equal(expected, values)
87
+ end
88
+
89
+ def self.deftest_allpair(name)
90
+ syms = ["m", "s", "p"]
91
+ for i in (0...syms.size)
92
+ for j in (0...syms.size)
93
+ self.class_eval %{
94
+ def test_#{name.to_s}_#{syms[i]}#{syms[j]}
95
+ @flare_cluster.prepare_master_and_slaves(@node_servers[0..1])
96
+ #{name.to_s}(@nodes[#{i}], @nodes[#{j}])
97
+ end
98
+ }
99
+ end
100
+ end
101
+ end
102
+
103
+ deftest_allpair :push_and_get
104
+ deftest_allpair :push_and_range_get
105
+ deftest_allpair :push_and_shift
106
+
107
+ end
108
+
@@ -0,0 +1,184 @@
1
+ #!/usr/bin/ruby
2
+ # -*- coding: utf-8; -*-
3
+
4
+ $LOAD_PATH.unshift File.dirname(__FILE__)+"/../lib"
5
+
6
+ require 'test/unit'
7
+ require 'flare/tools'
8
+ require 'flare/test/cluster'
9
+
10
+ class ReplicationTest < Test::Unit::TestCase
11
+ include Flare::Tools::Common
12
+
13
+ def setup
14
+ @flare_cluster = Flare::Test::Cluster.new('test')
15
+ sleep 1 # XXX
16
+ @node_servers = ['node1', 'node2', 'node3', 'node4', 'node5', 'node6'].map {|name| @flare_cluster.create_node(name)}
17
+ sleep 1 # XXX
18
+ @flare_cluster.wait_for_ready
19
+ @config = {
20
+ :command => 'dummy',
21
+ :index_server_hostname => @flare_cluster.indexname,
22
+ :index_server_port => @flare_cluster.indexport,
23
+ :dry_run => false,
24
+ :timeout => 10
25
+ }
26
+ @nodes = @node_servers.map do |node|
27
+ Flare::Tools::Node.open(node.hostname, node.port, 10)
28
+ end
29
+ end
30
+
31
+ def teardown
32
+ @nodes.map {|n| n.close}
33
+ end
34
+
35
+ def test_replication
36
+ @flare_cluster.prepare_master_and_slaves(@node_servers)
37
+ Flare::Tools::Node.open(@flare_cluster.indexname, @flare_cluster.indexport, 10) do |s|
38
+ puts string_of_nodelist(s.stats_nodes)
39
+ node = @node_servers[1]
40
+ puts "throwing requests to #{node.hostname}:#{node.port}."
41
+ Flare::Tools::Node.open(node.hostname, node.port, 10) do |n|
42
+ fmt = "key%010.10d"
43
+ (0...10).each do |i|
44
+ n.set(fmt % i, "All your base are belong to us.")
45
+ end
46
+ end
47
+ puts string_of_nodelist(s.stats_nodes)
48
+ end
49
+ end
50
+
51
+ def parallel(*args, &block)
52
+ args.map do |arg|
53
+ Thread.new do
54
+ block.call(arg)
55
+ end
56
+ end
57
+ end
58
+
59
+ def test_replication_consistent
60
+ replication_consistent(false)
61
+ end
62
+
63
+ def test_replication_consistent_noreply
64
+ replication_consistent(true, 0.1)
65
+ end
66
+
67
+ def replication_consistent(noreply, wait = 0)
68
+ @flare_cluster.prepare_master_and_slaves(@node_servers)
69
+
70
+ Flare::Tools::Node.open(@flare_cluster.indexname, @flare_cluster.indexport, 10) do |s|
71
+ puts string_of_nodelist(s.stats_nodes)
72
+ ntry = 256
73
+ nparallel = 128
74
+ nloop = 32
75
+ entries = (0...nparallel).map { |i|
76
+ {
77
+ :index => i,
78
+ :command_queue => Queue.new,
79
+ :hostname => @node_servers[0].hostname,
80
+ :port => @node_servers[0].port,
81
+ :result_queue => Queue.new,
82
+ :backlog_queue => Queue.new
83
+ }
84
+ }
85
+ puts "creating threads..."
86
+ entries.each { |entry| entry[:command_queue].enq("start") }
87
+ threads = parallel(*entries) do |entry|
88
+ Flare::Tools::Node.open(entry[:hostname], entry[:port], 10) do |n|
89
+ while command = entry[:command_queue].deq
90
+ case command
91
+ when "start"
92
+ # puts "#{entry[:index]}:#{entry[:hostname]}:#{entry[:port]}: start"
93
+ n.set("key", "0")
94
+ # puts "#{entry[:index]}:#{entry[:hostname]}:#{entry[:port]}: start end"
95
+ when "end"
96
+ entry[:result_queue].enq("terminated")
97
+ break
98
+ when "execute"
99
+ # puts "#{entry[:index]}:#{entry[:hostname]}:#{entry[:port]}: begin"
100
+ (0...nloop).each do |i|
101
+ if i%10 == 0
102
+ print "."
103
+ n.delete("key")
104
+ n.set("key", "111")
105
+ elsif i%10 == 1
106
+ n.decr("key", "1")
107
+ else
108
+ n.incr("key", "1")
109
+ end
110
+ Thread.pass
111
+ end
112
+ # puts "#{entry[:index]}:#{entry[:hostname]}:#{entry[:port]}: end"
113
+ entry[:result_queue].enq("finished")
114
+ when "execute_noreply"
115
+ count = 0
116
+ (0...nloop).each do |i|
117
+ case rand(8)
118
+ when 0
119
+ n.delete_noreply("key")
120
+ when 1
121
+ n.set_noreply("key", count.to_s)
122
+ when 2
123
+ # n.decr_noreply("key", "1")
124
+ n.set_noreply("key", count.to_s)
125
+ else
126
+ n.incr_noreply("key", "1")
127
+ # n.set_noreply("key", count.to_s)
128
+ end
129
+ count = count+1
130
+ count = 0 if count > 1000
131
+ end
132
+ entry[:result_queue].enq("finished")
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ (0...ntry).each do |n|
139
+ entries.each {|entry|
140
+ command = if noreply then "execute_noreply" else "execute" end
141
+ entry[:command_queue].enq(command)
142
+ }
143
+ print "waiting(#{n})..."
144
+ if noreply && wait > 0
145
+ while entries[0][:result_queue].empty?
146
+ @node_servers[0].stop
147
+ sleep wait
148
+ @node_servers[0].cont
149
+ sleep wait if entries[0][:result_queue].empty?
150
+ end
151
+ end
152
+ entries.map {|entry| assert_equal("finished", entry[:result_queue].deq) }
153
+ for i in 1..5
154
+ failed = false
155
+ sleep wait
156
+ results = @nodes.map do |n|
157
+ n.get("key")
158
+ end
159
+ slave_results = results.dup
160
+ master_result = slave_results.shift
161
+ slave_results.each {|r| failed = true if master_result != r}
162
+ puts results.join(' ')
163
+ if failed
164
+ sleep 1
165
+ if i == 5
166
+ puts "inconsistent data: master=#{master_result}\n"
167
+ end
168
+ else
169
+ break
170
+ end
171
+ end
172
+ slave_results.each {|r| assert_equal(master_result, r)}
173
+ end
174
+ entries.each {|entry| entry[:command_queue].enq("end") }
175
+ entries.map {|entry| assert_equal("terminated", entry[:result_queue].deq)}
176
+ threads.each do |t|
177
+ t.join
178
+ end
179
+ puts string_of_nodelist(s.stats_nodes)
180
+ puts "done."
181
+ end
182
+ end # func
183
+
184
+ end