fiber_connection_pool 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1358d4ff597e542badbfb30345fdef7b203406d7
4
- data.tar.gz: ea906574ace7273b3b95d50a29c6cbe63c994d53
3
+ metadata.gz: 6d5107300e5f00c78557fabe1ef21a964bc0d929
4
+ data.tar.gz: 06f8d340506aba8db7035d0c5f17fb6ddc4e563b
5
5
  SHA512:
6
- metadata.gz: 0fde3b29fb2ca35e6cee5727af9eb920572bd5895b074cb5c7e67cfc03dfa49f7d4a9a413e0d0c384531d6b6f8780cf6710fa3e49114e05d4fb55617ccb7ac7b
7
- data.tar.gz: ec672cc0be199ad25a6b70d57cd721d6d132b8d0370bba90077db48c383b0343e6716577b990a862074278fa504c5b57a7661be35c8f2d7cc292e485929d9662
6
+ metadata.gz: d258dabb23ce59218aa85cb5271a6efebc91817bd74748f2ad9933001a5b160f8373c38c2768f26694f3ff5bde7a1331455f6f515fd7af7ebfe8c24af81bc245
7
+ data.tar.gz: 03f4d89e6235a16501a1998752e6693d7b8e8cd824a5d7c49d956482c667549fd73d59ea0d3b7f9028d0023c67cd5bbf1c78c7359732c36a9874693ddbe285eb
data/Gemfile CHANGED
@@ -1,3 +1,9 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- gemspec(development_group: :runtime)
3
+ gemspec
4
+
5
+ group :development, :test do
6
+ gem 'pry'
7
+ gem 'pry-doc'
8
+ gem 'em-synchrony'
9
+ end
data/README.md CHANGED
@@ -2,6 +2,7 @@ fiber_connection_pool
2
2
  =====================
3
3
 
4
4
  [![Build Status](https://secure.travis-ci.org/rubencaro/fiber_connection_pool.png?branch=master)](http://travis-ci.org/rubencaro/fiber_connection_pool)
5
+ [![Gem Version](https://badge.fury.io/rb/fiber_connection_pool.png)](http://rubygems.org/gems/fiber_connection_pool)
5
6
 
6
7
  Fiber-based generic connection pool
7
8
 
@@ -16,3 +17,26 @@ with [Goliath](https://github.com/postrank-labs/goliath)
16
17
  and in promising experiments with
17
18
  [Reel](https://github.com/celluloid/reel)
18
19
  ([Celluloid](http://celluloid.io/) based) servers.
20
+
21
+ Install
22
+ ----------------
23
+
24
+ Add this line to your application's Gemfile:
25
+
26
+ gem 'fiber_connection_pool'
27
+
28
+ Or install it yourself as:
29
+
30
+ $ gem install fiber_connection_pool
31
+
32
+ Inside of your Ruby program, require FiberConnectionPool with:
33
+
34
+ require 'fiber_connection_pool'
35
+
36
+ Supported Platforms
37
+ -------------------
38
+
39
+ Used in production environments on Ruby 1.9.3 and 2.0.0.
40
+ Tested against Ruby 1.9.3, 2.0.0, and rbx-19mode ([See details..](http://travis-ci.org/rubencaro/fiber_connection_pool)).
41
+
42
+ TODO: sparkling docs
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'celluloid', github: 'celluloid/celluloid', branch: 'master'
4
+ gem 'celluloid-io', github: 'celluloid/celluloid-io', branch: 'master'
5
+ gem 'reel', github: 'celluloid/reel', branch: 'master'
6
+ gem 'ruby-mysql', github: 'rubencaro/ruby-mysql', branch: 'celluloid_io'
7
+ gem 'fiber_connection_pool'
@@ -0,0 +1,69 @@
1
+ require 'reel'
2
+ require 'mysql'
3
+ require 'fiber'
4
+ require 'fiber_connection_pool'
5
+ require 'celluloid/autostart'
6
+
7
+ Mysql.unixsocket_class = Celluloid::IO::UNIXSocket
8
+
9
+ class Dispatcher
10
+ include Celluloid::IO
11
+
12
+ def initialize(opts = {})
13
+ @db_pool_size = opts[:db_pool_size] || 5
14
+ @pool = FiberConnectionPool.new(:size => @db_pool_size) do
15
+ Mysql.connect 'localhost','user','pass','bogusdb',nil,'/var/run/mysqld/mysqld.sock'
16
+ end
17
+ puts "DB Pool of size #{@db_pool_size} ready..."
18
+ end
19
+
20
+ def dispatch(request)
21
+ print '.'
22
+ @pool.query 'select sleep(2);'
23
+ puts "Done #{Thread.current.to_s}, #{Fiber.current.to_s}"
24
+ request.respond :ok, "hello, world! #{Time.now.strftime('%T')}"
25
+ end
26
+ end
27
+
28
+
29
+ class DispatcherPool
30
+
31
+ def initialize(opts = {})
32
+ @size = opts[:size] || 5
33
+ @dispatchers = []
34
+ @size.times{ |i| @dispatchers << "dispatcher_#{i}".to_sym }
35
+ @dispatchers.each{ |d| Dispatcher.supervise_as d }
36
+ @next_dispatcher = 0
37
+ puts "Pool of #{@size} dispatchers ready."
38
+ end
39
+
40
+ def dispatch(request)
41
+ d = @next_dispatcher
42
+ @next_dispatcher += 1
43
+ @next_dispatcher = 0 if @next_dispatcher >= @dispatchers.count
44
+ Celluloid::Actor[@dispatchers[d]].dispatch(request)
45
+ rescue => ex
46
+ puts "Someone died: #{ex}"
47
+ request.respond :internal_server_error, "Someone died"
48
+ end
49
+
50
+ end
51
+
52
+
53
+ class MyServer < Reel::Server
54
+
55
+ def initialize(host = "127.0.0.1", port = 3000)
56
+ super(host, port, &method(:on_connection))
57
+ @dispatcher = DispatcherPool.new
58
+ puts "Listening on #{host}:#{port}..."
59
+ end
60
+
61
+ def on_connection(connection)
62
+ while request = connection.request
63
+ @dispatcher.dispatch(request)
64
+ end
65
+ end
66
+
67
+ end
68
+
69
+ MyServer.run
@@ -1,18 +1,25 @@
1
+ require 'fiber'
2
+
1
3
  class FiberConnectionPool
2
- VERSION = '0.1.0'
4
+ VERSION = '0.1.1'
3
5
 
4
6
  attr_accessor :saved_data
5
7
 
6
- def initialize(opts, &block)
8
+ # Initializes the pool with 'size' instances
9
+ # running the given block to get each one. Ex:
10
+ #
11
+ # pool = FiberConnectionPool.new(:size => 5) { MyConnection.new }
12
+ #
13
+ def initialize(opts)
14
+ raise ArgumentError.new('size > 0 is mandatory') if opts[:size].to_i <= 0
15
+
7
16
  @saved_data = {} # placeholder for requested save data
8
17
  @reserved = {} # map of in-progress connections
9
18
  @reserved_backup = {} # backup map of in-progress connections, to catch failures
10
19
  @available = [] # pool of free connections
11
20
  @pending = [] # pending reservations (FIFO)
12
21
 
13
- opts[:size].times do
14
- @available.push(block.call) if block_given?
15
- end
22
+ @available = Array.new(opts[:size].to_i) { yield }
16
23
  end
17
24
 
18
25
  def save_data_for_fiber
@@ -39,12 +46,16 @@ class FiberConnectionPool
39
46
  end
40
47
  end
41
48
 
49
+ ##
50
+ # avoid method_missing for most common methods
51
+ #
42
52
  def query(sql)
43
53
  execute(false,'query') do |conn|
44
54
  conn.query sql
45
55
  end
46
56
  end
47
57
 
58
+
48
59
  def recreate_connection(new_conn)
49
60
  bad_conn = @reserved_backup[Fiber.current.object_id]
50
61
  release_backup Fiber.current
@@ -3,8 +3,72 @@ require 'helper'
3
3
 
4
4
  class TestFiberConnectionPool < Minitest::Test
5
5
 
6
- def test_bogus
7
- assert true
6
+ def test_blocking_behaviour
7
+ # get pool and fibers
8
+ pool = FiberConnectionPool.new(:size => 5) { ::BlockingConnection.new(:delay => 0.05) }
9
+
10
+ fibers = Array.new(15){ Fiber.new { pool.do_something } }
11
+
12
+ a = Time.now
13
+ result = fibers.map(&:resume)
14
+ b = Time.now
15
+
16
+ # 15 fibers on a size 5 pool, but -blocking- connections
17
+ # with a 0.05 delay we expect to spend at least: 0.05*15 = 0.75
18
+ assert_operator((b - a), :>, 0.75)
19
+
20
+ # Also we only use the first connection from the pool,
21
+ # because as we are -blocking- it's always available
22
+ # again for the next request
23
+ assert_equal 1, result.uniq.count
8
24
  end
9
25
 
26
+ def test_em_synchrony_behaviour
27
+ require 'em-synchrony'
28
+
29
+ a = b = nil
30
+ info = { :threads => [], :fibers => [], :instances => []}
31
+
32
+ EM.synchrony do
33
+ # get pool and fibers
34
+ pool = FiberConnectionPool.new(:size => 5) { ::EMSynchronyConnection.new(:delay => 0.05) }
35
+
36
+ fibers = Array.new(15){ Fiber.new { pool.do_something(info) } }
37
+
38
+ a = Time.now
39
+ fibers.each{ |f| f.resume }
40
+ # wait all fibers to end
41
+ while fibers.any?{ |f| f.alive? } do
42
+ EM::Synchrony.sleep 0.01
43
+ end
44
+ b = Time.now
45
+ EM.stop
46
+ end
47
+
48
+ # 15 fibers on a size 5 pool, and -non-blocking- connections
49
+ # with a 0.05 delay we expect to spend at least: 0.05*15/5 = 0.15
50
+ # plus some breeze lost on precision on the wait loop
51
+ # then we should be under 0.20 for sure
52
+ assert_operator((b - a), :<, 0.20)
53
+
54
+ # we should have visited 1 thread, 15 fibers and 5 instances
55
+ info.dup.each{ |k,v| info[k] = v.uniq }
56
+ assert_equal 1, info[:threads].count
57
+ assert_equal 15, info[:fibers].count
58
+ assert_equal 5, info[:instances].count
59
+ end
60
+
61
+ def test_size_is_mandatory
62
+ assert_raises ArgumentError do
63
+ FiberConnectionPool.new { ::BlockingConnection.new }
64
+ end
65
+ assert_raises ArgumentError do
66
+ FiberConnectionPool.new(:size => 'a') { ::BlockingConnection.new }
67
+ end
68
+ assert_raises ArgumentError do
69
+ FiberConnectionPool.new(:size => 0) { ::BlockingConnection.new }
70
+ end
71
+ end
72
+
73
+
10
74
  end
data/test/helper.rb CHANGED
@@ -1,6 +1,24 @@
1
1
  require 'minitest/pride'
2
2
  require 'minitest/autorun'
3
3
 
4
- $VERBOSE = 1
5
-
6
4
  require_relative '../lib/fiber_connection_pool'
5
+
6
+ class BlockingConnection
7
+ def initialize(opts = {})
8
+ @delay = opts[:delay] || 0.05
9
+ end
10
+
11
+ def do_something
12
+ sleep @delay
13
+ self.object_id
14
+ end
15
+ end
16
+
17
+ class EMSynchronyConnection < BlockingConnection
18
+ def do_something(info)
19
+ info[:threads] << Thread.current.object_id
20
+ info[:fibers] << Fiber.current.object_id
21
+ info[:instances] << self.object_id
22
+ EM::Synchrony.sleep @delay
23
+ end
24
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fiber_connection_pool
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ruben Caro
@@ -56,6 +56,8 @@ files:
56
56
  - README.md
57
57
  - Rakefile
58
58
  - examples/.placeholder
59
+ - examples/reel_server/Gemfile
60
+ - examples/reel_server/main.rb
59
61
  - fiber_connection_pool.gemspec
60
62
  - lib/fiber_connection_pool.rb
61
63
  - test/fiber_connection_pool_test.rb