sequel-replica-failover 0.1.2 → 1.0.0

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