sequel-replica-failover 0.1.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 31cc90d47b9bbe08b846ea3af88bb29a647fe6d3
4
- data.tar.gz: 21ccb990de1601f8f49c4523343a92587abdfc62
3
+ metadata.gz: 4427d0bd15392df069157501d649dc8f1208ca14
4
+ data.tar.gz: bc0ee4ad2bcad88f2aaf3d724418dea7b5d47c82
5
5
  SHA512:
6
- metadata.gz: 0bf16b9998f95ac996b20731c872b481c11482d8d00f145d0a19eed7ad5341f8e01e15417615d210a2b73d6f98ff106f57a3e3f34e33550555c837dd95b0a88b
7
- data.tar.gz: ddcb1cbb66e57295b5530f8e6a3378152b1b9aaaadf0ad8da7f47169bb39988a62f8f5e9f8956454f485487b391e8442be7ebcbd625db264eb79cd75d88ad8e2
6
+ metadata.gz: 80086a61fbc3969a6696df41f25d4b97f9306528c502d0cfe9c289dc5e18dbc0a25f69de1863fe18fa1ea161148918d6aae0dc61ccf89b5236735e93ef5e230c
7
+ data.tar.gz: 473f829e1df49607fc6516fb5b3f583b820b12ba878f7057b08b0e4b43976a7112c1a464d96bffd2b6e5ba49549bba68ec32f386306a9109f6e3fbbb2e52eb7c
data/.travis.yml CHANGED
@@ -2,6 +2,7 @@ language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
4
  - 2.0.0-p247
5
+ - 2.1.2
5
6
  script: "bundle exec rspec"
6
7
  notifications:
