droonga-engine 1.0.3 → 1.0.4

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +7 -0
  3. data/bin/droonga-engine-absorb-data +82 -0
  4. data/bin/droonga-engine-catalog-generate +16 -13
  5. data/bin/droonga-engine-catalog-modify +108 -0
  6. data/bin/droonga-engine-join +115 -0
  7. data/bin/droonga-engine-unjoin +90 -0
  8. data/doc/text/news.md +8 -0
  9. data/droonga-engine.gemspec +2 -1
  10. data/lib/droonga/buffered_tcp_socket.rb +132 -0
  11. data/lib/droonga/catalog_generator.rb +87 -4
  12. data/lib/droonga/command/droonga_engine.rb +27 -7
  13. data/lib/droonga/command/droonga_engine_service.rb +3 -2
  14. data/lib/droonga/command/serf_event_handler.rb +211 -14
  15. data/lib/droonga/data_absorber.rb +55 -0
  16. data/lib/droonga/dispatcher.rb +25 -11
  17. data/lib/droonga/engine/version.rb +1 -1
  18. data/lib/droonga/engine.rb +24 -24
  19. data/lib/droonga/engine_state.rb +23 -0
  20. data/lib/droonga/{catalog_observer.rb → file_observer.rb} +12 -7
  21. data/lib/droonga/fluent_message_sender.rb +24 -37
  22. data/lib/droonga/forwarder.rb +30 -5
  23. data/lib/droonga/handler_messenger.rb +3 -2
  24. data/lib/droonga/handler_runner.rb +29 -16
  25. data/lib/droonga/job_pusher.rb +12 -0
  26. data/lib/droonga/line_buffer.rb +42 -0
  27. data/lib/droonga/logger.rb +10 -6
  28. data/lib/droonga/path.rb +16 -0
  29. data/lib/droonga/plugins/search/distributed_search_planner.rb +1 -1
  30. data/lib/droonga/plugins/system.rb +50 -0
  31. data/lib/droonga/processor.rb +9 -4
  32. data/lib/droonga/safe_file_writer.rb +39 -0
  33. data/lib/droonga/serf.rb +212 -14
  34. data/lib/droonga/test/stub_handler_messenger.rb +3 -0
  35. data/lib/droonga/worker.rb +6 -1
  36. data/test/command/config/default/catalog.json +1 -1
  37. data/test/command/config/version1/catalog.json +2 -2
  38. data/test/command/suite/system/status.expected +12 -0
  39. data/test/command/suite/system/status.test +5 -0
  40. data/test/unit/plugins/system/test_status.rb +79 -0
  41. data/test/unit/test_catalog_generator.rb +1 -1
  42. data/test/unit/test_line_buffer.rb +62 -0
  43. metadata +46 -12
  44. data/lib/droonga/live_nodes_list_observer.rb +0 -72
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d592eda2e4916fb9d0d4fbf48ad538cbff3c1d34
4
- data.tar.gz: 62255e98b9141709a42db836e525737a8e254343
3
+ metadata.gz: 333168fcdecd92b88dfc590746445e0ed31319c4
4
+ data.tar.gz: bb082dd818cfd171a131e0e45dc75269a1cd814d
5
5
  SHA512:
6
- metadata.gz: 163a844fae7ba67af1dc934380a374756439744e844de81e53fe9dc141f6a7b1ad457db25a0e7a1fda988d6709aab34e55b9ecd0efc3dabb6c6be508bf6fb466
7
- data.tar.gz: 183c9afe679f550c198e65f9f7f5dfab74b10ded4e84bfd40d1b93239227c571063e05920aa6e514eaa11a46a35cdad8f320265f60534b6944071c2132e79e94
6
+ metadata.gz: 38167299101e872c86cb07ac35449f023028ffaaa405477423c7882fd981c90677f4483671ea3c326e71338274326acbbfab644f668c77b7602d932b3c19717e
7
+ data.tar.gz: 41fe28134710faf85c694abe7c702e120d33188f321611e8627788b80ceabc5cca295f7d590621a85d1a87b936746a7d90e63cfeb938eb932287110c5548cfe1
data/Gemfile CHANGED
@@ -71,3 +71,10 @@ if File.exist?(grn2drn_dir)
71
71
  else
72
72
  gem "grn2drn", :github => "droonga/grn2drn"
73
73
  end
