Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
primitive fiber aware connection pool
- Loading branch information
Showing
8 changed files
with
149 additions
and
92 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
# Based on EM::Synchrony::ConnectionPool | ||
|
||
require 'swift/synchrony' | ||
|
||
module Swift | ||
class FiberConnectionPool | ||
|
||
def initialize opts, &block | ||
@reserved = {} # map of in-progress connections | ||
@available = [] # pool of free connections | ||
@pending = [] # pending reservations (FIFO) | ||
|
||
opts[:size].times do | ||
@available.push(block.call) | ||
end | ||
end | ||
|
||
private | ||
# Choose first available connection and pass it to the supplied | ||
# block. This will block indefinitely until there is an available | ||
# connection to service the request. | ||
def __reserve__ | ||
id = "#{Fiber.current.object_id}:#{rand}" | ||
fiber = Fiber.current | ||
begin | ||
yield acquire(id, fiber) | ||
ensure | ||
release(id) | ||
end | ||
end | ||
|
||
# Acquire a lock on a connection and assign it to executing fiber | ||
# - if connection is available, pass it back to the calling block | ||
# - if pool is full, yield the current fiber until connection is available | ||
def acquire id, fiber | ||
if conn = @available.pop | ||
@reserved[id] = conn | ||
else | ||
Fiber.yield @pending.push(fiber) | ||
acquire(id, fiber) | ||
end | ||
end | ||
|
||
# Release connection assigned to the supplied fiber and | ||
# resume any other pending connections (which will | ||
# immediately try to run acquire on the pool) | ||
def release(id) | ||
@available.push(@reserved.delete(id)) | ||
if pending = @pending.shift | ||
pending.resume | ||
end | ||
end | ||
|
||
# Allow the pool to behave as the underlying connection | ||
def method_missing method, *args, &blk | ||
__reserve__ do |conn| | ||
conn.__send__(method, *args, &blk) | ||
end | ||
end | ||
end # FiberConnectionPool | ||
|
||
def self.setup_connection_pool size, name, klass, *args | ||
(@repositories ||= {})[name] = FiberConnectionPool.new(size: size) {klass.new(*args)} | ||
end | ||
|
||
class Adapter::Sql | ||
def serialized_transaction &block | ||
Swift.scopes.push(self) | ||
execute('begin') | ||
res = yield(self) | ||
execute('commit') | ||
res | ||
rescue => e | ||
execute('rollback') | ||
raise e | ||
ensure | ||
Swift.scopes.pop | ||
end | ||
end # Adapter::Sql | ||
end # Swift |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
require 'helper' | ||
|
||
describe 'fiber connection pool' do | ||
before do | ||
skip 'swift/synchrony re-defines Adapter#execute' | ||
|
||
require 'swift/fiber_connection_pool' | ||
EM.synchrony do | ||
Swift.setup(:default, Swift::Adapter::Postgres, db: 'swift_test') | ||
Swift.db.execute('drop table if exists users') | ||
Swift.db.execute('create table users(id serial primary key, name text)') | ||
|
||
@user = Class.new(Swift::Record) do | ||
store :users | ||
attribute :id, Swift::Type::Integer, key: true, serial: true | ||
attribute :name, Swift::Type::String | ||
end | ||
|
||
10.times { @user.create(name: 'test') } | ||
EM.stop | ||
end | ||
end | ||
|
||
it 'can synchronize queries across fibers' do | ||
EM.run do | ||
Swift.setup_connection_pool 2, :default, Swift::Adapter::Postgres, db: 'swift_test' | ||
|
||
@counts = [] | ||
5.times do | ||
EM.synchrony do | ||
@counts << @user.execute('select * from users').selected_rows | ||
end | ||
end | ||
|
||
EM.add_timer(0.2) { EM.stop } | ||
end | ||
|
||
assert_equal 5, @counts.size | ||
assert_equal [10], @counts.uniq | ||
end | ||
end |