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
@@ -22,26 +22,92 @@ module Droonga
22
22
  include Loggable
23
23
  include ErrorMessages
24
24
 
25
+ attr_writer :write, :single_operation, :use_all_replicas, :collector_class
26
+
25
27
  def initialize(dataset)
26
28
  @dataset = dataset
29
+ @write = false
30
+ @single_operation = false
31
+ @use_all_replicas = false
32
+ @collector_class = nil
27
33
  end
28
34
 
29
- def plan(message)
30
- raise NotImplemented, "#{self.class.name}\##{__method__} must implement."
35
+ def plan(message, params={})
36
+ options = {
37
+ :record => params[:record],
38
+ }
39
+
40
+ #TODO: We don't have to reduce results of the message when
41
+ # the message doesn't have "replyTo" information, because:
42
+ #
43
+ # * Currently the "super step" mecahnism is not
44
+ # implemented yet.
45
+ # * So, reduced results won't be forwarded to other
46
+ # handlers directly. Results will be forwarded to
47
+ # the sender as the "response".
48
+ # * So, if "replyTo" information is not given, the
49
+ # reduced result will have no receiver.
50
+ #
51
+ # However, in the future after the "super step" mechanism
52
+ # is introduced, reduced results can be required even if
53
+ # the request message have no "replyTo" information.
54
+ # Then we must update this logic.
55
+ if @collector_class and message["replyTo"]
56
+ reduce_key = "result"
57
+ options[:reduce] = {
58
+ reduce_key => @collector_class.operator,
59
+ }
60
+ end
61
+
62
+ if options[:record] or single_operation?
63
+ scatter(message, options)
64
+ else
65
+ broadcast(message, options)
66
+ end
31
67
  end
32
68
 
33
69
  private
34
- def scatter(message, record, options={})
70
+ def write?
71
+ @write
72
+ end
73
+
74
+ def single_operation?
75
+ return false if write?
76
+ @single_operation
77
+ end
78
+
79
+ def use_all_replicas?
80
+ write? or @use_all_replicas
81
+ end
82
+
83
+ def scatter(message, options={})
35
84
  planner = DistributedCommandPlanner.new(@dataset, message)
36
- planner.scatter(record)
37
- planner.reduce(options[:reduce])
85
+ scatter_options = {
86
+ :write => write?,
87
+ :record => options[:record],
88
+ }
89
+ if single_operation?
90
+ scatter_options[:slice] = "random"
91
+ scatter_options[:replica] = "random"
92
+ end
93
+ planner.scatter(scatter_options)
94
+ planner.reduce(options[:reduce]) if options[:reduce]
38
95
  planner.plan
39
96
  end
40
97
 
41
98
  def broadcast(message, options={})
42
99
  planner = DistributedCommandPlanner.new(@dataset, message)
43
- planner.broadcast(:write => options[:write])
44
- planner.reduce(options[:reduce])
100
+ broadcast_options = {
101
+ :write => write?,
102
+ }
103
+ if use_all_replicas?
104
+ broadcast_options[:replica] = "all"
105
+ elsif single_operation?
106
+ broadcast_options[:slice] = "random"
107
+ broadcast_options[:replica] = "random"
108
+ end
109
+ planner.broadcast(broadcast_options)
110
+ planner.reduce(options[:reduce]) if options[:reduce]
45
111
  planner.plan
46
112
  end
47
113
  end