74
+
75
+ drndump_dir = File.join(parent_dir, "drndump")
76
+ if File.exist?(drndump_dir)
77
+ gem "drndump", :path => drndump_dir
78
+ else
79
+ gem "drndump", :github => "droonga/drndump"
80
+ end
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Copyright (C) 2014 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+
18
+ require "ostruct"
19
+ require "optparse"
20
+ require "open3"
21
+
22
+ require "droonga/engine/version"
23
+ require "droonga/catalog_generator"
24
+ require "droonga/serf"
25
+
26
+ options = OpenStruct.new
27
+ options.port = Droonga::CatalogGenerator::DEFAULT_PORT
28
+ options.tag = Droonga::CatalogGenerator::DEFAULT_TAG
29
+ options.dataset = Droonga::CatalogGenerator::DEFAULT_DATASET
30
+ parser = OptionParser.new
31
+ parser.version = Droonga::Engine::VERSION
32
+
33
+ parser.separator("")
34
+ parser.separator("Connection:")
35
+ parser.on("--source-host=HOST",
36
+ "Host name of the source cluster to be connected.") do |host|
37
+ options.source_host = host
38
+ end
39
+ parser.on("--destination-host=HOST",
40
+ "Host name of this cluster to be connected.") do |host|
41
+ options.destination_host = host
42
+ end
43
+ parser.on("--port=PORT", Integer,
44
+ "Port number of the source cluster to be connected.",
45
+ "(#{options.port})") do |port|
46
+ options.port = port
47
+ end
48
+
49
+ parser.separator("")
50
+ parser.separator("Data:")
51
+ parser.on("--tag=TAG",
52
+ "Tag name to be used to communicate with Droonga system.",
53
+ "(#{options.tag})") do |tag|
54
+ options.tag = tag
55
+ end
56
+ parser.on("--dataset=DATASET",
57
+ "Dataset to be absorbed.",
58
+ "(#{options.dataset})") do |dataset|
59
+ options.dataset = dataset
60
+ end
61
+
62
+ parser.parse!(ARGV)
63
+
64
+ unless options.source_host
65
+ raise "You must specify the source host via --source-host option."
66
+ end
67
+ unless options.destination_host
68
+ raise "You must specify the destination host via --destination-host option."
69
+ end
70
+
71
+ destination_node = "#{options.destination_host}:#{options.port}/#{options.tag}"
72
+
73
+ puts "Absorbing data..."
74
+ Droonga::Serf.send_query(destination_node, "absorb_data",
75
+ "node" => destination_node,
76
+ "source" => options.source_host,
77
+ "port" => options.port,
78
+ "tag" => options.tag,
79
+ "dataset" => options.dataset)
80
+ puts "Done."
81
+
82
+ exit(true)
@@ -18,11 +18,11 @@
18
18
  require "ostruct"
19
19
  require "optparse"
20
20
  require "json"
21
- require "tempfile"
22
21
  require "pathname"
23
22
 
24
23
  require "droonga/engine/version"
25
24
  require "droonga/catalog_generator"
25
+ require "droonga/safe_file_writer"
26
26
 
27
27
  generator = Droonga::CatalogGenerator.new
28
28
  current_dataset = {}
@@ -42,31 +42,38 @@ parser.on("--output=PATH",
42
42
  end
43
43
  parser.on("--dataset=NAME",
44
44
  "Add a dataset its name is NAME.",
45
- "And set the NAME to the current dataset.") do |name|
45
+ "And set the NAME to the current dataset.",
46
+ "(#{Droonga::CatalogGenerator::DEFAULT_DATASET})") do |name|
46
47
  current_dataset = datasets[name] = {}
47
48
  end
48
49
  parser.on("--n-workers=N", Integer,
49
- "Use N workers for the current dataset.") do |n|
50
+ "Use N workers for the current dataset.",
51
+ "(#{Droonga::CatalogGenerator::DEFAULT_N_WORKERS})") do |n|
50
52
  current_dataset[:n_workers] = n
51
53
  end
52
54
  parser.on("--hosts=NAME1,NAME2,...", Array,
53
- "Use given hosts for replicas of the current dataset.") do |hosts|
55
+ "Use given hosts for replicas of the current dataset.",
56
+ "(#{Droonga::CatalogGenerator::DEFAULT_HOSTS.join(",")})") do |hosts|
54
57
  current_dataset[:hosts] = hosts
