rubycut-sinatra-synchrony 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
@@ -0,0 +1,25 @@
1
+ Copyright 2012 Kyle Drake. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without modification, are
4
+ permitted provided that the following conditions are met:
5
+
6
+ 1. Redistributions of source code must retain the above copyright notice, this list of
7
+ conditions and the following disclaimer.
8
+
9
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
10
+ of conditions and the following disclaimer in the documentation and/or other materials
11
+ provided with the distribution.
12
+
13
+ THIS SOFTWARE IS PROVIDED BY GEOLOQI ``AS IS'' AND ANY EXPRESS OR IMPLIED
14
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
15
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GEOLOQI OR
16
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
17
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
18
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
19
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
20
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
21
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
+
23
+ The views and conclusions contained in the software and documentation are those of the
24
+ authors and should not be interpreted as representing official policies, either expressed
25
+ or implied, of Kyle Drake.
@@ -0,0 +1,133 @@
1
+ Sinatra::Synchrony [![](https://secure.travis-ci.org/kyledrake/sinatra-synchrony.png)](http://travis-ci.org/kyledrake/sinatra-synchrony)
2
+ ===
3
+
4
+ Sinatra + EM-Synchrony - fast, concurrent web applications with no callbacks!
5
+
6
+ Sinatra::Synchrony is a very small extension for Sinatra that dramatically improves the concurrency of your web application. Powered by [EventMachine](https://github.com/eventmachine/eventmachine) and [EM-Synchrony](https://github.com/igrigorik/em-synchrony), it increases the number of clients your application can serve per process when you have a lot of slow IO calls (like HTTP calls to external APIs). Because it uses [Fibers](http://www.ruby-doc.org/core-1.9/classes/Fiber.html) internally to handle concurrency, no callback gymnastics are required! Just develop as if you were writing a normal Sinatra web application, use non-blocking libraries (see below) and you're all set!
7
+
8
+ How it works
9
+ ---
10
+
11
+ * Loads [EventMachine](https://github.com/eventmachine/eventmachine) and [EM-Synchrony](https://github.com/igrigorik/em-synchrony). Requires app server with EventMachine and Ruby 1.9 support (Thin, Rainbows!, Heroku). It should also work on JRuby (with JRUBY_OPTS="--1.9").
12
+ * Inserts the [Rack::FiberPool](https://github.com/mperham/rack-fiber_pool) middleware, which automatically provides a Fiber for each incoming request, allowing EM-Synchrony to work.
13
+ * Adds [em-http-request](https://github.com/igrigorik/em-http-request), which you can use with EM::Synchrony to do concurrent HTTP calls to APIs! Or if you'd rather use a different client:
14
+ * Patches TCPSocket via EM-Synchrony. Any software that uses this (such as an HTTP Client that uses Net::HTTP) can run without blocking IO. [RestClient](https://github.com/archiloque/rest-client) works great with this!
15
+ * Patches Rack::Test so that it runs your tests within an EventMachine. Just test the same way you did before and it should just work.
16
+ * Patches Resolv via [em-resolv-replace](https://github.com/mperham/em-resolv-replace), enabling non-blocking DNS lookups magically, the way David Bowie would want it.
17
+
18
+ What it doesn't do (yet)
19
+ ---
20
+
21
+ Provide non-blocking drivers for everything. Right now the focus was to deal with the biggest concurrency problem for most apps, which is API calls to external websites. You don't have to make _everything_ non-blocking to speed up applications with this approach, which is the important thing to understand. For example, if your database access is under ten milliseconds, it's not as bad as an API call to an external web site that takes a few seconds. There are numerous non-blocking drivers available however, check out the [Protocol Implementations](https://github.com/eventmachine/eventmachine/wiki/Protocol-Implementations) page on the [EventMachine GitHub Wiki](https://github.com/eventmachine/eventmachine/wiki) for a full list. I would personally like to see plug-and-play drivers implemented for the major three ORMs (ActiveRecord, DataMapper, Sequel), because then I could simply drop them into this gem and you'd be non-blocking without requiring any special changes. For most of the web applications I work on, this would be all I need to eliminate my blocking IO problems forever!
22
+
23
+ Installation
24
+ ---
25
+ Install the gem:
26
+
27
+ gem install sinatra-synchrony
28
+
29
+ Register with Sinatra __at the top, before any other middleware or plugins are loaded__:
30
+
31
+ require 'sinatra/base'
32
+ require 'sinatra/synchrony'
33
+ class App < Sinatra::Base
34
+ register Sinatra::Synchrony
35
+ end
36
+
37
+ If you are developing with a classic style app, just require the gem and it will automatically load:
38
+
39
+ require 'sinatra'
40
+ require 'sinatra/synchrony'
41
+
42
+ get '/' do
43
+ 'Sinatra::Synchrony is loaded automatically in classic mode, nothing needed'
44
+ end
45
+
46
+ Net::HTTP / TCPSocket
47
+ ---
48
+ If you're using anything based on TCPSocket (such as Net::HTTP, which is used by many things), you can replace the native Ruby TCPSocket with one that supports EventMachine and allows for concurrency:
49
+
50
+ Sinatra::Synchrony.overload_tcpsocket!
51
+
52
+ This will allow you to use things like [RestClient](https://github.com/archiloque/rest-client) without any changes:
53
+
54
+ RestClient.get 'http://google.com'
55
+
56
+ This is not perfect though - the TCPSocket overload doesn't currently support SSL and will throw an exception. This is more for when you have ruby libraries that use Net::HTTP and you want to try something. If you intend to do HTTP requests, I strongly recommend using [Faraday](https://github.com/technoweenie/faraday) instead, which has support for [EM-HTTP-Request](https://github.com/igrigorik/em-http-request).
57
+
58
+ Please encourage Ruby library developers to use (or at least support) Faraday instead of Net::HTTP. Aside from the inability to be concurrent natively, it's a pretty weird and crappy interface, which makes it harder to replace it with something better.
59
+
60
+ Tests
61
+ ---
62
+ Add this to the top of your test file:
63
+
64
+ Sinatra::Synchrony.patch_tests!
65
+
66
+ Then just write your tests as usual, and all tests will be run within EventMachine. You must be in the __test__ environment so that Sinatra will not load Rack::FiberPool.
67
+
68
+ Benchmarks
69
+ ---
70
+ Despite enabling synchronous programming without callbacks, there is no performance hit to your application! All the performance benefits you expect from Thin/Rainbows and EventMachine are still there:
71
+
72
+ class App < Sinatra::Base
73
+ register Sinatra::Synchrony
74
+ get '/' do
75
+ 'Hello World!'
76
+ end
77
+ end
78
+
79
+ Benchmarked with rackup -s thin:
80
+
81
+ $ ab -c 50 -n 2000 http://127.0.0.1:9292/
82
+ ...
83
+ Requests per second: 3102.30 [#/sec] (mean)
84
+ Time per request: 16.117 [ms] (mean)
85
+ Time per request: 0.322 [ms] (mean, across all concurrent requests)
86
+
87
+ Connection Times (ms)
88
+ min mean[+/-sd] median max
89
+ Connect: 0 0 0.1 0 1
90
+ Processing: 5 16 7.7 13 38
91
+ Waiting: 3 13 7.0 10 35
92
+ Total: 6 16 7.7 13 38
93
+
94
+ Let's try a simple blocking IO example to prove it works. 100 hits to google.com:
95
+
96
+ require 'sinatra'
97
+ require 'sinatra/synchrony'
98
+ require 'rest-client'
99
+ require 'faraday'
100
+ Faraday.default_adapter = :em_synchrony
101
+
102
+ get '/' do
103
+ Faraday.get 'http://google.com'
104
+ end
105
+
106
+
107
+ $ ab -c 100 -n 100 http://127.0.0.1:9292/
108
+ ...
109
+ Time taken for tests: 0.256 seconds
110
+
111
+ For a perspective, this operation takes __33 seconds__ without this extension.
112
+
113
+ Geoloqi
114
+ ---
115
+ This gem was designed to help us develop faster games and internal applications for [Geoloqi](http://geoloqi.org): a real-time geofencing and location update platform. We wanted to share with you how we deal with concurrency issues, and also make it easy to utilize this for our other projects. One of these projects is our recently released [Geoloqi ruby adapter](http://github.com/kyledrake/geoloqi-ruby), which can utilize [Faraday](http://github.com/technoweenie/faraday) and sinatra-synchrony to provide massive concurrency with almost no changes required to your code.
116
+
117
+ TODO / Thoughts
118
+ ---
119
+ * We are using this in production without any problems, and it's very stable for us. But you should test before deploying anything with it.
120
+ * There is work underway to make this a Rack middleware, and integrate that middleware with this plugin. That way, many other frameworks can take advantage of this. There is also work exploratory work to provide support for non-EventMachine Reactor pattern implementations with this approach, but it's beyond the scope of this extension.
121
+
122
+ Author
123
+ ---
124
+ * [Kyle Drake](http://kyledrake.net)
125
+
126
+ Thanks
127
+ ---
128
+ * [Ilya Grigorik](http://www.igvita.com) and [PostRank](http://www.postrank.com) for their amazing work on em-synchrony, em-http-request, and countless articles explaining this.
129
+ * [Mike Perham](http://www.mikeperham.com) and [Carbon Five](http://carbonfive.com). For rack-fiber_pool, em-resolv-replace, and many blog posts and presentations on this.
130
+ * [Konstantin Haase](http://rkh.im/) for session overload suggestion.
131
+ * [Steeve Morin](http://github.com/steeve)
132
+ * [Ryan Caught](https://github.com/rcaught)
133
+ * The many Sinatra developers that liberated me from framework hell, and EventMachine developers that liberated me from blocking IO hell.
@@ -0,0 +1,15 @@
1
+ require 'bundler'
2
+
3
+ Bundler.setup
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ require "rake/testtask"
7
+
8
+ desc "Run all tests"
9
+ Rake::TestTask.new do |t|
10
+ t.libs << "spec"
11
+ t.test_files = FileList['spec/*_spec.rb']
12
+ t.verbose = true
13
+ end
14
+
15
+ task :default => :test
@@ -0,0 +1,42 @@
1
+ require 'sinatra/base'
2
+ require 'rack/fiber_pool'
3
+ require 'eventmachine'
4
+ require 'em-http-request'
5
+ require 'em-synchrony'
6
+
7
+ module Sinatra
8
+ module Synchrony
9
+ def self.registered(app)
10
+ app.disable :threaded
11
+ end
12
+
13
+ def setup_sessions(builder)
14
+ builder.use Rack::FiberPool, {:rescue_exception => handle_exception } unless test?
15
+ super
16
+ end
17
+
18
+ def handle_exception
19
+ Proc.new do |env, e|
20
+ if settings.show_exceptions?
21
+ request = Sinatra::Request.new(env)
22
+ printer = Sinatra::ShowExceptions.new(proc{ raise e })
23
+ s, h, b = printer.call(env)
24
+ [s, h, b]
25
+ else
26
+ [500, {}, ""]
27
+ end
28
+ end
29
+ end
30
+
31
+ class << self
32
+ def patch_tests!
33
+ require 'sinatra/synchrony/mock_session'
34
+ end
35
+
36
+ def overload_tcpsocket!
37
+ require 'sinatra/synchrony/tcpsocket'
38
+ end
39
+ end
40
+ end
41
+ register Synchrony
42
+ end
@@ -0,0 +1,13 @@
1
+ require 'rack/test'
2
+
3
+ module Rack
4
+ class MockSession
5
+ alias_method :request_original, :request
6
+ def request(uri, env)
7
+ EM.synchrony do
8
+ request_original uri, env
9
+ EM.stop
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,6 @@
1
+ require 'net/http'
2
+
3
+ original_verbosity = $VERBOSE
4
+ $VERBOSE = nil
5
+ TCPSocket = EventMachine::Synchrony::TCPSocket
6
+ $VERBOSE = original_verbosity
@@ -0,0 +1,44 @@
1
+ ENV['RACK_ENV'] = 'test'
2
+ require File.join(File.join(File.expand_path(File.dirname(__FILE__))), '..', 'lib', 'sinatra', 'synchrony')
3
+ require 'rack/test'
4
+ require 'minitest/autorun'
5
+ require 'wrong/adapters/minitest'
6
+ Sinatra::Synchrony.patch_tests!
7
+ Wrong.config.alias_assert :expect
8
+
9
+ def mock_app(base=Sinatra::Base, &block)
10
+ @app = Sinatra.new(base, &block)
11
+ @app.set :environment, :test
12
+ @app.disable :show_exceptions
13
+ @app.register Sinatra::Synchrony
14
+ end
15
+ def app; @app end
16
+
17
+ describe 'A mock app' do
18
+ include Rack::Test::Methods
19
+ it 'successfully completes a sleep call' do
20
+ mock_app {
21
+ get '/' do
22
+ EM::Synchrony.sleep(0.0001)
23
+ 'ok'
24
+ end
25
+ }
26
+ get '/'
27
+ expect { last_response.ok? }
28
+ expect { last_response.body == 'ok' }
29
+ end
30
+
31
+ it 'works with cookie' do
32
+ mock_app {
33
+ set :session_secret, 'secret'
34
+ enable :sessions
35
+ get '/' do
36
+ session[:key] = 'val'
37
+ 'ok'
38
+ end
39
+ }
40
+ get '/'
41
+ expect { last_response.ok? }
42
+ expect { last_response.headers['Set-Cookie'] }
43
+ end
44
+ end
metadata ADDED
@@ -0,0 +1,202 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubycut-sinatra-synchrony
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Kyle Drake
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-11-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: sinatra
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.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: '1.0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rack-fiber_pool
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '0.9'
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.9'
46
+ - !ruby/object:Gem::Dependency
47
+ name: eventmachine
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: em-http-request
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '1.0'
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: '1.0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: em-synchrony
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 1.0.1
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 1.0.1
94
+ - !ruby/object:Gem::Dependency
95
+ name: rake
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: rack-test
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: '0.5'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ~>
124
+ - !ruby/object:Gem::Version
125
+ version: '0.5'
126
+ - !ruby/object:Gem::Dependency
127
+ name: wrong
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ~>
132
+ - !ruby/object:Gem::Version
133
+ version: '0.5'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ~>
140
+ - !ruby/object:Gem::Version
141
+ version: '0.5'
142
+ - !ruby/object:Gem::Dependency
143
+ name: minitest
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ description: Bootstraps in code required to take advantage of EventMachine/EM-Synchrony's
159
+ concurrency enhancements for slow IO. Patches TCPSocket, which makes anything based
160
+ on it EM-aware (including RestClient). Includes patch for tests. Requires Fibers
161
+ (Ruby 1.9, JRuby and Rubinius in 1.9 mode)
162
+ email:
163
+ - kyledrake@gmail.com
164
+ executables: []
165
+ extensions: []
166
+ extra_rdoc_files: []
167
+ files:
168
+ - lib/sinatra/synchrony.rb
169
+ - lib/sinatra/synchrony/tcpsocket.rb
170
+ - lib/sinatra/synchrony/mock_session.rb
171
+ - spec/synchrony_spec.rb
172
+ - LICENSE.md
173
+ - Rakefile
174
+ - README.md
175
+ - Gemfile
176
+ homepage: https://github.com/kyledrake/sinatra-synchrony
177
+ licenses: []
178
+ post_install_message:
179
+ rdoc_options: []
180
+ require_paths:
181
+ - lib
182
+ required_ruby_version: !ruby/object:Gem::Requirement
183
+ none: false
184
+ requirements:
185
+ - - ! '>='
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ required_rubygems_version: !ruby/object:Gem::Requirement
189
+ none: false
190
+ requirements:
191
+ - - ! '>='
192
+ - !ruby/object:Gem::Version
193
+ version: 1.3.4
194
+ requirements: []
195
+ rubyforge_project: rubycut-sinatra-synchrony
196
+ rubygems_version: 1.8.23
197
+ signing_key:
198
+ specification_version: 3
199
+ summary: Bootstraps Sinatra with EM-Synchrony code, make TCPSocket EM-aware, provides
200
+ support for tests
201
+ test_files: []
202
+ has_rdoc: