Skip to content

Commit

Permalink
Refactored async design, now uses HTTP status code 100
Browse files Browse the repository at this point in the history
  • Loading branch information
raggi committed Jun 17, 2008
1 parent b1900c6 commit 26b7373
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 36 deletions.
63 changes: 34 additions & 29 deletions example/async_app.ru
Original file line number Diff line number Diff line change
Expand Up @@ -32,55 +32,60 @@
# Document Length: 12 bytes
#
# Concurrency Level: 100
# Time taken for tests: 5.251864 seconds
# Time taken for tests: 5.244146 seconds
# Complete requests: 500
# Failed requests: 0
# Write errors: 0
# Total transferred: 47000 bytes
# HTML transferred: 6000 bytes
# Requests per second: 95.20 [#/sec] (mean)
# Time per request: 1050.373 [ms] (mean)
# Time per request: 10.504 [ms] (mean, across all concurrent requests)
# Transfer rate: 8.57 [Kbytes/sec] received
# Requests per second: 95.34 [#/sec] (mean)
# Time per request: 1048.829 [ms] (mean)
# Time per request: 10.488 [ms] (mean, across all concurrent requests)
# Transfer rate: 8.58 [Kbytes/sec] received
#
# Connection Times (ms)
# min mean[+/-sd] median max
# Connect: 0 3 2.1 3 8
# Processing: 1033 1044 4.7 1045 1052
# Waiting: 1031 1040 4.9 1040 1051
# Total: 1041 1047 3.3 1048 1054
# Connect: 0 3 1.8 3 7
# Processing: 1034 1043 3.0 1044 1050
# Waiting: 1032 1039 3.8 1040 1049
# Total: 1041 1046 1.9 1047 1051
#
# Percentage of the requests served within a certain time (ms)
# 50% 1048
# 66% 1050
# 75% 1051
# 80% 1051
# 90% 1051
# 95% 1052
# 98% 1052
# 99% 1052
# 100% 1054 (longest request)
#
# 50% 1047
# 66% 1048
# 75% 1048
# 80% 1048
# 90% 1048
# 95% 1049
# 98% 1049
# 99% 1049
# 100% 1051 (longest request)


class AsyncApp

# Status code 100 means CONTINUE
AsyncResponse = [100,{},'']

def call(env)
@env = env
:async
# If we have fibers / threads available, we could fire off the processing
# here, but if we're trying to linearize, it's just as easy to wait until
# async is called.
AsyncResponse
end

def callback(instance, method)
process_async(instance, method)
end

def process_async(instance, method)
EventMachine::add_timer(1) {
def async(instance, method)
# Semi-emulate a long db request, instead of a timer, in reality we'd be
# waiting for the response data. Whilst this happens, other connections
# can be serviced.
# This could be any callback based thing though, a deferrable waiting on
# IO data, a db request, an http request, an smtp send, whatever.
EventMachine::add_timer(1) {
instance.send(method, [200, {}, 'Woah, async!'])
}
end

end

use Rack::CommonLogger
use Rack::Reloader
# use Rack::Lint
run AsyncApp.new
21 changes: 14 additions & 7 deletions lib/thin/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ class Connection < EventMachine::Connection
include Logging

# Rack application (adapter) served by this connection.
attr_accessor :app
attr_reader :app
attr_accessor :app_async
def app=(app)
@app = app
@app_async = app.respond_to?(:async) ? true : false
app
end

# Backend to the server
attr_accessor :backend
Expand Down Expand Up @@ -56,9 +62,7 @@ def pre_process
@request.remote_address = remote_address

# Process the request calling the Rack adapter
response = @app.call(@request.env)
@app.callback(self, :post_process) if :async == response
response
@app.call(@request.env)
rescue Object
handle_error
terminate_request
Expand All @@ -67,9 +71,12 @@ def pre_process

def post_process(result)
return unless result
return if :async == result


# An async app should return a [100, {}, ''], meaning continue.
@response.status, @response.headers, @response.body = result

# If we're going async, then setup our callback on the app, and get outta here.
return @app.async(self, :post_process) if @app_async && @response.status == 100

# Make the response persistent if requested by the client
@response.persistent! if @request.persistent?
Expand All @@ -86,7 +93,7 @@ def post_process(result)
rescue Object
handle_error
ensure
terminate_request unless result == :async
terminate_request unless @response.status == 100
end

def handle_error
Expand Down

0 comments on commit 26b7373

Please sign in to comment.