55
58
  end
56
59
  parser.on("--port=PORT", Integer,
57
- "Use the PORT as the port for the current dataset.") do |port|
60
+ "Use the PORT as the port for the current dataset.",
61
+ "(#{Droonga::CatalogGenerator::DEFAULT_PORT})") do |port|
58
62
  current_dataset[:port] = port
59
63
  end
60
64
  parser.on("--tag=TAG",
61
- "Use the TAG as the tag for the current dataset.") do |tag|
65
+ "Use the TAG as the tag for the current dataset.",
66
+ "(#{Droonga::CatalogGenerator::DEFAULT_TAG})") do |tag|
62
67
  current_dataset[:tag] = tag
63
68
  end
64
69
  parser.on("--n-slices=N", Integer,
65
- "Use N slices for each replica.") do |n|
70
+ "Use N slices for each replica.",
71
+ "(#{Droonga::CatalogGenerator::DEFAULT_N_SLICES})") do |n|
66
72
  current_dataset[:n_slices] = n
67
73
  end
68
74
  parser.on("--plugins=PLUGIN1,PLUGIN2,...", Array,
69
- "Use PLUGINS for the current dataset.") do |plugins|
75
+ "Use PLUGINS for the current dataset.",
76
+ "(#{Droonga::CatalogGenerator::DEFAULT_PLUGINS.join(",")})") do |plugins|
70
77
  current_dataset[:plugins] = plugins
71
78
  end
