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
@@ -52,13 +52,19 @@ module Droonga
52
52
  def send(tag, data)
53
53
  logger.trace("send: start")
54
54
  packed_fluent_message = create_packed_fluent_message(tag, data)
55
- connect unless connected?
55
+ unless connected?
56
+ logger.trace("send: reconnect")
57
+ connect
58
+ end
56
59
  @socket.write(packed_fluent_message)
57
60
  logger.trace("send: done")
58
61
  end
59
62
 
60
63
  def resume
61
- connect unless connected?
64
+ unless connected?
65
+ logger.trace("resume: reconnect to #{target_node}")
66
+ connect
67
+ end
62
68
  end
63
69
 
64
70
  private
@@ -69,22 +75,8 @@ module Droonga
69
75
  def connect
70
76
  logger.trace("connect: start")
71
77
 
72
- log_write_complete = lambda do
73
- logger.trace("write completed")
74
- end
75
- log_connect = lambda do
76
- logger.trace("connected to #{@host}:#{@port}")
77
- end
78
- log_failed = lambda do
79
- logger.error("failed to connect to #{@host}:#{@port}")
80
- @socket = nil
81
- end
82
- on_close = lambda do
83
- @socket = nil
84
- end
85
-
86
78
  if @buffering
87
- data_directory = Path.buffer + "#{@host}:#{@port}"
79
+ data_directory = Path.accidental_buffer + "#{target_node}"
88
80
  FileUtils.mkdir_p(data_directory.to_s)
89
81
  @socket = BufferedTCPSocket.connect(@host, @port, data_directory)
90
82
  @socket.resume
@@ -92,25 +84,36 @@ module Droonga
92
84
  @socket = Coolio::TCPSocket.connect(@host, @port)
93
85
  end
94
86
  @socket.on_write_complete do
95
- log_write_complete.call
87
+ logger.trace("write completed")
96
88
  end
97
89
  @socket.on_connect do
98
- log_connect.call
90
+ logger.trace("connected")
99
91
  end
100
92
  @socket.on_connect_failed do
101
- log_failed.call
93
+ logger.error("failed to connect")
94
+ @socket = nil
102
95
  end
103
96
  @socket.on_close do
104
- on_close.call
97
+ logger.trace("connection is closed by someone")
98
+ @socket = nil
105
99
  end
106
100
  @loop.attach(@socket)
101
+ # logger.trace("connect: new socket watcher attached",
102
+ # :watcher => @socket,
103
+ # :host => @host,
104
+ # :port => @port)
107
105
 
108
106
  logger.trace("connect: done")
109
107
  end
110
108
 
111
109
  def shutdown_socket
112
110
  return unless connected?
113
- @socket.close unless @socket.closed?
111
+ unless @socket.closed?
112
+ # logger.trace("shutdown_socket: socket watcher detaching",
113
+ # :watcher => @socket)
114
+ @socket.close
115
+ logger.trace("shutdown_socket: socket watcher detached")
116
+ end
114
117
  end
115
118
 
116
119
  def create_packed_fluent_message(tag, data)
@@ -121,8 +124,12 @@ module Droonga
121
124
  packed_fluent_message
122
125
  end
123
126
 
127
+ def target_node
128
+ "#{@host}:#{@port}"
129
+ end
130
+
124
131
  def log_tag
125
- "[#{Process.ppid}] fluent-message-sender"
132
+ "[#{Process.ppid}] fluent-message-sender: #{target_node}"
126
133
  end
127
134
  end
128
135
  end
@@ -0,0 +1,160 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2014-2015 Droonga Project
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License version 2.1 as published by the Free Software Foundation.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+ require "fileutils"
19
+ require "pathname"
20
+ require "msgpack"
21
+ require "time"
22
+
23
+ require "droonga/loggable"
24
+ require "droonga/path"
25
+ require "droonga/safe_file_writer"
26
+ require "droonga/serf"
27
+ require "droonga/timestamp"
28
+
29
+ module Droonga
30
+ class ForwardBuffer
31
+ include Loggable
32
+
33
+ SUFFIX = ".msgpack"
34
+
35
+ attr_writer :on_forward
36
+
37
+ def initialize(node_name)
38
+ @on_forward = nil
39
+
40
+ @packer = MessagePack::Packer.new
41
+ @unpacker = MessagePack::Unpacker.new
42
+
43
+ @target = node_name
44
+ @serf = Serf.new(ENV["DROONGA_ENGINE_NAME"])
45
+
46
+ dirname = node_name.gsub("/", ":")
47
+ @data_directory = Path.intentional_buffer + dirname
48
+ FileUtils.mkdir_p(@data_directory.to_s)
49
+ end
50
+
51
+ def add(message, destination)
52
+ logger.trace("add: start")
53
+ @serf.set_have_unprocessed_messages_for(@target)
54
+ buffered_message = {
55
+ "message" => message,
56
+ "destination" => destination,
57
+ }
58
+ @packer.pack(buffered_message)
59
+ file_path = create_buffered_message_path
60
+ SafeFileWriter.write(file_path) do |output, file|
61
+ output.puts(@packer.to_s)
62
+ end
63
+ @packer.clear
64
+ logger.trace("add: done", :path => file_path)
65
+ end
66
+
67
+ def start_forward
68
+ logger.trace("start_forward: start")
69
+ n_forwarded_messages = 0
70
+ buffered_messages.each do |buffered_message_path|
71
+ forwarded = forward(buffered_message_path)
72
+ n_forwarded_messages += 1 if forwarded
73
+ end
74
+ if n_forwarded_messages > 0 and
75
+ @process_messages_newer_than_timestamp
76
+ logger.info("#{n_forwarded_messages} new messages forwarded. " +
77
+ "The boundary is now cleared.")
78
+ # Don't clear the boundary while forwarding.
79
+ # Because buffered messages are not sorted by their "date",
80
+ # so older messages can appear after newer one.
81
+ # (ex. messages generated by Dispatcher)
82
+ @process_messages_newer_than_timestamp = nil
83
+ end
84
+ @serf.reset_have_unprocessed_messages_for(@target)
85
+ logger.trace("start_forward: done")
86
+ end
87
+
88
+ def buffered_messages
89
+ Pathname.glob("#{@data_directory}/*#{SUFFIX}").sort_by do |path|
90
+ path
91
+ end
92
+ end
93
+
94
+ def empty?
95
+ @data_directory.children.empty?
96
+ end
97
+
98
+ def process_messages_newer_than(timestamp)
99
+ @process_messages_newer_than_timestamp = timestamp
100
+ end
101
+
102
+ private
103
+ def forward(buffered_message_path)
104
+ logger.trace("forward: start (#{buffered_message_path})")
105
+
106
+ unless buffered_message_path.exist?
107
+ logger.warn("forward: no buffer file (maybe sent by other process)",
108
+ :path => buffered_message_path)
109
+ return false
110
+ end
111
+
112
+ file_contents = buffered_message_path.read
113
+ @unpacker.feed(file_contents)
114
+ buffered_message = @unpacker.read
115
+ @unpacker.reset
116
+
117
+ message = buffered_message["message"]
118
+ destination = buffered_message["destination"]
119
+
120
+ forwarded = false
121
+
122
+ if @process_messages_newer_than_timestamp
123
+ message_timestamp = Time.parse(message["date"])
124
+ logger.trace("Checking boundary of obsolete message",
125
+ :newer_than => @process_messages_newer_than_timestamp,
126
+ :message_at => message_timestamp)
127
+ if @process_messages_newer_than_timestamp >= message_timestamp
128
+ buffered_message = nil
129
+ end
130
+ end
131
+
132
+ if buffered_message
133
+ logger.trace("forward: Forwarding buffered message",
134
+ :message => message,
135
+ :destination => destination)
136
+ message["xSender"] = "forward-buffer"
137
+ on_forward(message, destination)
138
+ forwarded = true
139
+ end
140
+
141
+ FileUtils.rm_f(buffered_message_path.to_s)
142
+ logger.trace("forward: done (#{buffered_message_path})")
143
+
144
+ forwarded
145
+ end
146
+
147
+ def create_buffered_message_path(time_stamp=Time.now)
148
+ basename = Timestamp.stringify(time_stamp)
149
+ Path.unique_file_path(@data_directory, basename, SUFFIX)
150
+ end
151
+
152
+ def on_forward(message, destination)
153
+ @on_forward.call(message, destination) if @on_forward
154
+ end
155
+
156
+ def log_tag
157
+ "[#{Process.ppid}] forward-buffer"
158
+ end
159
+ end
160
+ end
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2013 Droonga Project
3
+ # Copyright (C) 2013-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
@@ -15,38 +15,45 @@
15
15
  # License along with this library; if not, write to the Free Software
16
16
  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
 
18
+ require "coolio"
19
+
18
20
  require "droonga/loggable"
19
21
  require "droonga/path"
20
- require "droonga/event_loop"
21
- require "droonga/buffered_tcp_socket"
22
22
  require "droonga/fluent_message_sender"
23
23
 
24
24
  module Droonga
25
25
  class Forwarder
26
26
  include Loggable
27
27
 
28
+ class AlreadyShutdown < StandardError
29
+ end
30
+
31
+ DEFAULT_AUTO_CLOSE_TIMEOUT_SECONDS = 60
32
+
28
33
  def initialize(loop, options={})
29
34
  @loop = loop
30
- @buffering = options[:buffering]
31
35
  @senders = {}
36
+ @auto_close_timers = {}
37
+ @shutting_down = false
38
+ @auto_close_timeout = options[:auto_close_timeout] ||
39
+ DEFAULT_AUTO_CLOSE_TIMEOUT_SECONDS
32
40
  end
33
41
 
34
42
  def start
35
43
  logger.trace("start: start")
36
- resume
37
44
  logger.trace("start: done")
38
45
  end
39
46
 
40
47
  def shutdown
41
48
  logger.trace("shutdown: start")
42
- @senders.each_value do |sender|
43
- sender.shutdown
44
- end
49
+ @shutting_down = true
50
+ clear_senders
45
51
  logger.trace("shutdown: done")
46
52
  end
47
53
 
48
54
  def forward(message, destination)
49
- logger.trace("forward: start")
55
+ logger.trace("forward: start", :message => message)
56
+ raise AlreadyShutdown.new if @shutting_down
50
57
  command = destination["type"]
51
58
  receiver = destination["to"]
52
59
  arguments = destination["arguments"]
@@ -54,33 +61,25 @@ module Droonga
54
61
  logger.trace("forward: done")
55
62
  end
56
63
 
57
- def resume
58
- return unless Path.buffer.exist?
59
- Pathname.glob("#{Path.buffer}/*") do |path|
60
- next unless path.directory?
61
-
62
- destination = path.basename.to_s
63
- sender = @senders[destination]
64
- if sender
65
- sender.resume
66
- next
67
- end
68
-
69
- chunk_loader = BufferedTCPSocket::ChunkLoader.new(path)
70
- unless chunk_loader.have_any_chunk?
71
- #FileUtils.rm_rf(path.to_s) # TODO re-enable this
72
- next
73
- end
74
-
75
- components = destination.split(":")
76
- port = components.pop.to_i
77
- next if port.zero?
78
- host = components.join(":")
79
-
80
- sender = create_sender(host, port)
81
- sender.resume
82
- @senders[destination] = sender
64
+ def refresh_connection_for(name)
65
+ logger.trace("refresh_connection_for(#{name}): start")
66
+ sender = @senders[name]
67
+ if sender
68
+ sender.shutdown
69
+ @senders.delete(name)
70
+ end
71
+ timer = @auto_close_timers[name]
72
+ if timer
73
+ timer.detach
74
+ @auto_close_timers.delete(name)
83
75
  end
76
+ logger.trace("refresh_connection_for(#{name}): done")
77
+ end
78
+
79
+ def refresh_all_connections
80
+ logger.trace("refresh_all_connections: start")
81
+ clear_senders
82
+ logger.trace("refresh_all_connections: done")
84
83
  end
85
84
 
86
85
  private
@@ -107,6 +106,7 @@ module Droonga
107
106
  :params => params)
108
107
  return
109
108
  end
109
+ set_auto_close_timer(host, port, params)
110
110
  override_message = {
111
111
  "type" => command,
112
112
  }
@@ -120,11 +120,15 @@ module Droonga
120
120
  logger.trace("output: done")
121
121
  end
122
122
 
123
- def find_sender(host, port, params)
123
+ def resolve_destination(host, port, params)
124
124
  connection_id = extract_connection_id(params)
125
125
  destination = "#{host}:#{port}"
126
126
  destination << "?#{connection_id}" if connection_id
127
+ destination
128
+ end
127
129
 
130
+ def find_sender(host, port, params)
131
+ destination = resolve_destination(host, port, params)
128
132
  @senders[destination] ||= create_sender(host, port)
129
133
  end
130
134
 
@@ -139,14 +143,43 @@ module Droonga
139
143
  end
140
144
 
141
145
  def create_sender(host, port)
142
- options = {
143
- :buffering => @buffering,
144
- }
145
- sender = FluentMessageSender.new(@loop, host, port, options)
146
+ sender = FluentMessageSender.new(@loop, host, port)
146
147
  sender.start
147
148
  sender
148
149
  end
149
150
 
151
+ def set_auto_close_timer(host, port, params)
152
+ destination = resolve_destination(host, port, params)
153
+
154
+ previous_timer = @auto_close_timers[destination]
155
+ previous_timer.detach if previous_timer
156
+
157
+ timer = Coolio::TimerWatcher.new(@auto_close_timeout)
158
+ timer.on_timer do
159
+ timer.detach
160
+ @auto_close_timers.delete(destination)
161
+ sender = @senders[destination]
162
+ if sender
163
+ logger.info("sender for #{destination} is automatically closed by timeout.")
164
+ sender.shutdown
165
+ @senders.delete(destination)
166
+ end
167
+ end
168
+ @loop.attach(timer)
169
+ @auto_close_timers[destination] = timer
170
+ end
171
+
172
+ def clear_senders
173
+ @senders.each_value do |sender|
174
+ sender.shutdown
175
+ end
176
+ @senders = {}
177
+ @auto_close_timers.each_value do |timer|
178
+ timer.detach
179
+ end
180
+ @auto_close_timers = {}
181
+ end
182
+
150
183
  def log_tag
151
184
  "[#{Process.ppid}] forwarder"
152
185
  end
@@ -35,12 +35,13 @@ module Droonga
35
35
  end
36
36
  end
37
37
 
38
- attr_reader :messenger, :loop
39
- def initialize(name, context, messenger, loop)
40
- @name = name
41
- @context = context
42
- @messenger = messenger
43
- @loop = loop
38
+ attr_reader :label, :messenger, :loop
39
+ def initialize(params)
40
+ @name = params[:name]
41
+ @label = params[:label]
42
+ @context = params[:context]
43
+ @messenger = params[:messenger]
44
+ @loop = params[:loop]
44
45
  end
45
46
 
46
47
  def handle(message)