my-sequel-synchrony 0.0.1
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.
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +75 -0
- data/Rakefile +2 -0
- data/lib/my-sequel-synchrony.rb +3 -0
- data/lib/my-sequel-synchrony/adapter.rb +55 -0
- data/lib/my-sequel-synchrony/connection_pool.rb +294 -0
- data/lib/my-sequel-synchrony/version.rb +7 -0
- data/my-sequel-synchrony.gemspec +23 -0
- metadata +118 -0
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Ilya Maykov
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# my-sequel-synchrony
|
2
|
+
|
3
|
+
A fiber-aware MySQL adapter for [Sequel](https://github.com/jeremyevans/sequel) that works with
|
4
|
+
[em-synchrony](https://github.com/igrigorik/em-synchrony).
|
5
|
+
Uses the Mysql2::EM::Client client with the em-synchrony extensions.
|
6
|
+
|
7
|
+
Note: this code is in an early stage of active development and has not been battle-tested in any
|
8
|
+
production environments. For now, use at your own risk, and please report any bugs you find!
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
gem 'my-sequel-synchrony'
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
$ gem install my-sequel-synchrony
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
Inside an `EM.synchrony` block or a synchrony-enabled web server (such as
|
27
|
+
[sinatra-synchrony](https://github.com/kyledrake/sinatra-synchrony)), simply create the
|
28
|
+
Sequel connection as usual, and specify the `:adapter => :mysql2synchrony` option:
|
29
|
+
|
30
|
+
@database = Sequel.connect(
|
31
|
+
:adapter => :mysql2synchrony,
|
32
|
+
:hostname => ...,
|
33
|
+
...)
|
34
|
+
|
35
|
+
And use it just like you would normally use a Sequel database object! If you have multiple DB shards
|
36
|
+
(either master/slaves, or data sharded across multiple masters), you can specify them by passing in
|
37
|
+
a `:servers` option just like you would with any other Sequel adapter that supports sharding (see
|
38
|
+
the [Sequel sharding doc](https://github.com/jeremyevans/sequel/blob/master/doc/sharding.rdoc)).
|
39
|
+
|
40
|
+
You can even run queries in parallel with `FiberIterator`:
|
41
|
+
|
42
|
+
require 'em-synchrony/fiber_iterator'
|
43
|
+
|
44
|
+
@shard_ids = [ :master, :slave1, :slave2 ]
|
45
|
+
|
46
|
+
# Assuming we're in a sinatra-synchrony webapp, this will run a simple "am i connected?" check
|
47
|
+
# on every shard in parallel and return 200 or 500 depending on the result.
|
48
|
+
get "/healthz" do
|
49
|
+
shard_errors = {}
|
50
|
+
EM::Synchrony::FiberIterator.new(@shard_ids, @shard_ids.size).each do |shard_id|
|
51
|
+
begin
|
52
|
+
@db.run("select 1;", :server => shard_id)
|
53
|
+
rescue Exception => e
|
54
|
+
shard_errors[shard_id] = e
|
55
|
+
end
|
56
|
+
end
|
57
|
+
halt 200, "All DB shards ok" if shard_errors.empty?
|
58
|
+
|
59
|
+
message = "#{shard_errors.size} DB shards had errors:\n"
|
60
|
+
message += shard_errors.map { |shard_id, error| "#{shard_id}: #{e.message}" }.join("\n")
|
61
|
+
halt 500, message
|
62
|
+
end
|
63
|
+
|
64
|
+
## Contributing
|
65
|
+
|
66
|
+
1. Fork it
|
67
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
68
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
69
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
70
|
+
5. Create new Pull Request
|
71
|
+
|
72
|
+
## Authors
|
73
|
+
|
74
|
+
* Bo Chen (bochen.chen@gmail.com)
|
75
|
+
* Ilya Maykov (ivmaykov@gmail.com)
|
data/Rakefile
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# Sequel adapter for MySQL that uses the Mysql2::EM::Client and a fiber-aware connection pool.
|
2
|
+
#
|
3
|
+
# See also:
|
4
|
+
# https://github.com/jeremyevans/sequel/blob/master/lib/sequel/adapters/mysql2.rb
|
5
|
+
# https://github.com/brianmario/mysql2/blob/master/lib/mysql2/em.rb
|
6
|
+
|
7
|
+
require "sequel"
|
8
|
+
require "sequel/adapters/mysql2"
|
9
|
+
require "em-synchrony/mysql2"
|
10
|
+
require "my-sequel-synchrony/connection_pool"
|
11
|
+
|
12
|
+
module Sequel::Mysql2::Synchrony
|
13
|
+
class Database < ::Sequel::Mysql2::Database
|
14
|
+
set_adapter_scheme :mysql2synchrony
|
15
|
+
|
16
|
+
def initialize(opts={})
|
17
|
+
pool_class = if opts.include?(:servers)
|
18
|
+
::Sequel::Mysql2::Synchrony::ShardedConnectionPool
|
19
|
+
else
|
20
|
+
::Sequel::Mysql2::Synchrony::ConnectionPool
|
21
|
+
end
|
22
|
+
super(opts.merge(:pool_class => pool_class, :connection_handling => :queue))
|
23
|
+
end
|
24
|
+
|
25
|
+
# Connect to the database. Similar to the Sequel::Mysql2#connect method, but uses the eventmachine
|
26
|
+
# client.
|
27
|
+
#
|
28
|
+
# NOTE: this code is mostly a copy of Sequel::Mysql2#connect() method, with the only difference being
|
29
|
+
# the connection class (Mysql::EM::Client instead of Mysql2::Client). This code could be removed if
|
30
|
+
# the parent class accepted a :use_eventmachine option.
|
31
|
+
def connect(server)
|
32
|
+
opts = server_opts(server)
|
33
|
+
opts[:host] ||= 'localhost'
|
34
|
+
opts[:username] ||= opts[:user]
|
35
|
+
opts[:flags] = ::Mysql2::Client::FOUND_ROWS if ::Mysql2::Client.const_defined?(:FOUND_ROWS)
|
36
|
+
conn = ::Mysql2::EM::Client.new(opts)
|
37
|
+
conn.query_options.merge!(:symbolize_keys=>true, :cache_rows=>false)
|
38
|
+
|
39
|
+
sqls = mysql_connection_setting_sqls
|
40
|
+
|
41
|
+
# Set encoding a slightly different way after connecting,
|
42
|
+
# in case the READ_DEFAULT_GROUP overrode the provided encoding.
|
43
|
+
# Doesn't work across implicit reconnects, but Sequel doesn't turn on
|
44
|
+
# that feature.
|
45
|
+
if encoding = opts[:encoding] || opts[:charset]
|
46
|
+
sqls.unshift("SET NAMES #{conn.escape(encoding.to_s)}")
|
47
|
+
end
|
48
|
+
|
49
|
+
sqls.each{|sql| log_yield(sql){conn.query(sql)}}
|
50
|
+
|
51
|
+
add_prepared_statements_cache(conn)
|
52
|
+
conn
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,294 @@
|
|
1
|
+
require "fiber"
|
2
|
+
require "set"
|
3
|
+
require "sequel"
|
4
|
+
require "sequel/adapters/mysql2"
|
5
|
+
|
6
|
+
# A Fiber-aware Sequel::ConnectionPool that works with EM::Synchrony! This version is not shard-aware,
|
7
|
+
# that may be a TODO in the future.
|
8
|
+
module Sequel::Mysql2::Synchrony
|
9
|
+
class ConnectionPool < ::Sequel::ConnectionPool
|
10
|
+
# The maximum number of connections this pool will create (per shard/server
|
11
|
+
# if sharding).
|
12
|
+
attr_reader :max_size
|
13
|
+
|
14
|
+
DEFAULT_MAX_SIZE = 4
|
15
|
+
|
16
|
+
# The following additional options are respected:
|
17
|
+
# * :connection_handling - Set how to handle available connections. By default,
|
18
|
+
# uses a a stack for performance. Can be set to :queue to use a queue, which reduces
|
19
|
+
# the chances of connections becoming stale. Can also be set to :disconnect, which will
|
20
|
+
# disconnect after every query or transaction.
|
21
|
+
#
|
22
|
+
# * :max_connections - The maximum number of connections the connection pool
|
23
|
+
# will open (default 4)
|
24
|
+
def initialize(opts = {}, &block)
|
25
|
+
super
|
26
|
+
@max_size = Integer(opts[:max_connections] || DEFAULT_MAX_SIZE)
|
27
|
+
raise(Sequel::Error, ':max_connections must be positive') if @max_size < 1
|
28
|
+
@connection_handling = opts[:connection_handling]
|
29
|
+
@available_connections = []
|
30
|
+
@waiting_fibers = []
|
31
|
+
@in_use_connections = {}
|
32
|
+
@pending_disconnects = Set.new
|
33
|
+
# TODO(ilyam): remove the debug prints once this is rock-solid and has good tests etc.
|
34
|
+
@debug = opts[:debug]
|
35
|
+
end
|
36
|
+
|
37
|
+
# The total number of open connections, either available or in-use.
|
38
|
+
def size
|
39
|
+
@in_use_connections.size + @available_connections.size
|
40
|
+
end
|
41
|
+
|
42
|
+
# Removes all connections currently available, optionally
|
43
|
+
# yielding each connection to the given block. This method has the effect of
|
44
|
+
# disconnecting from the database, assuming that no connections are currently
|
45
|
+
# being used. Schedules all in-use connections to be disconnected next time they are
|
46
|
+
# released into the available pool.
|
47
|
+
#
|
48
|
+
# After a disconnect, when connections are requested using #hold, the connection pool will
|
49
|
+
# create new connections to the database.
|
50
|
+
def disconnect(opts={}, &block)
|
51
|
+
if_debug? { dputs "disconnect(opts = #{opts.inspect})! Current fiber = #{Fiber.current.inspect}" }
|
52
|
+
connections_to_disconnect, @available_connections = @available_connections, []
|
53
|
+
@pending_disconnects += @in_use_connections.values.to_set
|
54
|
+
block ||= @disconnection_proc
|
55
|
+
connections_to_disconnect.each do |conn|
|
56
|
+
disconnect_conn(conn, opts, block)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Chooses the first available connection, or if none are
|
61
|
+
# available, creates a new connection. Passes the connection to the supplied
|
62
|
+
# block:
|
63
|
+
#
|
64
|
+
# pool.hold {|conn| conn.execute('DROP TABLE posts')}
|
65
|
+
#
|
66
|
+
# Pool#hold is re-entrant, meaning it can be called recursively in
|
67
|
+
# the same fiber safely.
|
68
|
+
#
|
69
|
+
# If no connection is immediately available and the pool is already using the maximum
|
70
|
+
# number of connections, Pool#hold will block the current fiber until a connection
|
71
|
+
# is available.
|
72
|
+
def hold(server = nil)
|
73
|
+
if_debug? { dputs "hold(server = #{server.inspect})! Current fiber = #{Fiber.current.inspect}" }
|
74
|
+
fiber, conn = Fiber.current, nil
|
75
|
+
if conn = @in_use_connections[fiber]
|
76
|
+
return yield(conn)
|
77
|
+
end
|
78
|
+
|
79
|
+
begin
|
80
|
+
conn = acquire(fiber)
|
81
|
+
yield conn
|
82
|
+
rescue Sequel::DatabaseDisconnectError => error
|
83
|
+
disconnect_conn(conn) if conn
|
84
|
+
conn = nil
|
85
|
+
@in_use_connections.delete(fiber)
|
86
|
+
resume_next_waiting_fiber
|
87
|
+
raise error
|
88
|
+
ensure
|
89
|
+
release(fiber) if conn
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
# Assigns a connection to the given fiber. Suspends the fiber until a connection is available.
|
96
|
+
# Suspended fibers are served in FIFO order.
|
97
|
+
def acquire(fiber)
|
98
|
+
if_debug? { dputs "acquire(fiber = #{fiber.inspect})! Current fiber = #{Fiber.current.inspect}" }
|
99
|
+
conn = nil
|
100
|
+
while conn.nil?
|
101
|
+
if conn = available
|
102
|
+
@in_use_connections[fiber] = conn
|
103
|
+
else
|
104
|
+
wait_for_connection(fiber)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
conn
|
108
|
+
end
|
109
|
+
|
110
|
+
# Releases the connection assigned to the given fiber back to the pool. May actually disconnect the
|
111
|
+
# connection in one of two cases:
|
112
|
+
# 1) A Pool#disconnect() was called while this connection was in use, which added it to the
|
113
|
+
# @pending_disconnects set.
|
114
|
+
# 2) The pool was initialized with :connection_handling => :disconnect
|
115
|
+
def release(fiber)
|
116
|
+
if_debug? { dputs "release(fiber = #{fiber.inspect})! Current fiber = #{Fiber.current.inspect}" }
|
117
|
+
conn = @in_use_connections.delete(fiber)
|
118
|
+
raise(Sequel::Error, "release called on fiber that doesn't own a connection!") unless conn
|
119
|
+
|
120
|
+
if (@connection_handling == :disconnect || @pending_disconnects.include?(conn))
|
121
|
+
disconnect_conn(conn)
|
122
|
+
elsif @connection_handling == :queue
|
123
|
+
@available_connections.unshift(conn)
|
124
|
+
else
|
125
|
+
@available_connections << conn
|
126
|
+
end
|
127
|
+
resume_next_waiting_fiber
|
128
|
+
end
|
129
|
+
|
130
|
+
# Disconnects the connection and removes it from @pending_disconnects if it was in the set.
|
131
|
+
def disconnect_conn(conn, opts={}, &block)
|
132
|
+
if_debug? do
|
133
|
+
dputs "disconnect_conn(conn = #{conn.inspect}, opts=#{opts.inspect}, block = #{block.inspect})! "\
|
134
|
+
"Current fiber = #{Fiber.current.inspect}"
|
135
|
+
end
|
136
|
+
block ||= @disconnection_proc
|
137
|
+
block.call(conn) if block
|
138
|
+
@pending_disconnects.delete(conn)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Suspends the given fiber until a connection becomes available.
|
142
|
+
def wait_for_connection(fiber)
|
143
|
+
if_debug? do
|
144
|
+
dputs "wait_for_connection(fiber = #{fiber.inspect})! Current fiber = #{Fiber.current.inspect}"
|
145
|
+
end
|
146
|
+
Fiber.yield @waiting_fibers.push fiber
|
147
|
+
end
|
148
|
+
|
149
|
+
# Resumes the next fiber waiting for a connection
|
150
|
+
def resume_next_waiting_fiber
|
151
|
+
if_debug? { dputs "resume_next_waiting_fiber()! Current fiber = #{Fiber.current.inspect}" }
|
152
|
+
if pending_fiber = @waiting_fibers.shift
|
153
|
+
if_debug? { dputs " => pending_fiber = #{pending_fiber.inspect}" }
|
154
|
+
pending_fiber.resume
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# Returns an available connection. If no connection is available, tries to create a new connection if the
|
159
|
+
# current pool size is smaller than the max pool size. May return nil.
|
160
|
+
def available
|
161
|
+
if_debug? { dputs "available()! Current fiber = #{Fiber.current.inspect}" }
|
162
|
+
@available_connections.pop || make_new(DEFAULT_SERVER)
|
163
|
+
end
|
164
|
+
|
165
|
+
# Alias the default make_new method, so subclasses can call it directly.
|
166
|
+
alias default_make_new make_new
|
167
|
+
|
168
|
+
# Creates a new connection to the given server if the size of the pool for
|
169
|
+
# the server is less than the maximum size of the pool.
|
170
|
+
def make_new(server)
|
171
|
+
if_debug? { dputs "make_new(server = #{server.inspect})! Current fiber = #{Fiber.current.inspect}" }
|
172
|
+
if size >= @max_size
|
173
|
+
dead_fibers = @in_use_connections.keys.reject { |fiber| fiber.alive? }
|
174
|
+
dead_fibers.each { |fiber| release(fiber) }
|
175
|
+
end
|
176
|
+
size < @max_size ? super(server) : nil
|
177
|
+
end
|
178
|
+
|
179
|
+
# Calls the given block if @debug is true
|
180
|
+
def if_debug?(&block)
|
181
|
+
block.call if @debug
|
182
|
+
end
|
183
|
+
|
184
|
+
# like puts, but goes to STDERR and prepends the string "DEBUG: "
|
185
|
+
def dputs(*args)
|
186
|
+
args.each { |arg| STDERR.puts "DEBUG: #{arg}" }
|
187
|
+
nil
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# The sharded version of the em-synchrony connection pool is automatically used if a :servers option is
|
192
|
+
# passed to Sequel.connect(). It keeps a mapping from server ids to non-sharded connection pools, and
|
193
|
+
# delegates calls to them as needed.
|
194
|
+
class ShardedConnectionPool < ::Sequel::ConnectionPool
|
195
|
+
# The following additional options are respected:
|
196
|
+
# * :servers - A hash of servers to use. Keys should be symbols. If not
|
197
|
+
# present, will use a single :default server. The server name symbol will
|
198
|
+
# be passed to the connection_proc.
|
199
|
+
# The values in the hash should be per-server specific options (such as different :hostname or :database)
|
200
|
+
#
|
201
|
+
# * :servers_hash - The base hash to use for the servers. By default,
|
202
|
+
# Sequel uses Hash.new(:default). You can use a hash with a default proc
|
203
|
+
# that raises an error if you want to catch all cases where a nonexistent
|
204
|
+
# server is used.
|
205
|
+
def initialize(opts = {}, &block)
|
206
|
+
# NOTE: @initialize_opts is passed to the internal non-sharded connection pools when they are created.
|
207
|
+
@initialize_opts = opts.dup
|
208
|
+
@initialize_opts.delete(:servers)
|
209
|
+
@initialize_opts.delete(:servers_hash)
|
210
|
+
@initialize_block = block
|
211
|
+
|
212
|
+
@server_ids = opts[:servers_hash] || Hash.new(:default)
|
213
|
+
@server_pools = {}
|
214
|
+
add_servers([:default] + (opts[:servers] ? opts[:servers].keys : []))
|
215
|
+
end
|
216
|
+
|
217
|
+
# Adds new servers to the connection pool. Primarily used in conjunction with master/slave
|
218
|
+
# or shard configurations. Allows for dynamic expansion of the potential slaves/shards
|
219
|
+
# at runtime. servers argument should be an array of symbols.
|
220
|
+
def add_servers(servers)
|
221
|
+
servers.each do |server|
|
222
|
+
next if @server_ids.has_key?(server)
|
223
|
+
|
224
|
+
@server_ids[server] = server
|
225
|
+
@server_pools[server] = ::Sequel::Mysql2::Synchrony::ConnectionPool.new(@initialize_opts) do
|
226
|
+
@initialize_block.call(server) if @initialize_block
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
# Remove servers from the connection pool. Primarily used in conjunction with master/slave
|
232
|
+
# or shard configurations. Similar to disconnecting from all given servers,
|
233
|
+
# except that after it is used, future requests for the server will use the
|
234
|
+
# :default server instead.
|
235
|
+
def remove_servers(servers)
|
236
|
+
raise(Sequel::Error, "cannot remove default server") if servers.include?(:default)
|
237
|
+
pools_to_disconnect = []
|
238
|
+
servers.each do |server|
|
239
|
+
pool = @server_pools.delete(server)
|
240
|
+
pools_to_disconnect << pool if pool
|
241
|
+
end
|
242
|
+
pools_to_disconnect.each { |pool| pool.disconnect }
|
243
|
+
end
|
244
|
+
|
245
|
+
# The total number of connections opened for the given server, should
|
246
|
+
# be equal to available_connections.length + allocated.length. Nonexistent
|
247
|
+
# servers will return the created count of the default server.
|
248
|
+
def size(server = :default)
|
249
|
+
@server_pools[@server_ids[server]].size
|
250
|
+
end
|
251
|
+
|
252
|
+
# Removes all connections currently available on all servers, optionally
|
253
|
+
# yielding each connection to the given block. This method has the effect of
|
254
|
+
# disconnecting from the database, assuming that no connections are currently
|
255
|
+
# being used. If connections are being used, they are scheduled to be
|
256
|
+
# disconnected as soon as they are returned to the pool.
|
257
|
+
#
|
258
|
+
# Once a connection is requested using #hold, the connection pool
|
259
|
+
# creates new connections to the database. Options:
|
260
|
+
# * :server - Should be a symbol specifing the server to disconnect from,
|
261
|
+
# or an array of symbols to specify multiple servers. If not specified,
|
262
|
+
# then all servers are disconnected.
|
263
|
+
def disconnect(opts = {}, &block)
|
264
|
+
servers_to_disconnect = @server_pools.keys
|
265
|
+
servers_to_disconnect &= Array(opts[:servers]) if opts[:servers]
|
266
|
+
servers_to_disconnect.each do |server_id|
|
267
|
+
@server_pools[server_id].disconnect(opts, &block)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
# Chooses the first available connection to the given server, or if none are
|
272
|
+
# available, creates a new connection. Passes the connection to the supplied
|
273
|
+
# block:
|
274
|
+
#
|
275
|
+
# pool.hold {|conn| conn.execute('DROP TABLE posts')}
|
276
|
+
#
|
277
|
+
# Pool#hold is re-entrant, meaning it can be called recursively in
|
278
|
+
# the same thread without blocking.
|
279
|
+
#
|
280
|
+
# If no connection is immediately available and the pool is already using the maximum
|
281
|
+
# number of connections, Pool#hold will block until a connection
|
282
|
+
# is available or the timeout expires. If the timeout expires before a
|
283
|
+
# connection can be acquired, a Sequel::PoolTimeout is
|
284
|
+
# raised.
|
285
|
+
def hold(server = :default, &block)
|
286
|
+
@server_pools[@server_ids[server]].hold(nil, &block)
|
287
|
+
end
|
288
|
+
|
289
|
+
# Return an array of symbols for servers in the connection pool.
|
290
|
+
def servers
|
291
|
+
@server_ids.keys
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/my-sequel-synchrony/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = [ "Ilya Maykov" ]
|
6
|
+
gem.email = [ "ivmaykov@gmail.com" ]
|
7
|
+
gem.description = "A fiber-aware MySQL adapter for Sequel that works with em-synchrony"
|
8
|
+
gem.summary = "A fiber-aware MySQL adapter for Sequel that works with em-synchrony"
|
9
|
+
gem.homepage = "https://github.com/ivmaykov/my-sequel-synchrony"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
# gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
# gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "my-sequel-synchrony"
|
15
|
+
gem.require_paths = [ "lib" ]
|
16
|
+
gem.version = Sequel::Mysql2::Synchrony::VERSION
|
17
|
+
gem.required_ruby_version = " >= 1.9.2"
|
18
|
+
|
19
|
+
gem.add_dependency("sequel", "~> 3.39.0")
|
20
|
+
gem.add_dependency("mysql2", "~> 0.3.11")
|
21
|
+
gem.add_dependency("em-synchrony", "~> 1.0.0")
|
22
|
+
gem.add_dependency("rake", "~> 0.8.7")
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: my-sequel-synchrony
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ilya Maykov
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-09-15 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: sequel
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.39.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 3.39.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: mysql2
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.3.11
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.3.11
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: em-synchrony
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.0.0
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.0.0
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rake
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 0.8.7
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 0.8.7
|
78
|
+
description: A fiber-aware MySQL adapter for Sequel that works with em-synchrony
|
79
|
+
email:
|
80
|
+
- ivmaykov@gmail.com
|
81
|
+
executables: []
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files: []
|
84
|
+
files:
|
85
|
+
- Gemfile
|
86
|
+
- LICENSE
|
87
|
+
- README.md
|
88
|
+
- Rakefile
|
89
|
+
- lib/my-sequel-synchrony.rb
|
90
|
+
- lib/my-sequel-synchrony/adapter.rb
|
91
|
+
- lib/my-sequel-synchrony/connection_pool.rb
|
92
|
+
- lib/my-sequel-synchrony/version.rb
|
93
|
+
- my-sequel-synchrony.gemspec
|
94
|
+
homepage: https://github.com/ivmaykov/my-sequel-synchrony
|
95
|
+
licenses: []
|
96
|
+
post_install_message:
|
97
|
+
rdoc_options: []
|
98
|
+
require_paths:
|
99
|
+
- lib
|
100
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ! '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: 1.9.2
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
+
none: false
|
108
|
+
requirements:
|
109
|
+
- - ! '>='
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
requirements: []
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 1.8.24
|
115
|
+
signing_key:
|
116
|
+
specification_version: 3
|
117
|
+
summary: A fiber-aware MySQL adapter for Sequel that works with em-synchrony
|
118
|
+
test_files: []
|