72
79
  parser.on("--schema=PATH",
@@ -99,12 +106,8 @@ def open_output(path)
99
106
  if path == "-"
100
107
  yield($stdout)
101
108
  else
102
- # Don't output the file directly to prevent loading of incomplete file!
103
- path = Pathname(path).expand_path
104
- Tempfile.open(path.basename.to_s, path.parent.to_s, "w") do |output|
109
+ Droonga::SafeFileWriter.write(path) do |output|
105
110
  yield(output)
106
- output.flush
107
- File.rename(output.path, path.to_s)
108
111
  end
109
112
  end
110
113
  end
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Copyright (C) 2014 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+
18
+ require "ostruct"
19
+ require "optparse"
20
+ require "json"
21
+ require "pathname"
22
+
23
+ require "droonga/engine/version"
24
+ require "droonga/catalog_generator"
25
+ require "droonga/safe_file_writer"
26
+
27
+ generator = Droonga::CatalogGenerator.new
28
+ current_dataset = {}
29
+ datasets = {
30
+ Droonga::CatalogGenerator::DEFAULT_DATASET => current_dataset
31
+ }
32
+
33
+ options = OpenStruct.new
34
+ options.source_path = "./catalog.json"
35
+ options.output_path = "-"
36
+ options.update = false
37
+ parser = OptionParser.new
38
+ parser.version = Droonga::Engine::VERSION
39
+ parser.on("--source=PATH",
40
+ "Path to an existing catalog.json.",
41
+ "\"-\" means the standard input.",
42
+ "(#{options.source_path})") do |path|
43
+ options.source_path = path
44
+ end
45
+ parser.on("--output=PATH",
46
+ "Output catalog.json to PATH.",
47
+ "\"-\" means the standard output.",
48
+ "(#{options.output_path})") do |path|
49
+ options.output_path = path
50
+ end
51
+ parser.on("--[no-]update",
52
+ "Update the source file itself, or not.",
53
+ "(#{options.update})") do |update|
54
+ options.update = update
55
+ end
56
+ parser.on("--dataset=NAME",
57
+ "Add a dataset its name is NAME.",
58
+ "And set the NAME to the current dataset.",
59
+ "(#{Droonga::CatalogGenerator::DEFAULT_DATASET})") do |name|
60
+ current_dataset = datasets[name] = {}
61
+ end
62
+ parser.on("--replica-hosts=NAME1,NAME2,...", Array,
63
+ "Use given hosts as replicas for the current dataset.") do |hosts|
64
+ current_dataset[:replica_hosts] = hosts
65
+ end
66
+ parser.on("--add-replica-hosts=NAME1,NAME2,...", Array,
67
+ "Use given hosts to be added as replicas to the current dataset.") do |hosts|
68
+ current_dataset[:add_replica_hosts] = hosts
69
+ end
70
+ parser.on("--remove-replica-hosts=NAME1,NAME2,...", Array,
71
+ "Use given hosts to be removed as replicas from the current dataset.") do |hosts|
72
+ current_dataset[:remove_replica_hosts] = hosts
73
+ end
74
+ parser.parse!(ARGV)
75
+
76
+ if options.source_path != "-" and options.update
77
+ options.output_path = options.source_path
78
+ end
79
+
80
+ def load_source(path)
81
+ source = nil
82
+ if path == "-"
83
+ source = $stdin.read
84
+ else
85
+ source_path = Pathname(path).expand_path
86
+ source = source_path.read
87
+ end
88
+ JSON.parse(source)
89
+ end
90
+
91
+ source_catalog = load_source(options.source_path)
92
+ generator.load(source_catalog)
93
+ generator.modify(datasets)
94
+
95
+ def open_output(path)
96
+ if path == "-"
97
+ yield($stdout)
98
+ else
99
+ Droonga::SafeFileWriter.write(path) do |output|
100
+ yield(output)
101
+ end
102
+ end
103
+ end
104
+
105
+ catalog = generator.generate
106
+ open_output(options.output_path) do |output|
107
+ output.puts(JSON.pretty_generate(catalog))
108
+ end
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Copyright (C) 2014 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+
18
+ require "ostruct"
19
+ require "optparse"
20
+ require "json"
21
+ require "pathname"
22
+
23
+ require "droonga/engine/version"
24
+ require "droonga/path"
25
+ require "droonga/catalog_generator"
26
+ require "droonga/safe_file_writer"
27
+ require "droonga/data_absorber"
28
+ require "droonga/serf"
29
+
30
+ options = OpenStruct.new
31
+ options.base_dir = ENV[Droonga::Path::BASE_DIR_ENV_NAME] || Dir.pwd
32
+ options.drndump = "drndump"
33
+ options.client = "droonga-request"
34
+ options.copy = true
35
+
36
+ parser = OptionParser.new
37
+ parser.version = Droonga::Engine::VERSION
38
+
39
+ parser.on("--base-dir=PATH",
40
+ "Path to the base directory the catalog.json is located in.",
41
+ "(#{options.base_dir})") do |path|
42
+ options.base_dir = path
43
+ end
44
+
45
+ parser.on("--[no-]copy",
46
+ "Do or don't copy data from the source cluster.",
47
+ "(#{options.copy})") do |copy|
48
+ options.copy = copy
49
+ end
50
+
51
+ parser.separator("")
52
+ parser.separator("Connections:")
53
+ parser.on("--host=HOST",
54
+ "Host name of the node to be joined.") do |host|
55
+ options.joining_host = host
56
+ end
57
+ parser.on("--replica-source-host=HOST",
58
+ "Host name of the soruce cluster to be connected.") do |host|
59
+ options.replica_source_host = host
60
+ end
61
+
62
+ parser.parse!(ARGV)
63
+
64
+
65
+ base_dir = Pathname(options.base_dir).expand_path
66
+ ENV[Droonga::Path::BASE_DIR_ENV_NAME] = base_dir.to_s
67
+
68
+ catalog_path = Droonga::Path.catalog
69
+ unless catalog_path.exist?
70
+ raise "Cannot load 'catalog.json'. You must specify correct path " +
71
+ "to the base directory via --base-dir option."
72
+ end
73
+ source_catalog = JSON.parse(catalog_path.read)
74
+
75
+
76
+ unless options.joining_host
77
+ raise "You must specify the host name or the IP address of the node " +
78
+ "to be joined via --host option."
79
+ end
80
+ unless options.replica_source_host
81
+ raise "You must specify the host name or the IP address of a node " +
82
+ "of an existing cluster via --replica-source-host option."
83
+ end
84
+
85
+
86
+ generator = Droonga::CatalogGenerator.new
87
+ generator.load(source_catalog)
88
+
89
+ dataset = generator.dataset_for_host(options.replica_source_host)
90
+ unless dataset
91
+ raise "Specified source host #{options.replica_source_host} is not a " +
92
+ "member of the cluster. You must specify correct host via " +
93
+ "--replica-source-host option."
94
+ end
95
+
96
+ if generator.dataset_for_host(options.joining_host)
97
+ raise "The joining node is already a member of the cluster. " +
98
+ "You cannot join a member twice."
99
+ end
100
+
101
+ options.tag = dataset.replicas.tag
102
+ options.port = dataset.replicas.port
103
+
104
+ options.joining_node = "#{options.joining_host}:#{options.port}/#{options.tag}"
105
+
106
+ puts "Joining new replica to the cluster..."
107
+ Droonga::Serf.send_query(options.joining_node, "join",
108
+ "node" => options.joining_node,
109
+ "type" => "replica",
110
+ "source" => options.replica_source_host,
111
+ "copy" => options.copy)
112
+
113
+ puts "Done."
114
+
115
+ exit(true)
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Copyright (C) 2014 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+
18
+ require "ostruct"
19
+ require "optparse"
20
+ require "json"
21
+ require "pathname"
22
+
23
+ require "droonga/engine/version"
24
+ require "droonga/path"
25
+ require "droonga/catalog_generator"
26
+ require "droonga/safe_file_writer"
27
+ require "droonga/serf"
28
+
29
+ options = OpenStruct.new
30
+ options.base_dir = ENV[Droonga::Path::BASE_DIR_ENV_NAME] || Dir.pwd
31
+
32
+ parser = OptionParser.new
33
+ parser.version = Droonga::Engine::VERSION
34
+
35
+ parser.on("--base-dir=PATH",
36
+ "Path to the base directory the catalog.json is located in.",
37
+ "(#{options.base_dir})") do |path|
38
+ options.base_dir = path
39
+ end
40
+ parser.on("--host=HOST",
41
+ "Host name of the replica removed from cluster.") do |host|
42
+ options.replica_remove_host = host
43
+ end
44
+
45
+ parser.parse!(ARGV)
46
+
47
+
48
+ base_dir = Pathname(options.base_dir).expand_path
49
+ ENV[Droonga::Path::BASE_DIR_ENV_NAME] = base_dir.to_s
50
+
51
+ catalog_path = Droonga::Path.catalog
52
+ unless catalog_path.exist?
53
+ raise "Cannot load 'catalog.json'. You must specify correct path " +
54
+ "to the base directory via --base-dir option."
55
+ end
56
+
57
+ unless options.replica_remove_host
58
+ raise "You must specify the host name or the IP address of a node to " +
59
+ "be removed from the cluster via --replica-remove-host option."
60
+ end
61
+
62
+ source_catalog = JSON.parse(catalog_path.read)
63
+ generator = Droonga::CatalogGenerator.new
64
+ generator.load(source_catalog)
65
+
66
+ dataset = generator.dataset_for_host(options.replica_remove_host)
67
+ unless dataset
68
+ raise "Specified host #{options.replica_remove_host} is not a member of "+
69
+ "the cluster. You must specify correct host via --replica-remove-host " +
70
+ "option."
71
+ end
72
+
73
+ options.dataset = dataset.name
74
+ options.tag = dataset.replicas.tag
75
+ options.port = dataset.replicas.port
76
+ options.other_hosts = dataset.replicas.hosts
77
+
78
+ remaining_host = options.other_hosts.first || options.replica_remove_host
79
+ options.remaining_node = "#{remaining_host}:#{options.port}/#{options.tag}"
80
+
81
+
82
+ puts "Unjoining replica from the cluster..."
83
+
84
+ Droonga::Serf.send_query(options.remaining_node, "remove_replicas",
85
+ "dataset" => options.dataset,
86
+ "hosts" => [options.replica_remove_host])
87
+
88
+ puts "Done."
89
+
90
+ exit(true)
data/doc/text/news.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # News
2
2
 
3
+ ## 1.0.4: 2014-06-29
4
+
5
+ * New command (and plugin) [`status`](http://droonga.org/reference/1.0.4/commands/status/) is now available.
6
+ * New command line tools are available.
7
+ * `droonga-engine-join` and `droonga-engine-unjoin` help you to modify cluster composition. See [the tutorial to add/remove replica](http://droonga.org/tutorial/1.0.4/add-replica/).
8
+ * `droonga-engine-absorb-data` helps you to duplicate clusters. See [the tutorial for dump/restore](http://droonga.org/tutorial/1.0.4/dump-restore/).
9
+ * `droonga-engine-catalog-modify` helps you to modify existing `catalog.json`.
10
+
3
11
  ## 1.0.3: 2014-05-29
4
12
 
5
13
  * Alive monitoring (based on [Serf](http://serfdom.io/)) lands.
@@ -41,10 +41,11 @@ Gem::Specification.new do |gem|
41
41
  gem.add_dependency "cool.io"
42
42
  gem.add_dependency "serverengine"
43
43
  gem.add_dependency "droonga-message-pack-packer", ">= 1.0.1"
44
- gem.add_dependency "listen", "~> 2.7"
45
44
  gem.add_dependency "faraday"
46
45
  gem.add_dependency "faraday_middleware"
47
46
  gem.add_dependency "archive-zip"
47
+ gem.add_dependency "sigdump"
48
+ gem.add_dependency "drndump"
48
49
  gem.add_development_dependency "rake"
49
50
  gem.add_development_dependency "bundler"
50
51
  gem.add_development_dependency "test-unit"
@@ -0,0 +1,132 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2014 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+
18
+ require "cool.io"
19
+
20
+ require "droonga/loggable"
21
+
22
+ module Droonga
23
+ class BufferedTCPSocket < Coolio::TCPSocket
24
+ include Loggable
25
+
26
+ def initialize(socket, data_directory)
27
+ super(socket)
28
+ @data_directory = data_directory
29
+ @_write_buffer = []
30
+ end
31
+
32
+ def on_connect
33
+ logger.trace("connected to #{@remote_host}:#{@remote_port}")
34
+ end
35
+
36
+ def write(data)
37
+ reserve_write(data)
38
+ schedule_write
39
+ data.bytesize
40
+ end
41
+
42
+ def reserve_write(data)
43
+ chunk = Chunk.new(@data_directory, data, Time.now, 0)
44
+ chunk.buffering
45
+ @_write_buffer << chunk
46
+ data.bytesize
47
+ end
48
+
49
+ def on_writable
50
+ begin
51
+ chunk = @_write_buffer.shift
52
+ written_size = @_io.write(chunk.data)
53
+ if written_size == chunk.data.bytesize
54
+ chunk.written
55
+ else
56
+ chunk.written_partial(written_size)
57
+ @_write_buffer.unshift(chunk)
58
+ end
59
+ rescue Errno::EINTR
60
+ return
61
+ rescue SystemCallError, IOError, SocketError
62
+ return close
63
+ end
64
+
65
+ if @_write_buffer.empty?
66
+ disable_write_watcher
67
+ on_write_complete
68
+ end
69
+ end
70
+
71
+ def resume
72
+ @_write_buffer = (load_chunks + @_write_buffer).sort_by do |chunk|
73
+ chunk.time_stamp
74
+ end
75
+ end
76
+
77
+ private
78
+ def load_chunks
79
+ FileUtils.mkdir_p(@data_directory.to_s)
80
+ Pathname.glob("#{@data_directory}/*.chunk").collect do |chunk_path|
81
+ Chunk.load(chunk_path)
82
+ end
83
+ end
84
+
85
+ def log_tag
86
+ "[#{Process.ppid}][#{Process.pid}] buffered-tcp-socket"
87
+ end
88
+
89
+ class Chunk
90
+ class << self
91
+ def load(path)
92
+ data_directory = path.dirname
93
+ time_stamp1, time_stamp2, revision, = path.basename.to_s.split(".", 4)
94
+ data = path.open("rb") {|file| file.read}
95
+ time_stamp = Time.iso8601("#{time_stamp1}.#{time_stamp2}")
96
+ revision = Integer(revision)
97
+ new(data_directory, data, time_stamp, revision)
98
+ end
99
+ end
100
+
101
+ attr_reader :data, :time_stamp
102
+ def initialize(data_directory, data, time_stamp, revision)
103
+ @data_directory = data_directory
104
+ @data = data
105
+ @time_stamp = time_stamp.utc
106
+ @revision = revision
107
+ end
108
+
109
+ def buffering
110
+ path.open("wb") do |file|
111
+ file.write(@data)
112
+ end
113
+ end
114
+
115
+ def written
116
+ FileUtils.rm_f(path.to_s)
117
+ end
118
+
119
+ def written_partial(size)
120
+ written
121
+ @data = @data[size..-1]
122
+ @revision += 1
123
+ buffering
124
+ end
125
+
126
+ private
127
+ def path
128
+ @data_directory + "#{@time_stamp.iso8601(6)}.#{@revision}.chunk"
129
+ end
130
+ end
131
+ end
132
+ end