droonga-engine 1.0.9 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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