connection-manager 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 68a246ccfb28bcc3f2a98ed195f85b27d7231465
4
+ data.tar.gz: 4cd7b0ff860ed9f20325d60dcfc4f3784ba4ce9b
5
+ SHA512:
6
+ metadata.gz: 71893e0a094fd795fe374d2bf908b666dd2d140f69d80a10f9967d9b521bc4f4e378518754ed20be2aee0acca51234985be6d1314c4ba60d1f1845d61d299ce6
7
+ data.tar.gz: d6e5faf6dbe381df88ea0992cbc5114f5911fe901434c3ff5683a3e2a8f73064a8c78c398cea7117fe12289891793d3db09a3f30fa0525688d128335a9a9624a
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.1
5
+ - 2.4.4
6
+ - 2.3.7
7
+ - 2.2.10
8
+ - 2.1.10
9
+ - 2.0.0-p648
10
+ before_install: gem install bundler -v 1.16.1
data/Dockerfile ADDED
@@ -0,0 +1,5 @@
1
+ FROM ruby:latest
2
+ RUN mkdir /usr/src/app
3
+ ADD . /usr/src/app/
4
+ WORKDIR /usr/src/app/
5
+ RUN ["bundle", "install"]
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/spuyet/connection_manager" }
4
+
5
+ # Specify your gem's dependencies in connection_manager.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,22 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ connection-manager (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ minitest (5.11.3)
10
+ rake (10.5.0)
11
+
12
+ PLATFORMS
13
+ ruby
14
+
15
+ DEPENDENCIES
16
+ bundler (~> 1.16)
17
+ connection-manager!
18
+ minitest (~> 5.0)
19
+ rake (~> 10.0)
20
+
21
+ BUNDLED WITH
22
+ 1.16.3
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Sébastien Puyet
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # ConnectionManager
2
+
3
+ [![Build Status](https://travis-ci.com/spuyet/connection_manager.svg?token=n5bcPpqTwxxsDsj9JB2x&branch=master)](https://travis-ci.com/spuyet/connection_manager)
4
+
5
+ A gem to manage your persistent connections.
6
+
7
+ ## Usage
8
+
9
+ Create a thread-safe connection manager:
10
+ ```ruby
11
+ $manager = ConnectionManager.new(timeout: 5)
12
+ ```
13
+
14
+ Push your connections at any time:
15
+ ```ruby
16
+ $manager.push("redis") { Redis.new }
17
+ ```
18
+
19
+ And use them according to your needs:
20
+ ```ruby
21
+ $manager.with("redis") do |redis|
22
+ redis.get("mykey")
23
+ end
24
+ ```
25
+
26
+ A metadata store per connection is also available allowing you to create custom behavior:
27
+ ```ruby
28
+ Thread.new do
29
+ $manager.with("redis") do |redis, metadata|
30
+ metadata[:last_used_at] = Time.now
31
+ redis.get("mykey")
32
+ end
33
+ end
34
+
35
+ Thread.new do
36
+ metadata = $manager.metadata("redis")
37
+ $manager.reset("redis") if metadata[:last_used_at] < 1.hour.ago
38
+ end
39
+ ```
40
+
41
+ ## Contributing
42
+
43
+ Bug reports and pull requests are welcome on GitHub at https://github.com/spuyet/connection_manager.
44
+
45
+ ## License
46
+
47
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
48
+
49
+ ## Author
50
+
51
+ Sébastien Puyet ([@spuyet](https://twitter.com/spuyet)): [sebastien@puyet.fr](mailto:sebastien@puyet.fr)
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "connection_manager"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,26 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "connection-manager/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "connection-manager"
8
+ spec.version = ConnectionManager::VERSION
9
+ spec.authors = ["Sébastien Puyet"]
10
+ spec.email = ["sebastien@puyet.fr"]
11
+
12
+ spec.summary = %q{A useful connection manager}
13
+ spec.homepage = "https://github.com/spuyet/connection_manager"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.16"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "minitest", "~> 5.0"
26
+ end
@@ -0,0 +1,173 @@
1
+ require "timeout"
2
+ require "connection-manager/version"
3
+ require "connection-manager/wrapper"
4
+
5
+ class ConnectionManager
6
+ class LockingError < StandardError; end
7
+
8
+ TIMEOUT_ARITY = Timeout.method(:timeout).arity
9
+
10
+ def initialize(**options)
11
+ @connection_timeout = options.fetch(:timeout, 0)
12
+ @manager_timeout = options.fetch(:manager_timeout, 0)
13
+ @connections = {}
14
+ @mutex = Mutex.new
15
+ end
16
+
17
+ def clear
18
+ execute do
19
+ connections.delete_if do |_, wrapper|
20
+ wrapper.synchronize do
21
+ wrapper.closed?
22
+ end
23
+ end
24
+ end
25
+ true
26
+ end
27
+
28
+ def close(key)
29
+ wrapper = execute do
30
+ connections[key.to_sym]
31
+ end
32
+ wrapper.synchronize do
33
+ wrapper.close
34
+ end if wrapper
35
+ end
36
+
37
+ def closed?(key)
38
+ wrapper = execute do
39
+ connections[key.to_sym]
40
+ end
41
+ wrapper.synchronize do
42
+ wrapper.closed?
43
+ end if wrapper
44
+ end
45
+
46
+ def delete(key)
47
+ execute do
48
+ wrapper = connections[key.to_sym]
49
+ wrapper.synchronize do
50
+ connections.delete(key.to_sym)
51
+ true
52
+ end if wrapper
53
+ end
54
+ end
55
+
56
+ def delete_if(&block)
57
+ execute do
58
+ connections.delete_if do |_, wrapper|
59
+ wrapper.synchronize do
60
+ block.call(wrapper.connection, wrapper.metadata)
61
+ end
62
+ end
63
+ end
64
+ true
65
+ end
66
+
67
+ def empty?
68
+ size == 0
69
+ end
70
+
71
+ def exists?(key)
72
+ execute do
73
+ connections.key? key.to_sym
74
+ end
75
+ end
76
+
77
+ def metadata(key)
78
+ wrapper = execute do
79
+ connections[key.to_sym]
80
+ end
81
+ wrapper.synchronize do
82
+ wrapper.metadata
83
+ end if wrapper
84
+ end
85
+
86
+ def open?(key)
87
+ wrapper = execute do
88
+ connections[key.to_sym]
89
+ end
90
+ wrapper.synchronize do
91
+ !wrapper.closed?
92
+ end if wrapper
93
+ end
94
+
95
+ def pop(key)
96
+ execute do
97
+ wrapper = connections[key.to_sym]
98
+ wrapper.synchronize do
99
+ connections.delete(key.to_sym).connection
100
+ end if wrapper
101
+ end
102
+ end
103
+
104
+ def push(key, **options, &block)
105
+ options[:timeout] ||= connection_timeout
106
+ execute do
107
+ previous_connection = connections[key.to_sym]
108
+ executor = if previous_connection
109
+ -> { previous_connection.synchronize { connections[key.to_sym] = Wrapper.new(options, &block) } }
110
+ else
111
+ -> { connections[key.to_sym] = Wrapper.new(options, &block) }
112
+ end
113
+ executor.call
114
+ end
115
+ true
116
+ end
117
+
118
+ def reset(key)
119
+ execute do
120
+ wrapper = connections[key.to_sym]
121
+ wrapper.synchronize do
122
+ wrapper.reset
123
+ end if wrapper
124
+ end
125
+ end
126
+
127
+ def shutdown
128
+ execute do
129
+ connections.values.map do |wrapper|
130
+ Thread.new do
131
+ # Keep compatibility with ruby < 2.4
132
+ Thread.current.report_on_exception = false if Thread.current.respond_to?(:report_on_exception=)
133
+ wrapper.synchronize { wrapper.close }
134
+ end
135
+ end.each(&:join)
136
+ end
137
+ true
138
+ end
139
+
140
+ def size
141
+ execute do
142
+ connections.keys.size
143
+ end
144
+ end
145
+
146
+ def with(key, **options, &block)
147
+ wrapper = execute do
148
+ connections[key.to_sym]
149
+ end
150
+ raise Connection::ClosedError if wrapper && wrapper.closed?
151
+
152
+ wrapper.synchronize(options) do
153
+ block.call(wrapper.connection, wrapper.metadata)
154
+ end if wrapper
155
+ end
156
+
157
+ private
158
+
159
+ attr_reader :connections, :mutex, :connection_timeout, :manager_timeout
160
+
161
+ def execute(&block)
162
+ Timeout.timeout(*lock_timeout_args) { mutex.lock }
163
+ block.call.tap do
164
+ mutex.unlock
165
+ end
166
+ end
167
+
168
+ def lock_timeout_args
169
+ [manager_timeout, LockingError].tap do |args|
170
+ args << "unable to acquire lock on time" if TIMEOUT_ARITY > 2
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,3 @@
1
+ class ConnectionManager
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,51 @@
1
+ class ConnectionManager::Connection
2
+ class LockingError < StandardError; end
3
+ class ClosedError < StandardError; end
4
+ end
5
+
6
+ class ConnectionManager::Wrapper
7
+ attr_reader :connection, :metadata
8
+
9
+ def initialize(options = {}, &block)
10
+ @closed = false
11
+ @close_method = options.fetch(:close_method, :close)
12
+ @initializer = block
13
+ @metadata = options.fetch(:metadata, {})
14
+ @mutex = Mutex.new
15
+ @timeout = options.fetch(:timeout, 0)
16
+ end
17
+
18
+ def close
19
+ return true if closed?
20
+ return false unless connection.respond_to?(close_method)
21
+ connection.public_send(close_method)
22
+ @closed = true
23
+ end
24
+
25
+ def closed?
26
+ @closed
27
+ end
28
+
29
+ def reset
30
+ @closed = false
31
+ @connection = nil
32
+ true
33
+ end
34
+
35
+ def synchronize(**options, &block)
36
+ @connection ||= initializer.call
37
+ timeout = options.fetch(:timeout, @timeout)
38
+ Timeout.timeout(*lock_timeout_args(timeout)) { mutex.lock }
39
+ block.call.tap { mutex.unlock }
40
+ end
41
+
42
+ private
43
+
44
+ attr_reader :close_method, :mutex, :initializer
45
+
46
+ def lock_timeout_args(timeout)
47
+ [timeout, ConnectionManager::Connection::LockingError].tap do |args|
48
+ args << "unable to acquire lock on time" if ConnectionManager::TIMEOUT_ARITY > 2
49
+ end
50
+ end
51
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: connection-manager
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sébastien Puyet
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-07-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ description:
56
+ email:
57
+ - sebastien@puyet.fr
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".travis.yml"
64
+ - Dockerfile
65
+ - Gemfile
66
+ - Gemfile.lock
67
+ - LICENSE.txt
68
+ - README.md
69
+ - Rakefile
70
+ - bin/console
71
+ - bin/setup
72
+ - connection-manager.gemspec
73
+ - lib/connection-manager.rb
74
+ - lib/connection-manager/version.rb
75
+ - lib/connection-manager/wrapper.rb
76
+ homepage: https://github.com/spuyet/connection_manager
77
+ licenses:
78
+ - MIT
79
+ metadata: {}
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ requirements: []
95
+ rubyforge_project:
96
+ rubygems_version: 2.5.1
97
+ signing_key:
98
+ specification_version: 4
99
+ summary: A useful connection manager
100
+ test_files: []