droonga-engine 1.0.9 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (195) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -0
  3. data/benchmark/timer-watcher/benchmark.rb +44 -0
  4. data/bin/droonga-engine-absorb-data +246 -187
  5. data/bin/droonga-engine-catalog-generate +12 -12
  6. data/bin/droonga-engine-catalog-modify +4 -4
  7. data/bin/droonga-engine-join +352 -171
  8. data/bin/droonga-engine-set-role +54 -0
  9. data/bin/droonga-engine-unjoin +107 -112
  10. data/droonga-engine.gemspec +3 -3
  11. data/install.sh +55 -36
  12. data/install/centos/functions.sh +2 -2
  13. data/install/debian/functions.sh +2 -2
  14. data/lib/droonga/address.rb +26 -24
  15. data/lib/droonga/buffered_tcp_socket.rb +65 -10
  16. data/lib/droonga/catalog/base.rb +9 -6
  17. data/lib/droonga/catalog/dataset.rb +17 -41
  18. data/lib/droonga/catalog/fetcher.rb +64 -0
  19. data/lib/droonga/catalog/generator.rb +245 -0
  20. data/lib/droonga/catalog/loader.rb +66 -0
  21. data/lib/droonga/{catalog_modifier.rb → catalog/modifier.rb} +11 -18
  22. data/lib/droonga/catalog/replicas_volume.rb +123 -0
  23. data/lib/droonga/catalog/schema.rb +37 -37
  24. data/lib/droonga/catalog/single_volume.rb +11 -3
  25. data/lib/droonga/catalog/slice.rb +10 -6
  26. data/lib/droonga/catalog/{collection_volume.rb → slices_volume.rb} +47 -11
  27. data/lib/droonga/catalog/version1.rb +47 -19
  28. data/lib/droonga/catalog/version2.rb +11 -10
  29. data/lib/droonga/catalog/version2_validator.rb +4 -4
  30. data/lib/droonga/catalog/volume.rb +17 -5
  31. data/lib/droonga/changable.rb +25 -0
  32. data/lib/droonga/cluster.rb +237 -0
  33. data/lib/droonga/collector_runner.rb +4 -0
  34. data/lib/droonga/collectors.rb +2 -1
  35. data/lib/droonga/collectors/recursive_sum.rb +26 -0
  36. data/lib/droonga/command/droonga_engine.rb +404 -127
  37. data/lib/droonga/command/droonga_engine_service.rb +47 -11
  38. data/lib/droonga/command/droonga_engine_worker.rb +21 -1
  39. data/lib/droonga/command/remote_command_base.rb +78 -0
  40. data/lib/droonga/command/serf_event_handler.rb +29 -20
  41. data/lib/droonga/data_absorber_client.rb +222 -0
  42. data/lib/droonga/database_scanner.rb +106 -0
  43. data/lib/droonga/{live_nodes_list_loader.rb → deferrable.rb} +11 -24
  44. data/lib/droonga/differ.rb +58 -0
  45. data/lib/droonga/dispatcher.rb +155 -32
  46. data/lib/droonga/distributed_command_planner.rb +9 -11
  47. data/lib/droonga/engine.rb +83 -78
  48. data/lib/droonga/engine/version.rb +1 -1
  49. data/lib/droonga/engine_node.rb +301 -0
  50. data/lib/droonga/engine_state.rb +62 -40
  51. data/lib/droonga/farm.rb +44 -5
  52. data/lib/droonga/file_observer.rb +16 -12
  53. data/lib/droonga/fluent_message_receiver.rb +98 -29
  54. data/lib/droonga/fluent_message_sender.rb +30 -23
  55. data/lib/droonga/forward_buffer.rb +160 -0
  56. data/lib/droonga/forwarder.rb +73 -40
  57. data/lib/droonga/handler.rb +7 -6
  58. data/lib/droonga/handler_messenger.rb +15 -6
  59. data/lib/droonga/handler_runner.rb +6 -1
  60. data/lib/droonga/internal_fluent_message_receiver.rb +28 -8
  61. data/lib/droonga/job_pusher.rb +10 -7
  62. data/lib/droonga/job_receiver.rb +6 -4
  63. data/lib/droonga/logger.rb +7 -1
  64. data/lib/droonga/node_name.rb +90 -0
  65. data/lib/droonga/node_role.rb +72 -0
  66. data/lib/droonga/path.rb +34 -9
  67. data/lib/droonga/planner.rb +73 -7
  68. data/lib/droonga/plugin/async_command.rb +154 -0
  69. data/lib/droonga/plugins/catalog.rb +1 -0
  70. data/lib/droonga/plugins/crud.rb +22 -6
  71. data/lib/droonga/plugins/dump.rb +66 -135
  72. data/lib/droonga/plugins/groonga/delete.rb +13 -0
  73. data/lib/droonga/plugins/search/distributed_search_planner.rb +4 -4
  74. data/lib/droonga/plugins/system.rb +5 -26
  75. data/lib/droonga/plugins/system/absorb_data.rb +405 -0
  76. data/lib/droonga/plugins/system/statistics.rb +71 -0
  77. data/lib/droonga/plugins/system/status.rb +53 -0
  78. data/lib/droonga/process_control_protocol.rb +3 -1
  79. data/lib/droonga/process_supervisor.rb +32 -15
  80. data/lib/droonga/reducer.rb +69 -0
  81. data/lib/droonga/safe_file_writer.rb +1 -1
  82. data/lib/droonga/serf.rb +207 -276
  83. data/lib/droonga/serf/agent.rb +228 -0
  84. data/lib/droonga/serf/command.rb +94 -0
  85. data/lib/droonga/serf/downloader.rb +120 -0
  86. data/lib/droonga/serf/remote_command.rb +348 -0
  87. data/lib/droonga/serf/tag.rb +56 -0
  88. data/lib/droonga/service_installation.rb +2 -2
  89. data/lib/droonga/session.rb +49 -1
  90. data/lib/droonga/single_step.rb +6 -11
  91. data/lib/droonga/single_step_definition.rb +32 -1
  92. data/lib/droonga/slice.rb +14 -9
  93. data/lib/droonga/supervisor.rb +27 -20
  94. data/lib/droonga/test/stub_handler_messenger.rb +2 -1
  95. data/lib/droonga/timestamp.rb +69 -0
  96. data/lib/droonga/worker_process_agent.rb +33 -15
  97. data/sample/cluster-state.json +8 -0
  98. data/sample/cluster/Rakefile +30 -6
  99. data/test/command/fixture/integer-key-table.jsons +11 -0
  100. data/test/command/fixture/string-key-table.jsons +11 -0
  101. data/test/command/run-test.rb +4 -0
  102. data/test/command/suite/add/error/invalid-integer.expected +3 -3
  103. data/test/command/suite/add/error/invalid-time.expected +3 -3
  104. data/test/command/suite/add/{minimum.expected → key-integer.expected} +0 -0
  105. data/test/command/suite/add/{minimum.test → key-integer.test} +0 -0
  106. data/test/command/suite/add/key-string.expected +6 -0
  107. data/test/command/suite/add/key-string.test +9 -0
  108. data/test/command/suite/add/mismatched-key-type/acceptable/integer-for-string.expected +6 -0
  109. data/test/command/suite/add/mismatched-key-type/acceptable/integer-for-string.test +9 -0
  110. data/test/command/suite/add/mismatched-key-type/acceptable/string-for-integer.expected +6 -0
  111. data/test/command/suite/add/mismatched-key-type/acceptable/string-for-integer.test +9 -0
  112. data/test/command/suite/add/without-values.expected +6 -0
  113. data/test/command/suite/add/without-values.test +11 -0
  114. data/test/command/suite/dump/column/index.expected +33 -1
  115. data/test/command/suite/dump/column/index.test +1 -0
  116. data/test/command/suite/dump/column/scalar.expected +29 -1
  117. data/test/command/suite/dump/column/scalar.test +1 -0
  118. data/test/command/suite/dump/column/vector.expected +29 -1
  119. data/test/command/suite/dump/column/vector.test +1 -0
  120. data/test/command/suite/dump/record/scalar.catalog.json +12 -0
  121. data/test/command/suite/dump/record/scalar.expected +84 -0
  122. data/test/command/suite/dump/record/scalar.test +16 -0
  123. data/test/command/suite/dump/record/vector/reference.expected +83 -1
  124. data/test/command/suite/dump/record/vector/reference.test +1 -0
  125. data/test/command/suite/dump/table/array.expected +27 -1
  126. data/test/command/suite/dump/table/array.test +1 -0
  127. data/test/command/suite/dump/table/double_array_trie.expected +27 -1
  128. data/test/command/suite/dump/table/double_array_trie.test +1 -0
  129. data/test/command/suite/dump/table/hash.expected +27 -1
  130. data/test/command/suite/dump/table/hash.test +1 -0
  131. data/test/command/suite/dump/table/patricia_trie.expected +27 -1
  132. data/test/command/suite/dump/table/patricia_trie.test +1 -0
  133. data/test/command/suite/groonga/delete/{success.expected → key-integer.expected} +0 -0
  134. data/test/command/suite/groonga/delete/key-integer.test +17 -0
  135. data/test/command/suite/groonga/delete/key-string.expected +19 -0
  136. data/test/command/suite/groonga/delete/{success.test → key-string.test} +4 -6
  137. data/test/command/suite/groonga/delete/mismatched-type-key/acceptable/integer-for-string.expected +19 -0
  138. data/test/command/suite/groonga/delete/mismatched-type-key/acceptable/integer-for-string.test +17 -0
  139. data/test/command/suite/groonga/delete/mismatched-type-key/acceptable/string-for-integer.expected +19 -0
  140. data/test/command/suite/groonga/delete/mismatched-type-key/acceptable/string-for-integer.test +17 -0
  141. data/test/command/suite/message/error/missing-dataset.test +1 -0
  142. data/test/command/suite/system/absorb-data/records.catalog.json +58 -0
  143. data/test/command/suite/system/absorb-data/records.expected +32 -0
  144. data/test/command/suite/system/absorb-data/records.test +24 -0
  145. data/test/command/suite/system/statistics/object/count/empty.expected +11 -0
  146. data/test/command/suite/system/statistics/object/count/empty.test +12 -0
  147. data/test/command/suite/system/statistics/object/count/per-volume/empty.catalog.json +36 -0
  148. data/test/command/suite/system/statistics/object/count/per-volume/empty.expected +19 -0
  149. data/test/command/suite/system/statistics/object/count/per-volume/empty.test +12 -0
  150. data/test/command/suite/system/statistics/object/count/per-volume/record.catalog.json +40 -0
  151. data/test/command/suite/system/statistics/object/count/per-volume/record.expected +19 -0
  152. data/test/command/suite/system/statistics/object/count/per-volume/record.test +23 -0
  153. data/test/command/suite/system/statistics/object/count/per-volume/schema.catalog.json +40 -0
  154. data/test/command/suite/system/statistics/object/count/per-volume/schema.expected +19 -0
  155. data/test/command/suite/system/statistics/object/count/per-volume/schema.test +13 -0
  156. data/test/command/suite/system/statistics/object/count/record.catalog.json +12 -0
  157. data/test/command/suite/system/statistics/object/count/record.expected +11 -0
  158. data/test/command/suite/system/statistics/object/count/record.test +23 -0
  159. data/test/command/suite/system/statistics/object/count/schema.catalog.json +12 -0
  160. data/test/command/suite/system/statistics/object/count/schema.expected +11 -0
  161. data/test/command/suite/system/statistics/object/count/schema.test +13 -0
  162. data/test/command/suite/system/status.expected +3 -2
  163. data/test/unit/catalog/test_dataset.rb +4 -1
  164. data/test/unit/{test_catalog_generator.rb → catalog/test_generator.rb} +2 -2
  165. data/test/unit/catalog/test_replicas_volume.rb +79 -0
  166. data/test/unit/catalog/test_single_volume.rb +2 -2
  167. data/test/unit/catalog/test_slice.rb +33 -1
  168. data/test/unit/catalog/{test_collection_volume.rb → test_slices_volume.rb} +72 -11
  169. data/test/unit/catalog/test_version2.rb +3 -0
  170. data/test/unit/helper/distributed_search_planner_helper.rb +2 -2
  171. data/test/unit/plugins/catalog/test_fetch.rb +4 -4
  172. data/test/unit/plugins/crud/test_add.rb +44 -4
  173. data/test/unit/plugins/groonga/test_column_create.rb +4 -4
  174. data/test/unit/plugins/groonga/test_column_list.rb +4 -4
  175. data/test/unit/plugins/groonga/test_column_remove.rb +4 -4
  176. data/test/unit/plugins/groonga/test_column_rename.rb +4 -4
  177. data/test/unit/plugins/groonga/test_delete.rb +73 -10
  178. data/test/unit/plugins/groonga/test_table_create.rb +4 -4
  179. data/test/unit/plugins/groonga/test_table_list.rb +4 -4
  180. data/test/unit/plugins/groonga/test_table_remove.rb +4 -4
  181. data/test/unit/plugins/search/test_handler.rb +4 -4
  182. data/test/unit/plugins/search/test_planner.rb +4 -2
  183. data/test/unit/plugins/system/test_status.rb +31 -15
  184. data/test/unit/plugins/test_watch.rb +16 -16
  185. data/test/unit/test_address.rb +4 -4
  186. metadata +134 -35
  187. data/lib/droonga/catalog/volume_collection.rb +0 -79
  188. data/lib/droonga/catalog_fetcher.rb +0 -53
  189. data/lib/droonga/catalog_generator.rb +0 -243
  190. data/lib/droonga/catalog_loader.rb +0 -56
  191. data/lib/droonga/command/remote.rb +0 -404
  192. data/lib/droonga/data_absorber.rb +0 -264
  193. data/lib/droonga/node_status.rb +0 -71
  194. data/lib/droonga/serf_downloader.rb +0 -115
  195. data/test/unit/catalog/test_volume_collection.rb +0 -78
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f2574dab76722981c14a23373e838f1e01d18220
4
- data.tar.gz: 9776825164dda315f20af0f0d610d2391cce93fd
3
+ metadata.gz: bd5aad47933f58aee93072477c535d458556b183
4
+ data.tar.gz: fc6e82289c99cc3ef4cdc269d0153ca6588dec7f
5
5
  SHA512:
