elasticsearch-transport 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.txt +13 -0
  4. data/README.md +276 -0
  5. data/Rakefile +67 -0
  6. data/elasticsearch-transport.gemspec +52 -0
  7. data/lib/elasticsearch-transport.rb +1 -0
  8. data/lib/elasticsearch/transport.rb +29 -0
  9. data/lib/elasticsearch/transport/client.rb +123 -0
  10. data/lib/elasticsearch/transport/extensions/test_cluster.rb +163 -0
  11. data/lib/elasticsearch/transport/transport/base.rb +236 -0
  12. data/lib/elasticsearch/transport/transport/connections/collection.rb +93 -0
  13. data/lib/elasticsearch/transport/transport/connections/connection.rb +117 -0
  14. data/lib/elasticsearch/transport/transport/connections/selector.rb +63 -0
  15. data/lib/elasticsearch/transport/transport/errors.rb +73 -0
  16. data/lib/elasticsearch/transport/transport/http/curb.rb +70 -0
  17. data/lib/elasticsearch/transport/transport/http/faraday.rb +59 -0
  18. data/lib/elasticsearch/transport/transport/response.rb +20 -0
  19. data/lib/elasticsearch/transport/transport/serializer/multi_json.rb +36 -0
  20. data/lib/elasticsearch/transport/transport/sniffer.rb +46 -0
  21. data/lib/elasticsearch/transport/version.rb +5 -0
  22. data/test/integration/client_test.rb +117 -0
  23. data/test/integration/transport_test.rb +37 -0
  24. data/test/profile/client_benchmark_test.rb +107 -0
  25. data/test/test_extensions.rb +139 -0
  26. data/test/test_helper.rb +58 -0
  27. data/test/unit/client_test.rb +109 -0
  28. data/test/unit/connection_collection_test.rb +83 -0
  29. data/test/unit/connection_selector_test.rb +64 -0
  30. data/test/unit/connection_test.rb +90 -0
  31. data/test/unit/serializer_test.rb +16 -0
  32. data/test/unit/sniffer_test.rb +146 -0
  33. data/test/unit/transport_base_test.rb +402 -0
  34. data/test/unit/transport_curb_test.rb +59 -0
  35. data/test/unit/transport_faraday_test.rb +73 -0
  36. metadata +342 -0
