woolen_common 0.0.7 → 0.0.8

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: 4aeb6013d053af960f118edcd50414ed21b10ea1
4
- data.tar.gz: fbf8e57654e519f7602adc9bd6b5b136b2cf2d6b
3
+ metadata.gz: 9d7a51c6d6588cdc16871b1e1c56a1a121bc4cc8
4
+ data.tar.gz: aa457dfb8dca27ac9dcf78003e00645b052eff73
5
5
  SHA512:
6
- metadata.gz: c55cea41a15a9d15948873ae0628c581d8b55a3d1bfdf187afe19862ccbcaba30483c57ad6b80b624422bffafb70903fdf9cfbc274abb84d30646a657b74e642
7
- data.tar.gz: 561ea2799300a3b7bb891b3e86391683e7b8d153cd3659f5d39e1bfb2b4f88a625891b3ffe1f24c6d3dcf1afc8671aaf9b8e7041bf514d86bb75b726e43c9711
6
+ metadata.gz: 9ea5baa5799fec9538c99cc34ccf22f5b0ac4a653f7bde9b884627185190c0764cad947de16cc355cf462bfd14cb28108332ac59bed10176713183a532d8d27c
7
+ data.tar.gz: 71486006f8525b1aa2506ad216ebca1ee144fee753d1400b94e3ffdd5fa13e1a8951c05a4a0ae3dd39ca5a310368c331bc4428af2f9004326b6729c31a7177da
@@ -0,0 +1,68 @@
1
+ # Global monotonic clock from Concurrent Ruby 1.0.
2
+ # Copyright (c) Jerry D'Antonio -- released under the MIT license.
3
+ # Slightly modified; used with permission.
4
+ # https://github.com/ruby-concurrency/concurrent-ruby
5
+
6
+ require 'thread'
7
+
8
+ module WoolenCommon
9
+ class ConnectionPool
10
+
11
+ class_definition = Class.new do
12
+
13
+ if defined?(Process::CLOCK_MONOTONIC)
14
+
15
+ # @!visibility private
16
+ def get_time
17
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
18
+ end
19
+
20
+ elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
21
+
22
+ # @!visibility private
23
+ def get_time
24
+ java.lang.System.nanoTime() / 1_000_000_000.0
25
+ end
26
+
27
+ else
28
+
29
+ # @!visibility private
30
+ def initialize
31
+ @mutex = Mutex.new
32
+ @last_time = Time.now.to_f
33
+ end
34
+
35
+ # @!visibility private
36
+ def get_time
37
+ @mutex.synchronize do
38
+ now = Time.now.to_f
39
+ if @last_time < now
40
+ @last_time = now
41
+ else # clock has moved back in time
42
+ @last_time += 0.000_001
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ ##
50
+ # Clock that cannot be set and represents monotonic time since
51
+ # some unspecified starting point.
52
+ #
53
+ # @!visibility private
54
+ GLOBAL_MONOTONIC_CLOCK = class_definition.new
55
+ private_constant :GLOBAL_MONOTONIC_CLOCK
56
+
57
+ class << self
58
+ ##
59
+ # Returns the current time a tracked by the application monotonic clock.
60
+ #
61
+ # @return [Float] The current monotonic time when `since` not given else
62
+ # the elapsed monotonic time between `since` and the current time
63
+ def monotonic_time
64
+ GLOBAL_MONOTONIC_CLOCK.get_time
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,179 @@
1
+ require 'thread'
2
+ require 'timeout'
3
+ require_relative 'monotonic_time'
4
+
5
+ module WoolenCommon
6
+ ##
7
+ # Raised when you attempt to retrieve a connection from a pool that has been
8
+ # shut down.
9
+
10
+ class ConnectionPool::PoolShuttingDownError < RuntimeError;
11
+ end
12
+
13
+ ##
14
+ # The TimedStack manages a pool of homogeneous connections (or any resource
15
+ # you wish to manage). Connections are created lazily up to a given maximum
16
+ # number.
17
+
18
+ # Examples:
19
+ #
20
+ # ts = TimedStack.new(1) { MyConnection.new }
21
+ #
22
+ # # fetch a connection
23
+ # conn = ts.pop
24
+ #
25
+ # # return a connection
26
+ # ts.push conn
27
+ #
28
+ # conn = ts.pop
29
+ # ts.pop timeout: 5
30
+ # #=> raises Timeout::Error after 5 seconds
31
+
32
+ class ConnectionPool::TimedStack
33
+
34
+ ##
35
+ # Creates a new pool with +size+ connections that are created from the given
36
+ # +block+.
37
+
38
+ def initialize(size = 0, &block)
39
+ @create_block = block
40
+ @created = 0
41
+ @que = []
42
+ @max = size
43
+ @mutex = Mutex.new
44
+ @resource = ConditionVariable.new
45
+ @shutdown_block = nil
46
+ end
47
+
48
+ ##
49
+ # Returns +obj+ to the stack. +options+ is ignored in TimedStack but may be
50
+ # used by subclasses that extend TimedStack.
51
+
52
+ def push(obj, options = {})
53
+ @mutex.synchronize do
54
+ if @shutdown_block
55
+ @shutdown_block.call(obj)
56
+ else
57
+ store_connection obj, options
58
+ end
59
+
60
+ @resource.broadcast
61
+ end
62
+ end
63
+
64
+ alias_method :<<, :push
65
+
66
+ ##
67
+ # Retrieves a connection from the stack. If a connection is available it is
68
+ # immediately returned. If no connection is available within the given
69
+ # timeout a Timeout::Error is raised.
70
+ #
71
+ # +:timeout+ is the only checked entry in +options+ and is preferred over
72
+ # the +timeout+ argument (which will be removed in a future release). Other
73
+ # options may be used by subclasses that extend TimedStack.
74
+
75
+ def pop(timeout = 0.5, options = {})
76
+ options, timeout = timeout, 0.5 if Hash === timeout
77
+ timeout = options.fetch :timeout, timeout
78
+
79
+ deadline = ConnectionPool.monotonic_time + timeout
80
+ @mutex.synchronize do
81
+ loop do
82
+ raise ConnectionPool::PoolShuttingDownError if @shutdown_block
83
+ return fetch_connection(options) if connection_stored?(options)
84
+
85
+ connection = try_create(options)
86
+ return connection if connection
87
+
88
+ to_wait = deadline - ConnectionPool.monotonic_time
89
+ raise Timeout::Error, "Waited #{timeout} sec" if to_wait <= 0
90
+ @resource.wait(@mutex, to_wait)
91
+ end
92
+ end
93
+ end
94
+
95
+ ##
96
+ # Shuts down the TimedStack which prevents connections from being checked
97
+ # out. The +block+ is called once for each connection on the stack.
98
+
99
+ def shutdown(&block)
100
+ raise ArgumentError, "shutdown must receive a block" unless block_given?
101
+
102
+ @mutex.synchronize do
103
+ @shutdown_block = block
104
+ @resource.broadcast
105
+
106
+ shutdown_connections
107
+ end
108
+ end
109
+
110
+ ##
111
+ # Returns +true+ if there are no available connections.
112
+
113
+ def empty?
114
+ (@created - @que.length) >= @max
115
+ end
116
+
117
+ ##
118
+ # The number of connections available on the stack.
119
+
120
+ def length
121
+ @max - @created + @que.length
122
+ end
123
+
124
+ private
125
+
126
+ ##
127
+ # This is an extension point for TimedStack and is called with a mutex.
128
+ #
129
+ # This method must returns true if a connection is available on the stack.
130
+
131
+ def connection_stored?(options = nil)
132
+ !@que.empty?
133
+ end
134
+
135
+ ##
136
+ # This is an extension point for TimedStack and is called with a mutex.
137
+ #
138
+ # This method must return a connection from the stack.
139
+
140
+ def fetch_connection(options = nil)
141
+ @que.pop
142
+ end
143
+
144
+ ##
145
+ # This is an extension point for TimedStack and is called with a mutex.
146
+ #
147
+ # This method must shut down all connections on the stack.
148
+
149
+ def shutdown_connections(options = nil)
150
+ while connection_stored?(options)
151
+ conn = fetch_connection(options)
152
+ @shutdown_block.call(conn)
153
+ end
154
+ end
155
+
156
+ ##
157
+ # This is an extension point for TimedStack and is called with a mutex.
158
+ #
159
+ # This method must return +obj+ to the stack.
160
+
161
+ def store_connection(obj, options = nil)
162
+ @que.push obj
163
+ end
164
+
165
+ ##
166
+ # This is an extension point for TimedStack and is called with a mutex.
167
+ #
168
+ # This method must create a connection if and only if the total number of
169
+ # connections allowed has not been met.
170
+
171
+ def try_create(options = nil)
172
+ unless @created == @max
173
+ object = @create_block.call
174
+ @created += 1
175
+ object
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,149 @@
1
+ require_relative 'connection_pool/timed_stack'
2
+
3
+
4
+ # Generic connection pool class for e.g. sharing a limited number of network connections
5
+ # among many threads. Note: Connections are lazily created.
6
+ #
7
+ # Example usage with block (faster):
8
+ #
9
+ # @pool = ConnectionPool.new { Redis.new }
10
+ #
11
+ # @pool.with do |redis|
12
+ # redis.lpop('my-list') if redis.llen('my-list') > 0
13
+ # end
14
+ #
15
+ # Using optional timeout override (for that single invocation)
16
+ #
17
+ # @pool.with(timeout: 2.0) do |redis|
18
+ # redis.lpop('my-list') if redis.llen('my-list') > 0
19
+ # end
20
+ #
21
+ # Example usage replacing an existing connection (slower):
22
+ #
23
+ # $redis = ConnectionPool.wrap { Redis.new }
24
+ #
25
+ # def do_work
26
+ # $redis.lpop('my-list') if $redis.llen('my-list') > 0
27
+ # end
28
+ #
29
+ # Accepts the following options:
30
+ # - :size - number of connections to pool, defaults to 5
31
+ # - :timeout - amount of time to wait for a connection if none currently available, defaults to 5 seconds
32
+ #
33
+ module WoolenCommon
34
+ class ConnectionPool
35
+ DEFAULTS = { size: 5, timeout: 5 }
36
+
37
+ class Error < RuntimeError
38
+ end
39
+
40
+ def self.wrap(options, &block)
41
+ Wrapper.new(options, &block)
42
+ end
43
+
44
+ def initialize(options = {}, &block)
45
+ raise ArgumentError, 'Connection pool requires a block' unless block
46
+
47
+ options = DEFAULTS.merge(options)
48
+
49
+ @size = options.fetch(:size)
50
+ @timeout = options.fetch(:timeout)
51
+
52
+ @available = TimedStack.new(@size, &block)
53
+ @key = :"current-#{@available.object_id}"
54
+ end
55
+
56
+ if Thread.respond_to?(:handle_interrupt)
57
+
58
+ # MRI
59
+ def with(options = {})
60
+ Thread.handle_interrupt(Exception => :never) do
61
+ conn = checkout(options)
62
+ begin
63
+ Thread.handle_interrupt(Exception => :immediate) do
64
+ yield conn
65
+ end
66
+ ensure
67
+ checkin
68
+ end
69
+ end
70
+ end
71
+
72
+ else
73
+
74
+ # jruby 1.7.x
75
+ def with(options = {})
76
+ conn = checkout(options)
77
+ begin
78
+ yield conn
79
+ ensure
80
+ checkin
81
+ end
82
+ end
83
+
84
+ end
85
+
86
+ def checkout(options = {})
87
+ conn = if stack.empty?
88
+ timeout = options[:timeout] || @timeout
89
+ @available.pop(timeout: timeout)
90
+ else
91
+ stack.last
92
+ end
93
+
94
+ stack.push conn
95
+ conn
96
+ end
97
+
98
+ def checkin
99
+ conn = pop_connection # mutates stack, must be on its own line
100
+ @available.push(conn) if stack.empty?
101
+
102
+ nil
103
+ end
104
+
105
+ def shutdown(&block)
106
+ @available.shutdown(&block)
107
+ end
108
+
109
+ private
110
+
111
+ def pop_connection
112
+ if stack.empty?
113
+ raise ConnectionPool::Error, 'no connections are checked out'
114
+ else
115
+ stack.pop
116
+ end
117
+ end
118
+
119
+ def stack
120
+ ::Thread.current[@key] ||= []
121
+ end
122
+
123
+ class Wrapper < ::BasicObject
124
+ METHODS = [:with, :pool_shutdown]
125
+
126
+ def initialize(options = {}, &block)
127
+ @pool = options.fetch(:pool) { ::ConnectionPool.new(options, &block) }
128
+ end
129
+
130
+ def with(&block)
131
+ @pool.with(&block)
132
+ end
133
+
134
+ def pool_shutdown(&block)
135
+ @pool.shutdown(&block)
136
+ end
137
+
138
+ def respond_to?(id, *args)
139
+ METHODS.include?(id) || with { |c| c.respond_to?(id, *args) }
140
+ end
141
+
142
+ def method_missing(name, *args, &block)
143
+ with do |connection|
144
+ connection.send(name, *args, &block)
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
@@ -2,7 +2,7 @@
2
2
  begin