6
- metadata.gz: 55b359cab3e5ca1bd0b7008233c54bbf413d27b97ee8d179ca09be701fa4f2749935cb37eaf743bfb0099600794c80d319edce22bedde28b69f685d944c8f193
7
- data.tar.gz: 09add0504d2886e51065af0c09a4d350db98c8fe07e9a6de6f317ca08af34971016f57c2ac3706d4317df110d125077045c8973c996f4bda382a187cd903ffa0
6
+ metadata.gz: 08909575ab516f97f404396904a53a0920bdc7a6ad7ab8673e7065d681698dee2f7029c71cb74731824892df0eeb87b8de4383d4aa9d0107823e7a00f363a7dc
7
+ data.tar.gz: fc2acfa0c8c133bc70ebad99f7d326e96fcc316fdedfb45347431e5ec3a96a751d4f00db03b9c21c4c44d03d26b8da5b0263615926e9321d50f44c60277f0cf9
@@ -9,6 +9,7 @@ rvm:
9
9
  - 1.9.3
10
10
  - 2.0.0
11
11
  - 2.1
12
+ - 2.2
12
13
  # - ruby-head
13
14
  before_install:
14
15
  # - GROONGA_MASTER=yes curl --silent --location https://raw.github.com/groonga/groonga/master/data/travis/setup.sh | sh
