fiber_connection_pool 0.1.0 → 0.1.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.
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