rack-timeout 0.1.0beta3 → 0.1.0beta4

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: d8cadc6366c07e2e809ab55bbcbc66ea82e387cd
4
- data.tar.gz: 600c25828d8e91b1556cd4b458833e109c02788d
3
+ metadata.gz: 36207506f9394cfa73916a31f64a957c79677931
4
+ data.tar.gz: 5da1c876b06d0e29cd507aac3d2374d8006e2a36
5
5
  SHA512:
6
- metadata.gz: a4f2117b12ddac87a0398622924219d12eaf84ec11fbcbb55e31f8fa9163eb579a1cb4c7a61d6ec3c0011de3699cf83a1688c80d40de173d1235294fa4b83184
7
- data.tar.gz: b54a7b0085fbefa6ecad6e2438b7ec6c99ba3c907a806ef34b8b6e2238b442dd9050e92a47aa9e738138467c081f0965419f085950570d6316bd8fa3d6aa4959
6
+ metadata.gz: 9035faeb15e556cc8a908124e81a2f4b76440baee9e77ed68f7685730bedb670928631db0def7c356ef3c2dc61924bc72f7261139f70e37f6f41f3b4b282d197
7
+ data.tar.gz: c08a2cd396936bad0970a21f251da1fd5c573cf3585b77d954a149f8b75230b03fbe4677f009690052c26d97d2b85d07bd812b818db324c1e255d197052a8fba
data/README.markdown CHANGED
@@ -1,5 +1,4 @@
1
- README is not entirely in sync with this release. E.g. the overtime stuff is not present in this
2
- release. There may be other discrepancies.
1
+ README is not very out-of-date for this release. Lots of comments in source though. README updates coming before next release.
3
2
 
4
3
  Rack::Timeout
5
4
  =============
@@ -101,13 +100,13 @@ containing the following fields:
101
100
  * `id`: a unique ID per request. Either `Heroku-Request-ID`, `X-Request-ID`, or a random ID
102
101
  generated internally.
103
102
 
104
- * `age`: time in seconds since `X-Request-Start` when the request is first seen by Rack::Timeout.
103
+ * `wait`: time in seconds since `X-Request-Start` when the request is first seen by Rack::Timeout.
105
104
  Only set if `X-Request-Start` is present.
106
105
 
107
106
  * `timeout`: timeout to be used, in seconds. Generally `Rack::Timeout.timeout`, unless
108
107
  `X-Request-Start` is present. See discussion above, under the Heroku Niceties section.
109
108
 
110
- * `duration`: set after a request completes (or times out). The time in seconds it took. This is
109
+ * `service`: set after a request completes (or times out). The time in seconds it took. This is
111
110
  also updated while a request is still active, around every second, with the time it's taken so
112
111
  far.
113
112
 
@@ -225,14 +224,14 @@ but can be removed by unregistering its observer:
225
224
 
226
225
  Each log line is a set of `key=value` pairs, containing the entries from the
227
226
  `env["rack-timeout.info"]` struct that are not `nil`. See the Request Lifetime section above for a
228
- description of each field. Note that while the values for `age`, `timeout`, and `duration` are
227
+ description of each field. Note that while the values for `wait`, `timeout`, and `service` are
229
228
  stored internally as seconds, they are logged as milliseconds for readability.
230
229
 
231
230
  A sample log excerpt might look like:
232
231
 
233
- source=rack-timeout id=13793c age=369ms timeout=10000ms state=ready at=info
234
- source=rack-timeout id=13793c age=369ms timeout=10000ms duration=15ms state=completed at=info
235
- source=rack-timeout id=ea7bd3 age=371ms timeout=10000ms state=timed_out at=error
232
+ source=rack-timeout id=13793c wait=369ms timeout=10000ms state=ready at=info
233
+ source=rack-timeout id=13793c wait=369ms timeout=10000ms service=15ms state=completed at=info
234
+ source=rack-timeout id=ea7bd3 wait=371ms timeout=10000ms state=timed_out at=error
236
235
 
237
236
  (IDs shortened for readability.)
238
237
 
data/lib/rack-timeout.rb CHANGED
@@ -8,4 +8,4 @@ if defined?(Rails) && [3,4].include?(Rails::VERSION::MAJOR)
8
8
  end
9
9
  end
10
10
 
11
- Rack::Timeout::StateChangeLogger.register!
11
+ Rack::Timeout::StageChangeLoggingObserver.register!
data/lib/rack/timeout.rb CHANGED
@@ -3,51 +3,102 @@ require 'securerandom'
3
3
 
4
4
  module Rack
5
5
  class Timeout
6
- class Error < RuntimeError; end
7
- class RequestExpiryError < Error; end
8
- class RequestTimeoutError < Error; end
9
-
10
- RequestDetails = Struct.new(:id, :age, :timeout, :duration, :state)
11
- ENV_INFO_KEY = 'rack-timeout.info'
12
- VALID_STATES = [:ready, :active, :expired, :timed_out, :completed]
13
- MAX_REQUEST_AGE = 30 # seconds
14
- @timeout = 15 # seconds
6
+ class Error < RuntimeError; end # superclass for the following…
7
+ class RequestExpiryError < Error; end # raised when a request is dropped without being given a chance to run (because too old)
8
+ class RequestTimeoutError < Error; end # raised when a request has run for too long
9
+
10
+ RequestDetails = Struct.new(
11
+ :id, # a unique identifier for the request. informative-only.
12
+ :wait, # seconds the request spent in the web server before being serviced by rack
13
+ :service, # time rack spent processing the request (updated ~ every second)
14
+ :timeout, # the actual computed timeout to be used for this request
15
+ :state, # the request's current state, see below:
16
+ )
17
+ VALID_STATES = [
18
+ :expired, # The request was too old by the time it reached rack (see wait_timeout, wait_overtime)
19
+ :ready, # We're about to start processing this request
20
+ :active, # This request is currently being handled
21
+ :timed_out, # This request has run for too long and we're raising a timeout error in it
22
+ :completed, # We're done with this request (also set after having timed out a request)
23
+ ]
24
+ ENV_INFO_KEY = 'rack-timeout.info' # key under which each request's RequestDetails instance is stored in its env.
25
+
26
+ # helper methods to setup getter/setters for timeout properties. Ensure they're always positive numbers or false. When set to false (or 0), their behaviour is disabled.
15
27
  class << self
16
- attr_accessor :timeout
28
+ def set_timeout_property(property_name, value)
29
+ unless value == false || (value.is_a?(Numeric) && value >= 0)
30
+ raise ArgumentError, "value for #{property_name} should be false, zero, or a positive number."
31
+ end
32
+ value = false if value.zero? # zero means we're disabling the feature
33
+ instance_variable_set("@#{property_name}", value)
34
+ end
35
+
36
+ def timeout_property(property_name, start_value)
37
+ singleton_class.instance_eval do
38
+ attr_reader property_name
39
+ define_method("#{property_name}=") { |v| set_timeout_property(property_name, v) }
40
+ end
41
+ set_timeout_property(property_name, start_value)
42
+ end
43
+ end
44
+
45
+ # all values are in seconds
46
+ timeout_property :wait_timeout, 30 # How long the request is allowed to have waited before reaching rack. If exceeded, the request is 'expired', i.e. dropped entirely without being passed down to the application.
47
+ timeout_property :wait_overtime, 60 # Additional time over @wait_timeout for requests with a body, like POST requests. These may take longer to be received by the server before being passed down to the application, but should not be expired.
48
+ timeout_property :service_timeout, 15 # How long the application can take to complete handling the request once it's passed down to it.
49
+
50
+ class << self
51
+ alias_method :timeout=, :service_timeout= # legacy compatibility setter
52
+ attr_accessor :service_past_wait # when false, reduces the request's computed timeout from the service_timeout value if the complete request lifetime (wait + service) would have been longer than wait_timeout (+ wait_overtime when applicable). When true, always uses the service_timeout value.
53
+ @service_past_wait = false # we default to false under the assumption that the router would drop a request that's not responded within wait_timeout, thus being there no point in servicing beyond seconds_service_left (see code further down) up until service_timeout.
17
54
  end
18
55
 
19
56
  def initialize(app)
20
57
  @app = app
21
58
  end
22
59
 
60
+ RT = self # shorthand reference
23
61
  def call(env)
24
- info = env[ENV_INFO_KEY] ||= RequestDetails.new
25
- info.id ||= env['HTTP_HEROKU_REQUEST_ID'] || env['HTTP_X_REQUEST_ID'] || SecureRandom.hex
26
- request_start = env['HTTP_X_REQUEST_START'] # unix timestamp in ms
27
- request_start = Time.at(request_start.to_f / 1000) if request_start
28
- info.age = Time.now - request_start if request_start
29
- time_left = MAX_REQUEST_AGE - info.age if info.age
30
- info.timeout = [self.class.timeout, time_left].compact.select { |n| n >= 0 }.min
31
-
32
- if time_left && time_left <= 0
33
- Rack::Timeout._set_state! env, :expired
34
- raise RequestExpiryError, "Request older than #{MAX_REQUEST_AGE} seconds."
62
+ info = (env[ENV_INFO_KEY] ||= RequestDetails.new)
63
+ info.id ||= env['HTTP_X_REQUEST_ID'] || SecureRandom.hex
64
+
65
+ time_started_service = Time.now # The time the request started being processed by rack
66
+ time_started_wait = RT._read_x_request_start(env) # The time the request was initially receibed by the web server (if available)
67
+ effective_overtime = (RT.wait_overtime && RT._request_has_body?(env)) ? RT.wait_overtime : 0 # additional wait timeout (if set and applicable)
68
+ seconds_service_left = nil
69
+
70
+ # if X-Request-Start is present and wait_timeout is set, expire requests older than wait_timeout (+wait_overtime when applicable)
71
+ if time_started_wait && RT.wait_timeout
72
+ seconds_waited = time_started_service - time_started_wait # how long it took between the web server first receiving the request and rack being able to handle it
73
+ seconds_waited = 0 if seconds_waited < 0 # make up for potential time drift between the routing server and the application server
74
+ final_wait_timeout = RT.wait_timeout + effective_overtime # how long the request will be allowed to have waited
75
+ seconds_service_left = final_wait_timeout - seconds_waited # first calculation of service timeout (relevant if request doesn't get expired, may be overriden later)
76
+ info.wait, info.timeout = seconds_waited, final_wait_timeout # updating the info properties; info.timeout will be the wait timeout at this point
77
+ if seconds_service_left <= 0 # expire requests that have waited for too long in the queue (as they are assumed to have been dropped by the web server / routing layer at this point)
78
+ RT._set_state! env, :expired
79
+ raise RequestExpiryError, "Request older than #{final_wait_timeout} seconds."
80
+ end
35
81
  end
36
82
 
37
- Rack::Timeout._set_state! env, :ready
38
- ready_time = Time.now
83
+ # pass request through if service_timeout is false (i.e., don't time it out at all.)
84
+ return @app.call(env) unless RT.service_timeout
85
+
86
+ # compute actual timeout to be used for this request; if service_past_wait is true, this is just service_timeout. If false (the default), and wait time was determined, we'll use the shortest value between seconds_service_left and service_timeout. See comment above at service_past_wait for justification.
87
+ info.timeout = RT.service_timeout # nice and simple, when service_past_wait is true, not so much otherwise:
88
+ info.timeout = seconds_service_left if !RT.service_past_wait && seconds_service_left && seconds_service_left > 0 && seconds_service_left < RT.service_timeout
39
89
 
90
+ RT._set_state! env, :ready
40
91
  begin
41
92
  app_thread = Thread.current
42
93
  timeout_thread = Thread.start do
43
94
  loop do
44
- info.duration = Time.now - ready_time
45
- sleep_seconds = [1, info.timeout - info.duration].min
95
+ info.service = Time.now - time_started_service
96
+ sleep_seconds = [1 - (info.service % 1), info.timeout - info.service].min
46
97
  break if sleep_seconds <= 0
47
- Rack::Timeout._set_state! env, :active
98
+ RT._set_state! env, :active
48
99
  sleep(sleep_seconds)
49
100
  end
50
- Rack::Timeout._set_state! env, :timed_out
101
+ RT._set_state! env, :timed_out
51
102
  app_thread.raise(RequestTimeoutError, "Request ran for longer than #{info.timeout} seconds.")
52
103
  end
53
104
  response = @app.call(env)
@@ -56,53 +107,69 @@ module Rack
56
107
  timeout_thread.join
57
108
  end
58
109
 
59
- info.duration = Time.now - ready_time
60
- Rack::Timeout._set_state! env, :completed
110
+ info.service = Time.now - time_started_service
111
+ RT._set_state! env, :completed
61
112
  response
62
113
  end
63
114
 
64
- # used internally
115
+ ### following methods are used internally (called by instances, so can't be private. _ marker should discourage people from calling them)
116
+
117
+ # X-Request-Start contains the time the request was first seen by the server. Format varies wildly amongst servers, yay!
118
+ # - nginx gives the time since epoch as seconds.milliseconds[1]. New Relic documentation recommends preceding it with t=[2], so might as well detect it.
119
+ # - Heroku gives the time since epoch in milliseconds. [3]
120
+ # - Apache uses t=microseconds[4], so we're not even going there.
121
+ #
122
+ # The sane way to handle this would be by knowing the server being used, instead let's just hack around with regular expressions and ignore apache entirely.
123
+ # [1]: http://nginx.org/en/docs/http/ngx_http_log_module.html#var_msec
124
+ # [2]: https://docs.newrelic.com/docs/apm/other-features/request-queueing/request-queue-server-configuration-examples#nginx
125
+ # [3]: https://devcenter.heroku.com/articles/http-routing#heroku-headers
126
+ # [4]: http://httpd.apache.org/docs/current/mod/mod_headers.html#header
127
+ #
128
+ # This is a code extraction for readability, this method is only called from a single point.
129
+ RX_NGINX_X_REQUEST_START = /^(?:t=)?(\d+)\.(\d{3})$/
130
+ RX_HEROKU_X_REQUEST_START = /^(\d+)$/
131
+ def self._read_x_request_start(env)
132
+ return unless s = env['HTTP_X_REQUEST_START']
133
+ return unless m = s.match(RX_HEROKU_X_REQUEST_START) || s.match(RX_NGINX_X_REQUEST_START)
134
+ Time.at(m[1,2].join.to_f / 1000)
135
+ end
136
+
137
+ # This method determines if a body is present. requests with a body (generally POST, PUT) can have a lengthy body which may have taken a while to be received by the web server, inflating their computed wait time. This in turn could lead to unwanted expirations. See wait_overtime property as a way to overcome those.
138
+ # This is a code extraction for readability, this method is only called from a single point.
139
+ def self._request_has_body?(env)
140
+ return true if env['HTTP_TRANSFER_ENCODING'] == 'chunked'
141
+ return false if env['CONTENT_LENGTH'].nil?
142
+ return false if env['CONTENT_LENGTH'].to_i.zero?
143
+ true
144
+ end
145
+
65
146
  def self._set_state!(env, state)
66
147
  raise "Invalid state: #{state.inspect}" unless VALID_STATES.include? state
67
- info = env[ENV_INFO_KEY]
68
- info.state = state
148
+ env[ENV_INFO_KEY].state = state
69
149
  notify_state_change_observers(env)
70
150
  end
71
151
 
72
-
73
152
  ### state change notification-related methods
153
+ @state_change_observers = {}
74
154
 
75
- OBSERVER_CALLBACK_METHOD_NAME = :rack_timeout_request_did_change_state_in
76
- @state_change_observers = {}
77
-
78
- # Registers an object or a block to be called back when a request changes state in rack-timeout.
155
+ # Registers a block to be called back when a request changes state in rack-timeout. The block will receive the request's env.
79
156
  #
80
157
  # `id` is anything that uniquely identifies this particular callback, mostly so it may be removed via `unregister_state_change_observer`.