@@ -0,0 +1,44 @@
1
+ require "coolio"
2
+
3
+ $times = 100000
4
+
5
+ def add_timeout_timer(loop)
6
+ timeout_timer = Coolio::TimerWatcher.new(60)
7
+ timeout_timer.on_timer do
8
+ timeout_timer.detach
9
+ timeout_timer = nil
10
+ end
11
+ loop.attach(timeout_timer)
12
+ timeout_timer
13
+ end
14
+
15
+ puts "start."
16
+
17
+ $start = Time.now
18
+ $timers = []
19
+ $loop = Cool.io::Loop.default
20
+ $times.times do
21
+ $timers << add_timeout_timer($loop)
22
+ end
23
+
24
+ $timeout_before = Time.now
25
+ timeout_timer = Coolio::TimerWatcher.new(1)
26
+ timeout_timer.on_timer do
27
+ $timeout_after = Time.now
28
+
29
+ timeout_timer.detach
30
+ $timers.each do |timer|
31
+ timer.detach
32
+ end
33
+
34
+ $finish = Time.now
35
+ delta = $finish - $start
36
+ delta -= $timeout_after - $timeout_before
37
+ puts "done."
38
+ puts "overhead: #{delta} seconds for #{$times} timers."
39
+ end
40
+ $loop.attach(timeout_timer)
41
+
42
+ $loop.run
43
+
44
+
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  #
3
- # Copyright (C) 2014 Droonga Project
3
+ # Copyright (C) 2014-2015 Droonga Project
4
4
  #
