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 ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in my-sequel-synchrony.gemspec
4
+ gemspec
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,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,3 @@
1
+ require "my-sequel-synchrony/adapter"
2
+ require "my-sequel-synchrony/connection_pool"
3
+ require "my-sequel-synchrony/version"
@@ -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,7 @@
1
+ module Sequel
2
+ module Mysql2
3
+ module Synchrony
4
+ VERSION = "0.0.1"
5
+ end
6
+ end
7
+ 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: []