3
3
  require 'net/ssh' rescue nil
4
4
  require 'net/sftp' rescue nil
5
- require 'connection_pool'
5
+ require "#{File.join(File.dirname(__FILE__), 'connection_pool')}"
6
6
  require "#{File.join(File.dirname(__FILE__), 'logger')}"
7
7
  module WoolenCommon
8
8
  class SshProxyPool
@@ -32,7 +32,7 @@ begin
32
32
  end
33
33
 
34
34
  def get_pool
35
- @ssh_connection_pool ||= ::ConnectionPool.new({:size => @max_ssh, :timeout => @time_out}) do
35
+ @ssh_connection_pool ||= ::WoolenCommon::ConnectionPool.new({:size => @max_ssh, :timeout => @time_out}) do
36
36
  debug "ip:#{@ip},@password:#{@password},@port:#{@port}"
37
37
  ::WoolenCommon::SshProxy.new(@ip, @user, :password => @password, :port => @port)
38
38
  end
@@ -1,3 +1,3 @@
1
1
  module WoolenCommon
2
- VERSION = '0.0.7'
2
+ VERSION = '0.0.8'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: woolen_common
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - just_woolen
@@ -66,20 +66,6 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: connection_pool
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :runtime
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
69
  description: The common helper for dev in ruby
84
70
  email:
85
71
  - just_woolen@qq.com
@@ -109,6 +95,9 @@ files:
109
95
  - lib/woolen_common/cache.rb
110
96
  - lib/woolen_common/common_helper.rb
111
97
  - lib/woolen_common/config_manager.rb
98
+ - lib/woolen_common/connection_pool.rb
99
+ - lib/woolen_common/connection_pool/monotonic_time.rb
100
+ - lib/woolen_common/connection_pool/timed_stack.rb
112
101
  - lib/woolen_common/drb_helper.rb
113
102
  - lib/woolen_common/ffi/win32_kernel32.rb
114
103
  - lib/woolen_common/logger.rb