Throttling and Caching
In order to provide maximum data throughput and at the same time protect your application from, and obviate coding for, server errors due to throttle rate violations and/or polling abuse, Soapi.JS2 implements a caching throttle implementation, Soapi.Net.ThrottleCache
, that throttles on a sliding window with a governor supplemented by a per route configurable request cache.
Throttling and caching is optional but strongly recommended and is enabled by default.
All http requests are handled by a static instance of ThrottleCache
that lives @ Soapi.Net.Throttle
.
e.g.
// internal code
Soapi.Net.Throttle =
new Soapi.Net.ThrottleCache(throttleWindowTime, throttleWindowCount,
maxActiveRequests, defaultCacheDuration);
// Allow [throttleWindowCount] requests per [throttleWindowTime]
// while active requests < maxActiveRequests
// Cache all results [defaultCacheDuration] if an explicit cache rule for that route,
// e.g. '/users/{id}' is not found.
Soapi.Net.Throttle.enqueue(request);
Throttling
As a request is accepted by the throttle, the internal request cache is checked for matching results, if found the request is satisified immediately and the workflow is complete.
If a cache hit is not found, the request is placed in a queue which is processed at a regular polling interval. The resolution of this polling timer is arbitrary and can be changed by stopping and restarting the throttle.
Soapi.Net.Throttle.suspend();
Soapi.Net.Throttle.start(pollingIntervalInMs);
At polling time, there are 3 conditions that must be met in order for a request to be sent to the API:
- that the cache has not received matching data while the request sat in the queue. If this is the case, the request is satsified from the cache.
- that the number of active requests does not exceed the configured maximum active request count. If the number of active requests meets or exceeds the max allowed, processing of the queue is deferred.
- that the interval, in ms, between now and the nth previous request, where nth is the window count, is equal to or greater than the configured window interval, otherwise processing of the queue is deferred.
When these conditions are met, a request is popped from the queue and sent to the API.
Caching
Each successful request, after the json has been converted into domain objects, it is placed in a request cache, keyed by the url.
Each cache item is scheduled to be removed from the cache [defaultCacheDuration] milliseconds after it is placed, unless an explicit cache rule has been added for that route, e.g.
// cache stackauth.com/1.0/sites for 5 minutes
Soapi.Net.Throttle.addCacheRule('/sites', 360000);
You can 'seed' the cache with data for fault tolerance or testing purposes by using the same mechanism used internally when a successful response is received and processed, e.g.
Soapi.Net.Throttle.signal(manuallyConstructedSites, '/sites', 'http://stackauth.com/sites?key=yourkey')
The single static instance of ThrottleCache will be used by any and all ApiContext instances you may have in play on any single page.
Example:
Throttle-cache log from AlphaQuickStart.htm with 30 requests per 5 second window with max 15 active requests and a 60 second default cache duration - note the cache scavenging happening as the browser was left idle.
throttle: 2010-07-20 20:27:44.830 - request processed: interval: 1282361264830ms
throttle: 2010-07-20 20:27:45.027 - request processed: interval: 197ms
throttle: 2010-07-20 20:27:45.037 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.047 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.057 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.067 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.077 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.087 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.097 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.107 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.117 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.128 - request processed: interval: 11ms
throttle: 2010-07-20 20:27:45.138 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.148 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.158 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.168 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.178 - active queue full
throttle: 2010-07-20 20:27:45.268 - request processed: interval: 100ms
throttle: 2010-07-20 20:27:45.278 - active queue full
throttle: 2010-07-20 20:27:45.568 - request processed: interval: 300ms
throttle: 2010-07-20 20:27:45.578 - active queue full
throttle: 2010-07-20 20:27:46.730 - request processed: interval: 1162ms
throttle: 2010-07-20 20:27:46.740 - active queue full
throttle: 2010-07-20 20:27:47.010 - request processed: interval: 280ms
throttle: 2010-07-20 20:27:47.020 - active queue full
throttle: 2010-07-20 20:27:47.040 - request processed: interval: 30ms
throttle: 2010-07-20 20:27:47.050 - active queue full
throttle: 2010-07-20 20:27:47.365 - request processed: interval: 325ms
throttle: 2010-07-20 20:27:47.376 - active queue full
throttle: 2010-07-20 20:27:47.449 - request processed: interval: 84ms
throttle: 2010-07-20 20:27:47.465 - active queue full
throttle: 2010-07-20 20:27:47.535 - request processed: interval: 86ms
throttle: 2010-07-20 20:27:47.546 - active queue full
throttle: 2010-07-20 20:27:47.746 - request processed: interval: 211ms
throttle: 2010-07-20 20:27:47.756 - active queue full
throttle: 2010-07-20 20:27:47.997 - request processed: interval: 251ms
throttle: 2010-07-20 20:27:48.007 - active queue full
throttle: 2010-07-20 20:27:48.097 - request processed: interval: 100ms
throttle: 2010-07-20 20:27:48.107 - active queue full
throttle: 2010-07-20 20:27:48.327 - request processed: interval: 230ms
throttle: 2010-07-20 20:27:48.337 - active queue full
throttle: 2010-07-20 20:27:48.898 - request processed: interval: 571ms
throttle: 2010-07-20 20:27:48.908 - active queue full
throttle: 2010-07-20 20:27:48.921 - request processed: interval: 23ms
throttle: 2010-07-20 20:27:48.939 - active queue full
throttle: 2010-07-20 20:27:49.020 - waiting 810ms
throttle: 2010-07-20 20:27:49.832 - request processed: interval: 911ms
throttle: 2010-07-20 20:27:49.843 - waiting 184ms
throttle: 2010-07-20 20:27:50.027 - request processed: interval: 195ms
throttle: 2010-07-20 20:27:50.037 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:50.047 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:50.057 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:50.067 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:50.077 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:50.087 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:50.097 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:50.107 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:50.117 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:50.128 - request processed: interval: 11ms
throttle: 2010-07-20 20:27:50.138 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:50.148 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:50.158 - active queue full
throttle: 2010-07-20 20:27:50.170 - request processed: interval: 22ms
throttle: 2010-07-20 20:27:50.181 - request processed: interval: 11ms
throttle: 2010-07-20 20:27:50.192 - active queue full
throttle: 2010-07-20 20:27:50.262 - waiting 6ms
throttle: 2010-07-20 20:27:50.273 - request processed: interval: 92ms
throttle: 2010-07-20 20:27:50.284 - waiting 284ms
cache: 2010-07-20 20:27:50.387 - served from cache: http://api.stackapps.com/1.0/users/14/answers
cache: 2010-07-20 20:27:50.388 - served from cache: http://api.stackapps.com/1.0/users/14/badges
cache: 2010-07-20 20:27:50.388 - served from cache: http://api.stackapps.com/1.0/users/14/comments
cache: 2010-07-20 20:27:50.388 - served from cache: http://api.stackapps.com/1.0/users/14/comments/18
cache: 2010-07-20 20:27:50.388 - served from cache: http://api.stackapps.com/1.0/users/14/favorites
cache: 2010-07-20 20:27:50.388 - served from cache: http://api.stackapps.com/1.0/users/14/mentioned
cache: 2010-07-20 20:27:50.389 - served from cache: http://api.stackapps.com/1.0/users/14/reputation
cache: 2010-07-20 20:27:50.389 - served from cache: http://api.stackapps.com/1.0/users/14/tags
cache: 2010-07-20 20:27:50.389 - served from cache: http://api.stackapps.com/1.0/users/14/timeline
throttle: 2010-07-20 20:27:50.575 - request processed: interval: 302ms
throttle: 2010-07-20 20:27:50.586 - waiting 1144ms
cache: 2010-07-20 20:27:51.397 - served from cache: http://api.stackapps.com/1.0/questions/386/answers
cache: 2010-07-20 20:27:51.397 - served from cache: http://api.stackapps.com/1.0/questions/386/comments
cache: 2010-07-20 20:27:51.397 - served from cache: http://api.stackapps.com/1.0/questions/386/timeline
cache: 2010-07-20 20:27:51.398 - served from cache: http://api.stackapps.com/1.0/revisions/386
cache: 2010-07-20 20:27:51.398 - served from cache: http://api.stackapps.com/1.0/revisions/386/e452e755-a3fe-45d0-976f-e319ec460200
throttle: 2010-07-20 20:27:51.732 - request processed: interval: 1157ms
throttle: 2010-07-20 20:27:51.742 - waiting 268ms
throttle: 2010-07-20 20:27:52.012 - request processed: interval: 280ms
throttle: 2010-07-20 20:27:52.022 - waiting 18ms
throttle: 2010-07-20 20:27:52.042 - request processed: interval: 30ms
throttle: 2010-07-20 20:27:52.052 - waiting 313ms
throttle: 2010-07-20 20:27:52.373 - request processed: interval: 331ms
throttle: 2010-07-20 20:27:52.383 - waiting 66ms
throttle: 2010-07-20 20:27:52.453 - request processed: interval: 80ms
throttle: 2010-07-20 20:27:52.463 - waiting 72ms
throttle: 2010-07-20 20:27:52.544 - request processed: interval: 91ms
throttle: 2010-07-20 20:27:52.554 - waiting 192ms
throttle: 2010-07-20 20:27:52.755 - request processed: interval: 211ms
throttle: 2010-07-20 20:27:52.766 - waiting 231ms
throttle: 2010-07-20 20:27:53.001 - request processed: interval: 246ms
throttle: 2010-07-20 20:27:53.011 - waiting 86ms
throttle: 2010-07-20 20:27:53.101 - request processed: interval: 100ms
throttle: 2010-07-20 20:27:53.112 - waiting 215ms
throttle: 2010-07-20 20:27:53.334 - request processed: interval: 233ms
throttle: 2010-07-20 20:27:53.345 - waiting 553ms
throttle: 2010-07-20 20:27:53.903 - request processed: interval: 569ms
throttle: 2010-07-20 20:27:53.914 - waiting 7ms
throttle: 2010-07-20 20:27:53.924 - request processed: interval: 21ms
throttle: 2010-07-20 20:27:53.935 - waiting 897ms
throttle: 2010-07-20 20:27:54.839 - request processed: interval: 915ms
throttle: 2010-07-20 20:27:54.849 - waiting 178ms
throttle: 2010-07-20 20:27:55.029 - request processed: interval: 190ms
throttle: 2010-07-20 20:27:55.040 - request processed: interval: 11ms
throttle: 2010-07-20 20:27:55.051 - request processed: interval: 11ms
throttle: 2010-07-20 20:27:55.062 - request processed: interval: 11ms
throttle: 2010-07-20 20:27:55.073 - request processed: interval: 11ms
throttle: 2010-07-20 20:27:55.084 - request processed: interval: 11ms
throttle: 2010-07-20 20:27:55.094 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:55.104 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:55.114 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:55.124 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:55.134 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:55.145 - request processed: interval: 11ms
throttle: 2010-07-20 20:27:55.155 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:55.166 - waiting 4ms
throttle: 2010-07-20 20:27:55.176 - request processed: interval: 21ms
throttle: 2010-07-20 20:27:55.187 - active queue full
throttle: 2010-07-20 20:27:56.079 - request processed: interval: 903ms
throttle: 2010-07-20 20:27:56.090 - active queue full
throttle: 2010-07-20 20:27:56.212 - request processed: interval: 133ms
throttle: 2010-07-20 20:27:56.222 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:56.232 - waiting 500ms
throttle: 2010-07-20 20:27:56.735 - request processed: interval: 513ms
throttle: 2010-07-20 20:27:56.745 - waiting 267ms
throttle: 2010-07-20 20:27:57.018 - request processed: interval: 283ms
throttle: 2010-07-20 20:27:57.028 - waiting 14ms
throttle: 2010-07-20 20:27:57.049 - request processed: interval: 31ms
throttle: 2010-07-20 20:27:57.059 - active queue full
throttle: 2010-07-20 20:27:57.261 - waiting 112ms
throttle: 2010-07-20 20:27:57.373 - request processed: interval: 324ms
throttle: 2010-07-20 20:27:57.383 - waiting 70ms
throttle: 2010-07-20 20:27:57.455 - request processed: interval: 82ms
throttle: 2010-07-20 20:27:57.465 - waiting 79ms
throttle: 2010-07-20 20:27:57.545 - request processed: interval: 90ms
throttle: 2010-07-20 20:27:57.566 - waiting 189ms
throttle: 2010-07-20 20:27:57.774 - request processed: interval: 229ms
throttle: 2010-07-20 20:27:57.796 - waiting 205ms
throttle: 2010-07-20 20:27:58.003 - request processed: interval: 229ms
throttle: 2010-07-20 20:27:58.023 - waiting 78ms
throttle: 2010-07-20 20:27:58.103 - request processed: interval: 100ms
throttle: 2010-07-20 20:27:58.123 - active queue full
throttle: 2010-07-20 20:27:58.351 - request processed: interval: 248ms
throttle: 2010-07-20 20:27:58.361 - active queue full
throttle: 2010-07-20 20:27:58.412 - waiting 491ms
throttle: 2010-07-20 20:27:58.907 - request processed: interval: 556ms
throttle: 2010-07-20 20:27:58.918 - waiting 6ms
throttle: 2010-07-20 20:27:58.928 - request processed: interval: 21ms
throttle: 2010-07-20 20:27:58.938 - waiting 901ms
throttle: 2010-07-20 20:27:59.842 - request processed: interval: 914ms
throttle: 2010-07-20 20:27:59.852 - waiting 177ms
throttle: 2010-07-20 20:28:0.033 - request processed: interval: 191ms
throttle: 2010-07-20 20:28:0.043 - request processed: interval: 10ms
throttle: 2010-07-20 20:28:0.053 - request processed: interval: 10ms
throttle: 2010-07-20 20:28:0.063 - request processed: interval: 10ms
throttle: 2010-07-20 20:28:0.073 - request processed: interval: 10ms
throttle: 2010-07-20 20:28:0.083 - waiting 1ms
throttle: 2010-07-20 20:28:0.093 - request processed: interval: 20ms
throttle: 2010-07-20 20:28:0.104 - request processed: interval: 11ms
throttle: 2010-07-20 20:28:0.115 - request processed: interval: 11ms
throttle: 2010-07-20 20:28:0.125 - request processed: interval: 10ms
throttle: 2010-07-20 20:28:0.135 - request processed: interval: 10ms
throttle: 2010-07-20 20:28:0.145 - request processed: interval: 10ms
throttle: 2010-07-20 20:28:0.155 - request processed: interval: 10ms
throttle: 2010-07-20 20:28:0.165 - request processed: interval: 10ms
throttle: 2010-07-20 20:28:0.175 - active queue full
throttle: 2010-07-20 20:28:0.216 - request processed: interval: 51ms
throttle: 2010-07-20 20:28:0.227 - active queue full
throttle: 2010-07-20 20:28:0.237 - waiting 842ms
throttle: 2010-07-20 20:28:1.081 - request processed: interval: 865ms
throttle: 2010-07-20 20:28:1.092 - waiting 120ms
throttle: 2010-07-20 20:28:1.215 - request processed: interval: 134ms
throttle: 2010-07-20 20:28:1.226 - request processed: interval: 11ms
throttle: 2010-07-20 20:28:1.237 - waiting 498ms
throttle: 2010-07-20 20:28:1.735 - request processed: interval: 509ms
throttle: 2010-07-20 20:28:1.746 - waiting 272ms
throttle: 2010-07-20 20:28:2.026 - request processed: interval: 291ms
throttle: 2010-07-20 20:28:2.036 - waiting 13ms
throttle: 2010-07-20 20:28:2.056 - request processed: interval: 30ms
throttle: 2010-07-20 20:28:2.066 - waiting 307ms
throttle: 2010-07-20 20:28:2.376 - request processed: interval: 320ms
throttle: 2010-07-20 20:28:2.387 - waiting 68ms
throttle: 2010-07-20 20:28:2.457 - request processed: interval: 81ms
throttle: 2010-07-20 20:28:2.467 - waiting 78ms
throttle: 2010-07-20 20:28:2.548 - request processed: interval: 91ms
throttle: 2010-07-20 20:28:2.559 - waiting 215ms
throttle: 2010-07-20 20:28:2.779 - request processed: interval: 231ms
throttle: 2010-07-20 20:28:2.790 - waiting 213ms
throttle: 2010-07-20 20:28:3.011 - request processed: interval: 232ms
throttle: 2010-07-20 20:28:3.022 - waiting 81ms
throttle: 2010-07-20 20:28:3.112 - request processed: interval: 101ms
throttle: 2010-07-20 20:28:3.122 - active queue full
throttle: 2010-07-20 20:28:3.343 - waiting 8ms
throttle: 2010-07-20 20:28:3.354 - request processed: interval: 242ms
throttle: 2010-07-20 20:28:3.365 - active queue full
throttle: 2010-07-20 20:28:3.385 - waiting 522ms
throttle: 2010-07-20 20:28:3.916 - request processed: interval: 562ms
throttle: 2010-07-20 20:28:3.927 - waiting 1ms
throttle: 2010-07-20 20:28:3.938 - request processed: interval: 22ms
throttle: 2010-07-20 20:28:3.948 - waiting 894ms
throttle: 2010-07-20 20:28:4.846 - request processed: interval: 908ms
throttle: 2010-07-20 20:28:4.856 - waiting 177ms
throttle: 2010-07-20 20:28:5.036 - request processed: interval: 190ms
throttle: 2010-07-20 20:28:5.046 - request processed: interval: 10ms
throttle: 2010-07-20 20:28:5.056 - request processed: interval: 10ms
throttle: 2010-07-20 20:28:5.066 - active queue full
throttle: 2010-07-20 20:28:5.106 - request processed: interval: 50ms
throttle: 2010-07-20 20:28:5.117 - active queue full
throttle: 2010-07-20 20:28:5.198 - request processed: interval: 92ms
throttle: 2010-07-20 20:28:5.208 - active queue full
throttle: 2010-07-20 20:28:5.270 - request processed: interval: 72ms
throttle: 2010-07-20 20:28:5.280 - active queue full
throttle: 2010-07-20 20:28:5.340 - request processed: interval: 70ms
throttle: 2010-07-20 20:28:7.424 - request processed: interval: 2084ms
throttle: 2010-07-20 20:28:9.329 - request processed: interval: 1905ms
throttle: 2010-07-20 20:28:13.267 - request processed: interval: 3938ms
throttle: 2010-07-20 20:28:14.359 - request processed: interval: 1092ms
cache: 2010-07-20 20:28:45.017 - removed from cache: http://stackauth.com/1.0/sites
cache: 2010-07-20 20:28:45.560 - removed from cache: http://api.stackapps.com/1.0/answers/704/comments
cache: 2010-07-20 20:28:46.720 - removed from cache: http://api.stackapps.com/1.0/badges/name
cache: 2010-07-20 20:28:47.002 - removed from cache: http://api.stackapps.com/1.0/answers/704
cache: 2010-07-20 20:28:47.037 - removed from cache: http://api.stackapps.com/1.0/badges
[-- truncated for size --]
cache: 2010-07-20 20:29:13.267 - removed from cache: http://api.stackapps.com/1.0/questions?pagesize=100&page=5
cache: 2010-07-20 20:29:14.356 - removed from cache: http://api.stackapps.com/1.0/questions?pagesize=100&page=6
cache: 2010-07-20 20:29:15.175 - removed from cache: http://api.stackapps.com/1.0/questions?pagesize=100&page=7
>>>
site.Tags().ForEach(function(tag){ });
it will be a novelty except for use in browser extensions but should be interesting. But the closures will be dramatically reduced.