5
5
  # This library is free software; you can redistribute it and/or
6
6
  # modify it under the terms of the GNU Lesser General Public
@@ -17,230 +17,289 @@
17
17
 
18
18
  require "ostruct"
19
19
  require "optparse"
20
- require "open3"
21
20
  require "socket"
21
+ require "coolio"
22
22
 
23
23
  require "droonga/engine/version"
24
- require "droonga/catalog_generator"
25
24
  require "droonga/path"
26
- require "droonga/data_absorber"
25
+ require "droonga/node_name"
26
+ require "droonga/data_absorber_client"
27
27
  require "droonga/serf"
28
28
  require "droonga/client"
29
29
 
30
- class AbsorbDataCommand
31
- def run
32
- parse_options
33
- assert_valid_options
34
- trap_signals
35
-
36
- puts "Start to absorb data from #{@options.source_host}"
37
- puts " to #{@options.destination_host}"
38
- puts " via #{@options.receiver_host} (this host)"
39
- puts " dataset = #{@options.dataset}"
40
- puts " port = #{@options.port}"
41
- puts " tag = #{@options.tag}"
42
- puts ""
43
- puts "Absorbing..."
44
-
45
- if @options.remote
46
- absorb_on_remote
47
- else
48
- absorb_on_local
49
- end
30
+ module Droonga
31
+ module Command
32
+ class AbsorbData
33
+ def run
34
+ @loop = Coolio::Loop.default
50
35
 
51
- puts "Done."
52
- exit(true)
53
- end
36
+ parse_options
37
+ assert_valid_options
38
+ trap_signals
54
39
 
55
- private
56
- def parse_options
57
- options = OpenStruct.new
58
- options.port = Droonga::CatalogGenerator::DEFAULT_PORT
59
- options.tag = Droonga::CatalogGenerator::DEFAULT_TAG
60
- options.dataset = Droonga::CatalogGenerator::DEFAULT_DATASET
61
- options.remote = true
62
- options.receiver_host = Socket.gethostname
63
- options.messages_per_second = Droonga::DataAbsorber::DEFAULT_MESSAGES_PER_SECOND
64
- parser = OptionParser.new
65
- parser.version = Droonga::Engine::VERSION
66
-
67
- parser.separator("")
68
- parser.separator("Connection:")
69
- parser.on("--source-host=HOST",
70
- "Host name of the source cluster to be connected.") do |host|
71
- options.source_host = host
72
- end
73
- parser.on("--destination-host=HOST",
74
- "Host name of this cluster to be connected.") do |host|
75
- options.destination_host = host
76
- end
77
- parser.on("--receiver-host=HOST",
78
- "Host name of this computer.",
79
- "(#{options.receiver_host})") do |host|
80
- options.receiver_host = host
81
- end
82
- parser.on("--port=PORT", Integer,
83
- "Port number of the source cluster to be connected.",
84
- "(#{options.port})") do |port|
85
- options.port = port
40
+ puts "Start to absorb data from #{@options.source_dataset} at #{source_node.to_s}"
41
+ puts " to #{@options.dataset} at #{destination_node.to_s}"
42
+ puts " via #{@options.receiver_host} (this host)"
43
+ puts ""
44
+ puts "Absorbing..."
45
+
46
+ #XXX If any command is received by the source node after changing of its role,
47
+ # the timestamp of last processed mesasge is unexpectedly updated by them.
48
+ # Be careful to not send any command to the source node on this timing!!
49
+ update_accept_messages_newer_than_timestamp
50
+
51
+ succeeded = absorb
52
+
53
+ if succeeded
54
+ puts "Done."
55
+ else
56
+ do_cancel
57
+ end
58
+ succeeded
86
59
  end