@@ -0,0 +1,154 @@
1
+ # Copyright (C) 2014-2015 Droonga Project
2
+ #
3
+ # This library is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU Lesser General Public
5
+ # License version 2.1 as published by the Free Software Foundation.
6
+ #
7
+ # This library is distributed in the hope that it will be useful,
8
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
9
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10
+ # Lesser General Public License for more details.
11
+ #
12
+ # You should have received a copy of the GNU Lesser General Public
13
+ # License along with this library; if not, write to the Free Software
14
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
+
16
+ require "coolio"
17
+
18
+ require "droonga/loggable"
19
+ require "droonga/handler"
20
+ require "droonga/error_messages"
21
+
22
+ module Droonga
23
+ module Plugins
24
+ module AsyncCommand
25
+ class Request
26
+ def initialize(message)
27
+ @message = message
28
+ end
29
+
30
+ def need_start?
31
+ reply_to
32
+ end
33
+
34
+ def id
35
+ @message["id"]
36
+ end
37
+
38
+ def dataset
39
+ @message.raw["dataset"]
40
+ end
41
+
42
+ def reply_to
43
+ (@message.raw["replyTo"] || {})["to"]
44
+ end
45
+
46
+ def request
47
+ @message.request
48
+ end
49
+ end
50
+
51
+ class Handler < Droonga::Handler
52
+ def handle(message)
53
+ request = request_class.new(message)
54
+ if request.need_start?
55
+ start(request)
56
+ {
57
+ "started" => true,
58
+ }
59
+ else
60
+ {
61
+ "started" => false,
62
+ }
63
+ end
64
+ end
65
+
66
+ private
67
+ def request_class
68
+ #XXX override me!
69
+ Request
70
+ end
71
+
72
+ def start(request)
73
+ #XXX override me!
74
+ # handler = MyAsyncHandler.new(loop, messenger, request)
75
+ # handler.start
76
+ end
77
+ end
78
+
79
+ class AsyncHandler
80
+ include Loggable
81
+
82
+ def initialize(loop, messenger, request)
83
+ @loop = loop
84
+ @messenger = messenger
85
+ @request = request
86
+ end
87
+
88
+ def start
89
+ #XXX override me!!
90
+ on_start
91
+ on_finish
92
+ end
93
+
94
+ private
95
+ def prefix
96
+ "" #XXX override me!!
97
+ end
98
+
99
+ def on_start
100
+ setup_forward_data
101
+ forward("#{prefix}.start")
102
+ end
103
+
104
+ def on_finish
105
+ forward("#{prefix}.end")
106
+ end
107
+
108
+ def setup_forward_data
109
+ @base_forward_message = {
110
+ "inReplyTo" => @request.id,
111
+ "dataset" => @request.dataset,
112
+ }
113
+ @forward_to = @request.reply_to
114
+ end
115
+
116
+ def error_name
117
+ "Failure" #XXX override me!!
118
+ end
119
+
120
+ def error_message
121
+ "failed to do" #XXX override me!!
122
+ end
123
+
124
+ def error(name, message)
125
+ message = {
126
+ "statusCode" => ErrorMessages::InternalServerError::STATUS_CODE,
127
+ "body" => {
128
+ "name" => name,
129
+ "message" => message,
130
+ },
131
+ }
132
+ error_message = @base_forward_message.merge(message)
133
+ @messenger.forward(error_message,
134
+ "to" => @forward_to,
135
+ "type" => "#{prefix}.error")
136
+ end
137
+
138
+ def forward(type, body=nil)
139
+ forward_message = @base_forward_message
140
+ if body
141
+ forward_message = forward_message.merge("body" => body)
142
+ end
143
+ @messenger.forward(forward_message,
144
+ "to" => @forward_to,
145
+ "type" => type)
146
+ end
147
+
148
+ def log_tag
149
+ "[#{Process.ppid}] async-handler"
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
@@ -32,6 +32,7 @@ module Droonga
32
32
 
33
33
  define_single_step do |step|
34
34
  step.name = "catalog.fetch"
35
+ step.single_operation = true
35
36
  step.handler = FetchHandler
36
37
  step.collector = Collectors::Or
37
38
  end
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2014 Droonga Project
1
+ # Copyright (C) 2013-2015 Droonga Project
2
2
  #
3
3
  # This library is free software; you can redistribute it and/or
4
4
  # modify it under the terms of the GNU Lesser General Public
@@ -70,8 +70,8 @@ module Droonga
70
70
  end
71
71
 
72
72
  class InvalidValue < BadRequest
73
- def initialize(column, value, request)
74
- super("The column #{column.inspect} cannot store the value #{value.inspect}.",
73
+ def initialize(column, value, reason, request)
74
+ super("The column #{column.inspect} cannot store the value #{value.inspect}: #{reason}",
75
75
  request)
76
76
  end
77
77
  end
@@ -107,7 +107,8 @@ module Droonga
107
107
  def add_record(table, request)
108
108
  record = nil
109
109
  if table.support_key?
110
- record = table.add(request["key"])
110
+ key = normalize_record_key(request["key"], table)
111
+ record = table.add(key)
111
112
  else
112
113
  record = table.add
113
114
  end
@@ -116,16 +117,31 @@ module Droonga
116
117
  record[column] = value
117
118
  rescue ::Groonga::InvalidArgument => error
118
119
  record.delete if record.added?
119
- raise InvalidValue.new(column, value, request)
120
+ raise InvalidValue.new(column, value, error.message, request)
120
121
  rescue ArgumentError => error
121
122
  record.delete if record.added?
122
- raise InvalidValue.new(column, value, request)
123
+ raise InvalidValue.new(column, value, error.message, request)
124
+ rescue TypeError => error
125
+ record.delete if record.added?
126
+ raise InvalidValue.new(column, value, error.message, request)
123
127
  rescue ::Groonga::NoSuchColumn => error
124
128
  record.delete if record.added?
125
129
  raise UnknownColumn.new(column, request["table"], request)
126
130
  end
127
131
  end
128
132
  end
133
+
134
+ def normalize_record_key(key, table)
135
+ case table.domain.name
136
+ when "Int8", "UInt8",
137
+ "Int16", "UInt16",
138
+ "Int32", "UInt32",
139
+ "Int64", "UInt64"
140
+ key.to_i
141
+ else
142
+ key.to_s
143
+ end
144
+ end
129
145
  end
130
146
 
131
147
  define_single_step do |step|
@@ -13,10 +13,13 @@
13
13
  # License along with this library; if not, write to the Free Software
14
14
  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
15
 
16
+ require "fiber"
16
17
  require "groonga"
17
18
 
18
19
  require "droonga/plugin"
20
+ require "droonga/plugin/async_command"
19
21
  require "droonga/error_messages"
22
+ require "droonga/database_scanner"
20
23
 
21
24
  module Droonga
22
25
  module Plugins
@@ -35,149 +38,99 @@ module Droonga
35
38
  extend Plugin
36
39
  register("dump")
37
40
 
38
- class Handler < Droonga::Handler
39
- def handle(message)
40
- request = Request.new(message)
41
- if request.need_dump?
42
- dumper = Dumper.new(@context, loop, messenger, request)
43
- dumper.start_dump
44
- true
45
- else
46
- false
47
- end
48
- end
49
- end
50
-
51
- class Request
52
- def initialize(message)
53
- @message = message
54
- end
55
-
56
- def need_dump?
57
- reply_to
58
- end
41
+ class Request < AsyncCommand::Request
42
+ DEFAULT_MESSAGES_PER_SECOND = 10000
59
43
 
60
- def id
61
- @message["id"]
44
+ def messages_per_second
45
+ request = (@message.request || {})
46
+ minimum_messages_per_second = 10
47
+ [
48
+ minimum_messages_per_second,
49
+ (request["messagesPerSecond"] || DEFAULT_MESSAGES_PER_SECOND).to_i,
50
+ ].max
62
51
  end
52
+ end
63
53
 
64
- def dataset
65
- @message.raw["dataset"]
54
+ class Handler < AsyncCommand::Handler
55
+ private
56
+ def request_class
57
+ Request
66
58
  end
67
59
 
68
- def reply_to
69
- (@message.raw["replyTo"] || {})["to"]
70
- end
71
-
72
- def messages_per_seconds
73
- request = (@message.request || {})
74
- minimum_messages_per_seconds = 10
75
- [
76
- minimum_messages_per_seconds,
77
- (request["messagesPerSecond"] || 10000).to_i,
78
- ].max
60
+ def start(request)
61
+ dumper = Dumper.new(@context, loop, messenger, request)
62
+ dumper.start
79
63
  end
80
64
  end
81
65
 
82
- class Dumper
83
- include Loggable
66
+ class Dumper < AsyncCommand::AsyncHandler
67
+ include DatabaseScanner
84
68
 
85
69
  def initialize(context, loop, messenger, request)
86
70
  @context = context
87
- @loop = loop
88
- @messenger = messenger
89
- @request = request
71
+ super(loop, messenger, request)
90
72
  end
91
73
 
92
- def start_dump
93
- setup_forward_data
94
-
95
- forward("dump.start")
74
+ def start
75
+ on_start
96
76
 
97
- dumper = Fiber.new do
77
+ runner = Fiber.new do
78
+ forecast
98
79
  dump_schema
99
80
  dump_records
100
81
  dump_indexes
101
- forward("dump.end")
102
- end
103
-
104
- on_error = lambda do |exception|
105
- message = "failed to dump"
106
- logger.exception(message, $!)
107
- error("DumpFailure", message)
82
+ on_finish
108
83
  end
109
84
 
110
85
  timer = Coolio::TimerWatcher.new(0.1, true)
111
86
  timer.on_timer do
112
- begin
113
- dumper.resume
114
- rescue FiberError
115
- timer.detach
116
- rescue
87
+ if runner.alive?
88
+ begin
89
+ runner.resume
90
+ rescue
91
+ timer.detach
92
+ logger.trace("start: timer detached by unexpected exception",
93
+ :watcher => timer)
94
+ logger.exception(error_message, $!)
95
+ error(error_name, error_message)
96
+ end
97
+ else
117
98
  timer.detach
118
- on_error.call($!)
99
+ logger.trace("start: timer detached by unexpected exception",
100
+ :watcher => timer)
119
101
  end
120
102
  end
121
103
 
122
104
  @loop.attach(timer)
105
+ logger.trace("start: timer attached",
106
+ :watcher => timer)
123
107
  end
124
108
 
125
109
  private
126
- def setup_forward_data
127
- @base_forward_message = {
128
- "inReplyTo" => @request.id,
129
- "dataset" => @request.dataset,
130
- }
131
- @forward_to = @request.reply_to
132
- @n_forwarded_messages = 0
133
- @messages_per_100msec = @request.messages_per_seconds / 10
110
+ def prefix
111
+ "dump"
134
112
  end
135
113
 
136
- def error(name, message)
137
- message = {
138
- "statusCode" => ErrorMessages::InternalServerError::STATUS_CODE,
139
- "body" => {
140
- "name" => name,
141
- "message" => message,
142
- },
143
- }
144
- error_message = @base_forward_message.merge(message)
145
- @messenger.forward(error_message,
146
- "to" => @forward_to,
147
- "type" => "dump.error")
114
+ def error_name
115
+ "DumpFailure"
148
116
  end
149
117
 
150
- def forward(type, body=nil)
151
- forward_message = @base_forward_message
152
- if body
153
- forward_message = forward_message.merge("body" => body)
154
- end
155
- @messenger.forward(forward_message,
156
- "to" => @forward_to,
157
- "type" => type)
118
+ def error_message
119
+ "failed to dump"
120
+ end
158
121
 
159
- @n_forwarded_messages += 1
160
- @n_forwarded_messages %= @messages_per_100msec
161
- Fiber.yield if @n_forwarded_messages.zero?
122
+ def forecast
123
+ forward("#{prefix}.forecast", "nMessages" => total_n_objects)
162
124
  end
163
125
 
164
126
  def dump_schema
165
- reference_tables = []
166
127
  each_table do |table|
167
- if reference_table?(table)
168
- reference_tables << table
169
- next
170
- end
171
- dump_table(table)
172
- end
173
-
174
- reference_tables.each do |table|
175
128
  dump_table(table)
176
129
  end
177
130
  end
178
131
 
179
132
  def dump_table(table)
180
- forward("dump.table", table_body(table))
133
+ forward("#{prefix}.table", table_body(table))
181
134
 
182
135
  columns = table.columns.sort_by(&:name)
183
136
  columns.each do |column|
@@ -210,7 +163,7 @@ module Droonga
210
163
  end
211
164
 
212
165
  def dump_column(column)
213
- forward("dump.column", column_body(column))
166
+ forward("#{prefix}.column", column_body(column))
214
167
  end
215
168
 
216
169
  def column_body(column)
@@ -270,7 +223,7 @@ module Droonga
270
223
  "key" => record.key,
271
224
  "values" => values,
272
225
  }
273
- forward("dump.record", body)
226
+ forward("#{prefix}.record", body)
274
227
  end
275
228
  end
276
229
  end
@@ -286,6 +239,8 @@ module Droonga
286
239
  element
287
240
  end
288
241
  end
242
+ when Hash
243
+ value["_key"]
289
244
  else
290
245
  value
291
246
  end
@@ -297,41 +252,17 @@ module Droonga
297
252
  end
298
253
  end
299
254
 
300
- def each_table
301
- options = {
302
- :ignore_missing_object => true,
303
- :order_by => :key,
304
- }
305
- @context.database.each(options) do |object|
306
- next unless table?(object)
307
- yield(object)
308
- end
309
- end
310
-
311
- def table?(object)
312
- object.is_a?(::Groonga::Table)
313
- end
314
-
315
- def index_only_table?(table)
316
- table.columns.all? do |column|
317
- index_column?(column)
318
- end
319
- end
320
-
321
- def reference_table?(table)
322
- table.support_key? and table?(table.domain)
323
- end
324
-
325
- def index_column?(column)
326
- column.is_a?(::Groonga::IndexColumn)
255
+ def setup_forward_data
256
+ super
257
+ @n_forwarded_messages = 0
258
+ @messages_per_100msec = @request.messages_per_second / 10
327
259
  end
328
260
 
329
- def each_index_columns
330
- each_table do |table|
331
- table.columns.each do |column|
332
- yield(column) if index_column?(column)
333
- end
334
- end
261
+ def forward(type, body=nil)
262
+ super
263
+ @n_forwarded_messages += 1
264
+ @n_forwarded_messages %= @messages_per_100msec
265
+ Fiber.yield if @n_forwarded_messages.zero?
335
266
  end
336
267
 
337
268
  def log_tag