Willy Tarreau | 8263b91 | 2011-09-05 01:04:44 +0200 | [diff] [blame] | 1 | 2010/01/24 - Design of multi-criteria request rate shaping. |
| 2 | |
| 3 | We want to be able to rate-shape traffic on multiple cirteria. For instance, we |
| 4 | may want to support shaping of per-host header requests, as well as per source. |
| 5 | |
| 6 | In order to achieve this, we will use checkpoints, one per criterion. Each of |
| 7 | these checkpoints will consist in a test, a rate counter and a queue. |
| 8 | |
| 9 | A request reaches the checkpoint and checks the counter. If the counter is |
| 10 | below the limit, it is updated and the request continues. If the limit is |
| 11 | reached, the request attaches itself into the queue and sleeps. The sleep time |
| 12 | is computed from the queue status, and updates the queue status. |
| 13 | |
| 14 | A task is dedicated to each queue. Its sole purpose is to be woken up when the |
| 15 | next task may wake up, to check the frequency counter, wake as many requests as |
| 16 | possible and update the counter. All the woken up requests are detached from |
| 17 | the queue. Maybe the task dedicated to the queue can be avoided and replaced |
| 18 | with all queued tasks's sleep counters, though this looks tricky. Or maybe it's |
| 19 | just the first request in the queue that should be responsible for waking up |
| 20 | other tasks, and not to forget to pass on this responsibility to next tasks if |
| 21 | it leaves the queue. |
| 22 | |
| 23 | The woken up request then goes on evaluating other criteria and possibly sleeps |
| 24 | again on another one. In the end, the task will have waited the amount of time |
| 25 | required to pass all checkpoints, and all checkpoints will be able to maintain |
| 26 | a permanent load of exactly their limit if enough streams flow through them. |
| 27 | |
| 28 | Since a request can only sleep in one queue at a time, it makes sense to use a |
| 29 | linked list element in each session to attach it to any queue. It could very |
| 30 | well be shared with the pendconn hooks which could then be part of the session. |
| 31 | |
| 32 | This mechanism could be used to rate-shape sessions and requests per backend |
| 33 | and per server. |
| 34 | |
| 35 | When rate-shaping on dynamic criteria, such as the source IP address, we have |
| 36 | to first extract the data pattern, then look it up in a table very similar to |
| 37 | the stickiness tables, but with a frequency counter. At the checkpoint, the |
| 38 | pattern is looked up, the entry created or refreshed, and the frequency counter |
| 39 | updated and checked. Then the request either goes on or sleeps as described |
| 40 | above, but if it sleeps, it's still in the checkpoint's queue, but with a date |
| 41 | computed from the criterion's status. |
| 42 | |
| 43 | This means that we need 3 distinct features : |
| 44 | |
| 45 | - optional pattern extraction |
| 46 | - per-pattern or per-queue frequency counter |
| 47 | - time-ordered queue with a task |
| 48 | |
| 49 | Based on past experiences with frequency counters, it does not appear very easy |
| 50 | to exactly compute sleep delays in advance for multiple requests. So most |
| 51 | likely we'll have to run per-criterion queues too, with only the head of the |
| 52 | queue holding a wake-up timeout. |
| 53 | |
| 54 | This finally leads us to the following : |
| 55 | |
| 56 | - optional pattern extraction |
| 57 | - per-pattern or per-queue frequency counter |
| 58 | - per-frequency counter queue |
| 59 | - head of the queue serves as a global queue timer. |
| 60 | |
| 61 | This brings us to a very flexible architecture : |
| 62 | - 1 list of rule-based checkpoints per frontend |
| 63 | - 1 list of rule-based checkpoints per backend |
| 64 | - 1 list of rule-based checkpoints per server |
| 65 | |
| 66 | Each of these lists have a lot of rules conditionned by ACLs, just like the |
| 67 | use-backend rules, except that all rules are evaluated in turn. |
| 68 | |
| 69 | Since we might sometimes just want to enable that without setting any limit and |
| 70 | just for enabling control in ACLs (or logging ?), we should probably try to |
| 71 | find a flexible way of declaring just a counter without a queue. |
| 72 | |
| 73 | These checkpoints could be of two types : |
| 74 | - rate-limit (described here) |
| 75 | - concurrency-limit (very similar with the counter and no timer). This |
| 76 | feature would require to keep track of all accounted criteria in a |
| 77 | request so that they can be released upon request completion. |
| 78 | |
| 79 | It should be possible to define a max of requests in the queue, above which a |
| 80 | 503 is returned. The same applies for the max delay in the queue. We could have |
| 81 | it per-task (currently it's the connection timeout) and abort tasks with a 503 |
| 82 | when the delay is exceeded. |
| 83 | |
| 84 | Per-server connection concurrency could be converted to use this mechanism |
| 85 | which is very similar. |
| 86 | |
| 87 | The construct should be flexible enough so that the counters may be checked |
| 88 | from ACLs. That would allow to reject connections or switch to an alternate |
| 89 | backend when some limits are reached. |
| 90 | |