87
- parser.on("--[no-]remote",
88
- "Run command in remote node or not.",
89
- "(#{options.remote})") do |remote|
90
- options.remote = remote
60
+
61
+ private
62
+ def parse_options
63
+ options = OpenStruct.new
64
+
65
+ options.host = Socket.gethostname
66
+ options.port = DataAbsorberClient::DEFAULT_PORT
67
+ options.tag = DataAbsorberClient::DEFAULT_TAG
68
+ options.dataset = DataAbsorberClient::DEFAULT_DATASET
69
+
70
+ options.source_host = DataAbsorberClient::DEFAULT_HOST
71
+ options.source_port = DataAbsorberClient::DEFAULT_PORT
72
+ options.source_tag = DataAbsorberClient::DEFAULT_TAG
73
+ options.source_dataset = DataAbsorberClient::DEFAULT_DATASET
74
+
75
+ options.receiver_host = Socket.gethostname
76
+
77
+ options.messages_per_second = DataAbsorberClient::DEFAULT_MESSAGES_PER_SECOND
78
+ options.progress_interval_seconds = DataAbsorberClient::DEFAULT_PROGRESS_INTERVAL_SECONDS
79
+
80
+ options.verbose = false
81
+
82
+ parser = OptionParser.new
83
+ parser.version = Engine::VERSION
84
+
85
+ parser.separator("")
86
+ parser.separator("Destination node:")
87
+ parser.on("--host=HOST",
88
+ "Host name of the destination node.") do |host|
89
+ options.host = host
90
+ end
91
+ parser.on("--port=PORT", Integer,
92
+ "Port number of the destination node.",
93
+ "(#{options.port})") do |port|
94
+ options.port = port
95
+ end
96
+ parser.on("--tag=TAG", Integer,
97
+ "Tag name of the destination node.",
98
+ "(#{options.tag})") do |tag|
99
+ options.tag = tag
100
+ end
101
+ parser.on("--dataset=DATASET",
102
+ "Name of the destination dataset.",
103
+ "(#{options.dataset})") do |dataset|
104
+ options.dataset = dataset
105
+ end
106
+
107
+ parser.separator("")
108
+ parser.separator("Source node:")
109
+ parser.on("--source-host=HOST",
110
+ "Host name of the source node.",
111
+ "(#{options.source_host})") do |host|
112
+ options.source_host = host
113
+ end
114
+ parser.on("--source-port=PORT", Integer,
115
+ "Port number of the source node.",
116
+ "(#{options.source_port})") do |host|
117
+ options.source_host = host
118
+ end
119
+ parser.on("--source-tag=TAG",
120
+ "Tag name of the source node.",
121
+ "(#{options.source_tag})") do |tag|
122
+ options.source_tag = tag
123
+ end
124
+ parser.on("--source-dataset=DATASET",
125
+ "Name of the source dataset.",
126
+ "(#{options.source_dataset})") do |dataset|
127
+ options.source_dataset = dataset
128
+ end
129
+
130
+ parser.separator("")
131
+ parser.separator("Connection:")
132
+ parser.on("--receiver-host=HOST",
133
+ "Host name of this computer.",
134
+ "(#{options.receiver_host})") do |host|
135
+ options.receiver_host = host
136
+ end
137
+
138
+ parser.separator("")
139
+ parser.separator("Miscellaneous:")
140
+ parser.on("--records-per-second=N", Integer,
141
+ "Maximum number of records per second to be absorbed.",
142
+ "'#{Client::RateLimiter::NO_LIMIT}' means no limit.",
143
+ "(#{options.messages_per_second})") do |n|
144
+ options.messages_per_second = n
145
+ end
146
+ parser.on("--progress-interval-seconds=N", Integer,
147
+ "Interval seconds to report progress.",
148
+ "(#{options.progress_interval_seconds})") do |n|
149
+ options.progress_interval_seconds = n
150
+ end
151
+ parser.on("--[no-]verbose",
152
+ "Output details for internal operations.",
153
+ "(#{options.verbose})") do |verbose|
154
+ options.verbose = verbose
155
+ end
156
+
157
+ parser.separator("")
158
+ parser.separator("For backward compatibility:")
159
+ parser.on("--destination-host=HOST",
160
+ "Alias to \"--host\".") do |host|
161
+ options.host = host
162
+ end
163
+
164
+ parser.parse!(ARGV)
165
+ @options = options
91
166
  end
92
167
 
93
- parser.separator("")
94
- parser.separator("Data:")
95
- parser.on("--tag=TAG",
96
- "Tag name to be used to communicate with Droonga system.",
97
- "(#{options.tag})") do |tag|
98
- options.tag = tag
168
+ def assert_valid_options
169
+ unless @options.source_host
170
+ raise "You must specify the source host via --source-host option."
171
+ end
172
+ unless @options.host
173
+ raise "You must specify the destination host via --host option."
174
+ end
99
175
  end
100
- parser.on("--dataset=DATASET",
101
- "Dataset to be absorbed.",
102
- "(#{options.dataset})") do |dataset|
103
- options.dataset = dataset
176
+
177
+ def source_node
178
+ @source_node ||= NodeName.new(:host => @options.source_host,
179
+ :port => @options.source_port,
180
+ :tag => @options.source_tag)
104
181
  end