@@ -0,0 +1,58 @@
1
+ RUBY_1_8 = defined?(RUBY_VERSION) && RUBY_VERSION < '1.9'
2
+
3
+ if RUBY_1_8
4
+ require 'rubygems'
5
+ gem 'test-unit'
6
+ end
7
+
8
+ require 'rubygems' if RUBY_1_8
9
+
10
+ require 'simplecov' and SimpleCov.start { add_filter "/test|test_/" } if ENV["COVERAGE"]
11
+
12
+ # Register `at_exit` handler for integration tests shutdown.
13
+ # MUST be called before requiring `test/unit`.
14
+ at_exit { Elasticsearch::Test::IntegrationTestCase.__run_at_exit_hooks }
15
+
16
+ require 'test/unit'
17
+ require 'shoulda-context'
18
+ require 'mocha/setup'
19
+ require 'ansi/code'
20
+ require 'turn' unless ENV["TM_FILEPATH"] || ENV["NOTURN"] || RUBY_1_8
21
+
22
+ require 'test_extensions'
23
+
24
+ require 'require-prof' if ENV["REQUIRE_PROF"]
25
+ require 'elasticsearch-transport'
26
+ require 'elasticsearch/transport/extensions/test_cluster'
27
+ require 'logger'
28
+
29
+ RequireProf.print_timing_infos if ENV["REQUIRE_PROF"]
30
+
31
+ class Test::Unit::TestCase
32
+ def setup
33
+ end
34
+
35
+ def teardown
36
+ end
37
+ end
38
+
39
+ module Elasticsearch
40
+ module Test
41
+ class IntegrationTestCase < ::Test::Unit::TestCase
42
+ extend IntegrationTestStartupShutdown
43
+
44
+ shutdown { Elasticsearch::TestCluster.stop if ENV['SERVER'] && started? }
45
+ context "IntegrationTest" do; should "noop on Ruby 1.8" do; end; end if RUBY_1_8
46
+ end
47
+ end
48
+
49
+ module Test
50
+ class ProfilingTest < ::Test::Unit::TestCase
51
+ extend IntegrationTestStartupShutdown
52
+ extend ProfilingTestSupport
53
+
54
+ shutdown { Elasticsearch::TestCluster.stop if ENV['SERVER'] && started? }
55
+ context "IntegrationTest" do; should "noop on Ruby 1.8" do; end; end if RUBY_1_8
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,109 @@
1
+ require 'test_helper'
2
+
3
+ class Elasticsearch::Transport::ClientTest < Test::Unit::TestCase
4
+
5
+ class DummyTransport
6
+ def initialize(*); end
7
+ end
8
+
9
+ context "Client" do
10
+ setup do
11
+ Elasticsearch::Transport::Client::DEFAULT_TRANSPORT_CLASS.any_instance.stubs(:__build_connections)
12
+ @client = Elasticsearch::Transport::Client.new
13
+ end
14
+
15
+ should "be aliased as Elasticsearch::Client" do
16
+ assert_nothing_raised do
17
+ assert_instance_of(Elasticsearch::Transport::Client, Elasticsearch::Client.new)
18
+ end
19
+ end
20
+
21
+ should "have default transport" do
22
+ assert_instance_of Elasticsearch::Transport::Client::DEFAULT_TRANSPORT_CLASS, @client.transport
23
+ end
24
+
25
+ should "instantiate custom transport class" do
26
+ client = Elasticsearch::Transport::Client.new :transport_class => DummyTransport
27
+ assert_instance_of DummyTransport, client.transport
28
+ end
29
+
30
+ should "take custom transport instance" do
31
+ client = Elasticsearch::Transport::Client.new :transport => DummyTransport.new
32
+ assert_instance_of DummyTransport, client.transport
33
+ end
34
+
35
+ should "delegate performing requests to transport" do
36
+ assert_respond_to @client, :perform_request
37
+ @client.transport.expects(:perform_request)
38
+ @client.perform_request 'GET', '/'
39
+ end
40
+
41
+ should "have default logger for transport" do
42
+ client = Elasticsearch::Transport::Client.new :log => true
43
+ assert_respond_to client.transport.logger, :info
44
+ end
45
+
46
+ should "have default tracer for transport" do
47
+ client = Elasticsearch::Transport::Client.new :trace => true
48
+ assert_respond_to client.transport.tracer, :info
49
+ end
50
+
51
+ context "when passed hosts" do
52
+ should "have localhost by default" do
53
+ c = Elasticsearch::Transport::Client.new
54
+ assert_equal 'localhost', c.transport.hosts.first[:host]
55
+ end
56
+
57
+ should "take :hosts, :host or :url" do
58
+ c1 = Elasticsearch::Transport::Client.new :hosts => ['foobar']
59
+ c2 = Elasticsearch::Transport::Client.new :host => 'foobar'
60
+ c3 = Elasticsearch::Transport::Client.new :url => 'foobar'
61
+ assert_equal 'foobar', c1.transport.hosts.first[:host]
62
+ assert_equal 'foobar', c2.transport.hosts.first[:host]
63
+ assert_equal 'foobar', c3.transport.hosts.first[:host]
64
+ end
65
+ end
66
+
67
+ context "extracting hosts" do
68
+ should "handle defaults" do
69
+ assert_equal [ {:host => 'localhost', :port => nil} ], @client.__extract_hosts
70
+ end
71
+
72
+ should "extract from string" do
73
+ assert_equal [ {:host => 'myhost', :port => nil} ], @client.__extract_hosts( 'myhost' )
74
+ end
75
+
76
+ should "extract from array" do
77
+ assert_equal [ {:host => 'myhost', :port => nil} ], @client.__extract_hosts( ['myhost'] )
78
+ end
79
+
80
+ should "extract from array with multiple hosts" do
81
+ assert_equal [ {:host => 'host1', :port => nil}, {:host => 'host2', :port => nil} ],
82
+ @client.__extract_hosts( ['host1', 'host2'] )
83
+ end
84
+
85
+ should "extract from array with ports" do
86
+ assert_equal [ {:host => 'host1', :port => '1000'}, {:host => 'host2', :port => '2000'} ],
87
+ @client.__extract_hosts( ['host1:1000', 'host2:2000'] )
88
+ end
89
+
90
+ should "pass Hashes over" do
91
+ assert_equal [ {:host => 'myhost', :port => '1000'} ],
92
+ @client.__extract_hosts( [{:host => 'myhost', :port => '1000'}] )
93
+ end
94
+
95
+ should "raise error for incompatible argument" do
96
+ assert_raise ArgumentError do
97
+ @client.__extract_hosts 123
98
+ end
99
+ end
100
+
101
+ should "randomize hosts" do
102
+ hosts = [ {:host => 'host1'}, {:host => 'host2'}, {:host => 'host3'}, {:host => 'host4'}, {:host => 'host5'}]
103
+ assert_not_equal hosts, @client.__extract_hosts(hosts, :randomize_hosts => true)
104
+ assert_same_elements hosts, @client.__extract_hosts(hosts, :randomize_hosts => true)
105
+ end
106
+ end
107
+
108
+ end
109
+ end
@@ -0,0 +1,83 @@
1
+ require 'test_helper'
2
+
3
+ class Elasticsearch::Transport::Transport::Connections::CollectionTest < Test::Unit::TestCase
4
+ include Elasticsearch::Transport::Transport::Connections
5
+
6
+ context "Connection collection" do
7
+
8
+ should "have empty array as default connections array" do
9
+ assert_equal [], Collection.new.connections
10
+ end
11
+
12
+ should "have default selector class" do
13
+ assert_not_nil Collection.new.selector
14
+ end
15
+
16
+ should "initialize a custom selector class" do
17
+ c = Collection.new :selector_class => Selector::Random
18
+ assert_instance_of Selector::Random, c.selector
19
+ end
20
+
21
+ should "take a custom selector instance" do
22
+ c = Collection.new :selector => Selector::Random.new
23
+ assert_instance_of Selector::Random, c.selector
24
+ end
25
+
26
+ should "get connection from selector" do
27
+ c = Collection.new
28
+ c.selector.expects(:select).returns('OK')
29
+ assert_equal 'OK', c.get_connection
30
+ end
31
+
32
+ should "return an array of hosts" do
33
+ c = Collection.new :connections => [ Connection.new(:host => 'foo'), Connection.new(:host => 'bar') ]
34
+ assert_equal ['foo', 'bar'], c.hosts
35
+ end
36
+
37
+ should "be enumerable" do
38
+ c = Collection.new :connections => [ Connection.new(:host => 'foo'), Connection.new(:host => 'bar') ]
39
+
40
+ assert_equal ['FOO', 'BAR'], c.map { |i| i.host.upcase }
41
+ assert_equal 'foo', c[0].host
42
+ assert_equal 'bar', c[1].host
43
+ assert_equal 2, c.size
44
+ end
45
+
46
+ context "with the dead pool" do
47
+ setup do
48
+ @collection = Collection.new :connections => [ Connection.new(:host => 'foo'), Connection.new(:host => 'bar') ]
49
+ @collection[1].dead!
50
+ end
51
+
52
+ should "not iterate over dead connections" do
53
+ assert_equal 1, @collection.size
54
+ assert_equal ['FOO'], @collection.map { |c| c.host.upcase }
55
+ assert_equal @collection.connections, @collection.alive
56
+ end
57
+
58
+ should "have dead connections collection" do
59
+ assert_equal 1, @collection.dead.size
60
+ assert_equal ['BAR'], @collection.dead.map { |c| c.host.upcase }
61
+ end
62
+
63
+ should "resurrect dead connection with least failures when no alive is available" do
64
+ c1 = Connection.new(:host => 'foo').dead!.dead!
65
+ c2 = Connection.new(:host => 'bar').dead!
66
+
67
+ @collection = Collection.new :connections => [ c1, c2 ]
68
+
69
+ assert_equal 0, @collection.size
70
+ assert_not_nil @collection.get_connection
71
+ assert_equal 1, @collection.size
72
+ assert_equal c2, @collection.first
73
+ end
74
+
75
+ should "return all connections" do
76
+ assert_equal 2, @collection.all.size
77
+ end
78
+
79
+ end
80
+
81
+ end
82
+
83
+ end
@@ -0,0 +1,64 @@
1
+ require 'test_helper'
2
+
3
+ class Elasticsearch::Transport::Transport::Connections::SelectorTest < Test::Unit::TestCase
4
+ include Elasticsearch::Transport::Transport::Connections::Selector
5
+
6
+ class DummyStrategySelector
7
+ include Elasticsearch::Transport::Transport::Connections::Selector::Base
8
+ end
9
+
10
+ class BackupStrategySelector
11
+ include Elasticsearch::Transport::Transport::Connections::Selector::Base
12
+
13
+ def select(options={})
14
+ connections.reject do |c|
15
+ c.host[:attributes] && c.host[:attributes][:backup]
16
+ end.send( defined?(RUBY_VERSION) && RUBY_VERSION > '1.9' ? :sample : :choice)
17
+ end
18
+ end
19
+
20
+ context "Connection selector" do
21
+
22
+ should "be initialized with connections" do
23
+ assert_equal [1, 2], Random.new(:connections => [1, 2]).connections
24
+ end
25
+
26
+ should "have the abstract select method" do
27
+ assert_raise(NoMethodError) { DummyStrategySelector.new.select }
28
+ end
29
+
30
+ context "in random strategy" do
31
+ setup do
32
+ @selector = Random.new :connections => ['A', 'B', 'C']
33
+ end
34
+
35
+ should "pick a connection" do
36
+ assert_not_nil @selector.select
37
+ end
38
+ end
39
+
40
+ context "in round-robin strategy" do
41
+ setup do
42
+ @selector = RoundRobin.new :connections => ['A', 'B', 'C']
43
+ end
44
+
45
+ should "rotate over connections" do
46
+ assert_equal 'A', @selector.select
47
+ assert_equal 'B', @selector.select
48
+ assert_equal 'C', @selector.select
49
+ assert_equal 'A', @selector.select
50
+ end
51
+ end
52
+
53
+ context "with a custom strategy" do
54
+
55
+ should "return proper connection" do
56
+ selector = BackupStrategySelector.new :connections => [ stub(:host => { :hostname => 'host1' }),
57
+ stub(:host => { :hostname => 'host2', :attributes => { :backup => true }}) ]
58
+ 10.times { assert_equal 'host1', selector.select.host[:hostname] }
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,90 @@
1
+ require 'test_helper'
2
+
3
+ class Elasticsearch::Transport::Transport::Connections::ConnectionTest < Test::Unit::TestCase
4
+ include Elasticsearch::Transport::Transport::Connections
5
+
6
+ context "Connection" do
7
+
8
+ should "be initialized with :host, :connection, and :options" do
9
+ c = Connection.new :host => 'x', :connection => 'y', :options => {}
10
+ assert_equal 'x', c.host
11
+ assert_equal 'y', c.connection
12
+ assert_instance_of Hash, c.options
13
+ end
14
+
15
+ should "return full path" do
16
+ c = Connection.new
17
+ assert_equal '_search', c.full_path('_search')
18
+ assert_equal '_search', c.full_path('_search', {})
19
+ assert_equal '_search?foo=bar', c.full_path('_search', {:foo => 'bar'})
20
+ assert_equal '_search?foo=bar+bam', c.full_path('_search', {:foo => 'bar bam'})
21
+ end
22
+
23
+ should "return full url" do
24
+ c = Connection.new :host => { :protocol => 'http', :host => 'localhost', :port => '9200' }
25
+ assert_equal 'http://localhost:9200/_search?foo=bar', c.full_url('_search', {:foo => 'bar'})
26
+ end
27
+
28
+ should "have a string representation" do
29
+ c = Connection.new :host => 'x'
30
+ assert_match /host: x/, c.to_s
31
+ assert_match /alive/, c.to_s
32
+ end
33
+
34
+ should "not be dead by default" do
35
+ c = Connection.new
36
+ assert ! c.dead?
37
+ end
38
+
39
+ should "be dead when marked" do
40
+ c = Connection.new.dead!
41
+ assert c.dead?
42
+ assert_equal 1, c.failures
43
+ assert_in_delta c.dead_since, Time.now, 1
44
+ end
45
+
46
+ should "be alive when marked" do
47
+ c = Connection.new.dead!
48
+ assert c.dead?
49
+ assert_equal 1, c.failures
50
+ assert_in_delta c.dead_since, Time.now, 1
51
+
52
+ c.alive!
53
+ assert ! c.dead?
54
+ assert_equal 1, c.failures
55
+ end
56
+
57
+ should "be healthy when marked" do
58
+ c = Connection.new.dead!
59
+ assert c.dead?
60
+ assert_equal 1, c.failures
61
+ assert_in_delta c.dead_since, Time.now, 1
62
+
63
+ c.healthy!
64
+ assert ! c.dead?
65
+ assert_equal 0, c.failures
66
+ end
67
+
68
+ should "be resurrected if timeout passed" do
69
+ c = Connection.new.dead!
70
+
71
+ now = Time.now + 60
72
+ Time.stubs(:now).returns(now)
73
+
74
+ assert c.resurrect!, c.inspect
75
+ assert ! c.dead?, c.inspect
76
+ end
77
+
78
+ should "be resurrected if timeout passed for multiple failures" do
79
+ c = Connection.new.dead!.dead!
80
+
81
+ now = Time.now + 60*2
82
+ Time.stubs(:now).returns(now)
83
+
84
+ assert c.resurrect!, c.inspect
85
+ assert ! c.dead?, c.inspect
86
+ end
87
+
88
+ end
89
+
90
+ end
@@ -0,0 +1,16 @@
1
+ require 'test_helper'
2
+
3
+ class Elasticsearch::Transport::Transport::SerializerTest < Test::Unit::TestCase
4
+
5
+ context "Serializer" do
6
+
7
+ should "use MultiJson by default" do
8
+ ::MultiJson.expects(:load)
9
+ ::MultiJson.expects(:dump)
10
+ Elasticsearch::Transport::Transport::Serializer::MultiJson.new.load('{}')
11
+ Elasticsearch::Transport::Transport::Serializer::MultiJson.new.dump({})
12
+ end
13
+
14
+ end
15
+
16
+ end
@@ -0,0 +1,146 @@
1
+ require 'test_helper'
2
+
3
+ class Elasticsearch::Transport::Transport::SnifferTest < Test::Unit::TestCase
4
+
5
+ class DummyTransport
6
+ include Elasticsearch::Transport::Transport::Base
7
+ def __build_connections; hosts; end
8
+ end
9
+
10
+ def __nodes_info(json)
11
+ Elasticsearch::Transport::Transport::Response.new 200, MultiJson.load(json)
12
+ end
13
+
14
+ context "Sniffer" do
15
+ setup do
16
+ @transport = DummyTransport.new
17
+ @sniffer = Elasticsearch::Transport::Transport::Sniffer.new @transport
18
+ end
19
+
20
+ should "be initialized with a transport instance" do
21
+ assert_equal @transport, @sniffer.transport
22
+ end
23
+
24
+ should "return an array of hosts as hashes" do
25
+ @transport.expects(:perform_request).returns __nodes_info <<-JSON
26
+ {
27
+ "ok" : true,
28
+ "cluster_name" : "elasticsearch_test",
29
+ "nodes" : {
30
+ "N1" : {
31
+ "name" : "Node 1",
32
+ "transport_address" : "inet[/192.168.1.23:9300]",
33
+ "hostname" : "testhost1",
34
+ "version" : "0.20.6",
35
+ "http_address" : "inet[/192.168.1.23:9200]",
36
+ "thrift_address" : "/192.168.1.23:9500",
37
+ "memcached_address" : "inet[/192.168.1.23:11211]"
38
+ }
39
+ }
40
+ }
41
+ JSON
42
+
43
+ hosts = @sniffer.hosts
44
+
45
+ assert_equal 1, hosts.size
46
+ assert_equal '192.168.1.23', hosts.first[:host]
47
+ assert_equal '9200', hosts.first[:port]
48
+ assert_equal 'Node 1', hosts.first['name']
49
+ end
50
+
51
+ should "skip hosts without a matching transport protocol" do
52
+ @transport = DummyTransport.new :options => { :protocol => 'memcached' }
53
+ @sniffer = Elasticsearch::Transport::Transport::Sniffer.new @transport
54
+
55
+ @transport.expects(:perform_request).returns __nodes_info <<-JSON
56
+ {
57
+ "ok" : true,
58
+ "cluster_name" : "elasticsearch_test",
59
+ "nodes" : {
60
+ "N1" : {
61
+ "name" : "Memcached Node",
62
+ "http_address" : "inet[/192.168.1.23:9200]",
63
+ "memcached_address" : "inet[/192.168.1.23:11211]"
64
+ },
65
+ "N2" : {
66
+ "name" : "HTTP Node",
67
+ "http_address" : "inet[/192.168.1.23:9200]"
68
+ }
69
+ }
70
+ }
71
+ JSON
72
+
73
+ hosts = @sniffer.hosts
74
+
75
+ assert_equal 1, hosts.size
76
+ assert_equal '192.168.1.23', hosts.first[:host]
77
+ assert_equal '11211', hosts.first[:port]
78
+ assert_equal 'Memcached Node', hosts.first['name']
79
+ end
80
+
81
+ should "have configurable timeout" do
82
+ @transport = DummyTransport.new :options => { :sniffer_timeout => 0.001 }
83
+ @sniffer = Elasticsearch::Transport::Transport::Sniffer.new @transport
84
+ assert_equal 0.001, @sniffer.timeout
85
+ end
86
+
87
+ should "have settable timeout" do
88
+ @transport = DummyTransport.new
89
+ @sniffer = Elasticsearch::Transport::Transport::Sniffer.new @transport
90
+ assert_equal 1, @sniffer.timeout
91
+
92
+ @sniffer.timeout = 2
93
+ assert_equal 2, @sniffer.timeout
94
+ end
95
+
96
+ should "raise error on timeout" do
97
+ @transport.expects(:perform_request).raises(Elasticsearch::Transport::Transport::SnifferTimeoutError)
98
+
99
+ # TODO: Try to inject sleep into `perform_request` or make this test less ridiculous anyhow...
100
+ assert_raise Elasticsearch::Transport::Transport::SnifferTimeoutError do
101
+ @sniffer.hosts
102
+ end
103
+ end
104
+
105
+ should "randomize hosts" do
106
+ @transport = DummyTransport.new :options => { :randomize_hosts => true }
107
+ @sniffer = Elasticsearch::Transport::Transport::Sniffer.new @transport
108
+
109
+ @transport.expects(:perform_request).returns __nodes_info <<-JSON
110
+ {
111
+ "ok" : true,
112
+ "cluster_name" : "elasticsearch_test",
113
+ "nodes" : {
114
+ "N1" : {
115
+ "name" : "Node 1",
116
+ "http_address" : "inet[/192.168.1.23:9200]"
117
+ },
118
+ "N2" : {
119
+ "name" : "Node 2",
120
+ "http_address" : "inet[/192.168.1.23:9201]"
121
+ },
122
+ "N3" : {
123
+ "name" : "Node 3",
124
+ "http_address" : "inet[/192.168.1.23:9202]"
125
+ },
126
+ "N4" : {
127
+ "name" : "Node 4",
128
+ "http_address" : "inet[/192.168.1.23:9203]"
129
+ },
130
+ "N5" : {
131
+ "name" : "Node 5",
132
+ "http_address" : "inet[/192.168.1.23:9204]"
133
+ }
134
+ }
135
+ }
136
+ JSON
137
+
138
+ hosts = @sniffer.hosts
139
+
140
+ assert_not_equal ['Node 1', 'Node 2', 'Node 3', 'Node 4', 'Node 5'], hosts.map { |h| h['name'] }
141
+ assert_same_elements ['Node 1', 'Node 2', 'Node 3', 'Node 4', 'Node 5'], hosts.map { |h| h['name'] }
142
+ end
143
+
144
+ end
145
+
146
+ end