{"action":"created","issue":{"url":"https://api.github.com/repos/kuzzleio/kuzzle/issues/217","repository_url":"https://api.github.com/repos/kuzzleio/kuzzle","labels_url":"https://api.github.com/repos/kuzzleio/kuzzle/issues/217/labels{/name}","comments_url":"https://api.github.com/repos/kuzzleio/kuzzle/issues/217/comments","events_url":"https://api.github.com/repos/kuzzleio/kuzzle/issues/217/events","html_url":"https://github.com/kuzzleio/kuzzle/pull/217","id":144596894,"number":217,"title":"Prevent event loop saturation","user":{"login":"scottinet","id":12997967,"avatar_url":"https://avatars.githubusercontent.com/u/12997967?v=3","gravatar_id":"","url":"https://api.github.com/users/scottinet","html_url":"https://github.com/scottinet","followers_url":"https://api.github.com/users/scottinet/followers","following_url":"https://api.github.com/users/scottinet/following{/other_user}","gists_url":"https://api.github.com/users/scottinet/gists{/gist_id}","starred_url":"https://api.github.com/users/scottinet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scottinet/subscriptions","organizations_url":"https://api.github.com/users/scottinet/orgs","repos_url":"https://api.github.com/users/scottinet/repos","events_url":"https://api.github.com/users/scottinet/events{/privacy}","received_events_url":"https://api.github.com/users/scottinet/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":2,"created_at":"2016-03-30T13:40:29Z","updated_at":"2016-03-30T14:04:20Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/kuzzleio/kuzzle/pulls/217","html_url":"https://github.com/kuzzleio/kuzzle/pull/217","diff_url":"https://github.com/kuzzleio/kuzzle/pull/217.diff","patch_url":"https://github.com/kuzzleio/kuzzle/pull/217.patch"},"body":"Prior to this pull request, when flooding Kuzzle (with >2000 requests/second), the NodeJS event loop was gradually filled with promises, Kuzzle was becoming slower and slower, eventually crashing if the flood persisted. If the flood stopped before Kuzzle crashes, it took several minutes to Kuzzle to recover from this state.\r\n\r\nThe purpose of this pull request is to make Kuzzle resilient under stressful conditions:\r\n\r\n* Replaced promises with callback in protocol plugins, in the router controller and in the funnel controller. This was made to keep tight control on the event loop, as promises go in it, while callbacks don't.\r\n* Added two properties in the `.kuzzlerc` configuration file:\r\n  * `maxConcurrentRequests` configures the maximum number of requests that can be executed at the same time. All requests submitted above that number are queued to be executed at a later time\r\n  * `maxRetainedRequests` configures the maximum number of requests that can be queued. Once this limit is reached, subsequent requests are discarded with a \"service temporarily unavailable\" error (status code 503)\r\n* The funnel controller now has 2 states:\r\n  * normal state: when a request is submitted, if it's under the `maxConcurrentRequests` limit, it's executed immediately, as before\r\n  * overloaded state: the funnel controller enters this state if the `maxConcurrentRequests` limit is reached. In this state, new requests are queued and a background task is started to replay delayed requests as soon as possible. The background task stops automatically when the funnel switch back to normal state\r\n* Moved token retrieval from the router controller to the request execution part in the funnel controller. This avoids repetition of code, and it helps controlling the event loop by performing these tasks once it was made sure that the request could be executed\r\n* Added a new `server:overload` hook, fired at most each 500ms. The purpose of this hook is to inform potential plugins, or Kuzzle itself, about the overload state. The overload percentage is provided to the listeners (for instance: '28.63%'). This percentage reflects the number of queued requests compared to the `maxRetainedRequests` property\r\n* Updated the SocketIO protocol plugin to use a callback instead of a promise for the `getRouter().execute` plugin context method\r\n* Updated protocol plugins documentation\r\n"},"comment":{"url":"https://api.github.com/repos/kuzzleio/kuzzle/issues/comments/203447036","html_url":"https://github.com/kuzzleio/kuzzle/pull/217#issuecomment-203447036","issue_url":"https://api.github.com/repos/kuzzleio/kuzzle/issues/217","id":203447036,"user":{"login":"dbengsch","id":16542512,"avatar_url":"https://avatars.githubusercontent.com/u/16542512?v=3","gravatar_id":"","url":"https://api.github.com/users/dbengsch","html_url":"https://github.com/dbengsch","followers_url":"https://api.github.com/users/dbengsch/followers","following_url":"https://api.github.com/users/dbengsch/following{/other_user}","gists_url":"https://api.github.com/users/dbengsch/gists{/gist_id}","starred_url":"https://api.github.com/users/dbengsch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dbengsch/subscriptions","organizations_url":"https://api.github.com/users/dbengsch/orgs","repos_url":"https://api.github.com/users/dbengsch/repos","events_url":"https://api.github.com/users/dbengsch/events{/privacy}","received_events_url":"https://api.github.com/users/dbengsch/received_events","type":"User","site_admin":false},"created_at":"2016-03-30T14:04:20Z","updated_at":"2016-03-30T14:04:20Z","body":"+1\r\n"}}