105
- parser.on("--records-per-second=N", Integer,
106
- "Maximum number of records per second to be absorbed.",
107
- "'#{Droonga::Client::RateLimiter::NO_LIMIT}' means no limit.",
108
- "(#{options.messages_per_second})") do |n|
109
- options.messages_per_second = n
182
+
183
+ def destination_node
184
+ @destination_node ||= NodeName.new(:host => @options.host,
185
+ :port => @options.port,
186
+ :tag => @options.tag)
110
187
  end
111
188
 
112
- parser.parse!(ARGV)
113
- @options = options
114
- end
189
+ def source_node_serf
190
+ @source_node_serf ||= Serf.new(source_node.to_s,
191
+ :verbose => @options.verbose)
192
+ end
115
193
 
116
- def assert_valid_options
117
- unless @options.source_host
118
- raise "You must specify the source host via --source-host option."
194
+ def destination_node_serf
195
+ @destination_node_serf ||= Serf.new(destination_node.to_s,
196
+ :verbose => @options.verbose)
119
197
  end
120
- unless @options.destination_host
121
- raise "You must specify the destination host via --destination-host option."
198
+
199
+ def absorber
200
+ @absorber ||= prepare_absorber
122
201
  end
123
- end
124
202
 
125
- def source_node
126
- "#{@options.source_host}:#{@options.port}/#{@options.tag}"
127
- end
203
+ def prepare_absorber
204
+ absorber_options = {
205
+ :host => @options.host,
206
+ :port => @options.port,
207
+ :tag => @options.tag,
208
+ :dataset => @options.dataset,
128
209
 
129
- def destination_node
130
- "#{@options.destination_host}:#{@options.port}/#{@options.tag}"
131
- end
210
+ :source_host => @options.source_host,
211
+ :source_port => @options.source_port,
212
+ :source_tag => @options.source_tag,
213
+ :source_dataset => @options.source_dataset,
132
214
 
133
- def run_remote_command(target, command, options)
134
- serf = Droonga::Serf.new(nil, target)
135
- result = serf.send_query(command, options)
136
- #puts result[:result]
137
- puts result[:error] unless result[:error].empty?
138
- result[:response]
139
- end
215
+ :receiver_host => @options.receiver_host,
140
216
 
141
- def absorber
142
- @absorber ||= prepare_absorber
143
- end
217
+ :messages_per_second => @options.messages_per_second,
218
+ :progress_interval_seconds => @options.progress_interval_seconds,
144
219
 
145
- def prepare_absorber
146
- absorber_options = {
147
- :dataset => @options.dataset,
148
- :source_host => @options.source_host,
149
- :destination_host => @options.destination_host,
150
- :receiver_host => @options.receiver_host,
151
- :port => @options.port,
152
- :tag => @options.tag,
153
- :messages_per_second => @options.messages_per_second,
154
- }
155
- Droonga::DataAbsorber.new(absorber_options)
156
- end
220
+ :client_options => {
221
+ :backend => :coolio,
222
+ :loop => @loop,
223
+ },
224
+ }
225
+ DataAbsorberClient.new(absorber_options)
226
+ end
157
227
 
158
- def absorb_on_remote
159
- start_time_in_seconds = Time.new.to_i
160
- run_remote_command(destination_node, "absorb_data",
161
- "node" => destination_node,
162
- "source" => @options.source_host,
163
- "port" => @options.port,
164
- "tag" => @options.tag,
165
- "dataset" => @options.dataset,
166
- "messages_per_second" => @options.messages_per_second)
167
- last_progress = ""
168
- while true
169
- sleep(3)
170
- response = run_remote_command(destination_node, "report_status",
171
- "node" => destination_node,
172
- "key" => "absorbing")
173
- if response
174
- absorbing = response["value"]
175
- break unless absorbing
176
- end
177
-
178
- progress = absorber.report_progress(start_time_in_seconds)
179
- if progress
180
- printf("%s", "#{" " * last_progress.size}\r")
181
- printf("%s", "#{progress}\r")
228
+ def absorb
229
+ last_progress = nil
230
+ absorber.run do |progress|
231
+ if last_progress
232
+ printf("%s", "#{" " * last_progress[:message].size}\r")
233
+ end
234
+ printf("%s", "#{progress[:message]}\r")
182
235
  last_progress = progress
183
236
  end
