droonga-engine 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
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