81
- #
82
- # The second parameter can be either an object that responds to `rack_timeout_request_did_change_state_in(env)` or a block. The object and the block cannot be both specified at the same time.
83
- #
84
- # Example calls:
85
- # Rack::Timeout.register_state_change_observer(:foo_reporter, FooStateReporter.new)
86
- # Rack::Timeout.register_state_change_observer(:bar) { |env| do_bar_things(env) }
87
- def self.register_state_change_observer(id, object = nil, &callback)
88
- raise RuntimeError, "An observer with the id #{id.inspect} is already set." if @state_change_observers.key? id
89
- raise ArgumentError, "Pass either a callback object or a block; never both." unless [object, callback].compact.length == 1
90
- raise RuntimeError, "Object must respond to rack_timeout_request_did_change_state_in" if object && !object.respond_to?(OBSERVER_CALLBACK_METHOD_NAME)
91
- callback.singleton_class.send :alias_method, OBSERVER_CALLBACK_METHOD_NAME, :call if callback
92
- @state_change_observers[id] = object || callback
158
+ def self.register_state_change_observer(id, &callback)
159
+ raise RuntimeError, "An observer with the id #{id.inspect} is already set." if @state_change_observers.key? id
160
+ raise ArgumentError, "A callback block is required." unless callback
161
+ @state_change_observers[id] = callback
93
162
  end
94
163
 
95
164
  # Removes the observer with the given id
96
165
  def self.unregister_state_change_observer(id)
97
- @state_change_observers.delete id
166
+ @state_change_observers.delete(id)
98
167
  end
99
168
 
100
-
101
169
  private
102
-
103
- # Sends out the notifications. Called internally at the end of `set_state!`
170
+ # Sends out the notifications. Called internally at the end of `_set_state!`
104
171
  def self.notify_state_change_observers(env)
105
- @state_change_observers.values.each { |observer| observer.send(OBSERVER_CALLBACK_METHOD_NAME, env) }
172
+ @state_change_observers.values.each { |observer| observer.call(env) }
106
173
  end
107
174
 
108
175
  end
@@ -1,73 +1,63 @@
1
1
  require 'logger'
2
2
 
3
- module Rack
4
- class Timeout
5
-
6
- # convenience method so the current logger can be accessed via Rack::Timeout.logger
7
- def self.logger
8
- @state_change_observers[:logger]
3
+ class Rack::Timeout
4
+ class StageChangeLoggingObserver
5
+ STATE_LOG_LEVEL = { :expired => :error,
6
+ :ready => :info,
7
+ :active => :debug,
8
+ :timed_out => :error,
9
+ :completed => :info,
10
+ }
11
+
12
+ # creates a logger and registers for state change notifications in Rack::Timeout
13
+ def self.register!(logger = nil)
14
+ new.register!(logger)
9
15
  end
10
16
 
11
- class StateChangeLogger < ::Logger
12
- SIMPLE_FORMATTER = ->(severity, timestamp, progname, msg) { "#{msg} at=#{severity.downcase}\n" }
13
- DEFAULT_LEVEL = INFO
14
- STATE_LOG_LEVEL = { ready: INFO,
15
- active: DEBUG,
16
- completed: INFO,
17
- expired: ERROR,
18
- timed_out: ERROR,
19
- }
20
-
21
-
22
- # creates a logger and registers for state change notifications in Rack::Timeout
23
- def self.register!(*a)
24
- new(*a).register!
25
- end
26
-
27
- # registers for state change notifications in Rack::Timeout
28
- def register!(target = ::Rack::Timeout)
29
- target.register_state_change_observer(:logger, self)
30
- end
31
-
32
- def initialize(device = $stderr, *a)
33
- super(device, *a)
34
- self.formatter = SIMPLE_FORMATTER
35
- self.level = self.class.determine_level
36
- end
17
+ # registers for state change notifications in Rack::Timeout (or other explicit target (potentially useful for testing))
18
+ def register!(logger = nil, target = ::Rack::Timeout)
19
+ @logger = logger
20
+ target.register_state_change_observer(:logger, &method(:log_state_change))
21
+ end
37
22
 
38
- # callback method from Rack::Timeout state change notifications
39
- def rack_timeout_request_did_change_state_in(env)
40
- log_state_change(env[ENV_INFO_KEY])
23
+ SIMPLE_FORMATTER = ->(severity, timestamp, progname, msg) { "#{msg} at=#{severity.downcase}\n" }
24
+ def self.mk_logger(device, level = ::Logger::INFO)
25
+ ::Logger.new(device).tap do |logger|
26
+ logger.level = level
27
+ logger.formatter = SIMPLE_FORMATTER
41
28
  end
