my-sequel-synchrony 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []