octoshark 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +142 -70
- data/lib/octoshark.rb +4 -19
- data/lib/octoshark/active_record_extensions.rb +3 -7
- data/lib/octoshark/connection_manager.rb +8 -106
- data/lib/octoshark/connection_pools_manager.rb +85 -0
- data/lib/octoshark/current_connection.rb +37 -0
- data/lib/octoshark/version.rb +1 -1
- data/spec/octoshark/active_record_extensions_spec.rb +4 -4
- data/spec/octoshark/connection_manager_spec.rb +21 -218
- data/spec/octoshark/connection_pools_manager_spec.rb +180 -0
- data/spec/octoshark/current_connection_spec.rb +74 -0
- metadata +9 -4
- data/spec/octoshark_spec.rb +0 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 250485b4a7d40ba746223723276e9edbee4fc8b0
|
4
|
+
data.tar.gz: 0c58c2fd4569f288fe87129c0566bba3d34790aa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e904f55fa222ea36745a01fedbf936dd2bca0bbfe116b7eaa69ab0e56ae5fef5503fe800d2f5065d8d69d6ee2b7244ba5644e5ff5e5adc66d032f9f3d1fc9470
|
7
|
+
data.tar.gz: 139e493c0f689d4865f218247a8e0bfafee8f9605e7cb14b0ad0c3356422d0a32e731fd5127b52da7d6f05e291112c373af40959324b1fa2c9f5d26799ac4469
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
# Change log
|
2
|
+
|
3
|
+
## 0.2.0 2016-08-29
|
4
|
+
|
5
|
+
- `Octoshark::ConnectionManager` is split in two managers `Octoshark::ConnectionPoolsManager` for persistent connections and `Octoshark::ConnectionManager` for non-persistent connections.
|
6
|
+
- `Octoshark` class methods like `connection_managers`, `reset_connection_managers!`, `disconnect!` are only relevant and moved to `Octoshark::ConnectionPoolsManager` class.
|
7
|
+
- `Octoshark::ConnectionManager#use_database` method has been removed and the functionality moved to `Octoshark::ConnectionPoolsManager#with_connection(name, database_name)` where the second optional argument `database_name` when specified will switch the connection to the database using the `use database` MySQL statement.
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
![Travis status](https://travis-ci.org/dalibor/octoshark.png)
|
4
4
|
|
5
|
-
Octoshark is a simple ActiveRecord connection manager. It provides
|
5
|
+
Octoshark is a simple ActiveRecord connection manager. It provides connection switching mechanisms that can be used in various scenarios like master-slave, sharding or multi-tenant architecture.
|
6
6
|
|
7
7
|
|
8
8
|
## Installation
|
@@ -28,138 +28,142 @@ $ gem install octoshark
|
|
28
28
|
|
29
29
|
## Usage
|
30
30
|
|
31
|
-
|
31
|
+
Octoshark has two connection managers: `ConnectionPoolsManager` for managing connection pools using persistent connections and `ConnectionManager` for managing non-persistent connections. It depends on your application performance and scaling requirements which one to use.
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
db2: { adapter: "sqlite3", database: "db/db2.sqlite" }
|
37
|
-
})
|
38
|
-
```
|
33
|
+
- If you have a limited number of consumers (application and worker servers), `ConnectionPoolsManager` would be the preferred option. Standard Rails application has a single connection pool, and `ConnectionPoolsManager` just makes it possible for application models to work with multiple connection pools.
|
34
|
+
|
35
|
+
- If you have a very big infrastructure with lots of consumers (application and worker servers) and you are hitting max connections limit on database servers, i.e. you need to scale horizontally, `ConnectionManager` is the option to use. Because it uses non-persistent connections it comes up with a performance penalty because connections are re-established over and over again. Some ActiveRecord plugins that depend on having an active database connection all the time might need a change in order to work with non-persistent connections.
|
39
36
|
|
40
|
-
|
37
|
+
`ConnectionPoolsManager` and `ConnectionManager` can be combined together and many of them can be used at the same time.
|
38
|
+
|
39
|
+
Here is how to create connection pools manager:
|
41
40
|
|
42
41
|
```ruby
|
43
|
-
|
44
|
-
def self.connection
|
45
|
-
CONN_MANAGER.current_connection
|
46
|
-
end
|
47
|
-
end
|
42
|
+
CONN_MANAGER = Octoshark::ConnectionPoolsManager.new({ c1: config1, c2: config2 })
|
48
43
|
```
|
49
44
|
|
50
|
-
|
45
|
+
`config1` and `config2` are standard ActiveRecord database configs:
|
51
46
|
|
52
47
|
```ruby
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
48
|
+
config = {
|
49
|
+
adapter: 'mysql2',
|
50
|
+
host: 'localhost',
|
51
|
+
port: 3306,
|
52
|
+
database: 'database',
|
53
|
+
username: 'root',
|
54
|
+
password: 'pass',
|
55
|
+
pool: 1,
|
56
|
+
encoding: 'utf8',
|
57
|
+
reconnect: false
|
58
|
+
}
|
62
59
|
```
|
63
60
|
|
64
|
-
To
|
61
|
+
To switch a connection using a specific pool:
|
65
62
|
|
66
63
|
```ruby
|
67
|
-
CONN_MANAGER.with_connection(:
|
68
|
-
|
69
|
-
Post.first
|
64
|
+
CONN_MANAGER.with_connection(:c1) do |connection|
|
65
|
+
connection.execute("SELECT 1")
|
70
66
|
end
|
71
67
|
```
|
72
68
|
|
73
69
|
Multiple `with_connection` blocks can be nested:
|
74
70
|
|
75
71
|
```ruby
|
76
|
-
CONN_MANAGER.with_connection(
|
77
|
-
# run queries on
|
72
|
+
CONN_MANAGER.with_connection(config1) do
|
73
|
+
# run queries on connection specified with config1
|
78
74
|
|
79
|
-
CONN_MANAGER.with_connection(
|
80
|
-
# run queries on
|
75
|
+
CONN_MANAGER.with_connection(config2) do
|
76
|
+
# run queries on connection specified with config2
|
81
77
|
end
|
82
78
|
|
83
|
-
# run queries on
|
79
|
+
# run queries on connection specified with config1
|
84
80
|
end
|
85
81
|
```
|
86
82
|
|
87
|
-
`
|
83
|
+
If you establish a connection to database server instead of to database, then you can use the second optional argument `database_name` to tell the connection manager to switch the connection to that database within the same connection. This is useful when you want to have fewer connection pools and keep number of active connection per database server under control. This option is only MySQL specific for now, it uses `USE database_name` statement to switch the connection.
|
88
84
|
|
85
|
+
```ruby
|
86
|
+
CONN_MANAGER.with_connection(:c1, database_name) do |connection|
|
87
|
+
connection.execute("SELECT 1")
|
88
|
+
end
|
89
|
+
```
|
89
90
|
|
90
|
-
|
91
|
-
|
92
|
-
Some models are in the core DB, and others in shard DBs. Shard is selected based on a user attribute. For core models use the default ActiveRecord connection and for sharded models define and use Octoshark connections.
|
91
|
+
Using non-persistent connections with `Octoshark::ConnectionManager` has the same API:
|
93
92
|
|
94
|
-
Switch the connection in a controller with an around filter:
|
95
93
|
|
96
94
|
```ruby
|
97
|
-
|
95
|
+
CONN_MANAGER = Octoshark::ConnectionManager.new
|
96
|
+
```
|
98
97
|
|
99
|
-
|
100
|
-
|
98
|
+
Opening a new connection, executing query and closing the connection:
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
CONN_MANAGER.with_connection(config) do |connection|
|
102
|
+
connection.execute("SELECT 1")
|
101
103
|
end
|
102
104
|
```
|
103
105
|
|
104
|
-
Similar approach applies to other application entry-points like background jobs.
|
105
106
|
|
107
|
+
## Using Octoshark with ActiveRecord models
|
106
108
|
|
107
|
-
|
108
|
-
|
109
|
-
All models are in master and slave databases. For master models use the default ActiveRecord connection and for slave models define and use Octoshark connections.
|
109
|
+
To tell an ActiveRecord model to use the Octoshark connection we can override the `Model.connection` method.
|
110
110
|
|
111
111
|
```ruby
|
112
|
-
class ActiveRecord::Base
|
112
|
+
class Post < ActiveRecord::Base
|
113
113
|
def self.connection
|
114
|
-
|
115
|
-
CONN_MANAGER.current_or_default_connection
|
114
|
+
CONN_MANAGER.current_connection
|
116
115
|
end
|
117
116
|
end
|
118
117
|
```
|
119
118
|
|
120
|
-
|
121
|
-
|
119
|
+
Alternatively, we can extract it as a module and include in multiple models.
|
122
120
|
|
123
|
-
|
121
|
+
```ruby
|
122
|
+
module ShardingModel
|
123
|
+
extend ActiveSupport::Concern
|
124
124
|
|
125
|
-
|
125
|
+
module ClassMethods
|
126
|
+
def connection
|
127
|
+
CONN_MANAGER.current_connection
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
```
|
126
132
|
|
127
|
-
|
133
|
+
To use a specific database connection:
|
128
134
|
|
129
135
|
```ruby
|
130
|
-
|
131
|
-
|
136
|
+
CONN_MANAGER.with_connection(:c1) do
|
137
|
+
# run queries on c1
|
138
|
+
Post.first
|
139
|
+
end
|
140
|
+
```
|
132
141
|
|
133
|
-
|
142
|
+
This connection switching in Rails applications is usually done from within an `around_filter` for controllers and in a similar way for other application "entry-points" like background jobs:
|
143
|
+
|
144
|
+
```ruby
|
134
145
|
around_filter :select_shard
|
135
146
|
|
136
147
|
def select_shard(&block)
|
137
|
-
CONN_MANAGER.
|
148
|
+
CONN_MANAGER.with_connection(current_user.shard, &block)
|
138
149
|
end
|
139
150
|
```
|
140
151
|
|
141
|
-
`CONN_MANAGER.
|
152
|
+
`CONN_MANAGER.current_connection` returns the active connection while the execution is in the `with_connection` block or raises `Octoshark::Error::NoCurrentConnection` outside of the `with_connection` block. In some cases, falling back to the default database connection for the Rails app might be preferable which can be done using `CONN_MANAGER.current_or_default_connection`.
|
142
153
|
|
143
|
-
Alternatively, for better performance (only supported on MySQL), database connection can be switched with `use database` statement. Once connection manager is defined with connectino configs to database servers, selecting a database can be done with:
|
144
154
|
|
145
|
-
|
146
|
-
CONN_MANAGER.use_database(:db1, 'database') do
|
147
|
-
# run queries on database server identified by 'db1' using database 'database'
|
148
|
-
end
|
149
|
-
```
|
150
|
-
|
151
|
-
## Octoshark.reset_connection_managers!
|
155
|
+
## Octoshark::ConnectionPoolsManager.reset_connection_managers!
|
152
156
|
|
153
|
-
|
157
|
+
When using `Octoshark::ConnectionPoolsManager`, whenever ActiveRecord::Base calls `establish_connection` (usually by an ancestor process that must have subsequently forked), `Octoshark.reset_connection_managers!` is automatically called to re-establish the Octoshark connections. It prevents `ActiveRecord::ConnectionNotEstablished` in the scenarios like:
|
154
158
|
|
155
159
|
* Unicorn before/after fork
|
156
160
|
* Spring prefork/serve
|
157
161
|
* Some rake tasks like `rake db:test:prepare`
|
158
162
|
|
159
163
|
|
160
|
-
##
|
164
|
+
## Cleaning test databases
|
161
165
|
|
162
|
-
|
166
|
+
When using persistent connections, you can use tools like [DatabaseCleaner](https://github.com/DatabaseCleaner/database_cleaner) or [DatabaseRewinder](https://github.com/amatsuda/database_rewinder) to clean test databases. Here's an example of RSpec config for `DatabaseCleaner`:
|
163
167
|
|
164
168
|
```ruby
|
165
169
|
config.before(:suite) do
|
@@ -179,7 +183,7 @@ end
|
|
179
183
|
|
180
184
|
def setup_database_cleaner
|
181
185
|
DatabaseCleaner[:active_record, {connection: ActiveRecord::Base.connection_pool}]
|
182
|
-
Octoshark.connection_managers.each do |manager|
|
186
|
+
Octoshark::ConnectionPoolsManager.connection_managers.each do |manager|
|
183
187
|
manager.connection_pools.each_pair do |connection_name, connection_pool|
|
184
188
|
DatabaseCleaner[:active_record, {connection: connection_pool}]
|
185
189
|
end
|
@@ -187,6 +191,74 @@ def setup_database_cleaner
|
|
187
191
|
end
|
188
192
|
```
|
189
193
|
|
194
|
+
When using non-persistent connections where transaction rollback as a cleaning strategy will not work, we can use a custom solution inspired by `DatabaseRewinder`. It also works with dynamic databases created on the fly in the test suite.
|
195
|
+
|
196
|
+
```ruby
|
197
|
+
module DatabaseCleaner
|
198
|
+
module InsertRecorder
|
199
|
+
def execute(sql, *)
|
200
|
+
DatabaseCleaner.record_inserted_table(self, sql)
|
201
|
+
super
|
202
|
+
end
|
203
|
+
|
204
|
+
def exec_query(sql, *)
|
205
|
+
DatabaseCleaner.record_inserted_table(self, sql)
|
206
|
+
super
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
@@tables = []
|
211
|
+
|
212
|
+
def self.tables
|
213
|
+
@@tables
|
214
|
+
end
|
215
|
+
|
216
|
+
def self.record_inserted_table(connection, sql)
|
217
|
+
match = sql.match(/\AINSERT(?:\s+IGNORE)?\s+INTO\s+(?:\.*[`"]?([^.\s`"]+)[`"]?)*/i)
|
218
|
+
tables << match[1] if match && !tables.include?(match[1])
|
219
|
+
end
|
220
|
+
|
221
|
+
def self.clean
|
222
|
+
CoreDB.with_connection do |connection|
|
223
|
+
(
|
224
|
+
connection.tables.reject { |t| t == ActiveRecord::Migrator.schema_migrations_table_name } & tables
|
225
|
+
).each do |table|
|
226
|
+
connection.disable_referential_integrity do
|
227
|
+
connection.execute "DELETE FROM #{connection.quote_table_name(table)};"
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
@@tables = []
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
require 'active_record/connection_adapters/abstract_mysql_adapter'
|
236
|
+
ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter.send(:prepend, DatabaseCleaner::InsertRecorder)
|
237
|
+
```
|
238
|
+
|
239
|
+
|
240
|
+
## Development Setup
|
241
|
+
|
242
|
+
Setup database config and create databases:
|
243
|
+
|
244
|
+
```bash
|
245
|
+
cp spec/support/config.yml.template spec/support/config.yml
|
246
|
+
rake db:create
|
247
|
+
```
|
248
|
+
|
249
|
+
Run specs:
|
250
|
+
|
251
|
+
```bash
|
252
|
+
bundle exec rspec spec
|
253
|
+
```
|
254
|
+
|
255
|
+
Install different active record versions defined in `Appraisals` and run specs for all of them:
|
256
|
+
|
257
|
+
```bash
|
258
|
+
bundle exec appraisal
|
259
|
+
bundle exec appraisal rspec spec
|
260
|
+
```
|
261
|
+
|
190
262
|
|
191
263
|
## Logo
|
192
264
|
|
data/lib/octoshark.rb
CHANGED
@@ -3,23 +3,8 @@ require 'active_record'
|
|
3
3
|
require 'octoshark/active_record_extensions'
|
4
4
|
|
5
5
|
module Octoshark
|
6
|
-
autoload :
|
7
|
-
autoload :
|
8
|
-
|
9
|
-
|
10
|
-
# automatically reconnect on connection establish.
|
11
|
-
@@connection_managers = []
|
12
|
-
|
13
|
-
def self.connection_managers
|
14
|
-
@@connection_managers
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.reset_connection_managers!
|
18
|
-
connection_managers.map(&:reset!)
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.disconnect!
|
22
|
-
connection_managers.map(&:disconnect!)
|
23
|
-
@@connection_managers = []
|
24
|
-
end
|
6
|
+
autoload :CurrentConnection, 'octoshark/current_connection'
|
7
|
+
autoload :ConnectionManager, 'octoshark/connection_manager'
|
8
|
+
autoload :ConnectionPoolsManager, 'octoshark/connection_pools_manager'
|
9
|
+
autoload :Error, 'octoshark/error'
|
25
10
|
end
|
@@ -15,7 +15,7 @@ module Octoshark
|
|
15
15
|
# Octoshark connection managers.
|
16
16
|
def establish_connection_with_octoshark(*args)
|
17
17
|
establish_connection_without_octoshark(*args)
|
18
|
-
Octoshark.reset_connection_managers!
|
18
|
+
Octoshark::ConnectionPoolsManager.reset_connection_managers!
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
@@ -30,12 +30,8 @@ module Octoshark
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def log_with_octoshark(sql, name = "SQL", *other_args, &block)
|
33
|
-
if connection_name
|
34
|
-
|
35
|
-
name = "[Octoshark: #{connection_name} #{database_name}] #{name}"
|
36
|
-
else
|
37
|
-
name = "[Octoshark: #{connection_name}] #{name}"
|
38
|
-
end
|
33
|
+
if connection_name || database_name
|
34
|
+
name = "[Octoshark: #{[connection_name, database_name].compact.join(' ')}] #{name}"
|
39
35
|
end
|
40
36
|
|
41
37
|
log_without_octoshark(sql, name, *other_args, &block)
|
@@ -1,118 +1,20 @@
|
|
1
1
|
module Octoshark
|
2
2
|
class ConnectionManager
|
3
|
+
include CurrentConnection
|
3
4
|
|
4
|
-
|
5
|
+
def with_connection(config, connection_name: nil, &block)
|
6
|
+
connection_method = "#{config[:adapter]}_connection"
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
Octoshark.connection_managers << self
|
11
|
-
end
|
12
|
-
|
13
|
-
def reset!
|
14
|
-
disconnect!
|
15
|
-
setup_connection_pools
|
16
|
-
end
|
17
|
-
|
18
|
-
def current_connection
|
19
|
-
Thread.current[identifier] || raise(Octoshark::Error::NoCurrentConnection, "No current connection")
|
20
|
-
end
|
21
|
-
|
22
|
-
def current_connection?
|
23
|
-
!Thread.current[identifier].nil?
|
24
|
-
end
|
25
|
-
|
26
|
-
def current_or_default_connection
|
27
|
-
Thread.current[identifier] || ActiveRecord::Base.connection_pool.connection
|
28
|
-
end
|
29
|
-
|
30
|
-
def with_connection(name, &block)
|
31
|
-
connection_pool = find_connection_pool(name)
|
32
|
-
with_connection_pool(name, connection_pool, &block)
|
33
|
-
end
|
34
|
-
|
35
|
-
def with_new_connection(name, config, reusable: false, &block)
|
36
|
-
if reusable
|
37
|
-
connection_pool = @connection_pools[name] ||= create_connection_pool(config)
|
38
|
-
with_connection_pool(name, connection_pool, &block)
|
39
|
-
else
|
40
|
-
connection_pool = create_connection_pool(config)
|
41
|
-
with_connection_pool(name, connection_pool, &block).tap do
|
42
|
-
connection_pool.disconnect!
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def use_database(name, database_name, &block)
|
48
|
-
connection_pool = find_connection_pool(name)
|
49
|
-
with_connection_pool(name, connection_pool, database_name, &block)
|
50
|
-
end
|
51
|
-
|
52
|
-
def without_connection(&block)
|
53
|
-
change_connection_reference(nil) do
|
54
|
-
yield
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def find_connection_pool(name)
|
59
|
-
@connection_pools[name] || raise(Octoshark::Error::NoConnection, "No such database connection '#{name}'")
|
60
|
-
end
|
61
|
-
|
62
|
-
def disconnect!
|
63
|
-
@connection_pools.values.each do |connection_pool|
|
64
|
-
connection_pool.disconnect!
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def identifier
|
69
|
-
@identifier ||= "octoshark_#{object_id}"
|
70
|
-
end
|
71
|
-
|
72
|
-
private
|
73
|
-
def spec_class
|
74
|
-
if defined?(ActiveRecord::ConnectionAdapters::ConnectionSpecification)
|
75
|
-
spec_class = ActiveRecord::ConnectionAdapters::ConnectionSpecification
|
76
|
-
else
|
77
|
-
spec_class = ActiveRecord::Base::ConnectionSpecification
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def change_connection_reference(connection, &block)
|
82
|
-
previous_connection = Thread.current[identifier]
|
83
|
-
Thread.current[identifier] = connection
|
8
|
+
connection = ActiveRecord::Base.send(connection_method, config)
|
9
|
+
connection.connection_name = connection_name
|
10
|
+
connection.database_name = config[:database] if config[:database]
|
84
11
|
|
85
12
|
begin
|
86
|
-
yield
|
87
|
-
ensure
|
88
|
-
Thread.current[identifier] = previous_connection
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def setup_connection_pools
|
93
|
-
@connection_pools = HashWithIndifferentAccess.new
|
94
|
-
|
95
|
-
@configs.each_pair do |name, config|
|
96
|
-
@connection_pools[name] = create_connection_pool(config)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def create_connection_pool(config)
|
101
|
-
spec = spec_class.new(config, "#{config[:adapter]}_connection")
|
102
|
-
ActiveRecord::ConnectionAdapters::ConnectionPool.new(spec)
|
103
|
-
end
|
104
|
-
|
105
|
-
def with_connection_pool(name, connection_pool, database_name = nil, &block)
|
106
|
-
connection_pool.with_connection do |connection|
|
107
|
-
connection.connection_name = name
|
108
|
-
if database_name
|
109
|
-
connection.database_name = database_name
|
110
|
-
connection.execute("use #{database_name}")
|
111
|
-
end
|
112
|
-
|
113
13
|
change_connection_reference(connection) do
|
114
14
|
yield(connection)
|
115
15
|
end
|
16
|
+
ensure
|
17
|
+
connection.disconnect!
|
116
18
|
end
|
117
19
|
end
|
118
20
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Octoshark
|
2
|
+
class ConnectionPoolsManager
|
3
|
+
include CurrentConnection
|
4
|
+
|
5
|
+
# Octoshark needs to keep track of all persistent connection managers
|
6
|
+
# in order to automatically reconnect on connection establish.
|
7
|
+
@@connection_managers = []
|
8
|
+
|
9
|
+
def self.connection_managers
|
10
|
+
@@connection_managers
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.reset_connection_managers!
|
14
|
+
connection_managers.map(&:reset!)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.disconnect!
|
18
|
+
connection_managers.map(&:disconnect!)
|
19
|
+
@@connection_managers = []
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :connection_pools
|
23
|
+
|
24
|
+
def initialize(configs = {})
|
25
|
+
@configs = configs.with_indifferent_access
|
26
|
+
setup_connection_pools
|
27
|
+
|
28
|
+
self.class.connection_managers << self
|
29
|
+
end
|
30
|
+
|
31
|
+
def reset!
|
32
|
+
disconnect!
|
33
|
+
setup_connection_pools
|
34
|
+
end
|
35
|
+
|
36
|
+
def with_connection(name, database_name = nil, &block)
|
37
|
+
connection_pool = find_connection_pool(name)
|
38
|
+
|
39
|
+
connection_pool.with_connection do |connection|
|
40
|
+
connection.connection_name = name
|
41
|
+
|
42
|
+
if database_name
|
43
|
+
connection.database_name = database_name
|
44
|
+
connection.execute("use #{database_name}")
|
45
|
+
end
|
46
|
+
|
47
|
+
change_connection_reference(connection) do
|
48
|
+
yield(connection)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_connection_pool(name)
|
54
|
+
@connection_pools[name] || raise(Octoshark::Error::NoConnection, "No such database connection '#{name}'")
|
55
|
+
end
|
56
|
+
|
57
|
+
def disconnect!
|
58
|
+
@connection_pools.values.each do |connection_pool|
|
59
|
+
connection_pool.disconnect!
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
def spec_class
|
65
|
+
if defined?(ActiveRecord::ConnectionAdapters::ConnectionSpecification)
|
66
|
+
spec_class = ActiveRecord::ConnectionAdapters::ConnectionSpecification
|
67
|
+
else
|
68
|
+
spec_class = ActiveRecord::Base::ConnectionSpecification
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def setup_connection_pools
|
73
|
+
@connection_pools = HashWithIndifferentAccess.new
|
74
|
+
|
75
|
+
@configs.each_pair do |name, config|
|
76
|
+
@connection_pools[name] = create_connection_pool(config)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def create_connection_pool(config)
|
81
|
+
spec = spec_class.new(config, "#{config[:adapter]}_connection")
|
82
|
+
ActiveRecord::ConnectionAdapters::ConnectionPool.new(spec)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Octoshark
|
2
|
+
module CurrentConnection
|
3
|
+
def current_connection
|
4
|
+
Thread.current[identifier] || raise(Octoshark::Error::NoCurrentConnection, "No current connection")
|
5
|
+
end
|
6
|
+
|
7
|
+
def current_connection?
|
8
|
+
!Thread.current[identifier].nil?
|
9
|
+
end
|
10
|
+
|
11
|
+
def current_or_default_connection
|
12
|
+
Thread.current[identifier] || ActiveRecord::Base.connection_pool.connection
|
13
|
+
end
|
14
|
+
|
15
|
+
def without_connection(&block)
|
16
|
+
change_connection_reference(nil) do
|
17
|
+
yield
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def identifier
|
22
|
+
@identifier ||= "octoshark_#{object_id}"
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def change_connection_reference(connection, &block)
|
27
|
+
previous_connection = Thread.current[identifier]
|
28
|
+
Thread.current[identifier] = connection
|
29
|
+
|
30
|
+
begin
|
31
|
+
yield
|
32
|
+
ensure
|
33
|
+
Thread.current[identifier] = previous_connection
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/octoshark/version.rb
CHANGED
@@ -8,7 +8,7 @@ describe "ActiveRecord Extensions" do
|
|
8
8
|
|
9
9
|
ActiveRecord::Base.logger = logger
|
10
10
|
|
11
|
-
manager = Octoshark::
|
11
|
+
manager = Octoshark::ConnectionPoolsManager.new(configs)
|
12
12
|
manager.with_connection(:db1) do |connection|
|
13
13
|
connection.execute("SELECT 1")
|
14
14
|
end
|
@@ -25,8 +25,8 @@ describe "ActiveRecord Extensions" do
|
|
25
25
|
|
26
26
|
ActiveRecord::Base.logger = logger
|
27
27
|
|
28
|
-
manager = Octoshark::
|
29
|
-
manager.
|
28
|
+
manager = Octoshark::ConnectionPoolsManager.new(mysql2_configs)
|
29
|
+
manager.with_connection(:db1, database_name) do |connection|
|
30
30
|
connection.execute("SELECT 1")
|
31
31
|
end
|
32
32
|
|
@@ -41,7 +41,7 @@ describe "ActiveRecord Extensions" do
|
|
41
41
|
|
42
42
|
ActiveRecord::Base.logger = logger
|
43
43
|
|
44
|
-
manager = Octoshark::
|
44
|
+
manager = Octoshark::ConnectionPoolsManager.new(configs)
|
45
45
|
manager.with_connection(:db1) do |connection|
|
46
46
|
ActiveRecord::Base.connection.execute("SELECT 1")
|
47
47
|
end
|
@@ -1,245 +1,48 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Octoshark::ConnectionManager do
|
4
|
-
describe "#
|
5
|
-
it "
|
4
|
+
describe "#with_connection", mysql2: true do
|
5
|
+
it "creates temporary connection to database server" do
|
6
6
|
manager = Octoshark::ConnectionManager.new
|
7
|
+
connection_reference = nil
|
7
8
|
|
8
|
-
|
9
|
-
expect(manager.connection_pools[:default]).to be_nil
|
10
|
-
end
|
11
|
-
|
12
|
-
it "initializes connection manager with custom connections" do
|
13
|
-
manager = Octoshark::ConnectionManager.new(configs)
|
14
|
-
|
15
|
-
expect(manager.connection_pools.length).to eq(2)
|
16
|
-
expect(manager.connection_pools[:db1]).to be_an_instance_of(ActiveRecord::ConnectionAdapters::ConnectionPool)
|
17
|
-
expect(manager.connection_pools[:db2]).to be_an_instance_of(ActiveRecord::ConnectionAdapters::ConnectionPool)
|
18
|
-
end
|
19
|
-
|
20
|
-
it "accepts configs with string keys" do
|
21
|
-
configs = { 'db1' => { 'adapter' => "sqlite3", 'database' => "tmp/db1.sqlite" } }
|
22
|
-
manager = Octoshark::ConnectionManager.new(configs)
|
23
|
-
|
24
|
-
expect { manager.connection_pools[:db1].connection }.not_to raise_error
|
25
|
-
end
|
26
|
-
end
|
9
|
+
config = mysql2_configs[:db1]
|
27
10
|
|
28
|
-
|
29
|
-
|
30
|
-
manager1 = Octoshark::ConnectionManager.new
|
31
|
-
manager2 = Octoshark::ConnectionManager.new
|
32
|
-
expect(manager1.identifier).to_not eq(manager2.identifier)
|
33
|
-
end
|
34
|
-
end
|
11
|
+
manager.with_connection(config.except(:database)) do |connection|
|
12
|
+
connection_reference = connection
|
35
13
|
|
36
|
-
|
37
|
-
|
38
|
-
manager = Octoshark::ConnectionManager.new(configs)
|
39
|
-
manager.with_connection(:db1) do |connection|
|
40
|
-
expect(manager.current_connection).to eq(connection)
|
14
|
+
connection.execute("SELECT 1")
|
15
|
+
expect(connection.database_name).to be_nil
|
41
16
|
end
|
42
|
-
end
|
43
|
-
|
44
|
-
it "raises error when no current connection" do
|
45
|
-
manager = Octoshark::ConnectionManager.new
|
46
|
-
|
47
|
-
expect { manager.current_connection }.to raise_error(Octoshark::Error::NoCurrentConnection)
|
48
|
-
end
|
49
|
-
end
|
50
17
|
|
51
|
-
|
52
|
-
it "returns true if current one" do
|
53
|
-
manager = Octoshark::ConnectionManager.new(configs)
|
54
|
-
manager.with_connection(:db1) do
|
55
|
-
expect(manager.current_connection?).to be_truthy
|
56
|
-
end
|
18
|
+
expect(connection_reference.active?).to eq(false)
|
57
19
|
end
|
58
20
|
|
59
|
-
it "
|
21
|
+
it "creates temporary connection to specific database", mysql2: true do
|
60
22
|
manager = Octoshark::ConnectionManager.new
|
23
|
+
connection_reference = nil
|
61
24
|
|
62
|
-
|
63
|
-
|
64
|
-
end
|
65
|
-
|
66
|
-
describe "#current_or_default_connection" do
|
67
|
-
it "returns current connection" do
|
68
|
-
manager = Octoshark::ConnectionManager.new(configs)
|
69
|
-
manager.with_connection(:db1) do |db1|
|
70
|
-
expect(manager.current_or_default_connection).to eq(db1)
|
71
|
-
end
|
72
|
-
end
|
25
|
+
config = mysql2_configs[:db1]
|
26
|
+
database_name = config.fetch('database')
|
73
27
|
|
74
|
-
|
75
|
-
|
28
|
+
manager.with_connection(config) do |connection|
|
29
|
+
connection_reference = connection
|
76
30
|
|
77
|
-
|
78
|
-
|
79
|
-
end
|
80
|
-
|
81
|
-
describe '#find_connection_pool' do
|
82
|
-
it "can find connection pool by name" do
|
83
|
-
manager = Octoshark::ConnectionManager.new(configs)
|
84
|
-
expect(manager.find_connection_pool(:db1)).to be_an_instance_of(ActiveRecord::ConnectionAdapters::ConnectionPool)
|
85
|
-
end
|
86
|
-
|
87
|
-
it "raises Octoshark::Error::NoConnection when no pool with that name" do
|
88
|
-
manager = Octoshark::ConnectionManager.new({})
|
89
|
-
expect { manager.find_connection_pool(:invalid) }.to raise_error(Octoshark::Error::NoConnection)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
describe '#with_connection' do
|
94
|
-
it "can use multiple connections" do
|
95
|
-
manager = Octoshark::ConnectionManager.new(configs)
|
96
|
-
|
97
|
-
manager.with_connection(:db1) do
|
98
|
-
expect(db(manager.current_connection)).to eq("db1")
|
31
|
+
connection.execute("SELECT 1")
|
32
|
+
expect(connection.database_name).to eq(database_name)
|
99
33
|
end
|
100
34
|
|
101
|
-
|
102
|
-
expect(db(manager.current_connection)).to eq("db2")
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
it "can nest connection" do
|
107
|
-
manager = Octoshark::ConnectionManager.new(configs)
|
108
|
-
|
109
|
-
manager.with_connection(:db1) do
|
110
|
-
expect(db(manager.current_connection)).to eq("db1")
|
111
|
-
|
112
|
-
manager.with_connection(:db2) do
|
113
|
-
expect(db(manager.current_connection)).to eq("db2")
|
114
|
-
end
|
115
|
-
|
116
|
-
expect(db(manager.current_connection)).to eq("db1")
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
it "returns value from execution" do
|
121
|
-
manager = Octoshark::ConnectionManager.new(configs)
|
122
|
-
result = manager.with_connection(:db1) { |connection| connection.execute("SELECT 1") }
|
123
|
-
expect(result).to eq([{"1"=>1, 0=>1}])
|
124
|
-
end
|
125
|
-
|
126
|
-
it "raises Octoshark::Error::NoConnection" do
|
127
|
-
manager = Octoshark::ConnectionManager.new({})
|
128
|
-
|
129
|
-
expect { manager.with_connection(:invalid) }.to raise_error(Octoshark::Error::NoConnection)
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
describe "#with_new_connection" do
|
134
|
-
it "creates temporary connection" do
|
135
|
-
manager = Octoshark::ConnectionManager.new
|
136
|
-
result = manager.with_new_connection(:db1, configs[:db1]) { |connection| connection.execute("SELECT 1") }
|
137
|
-
|
138
|
-
expect(manager.connection_pools).to be_blank
|
35
|
+
expect(connection_reference.active?).to eq(false)
|
139
36
|
end
|
140
37
|
|
141
38
|
it "returns query results with temporary connection" do
|
142
39
|
manager = Octoshark::ConnectionManager.new
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
end
|
147
|
-
|
148
|
-
it "creates persistent connection" do
|
149
|
-
connection_id = nil
|
150
|
-
manager = Octoshark::ConnectionManager.new
|
151
|
-
expect(manager.connection_pools.length).to eq(0)
|
152
|
-
|
153
|
-
manager.with_new_connection(:db1, configs[:db1], reusable: true) do |connection|
|
154
|
-
connection_id = connection.object_id
|
40
|
+
config = configs[:db1]
|
41
|
+
result = manager.with_connection(config) do |connection|
|
42
|
+
connection.execute("SELECT 1")
|
155
43
|
end
|
156
|
-
expect(manager.connection_pools.length).to eq(1)
|
157
|
-
|
158
|
-
manager.with_new_connection(:db1, configs[:db1], reusable: true) do |connection|
|
159
|
-
expect(connection.object_id).to eq(connection_id)
|
160
|
-
end
|
161
|
-
expect(manager.connection_pools.length).to eq(1)
|
162
|
-
end
|
163
44
|
|
164
|
-
it "returns query results with persistent connection" do
|
165
|
-
manager = Octoshark::ConnectionManager.new
|
166
|
-
|
167
|
-
result = manager.with_new_connection(:db1, configs[:db1], reusable: true) { |connection| connection.execute("SELECT 1") }
|
168
|
-
expect(result).to eq([{"1"=>1, 0=>1}])
|
169
|
-
|
170
|
-
result = manager.with_new_connection(:db1, configs[:db1], reusable: true) { |connection| connection.execute("SELECT 1") }
|
171
45
|
expect(result).to eq([{"1"=>1, 0=>1}])
|
172
46
|
end
|
173
47
|
end
|
174
|
-
|
175
|
-
describe "#use_database" do
|
176
|
-
it "can nest connection", mysql2: true do
|
177
|
-
manager = Octoshark::ConnectionManager.new(mysql2_configs)
|
178
|
-
|
179
|
-
db1 = mysql2_configs[:db1]['database']
|
180
|
-
db2 = mysql2_configs[:db2]['database']
|
181
|
-
|
182
|
-
manager.use_database(:db1, db1) do
|
183
|
-
expect(db(manager.current_connection)).to eq(db1)
|
184
|
-
|
185
|
-
manager.use_database(:db2, db2) do
|
186
|
-
expect(db(manager.current_connection)).to eq(db2)
|
187
|
-
end
|
188
|
-
|
189
|
-
expect(db(manager.current_connection)).to eq(db1)
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
it "returns value from execution", mysql2: true do
|
194
|
-
manager = Octoshark::ConnectionManager.new(mysql2_configs)
|
195
|
-
db1 = mysql2_configs[:db1]['database']
|
196
|
-
result = manager.use_database(:db1, db1) { |connection| connection.execute("SELECT 1") }.to_a
|
197
|
-
expect(result).to eq([[1]])
|
198
|
-
end
|
199
|
-
end
|
200
|
-
|
201
|
-
describe '#without_connection' do
|
202
|
-
it "can reset current connection temporarily inside nested connection block" do
|
203
|
-
manager = Octoshark::ConnectionManager.new(configs)
|
204
|
-
|
205
|
-
manager.with_connection(:db1) do
|
206
|
-
expect(db(manager.current_connection)).to eq("db1")
|
207
|
-
|
208
|
-
manager.without_connection do
|
209
|
-
expect { manager.current_connection }.to raise_error(Octoshark::Error::NoCurrentConnection)
|
210
|
-
end
|
211
|
-
|
212
|
-
expect(db(manager.current_connection)).to eq("db1")
|
213
|
-
end
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
describe "#disconnect!" do
|
218
|
-
it "removes all connections from connection pools" do
|
219
|
-
manager = Octoshark::ConnectionManager.new(configs)
|
220
|
-
|
221
|
-
manager.with_connection(:db1) { |connection| connection.execute("SELECT 1") }
|
222
|
-
expect(manager.find_connection_pool(:db1)).to be_connected
|
223
|
-
|
224
|
-
manager.disconnect!
|
225
|
-
|
226
|
-
expect(manager.find_connection_pool(:db1)).to_not be_connected
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
describe ".reset!" do
|
231
|
-
it "gets new connection pools ready to rock" do
|
232
|
-
manager = Octoshark::ConnectionManager.new(configs)
|
233
|
-
|
234
|
-
manager.with_connection(:db1) { |connection| connection.execute("SELECT 1") }
|
235
|
-
expect(manager.connection_pools[:db1].connections.count).to eq(1)
|
236
|
-
|
237
|
-
manager.reset!
|
238
|
-
|
239
|
-
expect(manager.connection_pools[:db1].connections.count).to eq(0)
|
240
|
-
|
241
|
-
manager.with_connection(:db1) { |connection| connection.execute("SELECT 1") }
|
242
|
-
expect(manager.connection_pools[:db1].connections.count).to eq(1)
|
243
|
-
end
|
244
|
-
end
|
245
48
|
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Octoshark::ConnectionPoolsManager do
|
4
|
+
describe "#initialize" do
|
5
|
+
it "initializes connection manager with default connection" do
|
6
|
+
manager = Octoshark::ConnectionPoolsManager.new
|
7
|
+
|
8
|
+
expect(manager.connection_pools.length).to eq(0)
|
9
|
+
expect(manager.connection_pools[:default]).to be_nil
|
10
|
+
end
|
11
|
+
|
12
|
+
it "initializes connection manager with custom connections" do
|
13
|
+
manager = Octoshark::ConnectionPoolsManager.new(configs)
|
14
|
+
|
15
|
+
expect(manager.connection_pools.length).to eq(2)
|
16
|
+
expect(manager.connection_pools[:db1]).to be_an_instance_of(ActiveRecord::ConnectionAdapters::ConnectionPool)
|
17
|
+
expect(manager.connection_pools[:db2]).to be_an_instance_of(ActiveRecord::ConnectionAdapters::ConnectionPool)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "accepts configs with string keys" do
|
21
|
+
configs = { 'db1' => { 'adapter' => "sqlite3", 'database' => "tmp/db1.sqlite" } }
|
22
|
+
manager = Octoshark::ConnectionPoolsManager.new(configs)
|
23
|
+
|
24
|
+
expect { manager.connection_pools[:db1].connection }.not_to raise_error
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#find_connection_pool' do
|
29
|
+
it "can find connection pool by name" do
|
30
|
+
manager = Octoshark::ConnectionPoolsManager.new(configs)
|
31
|
+
expect(manager.find_connection_pool(:db1)).to be_an_instance_of(ActiveRecord::ConnectionAdapters::ConnectionPool)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "raises Octoshark::Error::NoConnection when no pool with that name" do
|
35
|
+
manager = Octoshark::ConnectionPoolsManager.new({})
|
36
|
+
expect { manager.find_connection_pool(:invalid) }.to raise_error(Octoshark::Error::NoConnection)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#with_connection' do
|
41
|
+
it "can use multiple connections" do
|
42
|
+
manager = Octoshark::ConnectionPoolsManager.new(configs)
|
43
|
+
|
44
|
+
manager.with_connection(:db1) do
|
45
|
+
expect(db(manager.current_connection)).to eq("db1")
|
46
|
+
end
|
47
|
+
|
48
|
+
manager.with_connection(:db2) do
|
49
|
+
expect(db(manager.current_connection)).to eq("db2")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
it "can nest connection" do
|
54
|
+
manager = Octoshark::ConnectionPoolsManager.new(configs)
|
55
|
+
|
56
|
+
manager.with_connection(:db1) do
|
57
|
+
expect(db(manager.current_connection)).to eq("db1")
|
58
|
+
|
59
|
+
manager.with_connection(:db2) do
|
60
|
+
expect(db(manager.current_connection)).to eq("db2")
|
61
|
+
end
|
62
|
+
|
63
|
+
expect(db(manager.current_connection)).to eq("db1")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
it "returns value from execution" do
|
68
|
+
manager = Octoshark::ConnectionPoolsManager.new(configs)
|
69
|
+
result = manager.with_connection(:db1) { |connection| connection.execute("SELECT 1") }
|
70
|
+
expect(result).to eq([{"1"=>1, 0=>1}])
|
71
|
+
end
|
72
|
+
|
73
|
+
it "raises Octoshark::Error::NoConnection" do
|
74
|
+
manager = Octoshark::ConnectionPoolsManager.new({})
|
75
|
+
|
76
|
+
expect { manager.with_connection(:invalid) }.to raise_error(Octoshark::Error::NoConnection)
|
77
|
+
end
|
78
|
+
|
79
|
+
context "using specific database", mysql2: true do
|
80
|
+
it "can use specific database" do
|
81
|
+
manager = Octoshark::ConnectionPoolsManager.new(mysql2_configs)
|
82
|
+
|
83
|
+
db1 = mysql2_configs[:db1]['database']
|
84
|
+
db2 = mysql2_configs[:db2]['database']
|
85
|
+
|
86
|
+
manager.with_connection(:db1, db1) do
|
87
|
+
expect(db(manager.current_connection)).to eq(db1)
|
88
|
+
|
89
|
+
manager.with_connection(:db2, db2) do
|
90
|
+
expect(db(manager.current_connection)).to eq(db2)
|
91
|
+
end
|
92
|
+
|
93
|
+
expect(db(manager.current_connection)).to eq(db1)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
it "sets database name on the connection" do
|
98
|
+
manager = Octoshark::ConnectionPoolsManager.new(mysql2_configs)
|
99
|
+
|
100
|
+
db1 = mysql2_configs[:db1]['database']
|
101
|
+
|
102
|
+
manager.with_connection(:db1, db1) do |connection|
|
103
|
+
expect(connection.database_name).to eq('octoshark_db1')
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
it "returns value from execution" do
|
108
|
+
manager = Octoshark::ConnectionPoolsManager.new(mysql2_configs)
|
109
|
+
db1 = mysql2_configs[:db1]['database']
|
110
|
+
result = manager.with_connection(:db1, db1) { |connection| connection.execute("SELECT 1") }.to_a
|
111
|
+
expect(result).to eq([[1]])
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "#disconnect!" do
|
117
|
+
it "removes all connections from connection pools" do
|
118
|
+
manager = Octoshark::ConnectionPoolsManager.new(configs)
|
119
|
+
|
120
|
+
manager.with_connection(:db1) { |connection| connection.execute("SELECT 1") }
|
121
|
+
expect(manager.find_connection_pool(:db1)).to be_connected
|
122
|
+
|
123
|
+
manager.disconnect!
|
124
|
+
|
125
|
+
expect(manager.find_connection_pool(:db1)).to_not be_connected
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe ".reset!" do
|
130
|
+
it "gets new connection pools ready to rock" do
|
131
|
+
manager = Octoshark::ConnectionPoolsManager.new(configs)
|
132
|
+
|
133
|
+
manager.with_connection(:db1) { |connection| connection.execute("SELECT 1") }
|
134
|
+
expect(manager.connection_pools[:db1].connections.count).to eq(1)
|
135
|
+
|
136
|
+
manager.reset!
|
137
|
+
|
138
|
+
expect(manager.connection_pools[:db1].connections.count).to eq(0)
|
139
|
+
|
140
|
+
manager.with_connection(:db1) { |connection| connection.execute("SELECT 1") }
|
141
|
+
expect(manager.connection_pools[:db1].connections.count).to eq(1)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
describe ".reset_connection_managers!" do
|
146
|
+
it "resets connection managers" do
|
147
|
+
manager = Octoshark::ConnectionPoolsManager.new(configs)
|
148
|
+
old_pools = manager.connection_pools.map(&:object_id)
|
149
|
+
|
150
|
+
Octoshark::ConnectionPoolsManager.reset_connection_managers!
|
151
|
+
|
152
|
+
new_pools = manager.connection_pools.map(&:object_id)
|
153
|
+
expect(new_pools).to_not eq(old_pools)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe ".disconnect!" do
|
158
|
+
it "disconnects connection managers" do
|
159
|
+
manager = Octoshark::ConnectionPoolsManager.new(configs)
|
160
|
+
|
161
|
+
Octoshark::ConnectionPoolsManager.disconnect!
|
162
|
+
|
163
|
+
expect(Octoshark::ConnectionPoolsManager.connection_managers).to be_blank
|
164
|
+
end
|
165
|
+
|
166
|
+
it "cleans old connections" do
|
167
|
+
manager = Octoshark::ConnectionPoolsManager.new(configs)
|
168
|
+
|
169
|
+
manager.with_connection(:db1) { |connection| connection.execute("SELECT 1") }
|
170
|
+
manager.with_connection(:db2) { |connection| connection.execute("SELECT 1") }
|
171
|
+
expect(manager.connection_pools[:db1].connections.count).to eq(1)
|
172
|
+
expect(manager.connection_pools[:db2].connections.count).to eq(1)
|
173
|
+
|
174
|
+
Octoshark::ConnectionPoolsManager.disconnect!
|
175
|
+
|
176
|
+
expect(manager.connection_pools[:db1].connections.count).to eq(0)
|
177
|
+
expect(manager.connection_pools[:db2].connections.count).to eq(0)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "connection manager" do
|
4
|
+
let(:config) { configs[:db1] }
|
5
|
+
|
6
|
+
describe "#identifier" do
|
7
|
+
it "has unique identifiers" do
|
8
|
+
manager1 = Octoshark::ConnectionManager.new
|
9
|
+
manager2 = Octoshark::ConnectionManager.new
|
10
|
+
expect(manager1.identifier).to_not eq(manager2.identifier)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#current_connection" do
|
15
|
+
it "returns last used connection as current one" do
|
16
|
+
manager = Octoshark::ConnectionManager.new
|
17
|
+
manager.with_connection(config) do |connection|
|
18
|
+
expect(manager.current_connection).to eq(connection)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it "raises error when no current connection" do
|
23
|
+
manager = Octoshark::ConnectionManager.new
|
24
|
+
|
25
|
+
expect { manager.current_connection }.to raise_error(Octoshark::Error::NoCurrentConnection)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#current_connection?" do
|
30
|
+
it "returns true if current one" do
|
31
|
+
manager = Octoshark::ConnectionManager.new
|
32
|
+
manager.with_connection(config) do
|
33
|
+
expect(manager.current_connection?).to be_truthy
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it "returns false if no current one" do
|
38
|
+
manager = Octoshark::ConnectionManager.new
|
39
|
+
|
40
|
+
expect(manager.current_connection?).to be_falsey
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#current_or_default_connection" do
|
45
|
+
it "returns current connection" do
|
46
|
+
manager = Octoshark::ConnectionManager.new
|
47
|
+
manager.with_connection(config) do |connection|
|
48
|
+
expect(manager.current_or_default_connection).to eq(connection)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it "returns default connection when no current connection" do
|
53
|
+
manager = Octoshark::ConnectionManager.new
|
54
|
+
|
55
|
+
expect(manager.current_or_default_connection).to eq(ActiveRecord::Base.connection_pool.connection)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#without_connection' do
|
60
|
+
it "can reset current connection temporarily inside nested connection block" do
|
61
|
+
manager = Octoshark::ConnectionManager.new
|
62
|
+
|
63
|
+
manager.with_connection(config) do
|
64
|
+
expect(db(manager.current_connection)).to eq("db1")
|
65
|
+
|
66
|
+
manager.without_connection do
|
67
|
+
expect { manager.current_connection }.to raise_error(Octoshark::Error::NoCurrentConnection)
|
68
|
+
end
|
69
|
+
|
70
|
+
expect(db(manager.current_connection)).to eq("db1")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: octoshark
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dalibor Nasevic
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-08-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -120,6 +120,7 @@ files:
|
|
120
120
|
- ".ruby-version"
|
121
121
|
- ".travis.yml"
|
122
122
|
- Appraisals
|
123
|
+
- CHANGELOG.md
|
123
124
|
- Gemfile
|
124
125
|
- LICENSE.txt
|
125
126
|
- README.md
|
@@ -132,12 +133,15 @@ files:
|
|
132
133
|
- lib/octoshark.rb
|
133
134
|
- lib/octoshark/active_record_extensions.rb
|
134
135
|
- lib/octoshark/connection_manager.rb
|
136
|
+
- lib/octoshark/connection_pools_manager.rb
|
137
|
+
- lib/octoshark/current_connection.rb
|
135
138
|
- lib/octoshark/error.rb
|
136
139
|
- lib/octoshark/version.rb
|
137
140
|
- octoshark.gemspec
|
138
141
|
- spec/octoshark/active_record_extensions_spec.rb
|
139
142
|
- spec/octoshark/connection_manager_spec.rb
|
140
|
-
- spec/
|
143
|
+
- spec/octoshark/connection_pools_manager_spec.rb
|
144
|
+
- spec/octoshark/current_connection_spec.rb
|
141
145
|
- spec/spec_helper.rb
|
142
146
|
- spec/support/config.yml.template
|
143
147
|
- spec/support/config.yml.travis
|
@@ -169,7 +173,8 @@ summary: Octoshark is an ActiveRecord connection switcher
|
|
169
173
|
test_files:
|
170
174
|
- spec/octoshark/active_record_extensions_spec.rb
|
171
175
|
- spec/octoshark/connection_manager_spec.rb
|
172
|
-
- spec/
|
176
|
+
- spec/octoshark/connection_pools_manager_spec.rb
|
177
|
+
- spec/octoshark/current_connection_spec.rb
|
173
178
|
- spec/spec_helper.rb
|
174
179
|
- spec/support/config.yml.template
|
175
180
|
- spec/support/config.yml.travis
|
data/spec/octoshark_spec.rb
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Octoshark do
|
4
|
-
|
5
|
-
describe ".reset_connection_managers!" do
|
6
|
-
it "resets connection managers" do
|
7
|
-
manager = Octoshark::ConnectionManager.new(configs)
|
8
|
-
old_pools = manager.connection_pools.map(&:object_id)
|
9
|
-
|
10
|
-
Octoshark.reset_connection_managers!
|
11
|
-
|
12
|
-
new_pools = manager.connection_pools.map(&:object_id)
|
13
|
-
expect(new_pools).to_not eq(old_pools)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
describe ".disconnect!" do
|
18
|
-
it "disconnects connection managers" do
|
19
|
-
manager = Octoshark::ConnectionManager.new(configs)
|
20
|
-
|
21
|
-
Octoshark.disconnect!
|
22
|
-
|
23
|
-
expect(Octoshark.connection_managers).to be_blank
|
24
|
-
end
|
25
|
-
|
26
|
-
it "cleans old connections" do
|
27
|
-
manager = Octoshark::ConnectionManager.new(configs)
|
28
|
-
|
29
|
-
manager.with_connection(:db1) { |connection| connection.execute("SELECT 1") }
|
30
|
-
manager.with_connection(:db2) { |connection| connection.execute("SELECT 1") }
|
31
|
-
expect(manager.connection_pools[:db1].connections.count).to eq(1)
|
32
|
-
expect(manager.connection_pools[:db2].connections.count).to eq(1)
|
33
|
-
|
34
|
-
Octoshark.disconnect!
|
35
|
-
|
36
|
-
expect(manager.connection_pools[:db1].connections.count).to eq(0)
|
37
|
-
expect(manager.connection_pools[:db2].connections.count).to eq(0)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|