237
+ @loop.run
238
+
239
+ if absorber.error_message
240
+ puts(absorber.error_message)
241
+ return false
242
+ end
243
+
244
+ puts ""
245
+ true
184
246
  end
185
- puts ""
186
-
187
- response = run_remote_command(source_node, "report_status",
188
- "node" => source_node,
189
- "key" => "last_processed_message_timestamp")
190
- timestamp = response["value"]
191
- if timestamp and not timestamp.empty?
192
- puts "The timestamp of the last processed message in the source node: #{timestamp}"
193
- puts "Setting effective message timestamp for the destination node..."
194
- response = run_remote_command(destination_node, "set_status",
195
- "node" => destination_node,
196
- "key" => "effective_message_timestamp",
197
- "value" => timestamp)
247
+
248
+ GETTING_LAST_MESSAGE_TIMESTAMP_MAX_RETRY_COUNT = 10
249
+ GETTING_LAST_MESSAGE_TIMESTAMP_RETRY_INTERVAL_SECONDS = 10
250
+
251
+ def try_get_last_message_timestamp(retry_count=0)
252
+ puts "Getting the timestamp of the last processed message in the source node..."
253
+ timestamp = source_node_serf.last_message_timestamp
254
+ unless timestamp
255
+ if retry_count < GETTING_LAST_MESSAGE_TIMESTAMP_MAX_RETRY_COUNT
256
+ puts "Failed. Retrying..."
257
+ sleep(GETTING_LAST_MESSAGE_TIMESTAMP_RETRY_INTERVAL_SECONDS)
258
+ timestamp = try_get_last_message_timestamp(retry_count + 1)
259
+ end
260
+ end
261
+ timestamp
198
262
  end
199
- end
200
263
 
201
- def absorb_on_local
202
- last_progress = ""
203
- absorber.absorb do |live_status|
204
- if live_status[:progress]
205
- progress = live_status[:progress]
264
+ def update_accept_messages_newer_than_timestamp
265
+ timestamp = try_get_last_message_timestamp
266
+ if timestamp and not timestamp.empty?
267
+ puts "The timestamp of the last processed message in the source node: #{timestamp}"
268
+ puts "Setting the destination node to ignore messages older than the timestamp..."
269
+ destination_node_serf.ensure_restarted do
270
+ destination_node_serf.send_query("accept_messages_newer_than",
271
+ "node" => destination_node.to_s,
272
+ "timestamp" => timestamp)
273
+ end
206
274
  else
207
- progress = live_status[:output]
275
+ $stderr.puts("WARNING: Couldn't get the time stamp of " +
276
+ "the last processed message from the source node. " +
277
+ "Any message will be forwarded to the destination node.")
208
278
  end
209
- printf("%s", "#{" " * last_progress.size}\r")
210
- printf("%s", "#{progress}\r")
211
- last_progress = progress
212
279
  end
213
- response = run_remote_command(source_node, "report_status",
214
- "node" => source_node,
215
- "key" => "last_processed_message_timestamp")
216
- timestamp = response["value"]
217
- puts "The timestamp of the last processed message in the source node: #{timestamp}"
218
- if timestamp and not timestamp.empty?
219
- status = NodeStatus.new
220
- status.set(:effective_message_timestamp, timestamp)
221
- end
222
- end
223
280
 
224
- def trap_signals
225
- trap(:TERM) do
226
- do_cancel
227
- trap(:TERM, "DEFAULT")
228
- end
281
+ def trap_signals
282
+ trap(:TERM) do
283
+ trap(:TERM, "DEFAULT")
284
+ do_cancel
285
+ end
229
286
 
230
- trap(:INT) do
231
- do_cancel
232
- trap(:INT, "DEFAULT")
233
- end
287
+ trap(:INT) do
288
+ trap(:INT, "DEFAULT")
289
+ do_cancel
290
+ end
234
291
 
235
- trap(:QUIT) do
236
- do_cancel
237
- trap(:QUIT, "DEFAULT")
292
+ trap(:QUIT) do
293
+ trap(:QUIT, "DEFAULT")
294
+ do_cancel
295
+ end
238
296
  end
239
- end
240
297
 
241
- def do_cancel
242
- #XXX we have to write more codes to cancel remote processes!
298
+ def do_cancel
299
+ #XXX we have to write more codes to cancel remote processes!
300
+ end
301
+ end
243
302
  end
244
303
  end
245
304
 
246
- AbsorbDataCommand.new.run
305
+ exit(Droonga::Command::AbsorbData.new.run)