29
+ end
42
30
 
31
+ class << self
32
+ attr_accessor :logger
33
+ end
34
+ def logger(env = nil)
35
+ self.class.logger ||
36
+ (defined?(::Rails) && Rails.logger) ||
37
+ (env && env['rack.logger']) ||
38
+ (env && env['rack.errors'] && self.class.mk_logger(env['rack.errors'])) ||
39
+ (@fallback_logger ||= self.class.mk_logger($stderr))
40
+ end
43
41
 
44
- private
45
-
46
- # log level is, by precedence, one of: $RACK_TIMEOUT_LOG_LEVEL > $LOG_LEVEL > INFO
47
- def self.determine_level
48
- env_log_level = ENV.values_at("RACK_TIMEOUT_LOG_LEVEL", "LOG_LEVEL").compact.map(&:upcase).first
49
- env_log_level = const_get(env_log_level) if env_log_level && const_defined?(env_log_level)
50
- env_log_level || DEFAULT_LEVEL
51
- end
52
-
53
- # helper method used for formatting in #log_state_change
54
- def ms(s)
55
- '%.fms' % (s * 1000)
56
- end
42
+ # helper method used for formatting in #log_state_change
43
+ def ms(s)
44
+ '%.fms' % (s * 1000)
45
+ end
57
46
 
58
- # generates the actual log string
59
- def log_state_change(info)
60
- add(STATE_LOG_LEVEL[info.state]) do
61
- s = 'source=rack-timeout'
62
- s << ' id=' << info.id if info.id
63
- s << ' age=' << ms(info.age) if info.age
64
- s << ' timeout=' << ms(info.timeout) if info.timeout
65
- s << ' duration=' << ms(info.duration) if info.duration
66
- s << ' state=' << info.state.to_s if info.state
67
- s
68
- end
47
+ # generates the actual log string
48
+ def log_state_change(env)
49
+ info = env[ENV_INFO_KEY]
50
+ level = STATE_LOG_LEVEL[info.state]
51
+ logger(env).send(level) do
52
+ s = 'source=rack-timeout'
53
+ s << ' id=' << info.id if info.id
54
+ s << ' wait=' << ms(info.wait) if info.wait
55
+ s << ' timeout=' << ms(info.timeout) if info.timeout
56
+ s << ' service=' << ms(info.service) if info.service
57
+ s << ' state=' << info.state.to_s if info.state
58
+ s
69
59
  end
70
-
71
60
  end
61
+
72
62
  end
73
63
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-timeout
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0beta3
4
+ version: 0.1.0beta4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Caio Chassot
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-07-01 00:00:00.000000000 Z
11
+ date: 2014-09-30 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Rack middleware which aborts requests that have been running for longer
14
14
  than a specified timeout.
@@ -19,9 +19,9 @@ extra_rdoc_files: []
19
19
  files:
20
20
  - MIT-LICENSE
21
21
  - README.markdown
22
- - lib/rack/timeout/logger.rb
23
- - lib/rack/timeout.rb
24
22
  - lib/rack-timeout.rb
23
+ - lib/rack/timeout.rb
24
+ - lib/rack/timeout/logger.rb
25
25
  homepage: http://github.com/kch/rack-timeout
26
26
  licenses:
27
27
  - MIT
@@ -32,17 +32,17 @@ require_paths:
32
32
  - lib
33
33
  required_ruby_version: !ruby/object:Gem::Requirement
34
34
  requirements:
35
- - - '>='
35
+ - - ">="
36
36
  - !ruby/object:Gem::Version
37
37
  version: '0'
38
38
  required_rubygems_version: !ruby/object:Gem::Requirement
39
39
  requirements:
40
- - - '>'
40
+ - - ">"
41
41
  - !ruby/object:Gem::Version
42
42
  version: 1.3.1
43
43
  requirements: []
44
44
  rubyforge_project:
45
- rubygems_version: 2.0.3
45
+ rubygems_version: 2.2.2
46
46
  signing_key:
47
47
  specification_version: 4
48
48
  summary: Abort requests that are taking too long