son_of_a_batch 0.0.2

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.
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env ruby
2
+ $:<< './lib'
3
+
4
+ # A simple dashboard for
5
+ #
6
+ # See
7
+ # app/views -- templates
8
+ # public -- static files
9
+ # config/son_of_a_batch.rb -- configuration
10
+ #
11
+
12
+ require 'boot'
13
+ require 'gorillib'
14
+ require 'tilt'
15
+ require 'yajl/json_gem'
16
+
17
+ require 'goliath'
18
+ require 'goliath/rack/templates'
19
+ require 'goliath/plugins/latency'
20
+ require 'em-synchrony/em-http'
21
+ require 'rack/abstract_format'
22
+
23
+ class SonOfABatch < Goliath::API
24
+ use Goliath::Rack::Params # parse query & body params
25
+ use Goliath::Rack::Formatters::JSON # JSON output formatter
26
+ use Goliath::Rack::Render # auto-negotiate response format
27
+ use Rack::AbstractFormat, 'application/json'
28
+
29
+ include Goliath::Rack::Templates # render templated files from ./views
30
+ use(Rack::Static, # render static files from ./public
31
+ :root => Goliath::Application.root_path("public"), :urls => ["/favicon.ico", '/stylesheets', '/javascripts', '/images'])
32
+ # plugin Goliath::Plugin::Latency # ask eventmachine reactor to track its latency
33
+
34
+ TARGET_CONCURRENCY = 10
35
+ QUERIES = [ 1.0, 60.5, 2.5, 0.5, 1.0, 0.25, 1.0, 60.5, 2.5, 0.5, 1.0, 0.25 ]
36
+
37
+ def recent_latency
38
+ Goliath::Plugin::Latency.recent_latency if defined?(Goliath::Plugin::Latency)
39
+ end
40
+
41
+ def response(env)
42
+ batch_id = "%7.04f" % (env[:start_time].to_f - 100*(env[:start_time].to_f.to_i / 100))
43
+ case env['PATH_INFO']
44
+ when '/' then return [200, {}, haml(:root)]
45
+ when '/debug' then return [200, {}, haml(:debug)]
46
+ when '/get' then :pass
47
+ else raise Goliath::Validation::NotFoundError
48
+ end
49
+
50
+ env.logger.debug "req #{object_id} @#{batch_id}: constructing request group"
51
+ BatchIterator.new(env, batch_id, QUERIES.each_with_index.to_a, TARGET_CONCURRENCY).perform
52
+ env.logger.debug "req #{object_id} @#{batch_id}: constructed request group"
53
+ chunked_streaming_response(200, {'X-Responder' => self.class.to_s })
54
+ end
55
+ end
56
+
57
+
58
+ class BatchIterator < EM::Synchrony::Iterator
59
+
60
+ TARGET_URL_BASE = "http://localhost:9002/meta/http/sleepy.json"
61
+ HTTP_REQUEST_OPTIONS = { :connect_timeout => 1.0, :inactivity_timeout => 1.2 }
62
+
63
+ attr_reader :requests, :responses
64
+
65
+ def initialize env, batch_id, *args
66
+ @env = env
67
+ @batch_id = batch_id
68
+ @requests = []
69
+ @responses = {:results => {}, :errors => {}}
70
+ @seen_first_result = false
71
+ super *args
72
+ end
73
+
74
+ def handle_result req_id, req
75
+ @responses[:results][req_id] = { :status => req.response_header.http_status, :body => req.response }
76
+ sep = @seen_first_result ? ",#{SEP}" : ""
77
+ key = %Q{"#{req_id}":}
78
+ body = JSON.generate(@responses[:results][req_id])
79
+ @env.chunked_stream_send([ sep, key, body ].join)
80
+ end
81
+
82
+ def handle_error req_id, req
83
+ err = req.error.blank? ? 'request error' : req.error
84
+ @responses[:errors][req_id] = { :error => err }
85
+ p req.error
86
+ end
87
+
88
+ def perform
89
+ EM.synchrony do
90
+ @env.logger.debug "req #{object_id} @#{@batch_id}: synchrony block start"
91
+
92
+ EM.next_tick{ beg_batch ; beg_results_block }
93
+ each(
94
+ proc{|(delay, req_id), iter|
95
+ @env.logger.debug "req #{object_id} @#{@batch_id}: request [#{req_id}, #{delay}]\tconstructing"
96
+ req = EM::HttpRequest.new(TARGET_URL_BASE, HTTP_REQUEST_OPTIONS).aget(:query => { :delay => delay })
97
+
98
+ req.callback do
99
+ @env.logger.debug "req #{object_id} @#{@batch_id}: request [#{req_id}, #{delay}]:\t callback"
100
+ handle_result(req_id, req)
101
+ @seen_first_result ||= true
102
+ @env.logger.debug "req #{object_id} @#{@batch_id}: request [#{req_id}, #{delay}]:\t iter.next"
103
+ iter.next
104
+ end
105
+
106
+ req.errback do
107
+ @env.logger.debug "req #{object_id} @#{@batch_id}: request [#{req_id}, #{delay}]:\t errback"
108
+ handle_error(req_id, req)
109
+ @env.logger.debug "req #{object_id} @#{@batch_id}: request [#{req_id}, #{delay}]:\t iter.next"
110
+ iter.next
111
+ end
112
+
113
+ @env.logger.debug "req #{object_id} @#{@batch_id}: request [#{req_id}, #{delay}]\tconstructed"
114
+
115
+ }, proc{
116
+ end_results_block
117
+ @env.chunked_stream_send [",", SEP].join
118
+ @env.chunked_stream_send JSON.generate({:errors => responses[:errors], :completed_in => (Time.now.utc.to_f - @env[:start_time].to_f)})[1..-2]
119
+ end_batch
120
+ @env.logger.debug "req #{object_id} @#{@batch_id}: closing stream"
121
+ @env.chunked_stream_close
122
+ }
123
+ )
124
+ @env.logger.debug "req #{object_id} @#{@batch_id}: synchrony block end"
125
+ end
126
+ end
127
+
128
+
129
+ SEP = "\n"
130
+ BEG_BATCH = "{"
131
+ BEG_RESULTS = %Q<"results":\{>
132
+ END_RESULTS = "}"
133
+ END_BATCH = "}"
134
+
135
+ def beg_batch
136
+ @env.chunked_stream_send( [BEG_BATCH, SEP].join )
137
+ end
138
+
139
+ def beg_results_block
140
+ @env.chunked_stream_send( [BEG_RESULTS, SEP].join )
141
+ end
142
+
143
+ def end_results_block
144
+ @env.chunked_stream_send [SEP, END_RESULTS].join
145
+ end
146
+
147
+ def end_batch
148
+ @env.chunked_stream_send( [SEP, END_BATCH].join )
149
+ end
150
+
151
+ end
152
+
153
+
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "SonOfABatch" do
4
+ it "fails" do
5
+ fail "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
@@ -0,0 +1,25 @@
1
+ require 'rspec'
2
+ require 'spork'
3
+ require 'bundler'
4
+ $:<< '../lib' << 'lib' << 'vendor/goliath/lib'
5
+
6
+ Spork.prefork do
7
+ Bundler.setup
8
+ Bundler.require
9
+
10
+ require 'goliath/test_helper'
11
+
12
+ ::RSpec.configure do |c|
13
+ c.include Goliath::TestHelper, :example_group => {
14
+ :file_path => /spec\/integration/
15
+ }
16
+ end
17
+ end
18
+
19
+ Spork.each_run do
20
+ # This code will be run each time you run your specs.
21
+ end
22
+
23
+
24
+
25
+
metadata ADDED
@@ -0,0 +1,203 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: son_of_a_batch
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.2
6
+ platform: ruby
7
+ authors:
8
+ - Philip (flip) Kromer for Infochimps
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-04-23 00:00:00 -05:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: em-synchrony
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: em-http-request
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: "0"
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: yajl-ruby
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.8.2
46
+ type: :runtime
47
+ prerelease: false
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: gorillib
51
+ requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ version: 0.0.4
57
+ type: :runtime
58
+ prerelease: false
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: rack-respond_to
62
+ requirement: &id005 !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: "0"
68
+ type: :runtime
69
+ prerelease: false
70
+ version_requirements: *id005
71
+ - !ruby/object:Gem::Dependency
72
+ name: rack-abstract-format
73
+ requirement: &id006 !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: "0"
79
+ type: :runtime
80
+ prerelease: false
81
+ version_requirements: *id006
82
+ - !ruby/object:Gem::Dependency
83
+ name: bundler
84
+ requirement: &id007 !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: 1.0.12
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: *id007
93
+ - !ruby/object:Gem::Dependency
94
+ name: yard
95
+ requirement: &id008 !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ~>
99
+ - !ruby/object:Gem::Version
100
+ version: 0.6.7
101
+ type: :development
102
+ prerelease: false
103
+ version_requirements: *id008
104
+ - !ruby/object:Gem::Dependency
105
+ name: jeweler
106
+ requirement: &id009 !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ~>
110
+ - !ruby/object:Gem::Version
111
+ version: 1.5.2
112
+ type: :development
113
+ prerelease: false
114
+ version_requirements: *id009
115
+ - !ruby/object:Gem::Dependency
116
+ name: rspec
117
+ requirement: &id010 !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ~>
121
+ - !ruby/object:Gem::Version
122
+ version: 2.5.0
123
+ type: :development
124
+ prerelease: false
125
+ version_requirements: *id010
126
+ - !ruby/object:Gem::Dependency
127
+ name: rcov
128
+ requirement: &id011 !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: "0"
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: *id011
137
+ description: Smelt from a plentiferous gallimaufry of requests an agglomerated bale of responses. With, y'know, concurrency and all that.
138
+ email: coders@infochimps.com
139
+ executables: []
140
+
141
+ extensions: []
142
+
143
+ extra_rdoc_files:
144
+ - LICENSE.txt
145
+ - README.textile
146
+ files:
147
+ - .document
148
+ - .rspec
149
+ - Gemfile
150
+ - Gemfile.lock
151
+ - LICENSE.txt
152
+ - README.textile
153
+ - Rakefile
154
+ - VERSION
155
+ - app/endpoints/sleepy.rb
156
+ - app/views/debug.haml
157
+ - app/views/layout.haml
158
+ - app/views/root.haml
159
+ - config/bootstrap.rb
160
+ - config/son_of_a_batch-private.example.rb
161
+ - config/son_of_a_batch-private.rb
162
+ - config/son_of_a_batch.rb
163
+ - lib/boot.rb
164
+ - lib/son_of_a_batch.rb
165
+ - public/stylesheets/style.css
166
+ - son_of_a_batch.gemspec
167
+ - son_of_a_batch.rb
168
+ - spec/son_of_a_batch_spec.rb
169
+ - spec/spec_helper.rb
170
+ has_rdoc: true
171
+ homepage: http://infochimps.com/labs
172
+ licenses:
173
+ - MIT
174
+ post_install_message:
175
+ rdoc_options: []
176
+
177
+ require_paths:
178
+ - lib
179
+ required_ruby_version: !ruby/object:Gem::Requirement
180
+ none: false
181
+ requirements:
182
+ - - ">="
183
+ - !ruby/object:Gem::Version
184
+ hash: 1754308861720703327
185
+ segments:
186
+ - 0
187
+ version: "0"
188
+ required_rubygems_version: !ruby/object:Gem::Requirement
189
+ none: false
190
+ requirements:
191
+ - - ">="
192
+ - !ruby/object:Gem::Version
193
+ version: "0"
194
+ requirements: []
195
+
196
+ rubyforge_project:
197
+ rubygems_version: 1.5.0
198
+ signing_key:
199
+ specification_version: 3
200
+ summary: Smelt from a plentiferous gallimaufry of requests an agglomerated bale of responses. With, y'know, concurrency and all that.
201
+ test_files:
202
+ - spec/son_of_a_batch_spec.rb
203
+ - spec/spec_helper.rb