7
8
  email: false
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Automatic read-only failover for Sequel
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/sequel-replica-failover.png)](http://badge.fury.io/rb/sequel-replica-failover)
4
+ [![Build
5
+ Status](https://travis-ci.org/wanelo/sequel-replica-failover.svg?branch=master)](https://travis-ci.org/wanelo/sequel-replica-failover)
4
6
  [![Code Climate](https://codeclimate.com/github/wanelo/sequel-replica-failover.png)](https://codeclimate.com/github/wanelo/sequel-replica-failover)
5
7
 
6
8
  This provides a NOT-THREADSAFE sharded connection pool for failing over between configured replicas.
@@ -1,5 +1,5 @@
1
1
  module Sequel
2
2
  module ReplicaFailover
3
- VERSION = '0.1.2'
3
+ VERSION = '1.0.0'
4
4
  end
5
5
  end
@@ -8,10 +8,30 @@ class Sequel::ShardedSingleFailoverConnectionPool < Sequel::ShardedSingleConnect
8
8
  @pool_retry_count = opts[:pool_retry_count] || 5
9
9
  end
10
10
 
11
+ @on_disconnect = []
12
+ @on_unstick = []
13
+
11
14
  class << self
12
- attr_accessor :on_disconnect
15
+ attr_accessor :on_disconnect, :on_unstick
16
+
17
+ def register_on_disconnect_callback(callback)
18
+ @on_disconnect << callback
19
+ end
20
+
21
+ def clear_on_disconnect_callbacks
22
+ @on_disconnect.clear
23
+ end
24
+
25
+ def register_on_unstick_callback(callback)
26
+ @on_unstick << callback
27
+ end
28
+
29
+ def clear_on_unstick_callbacks
30
+ @on_unstick.clear
31
+ end
13
32
  end
14
33
 
34
+
15
35
  # Yields the connection to the supplied block for the given server.
16
36
  # This method simulates the ConnectionPool#hold API.
17
37
  def hold(server=:default, &block)
@@ -23,31 +43,33 @@ class Sequel::ShardedSingleFailoverConnectionPool < Sequel::ShardedSingleConnect
23
43
 
24
44
  super(server, &block)
25
45
  rescue Sequel::DatabaseDisconnectError, Sequel::DatabaseConnectionError => e
26
- if server == :read_only && !@db.in_transaction?(server: :read_only)
27
- self.class.on_disconnect.call(e, self) if self.class.on_disconnect
28
- disconnect_server(server)
29
- @conns[server] = nil
46
+ raise if server != :read_only
47
+ raise if @db.in_transaction?(server: :read_only)
30
48
 
31
- stick
49
+ unless self.class.on_disconnect.empty?
50
+ self.class.on_disconnect.each { |callback| callback.call(e, self) }
51
+ end
52
+ disconnect_server(server)
53
+ @conns[server] = nil
32
54
 
33
- if @stuck_times >= @pool_retry_count
34
- unstick(server)
35
- raise
36
- end
55
+ stick
37
56
 
38
- hold(server, &block)
39
- else
57
+ if @stuck_times >= @pool_retry_count
58
+ unstick(server)
40
59
  raise
41
60
  end
61
+
62
+ hold(server, &block)
42
63
  end
43
64
 
44
65
  def pool_type
45
66
  :sharded_single_failover
46
67
  end
47
68
 
48
- private
49
-
50
69
  def unstick(server)
70
+ unless self.class.on_unstick.empty?
71
+ self.class.on_unstick.each { |callback| callback.call(self) }
72
+ end
51
73
  probe(server.to_s) { |p| p.unstick }
52
74
  disconnect_server(server)
53
75
  @conns[server] = nil
@@ -55,6 +77,8 @@ class Sequel::ShardedSingleFailoverConnectionPool < Sequel::ShardedSingleConnect
55
77
  @stuck_times = nil
56
78
  end
57
79
 
80
+ private
81
+
58
82
  def stick
59
83
  @stuck_times ||= 0
60
84
  probe(@stuck_times) { |p| p.stick }
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Sequel::ReplicaFailover::DTraceProvider do
4
4
  describe 'initialize' do
5
5
  it 'creates a new provider' do
6
- USDT::Provider.should_receive(:create).with(:ruby, :sequel_replica_failover)
6
+ expect(USDT::Provider).to receive(:create).with(:ruby, :sequel_replica_failover)
7
7
  Sequel::ReplicaFailover::DTraceProvider.new
8
8
  end
9
9
  end
@@ -1,88 +1,119 @@
1
1
  require 'spec_helper'
2
- CONNECTION_POOL_DEFAULTS = {:pool_retry_count => 3,
3
- :pool_stick_timeout => 15,
4
- :pool_timeout=>5,
5
- :pool_sleep_time=>0.001,
6
- :max_connections=>4,
7
- :pool_class => Sequel::ShardedSingleFailoverConnectionPool,
8
- :servers => { :read_only => {} } }
9
-
10
- mock_db = lambda do |*a, &b|
11
- db = Sequel.mock
12
- (class << db; self end).send(:define_method, :connect){|c| b.arity == 1 ? b.call(c) : b.call} if b
13
- if b2 = a.shift
14
- (class << db; self end).send(:define_method, :disconnect_connection){|c| b2.arity == 1 ? b2.call(c) : b2.call}
15
- end
16
- db
17
- end
18
2
 
19
3
  describe Sequel::ShardedSingleFailoverConnectionPool do
20
- describe '#hold' do
21
- before do
22
- msp = proc { @max_size=3 }
23
- @connection_pool = Sequel::ConnectionPool.get_pool(mock_db.call(proc { |c| msp.call }) { :got_connection }, CONNECTION_POOL_DEFAULTS)
4
+ CONNECTION_POOL_DEFAULTS = {:pool_retry_count => 3,
5
+ :pool_stick_timeout => 15,
6
+ :pool_timeout=>5,
7
+ :pool_sleep_time=>0.001,
8
+ :max_connections=>4,
9
+ :pool_class => Sequel::ShardedSingleFailoverConnectionPool,
10
+ :servers => { :read_only => {} } }
11
+
12
+ mock_db = lambda do |*a, &b|
13
+ db = Sequel.mock
14
+ (class << db; self end).send(:define_method, :connect){|c| b.arity == 1 ? b.call(c) : b.call} if b
15
+ if b2 = a.shift
16
+ (class << db; self end).send(:define_method, :disconnect_connection){|c| b2.arity == 1 ? b2.call(c) : b2.call}
24
17
  end
18
+ db
19
+ end
20
+
21
+ after do
22
+ Sequel::ShardedSingleFailoverConnectionPool.clear_on_disconnect_callbacks
23
+ Sequel::ShardedSingleFailoverConnectionPool.clear_on_unstick_callbacks
24
+ end
25
+
26
+ let(:msp) { proc { @max_size=3 } }
27
+ let(:connection_pool) { Sequel::ConnectionPool.get_pool(mock_db.call(proc { |c| msp.call }) { :got_connection }, CONNECTION_POOL_DEFAULTS) }
25
28
 
29
+ describe '#hold' do
26
30
  context 'with read_only server' do
27
31
  context 'when block raises a database connection error' do
28
32
  it 'retries until the a successful connection is made' do
29
33
  call_count = 0
30
- proc { @connection_pool.hold(:read_only) { call_count += 1; raise Sequel::DatabaseDisconnectError if call_count == 1 } }.should_not raise_error
34
+ expect {
35
+ connection_pool.hold(:read_only) { call_count += 1; raise Sequel::DatabaseDisconnectError if call_count == 1 }
36
+ }.not_to raise_error
31
37
  expect(call_count).to eq(2)
32
38
  end
33
39
 
34
40
  it 'only retries N number of times before actually raising the error' do
35
41
  call_count = 0
36
- proc { @connection_pool.hold(:read_only) { call_count += 1; raise Sequel::DatabaseDisconnectError } }.should raise_error(Sequel::DatabaseDisconnectError)
42
+ expect {
43
+ connection_pool.hold(:read_only) { call_count += 1; raise Sequel::DatabaseDisconnectError }
44
+ }.to raise_error(Sequel::DatabaseDisconnectError)
37
45
  expect(call_count).to eq(3)
38
46
  end
39
47
 
40
48
  it 'sticks for N number of seconds to a working connection' do
41
49
  Timecop.freeze -16 do
42
50
  call_count = 0
43
- proc { @connection_pool.hold(:read_only) { call_count += 1; raise Sequel::DatabaseDisconnectError if call_count == 1 } }.should_not raise_error
51
+ expect {
52
+ connection_pool.hold(:read_only) { call_count += 1; raise Sequel::DatabaseDisconnectError if call_count == 1 }
53
+ }.not_to raise_error
44
54
  expect(call_count).to eq(2)
45
- expect(@connection_pool.size).to eq(1)
55
+ expect(connection_pool.size).to eq(1)
46
56
  end
47
57
 
48
- @connection_pool.should_receive(:make_new).once
49
- @connection_pool.hold(:read_only) {}
58
+ expect(connection_pool).to receive(:make_new).once
59
+ connection_pool.hold(:read_only) {}
50
60
  end
51
61
 
52
62
  context 'with an on_disconnect callback' do
53
63
  it 'calls the callback with the error' do
54
64
  callback = double("callback")
55
- Sequel::ShardedSingleFailoverConnectionPool.on_disconnect = callback
65
+ Sequel::ShardedSingleFailoverConnectionPool.register_on_disconnect_callback callback
56
66
 
57
- expect(callback).to receive(:call).with(an_instance_of(Sequel::DatabaseDisconnectError), @connection_pool)
67
+ expect(callback).to receive(:call).with(an_instance_of(Sequel::DatabaseDisconnectError), connection_pool)
58
68
  call_count = 0
59
- @connection_pool.hold(:read_only) { call_count += 1; raise Sequel::DatabaseDisconnectError if call_count == 1 }
69
+ connection_pool.hold(:read_only) { call_count += 1; raise Sequel::DatabaseDisconnectError if call_count == 1 }
60
70
  end
61
71
  end
62
72
 
63
73
  context 'when in a transaction' do
64
74
  it 'raises an exception' do
65
- Sequel::Mock::Database.any_instance.should_receive(:in_transaction?).and_return(true)
66
- proc { @connection_pool.hold(:read_only) { raise Sequel::DatabaseDisconnectError } }.should raise_error(Sequel::DatabaseDisconnectError)
75
+ allow_any_instance_of(Sequel::Mock::Database).to receive(:in_transaction?).and_return(true)
76
+ expect {
77
+ connection_pool.hold(:read_only) { raise Sequel::DatabaseDisconnectError }
78
+ }.to raise_error(Sequel::DatabaseDisconnectError)
67
79
  end
68
80
  end
69
81
  end
70
82
  end
71
83
 
72
- context 'with default or arbritrary server' do
84
+ context 'with default or arbitrary server' do
73
85
  it 'does no retry logic and raises error' do
74
86
  call_count = 0
75
- proc { @connection_pool.hold { call_count += 1; raise Sequel::DatabaseDisconnectError if call_count == 1 } }.should raise_error(Sequel::DatabaseDisconnectError)
87
+ expect {
88
+ connection_pool.hold { call_count += 1; raise Sequel::DatabaseDisconnectError if call_count == 1 }
89
+ }.to raise_error(Sequel::DatabaseDisconnectError)
76
90
  expect(call_count).to eq(1)
77
91
  end
78
92
  end
79
93
  end
80
94
 
81
- describe '.on_disconnect' do
82
- it 'sets the on_disconnect attribute' do
95
+ describe '#unstick' do
96
+ it 'calls on_unstick callbacks' do
97
+ callback = double(call: true)
98
+ Sequel::ShardedSingleFailoverConnectionPool.register_on_unstick_callback callback
99
+ connection_pool.unstick(:read_only)
100
+ expect(callback).to have_received(:call).with(connection_pool)
101
+ end
102
+ end
103
+
104
+ describe '.register_on_disconnect_callback' do
105
+ it 'adds to the on_disconnect attribute' do
106
+ callback = Proc.new{ puts "woo" }
107
+ Sequel::ShardedSingleFailoverConnectionPool.register_on_disconnect_callback callback
108
+ expect(Sequel::ShardedSingleFailoverConnectionPool.on_disconnect).to eq([callback])
109
+ end
110
+ end
111
+
112
+ describe '.register_on_disconnect_callback' do
113
+ it 'adds to the on_unstick attribute' do
83
114
  callback = Proc.new{ puts "woo" }
84
- Sequel::ShardedSingleFailoverConnectionPool.on_disconnect = callback
85
- expect(Sequel::ShardedSingleFailoverConnectionPool.on_disconnect).to eq(callback)
115
+ Sequel::ShardedSingleFailoverConnectionPool.register_on_unstick_callback callback
116
+ expect(Sequel::ShardedSingleFailoverConnectionPool.on_unstick).to eq([callback])
86
117
  end
87
118
  end
88
119
  end
data/spec/spec_helper.rb CHANGED
@@ -8,7 +8,6 @@ require 'sequel-replica-failover'
8
8
  require 'timecop'
9
9
 
10
10
  RSpec.configure do |config|
11
- config.treat_symbols_as_metadata_keys_with_true_values = true
12
11
  config.run_all_when_everything_filtered = true
13
12
  config.filter_run :focus
14
13
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel-replica-failover
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Henry
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-23 00:00:00.000000000 Z
11
+ date: 2014-06-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sequel
@@ -151,7 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
151
151
  version: '0'
152
152
  requirements: []
153
153
  rubyforge_project:
154
- rubygems_version: 2.2.0
154
+ rubygems_version: 2.2.2
155
155
  signing_key:
156
156
  specification_version: 4
157
157
  summary: Automatically failover when replicas go down.