elasticsearch-transport 0.0.2

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 (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