BDE 4.39.x Production Release
Loading...
Searching...
No Matches
balb_ratelimiter.h
Go to the documentation of this file.
1/// @file balb_ratelimiter.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// balb_ratelimiter.h -*-C++-*-
8#ifndef INCLUDED_BALB_RATELIMITER
9#define INCLUDED_BALB_RATELIMITER
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup balb_ratelimiter balb_ratelimiter
15/// @brief Provide a mechanism to limit peak and sustained consumption rates.
16/// @addtogroup bal
17/// @{
18/// @addtogroup balb
19/// @{
20/// @addtogroup balb_ratelimiter
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#balb_ratelimiter-purpose"> Purpose</a>
25/// * <a href="#balb_ratelimiter-classes"> Classes </a>
26/// * <a href="#balb_ratelimiter-description"> Description </a>
27/// * <a href="#balb_ratelimiter-internal-model"> Internal Model </a>
28/// * <a href="#balb_ratelimiter-monitoring-resource-usage"> Monitoring Resource Usage </a>
29/// * <a href="#balb_ratelimiter-time-synchronization"> Time Synchronization </a>
30/// * <a href="#balb_ratelimiter-usage"> Usage </a>
31/// * <a href="#balb_ratelimiter-example-1-controlling-network-traffic-generation"> Example 1: Controlling Network Traffic Generation </a>
32///
33/// # Purpose {#balb_ratelimiter-purpose}
34/// Provide a mechanism to limit peak and sustained consumption rates.
35///
36/// # Classes {#balb_ratelimiter-classes}
37///
38/// - balb::RateLimiter: mechanism to monitor resource consumption rates
39///
40/// @see balb_leakybucket
41///
42/// # Description {#balb_ratelimiter-description}
43/// This component provides a mechanism, `balb::RateLimiter`, that
44/// enables clients to monitor and control the use of a resource such that the
45/// peak consumption rate and the sustained consumption rate do not exceed their
46/// respective configured limits.
47///
48/// The limits on resource consumption rates of a `balb::RateLimiter` object are
49/// configured using a specified peak rate (measured in `units/s`) along with
50/// its time-window, and a specified sustained rate (measured in `units/s`)
51/// along with its time-window. The peak-rate time-window indicates a sliding
52/// time period over which the average consumption rate shall not exceed the
53/// peak-rate; similarly, the sustained-rate time-window indicates a sliding
54/// time period over which the average consumption rate shall not exceed the
55/// sustained rate. `unit` is a generic unit of measurement (e.g., bytes,
56/// megabytes, number of messages, packets, liters, clock cycles, etc.).
57///
58/// ## Internal Model {#balb_ratelimiter-internal-model}
59///
60///
61/// Internally, a rate limiter (currently) models resource usage using two
62/// corresponding `balb::LeakyBucket` objects, one for limiting peak resource
63/// usage and one for limiting sustained resource usage. Each leaky bucket
64/// provides an approximation for a moving total, where the configured time
65/// window corresponds to the period of the moving total, and that time window
66/// multiplied by the corresponding rate indicates the sum that the moving total
67/// may not exceed (i.e., the capacity of the leaky bucket). As the units are
68/// submitted to a rate limiter, they are added to both the peak and sustained
69/// rate moving-totals, and then removed over time at the corresponding
70/// configured rate.
71///
72/// Figure 1 illustrates the behavior of a rate limiter during a typical usage
73/// scenario using moving-totals:
74/// @code
75/// Fig. 1:
76///
77/// Rp (peak rate) = 2 units/s
78/// Wp (peak-rate time-window) = 2 s
79/// Rs (sustained rate) = 1 units/s
80/// Ws (sustained-rate time-window) = 7 s
81///
82/// Submit 5 Submit 7
83///
84/// | | | | | | | | | | 7|~~~|
85/// 12| | 6| | 12| | 6| | 12| | 6|~~~|
86/// 11| | 5|~~~| 11| | 5| | 11| | 5|~~~|
87/// 10| | Lp-4|~~~| 10| | Lp-4|---| 10| | Lp-4|~~~|
88/// 9| | 3|~~~| 9| | 3| | 9|~~~| 3|~~~|
89/// 8| | 2|~~~| 8| | 2| | 8|~~~| 2|~~~|
90/// Ls-7|---| 1|~~~| Ls-7|---| 1|~~~| Ls-7|~~~| 1|~~~|
91/// 6| | +- -+ 6| | +- -+ 6|~~~| +- -+
92/// 5|~~~| 5| | 5|~~~|
93/// 4|~~~| 4| | 4|~~~|
94/// 3|~~~| 3|~~~| 3|~~~|
95/// 2|~~~| 2|~~~| 2|~~~|
96/// 1|~~~| 1|~~~| 1|~~~|
97/// +- -+ +- -+ +- -+
98///
99/// Time: t0 t0 + 2s t0 + 2s
100///
101///
102/// Submit 2
103///
104/// | | 7| | | | 7| | | | 7| |
105/// 12| | 6| | 12| | 6| | 12| | 6| |
106/// 11| | 5| | 11| | 5| | 11| | 5| |
107/// 10| | Lp-4|---| 10| | Lp-4|---| 10| | Lp-4|---|
108/// 9| | 3|~~~| 9| | 3| | 9| | 3|~~~|
109/// 8| | 2|~~~| 8| | 2| | 8| | 2|~~~|
110/// Ls-7|~~~| 1|~~~| Ls-7|---| 1|~~~| Ls-7|~~~| 1|~~~|
111/// 6|~~~| +- -+ 6|---| +- -+ 6|~~~| +- -+
112/// 5|~~~| 5|~~~| 5|~~~|
113/// 4|~~~| 4|~~~| 4|~~~|
114/// 3|~~~| 3|~~~| 3|~~~|
115/// 2|~~~| 2|~~~| 2|~~~|
116/// 1|~~~| 1|~~~| 1|~~~|
117/// +- -+ +- -+ +- -+
118///
119/// Time: t0 + 4s t0 + 6s t0 + 6s
120/// @endcode
121/// Suppose we have a rate limiter with a peak rate of `Rp = 2 units/s`, a
122/// peak-rate time-window of `Wp = 2 s`, a sustained rate of `Rs = 1 units/s`,
123/// and a sustained-rate time-window of `Ws = 7 s`.
124///
125/// This rate limiter maintains a moving-total having a capacity
126/// `Lp = Rp * Wp = 4 units` that controls the peak rate and another
127/// moving-total having a capacity `Ls = Rs * Ws = 7 units` that controls the
128/// sustained rate.
129///
130/// Figure 1 shows the following sequence of events:
131/// * At time `t0s`, we submit 5 units. The submitted units are added to the
132/// both moving-totals, and as a result the `Lp` is exceeded, which means
133/// that the average consumption rate over the peak-rate time-window has
134/// exceeded the peak rate. Note that we can not submit any more units at
135/// this time even though `Ls` is not exceeded (the average consumption rate
136/// over the sustained-rate time-windows has not exceeded the sustained
137/// rate).
138/// * At time `t0 + 2s` the number of units contained moving-totals are
139/// recalculated. As a result, 4 units (`Rp * 2 s`) are subtracted from the
140/// peak rate moving-total, and 2 units (`Rs * 2 s`) are subtracted from the
141/// sustained rate moving-total. Now, capacities of both moving-totals are
142/// no longer exceeded, so we are free to submit more units. We submit 7
143/// units, causing both `Lp` and `Ls` to be exceeded.
144/// * At time `t0 + 4s`, the moving-totals are again updated. The `Lp` limit
145/// is no longer exceeded. The number of units held by the moving-total
146/// tracking sustained rate matches the moving-total's capacity, and this
147/// boundary condition imply and no units can be submitted, because
148/// submitting any amount of units would cause `Ls` to be exceeded.
149/// * At time `t0 + 6s`, the moving-totals are again updated. Both `Lp` and
150/// `Ls` are no longer exceeded. We submit 2 units. The `Lp` limit is not
151/// exceeded, but `Ls` limit is exceeded.
152///
153/// ## Monitoring Resource Usage {#balb_ratelimiter-monitoring-resource-usage}
154///
155///
156/// A `balb::LeakyBucket` provides methods to both submit units and reserve
157/// units for future submission. Submitting a unit indicates that it has been
158/// consumed by the entity being modeled, and it is added to the moving-totals
159/// tracking both peak and sustained resource usage.
160///
161/// Reserving a unit guarantees that available capacity will be reserved so that
162/// unit can be submitted in the future without exceeding the configured limits.
163/// Reserved units may be later submitted using the `submitReserved` method or
164/// canceled using the `cancelReserved` method. Reserved units permanently
165/// reside in the two moving-totals of consumed units, resulting in the
166/// reduction in the effective capacities of the moving-totals, until the
167/// reserved units are canceled or submitted. Reserving units effectively
168/// shortens the time-window during which the average sustained and peak rate
169/// are enforced. Therefore, the time interval between reserving units and
170/// submitting or canceling them should be kept as short as possible. For a
171/// practical example of using reserved units, please see
172/// @ref balb_reservationguard .
173///
174/// The recommended usage of a rate limiter is to first check whether 1 unit can
175/// be added without exceeding the rate limiter's configured limits, and if so,
176/// consume the desired amount of the resource. Afterwards, submit the amount
177/// of consumed resource to the rate limiter.
178///
179/// Whether submitting more units would exceed the configured limits can be
180/// determined using the `wouldExceedBandwidth` method. The estimated amount of
181/// time to wait before 1 more unit will be allowed to be submitted can be
182/// determined using the `calculateTimeToSubmit` method.
183///
184/// ## Time Synchronization {#balb_ratelimiter-time-synchronization}
185///
186///
187/// rate limiter does not utilize an internal timer, so timing must be handled
188/// manually. Clients can specify an initial time interval for a rate limiter
189/// object at construction or using the `reset` method. Whenever the state of a
190/// rate limiter object needs to be updated, clients must invoke the
191/// `updateState` method specifying the current time interval. Since rate
192/// limiter cares only about the elapsed time (not absolute time), the specified
193/// time intervals may be relative to any arbitrary time origin, though all of
194/// them must refer to the same origin. For the sake of consistency, clients
195/// are encouraged to use the unix epoch time (such as the values returned by
196/// `bdlt::CurrentTime::now`).
197///
198/// ## Usage {#balb_ratelimiter-usage}
199///
200///
201/// This section illustrates the intended use of this component.
202///
203/// ### Example 1: Controlling Network Traffic Generation {#balb_ratelimiter-example-1-controlling-network-traffic-generation}
204///
205///
206/// Suppose that we want to send data over a network interface with the load
207/// spike limitations explained below:
208///
209/// * The long term average rate of resource usage (i.e., the sustained rate)
210/// should not exceed 1024 bytes/s (`Rs`).
211/// * The period over which to monitor the long term average rate (i.e., the
212/// sustained-rate time-window) should be 0.5s (`Wp`).
213/// * The peak resource usage (i.e., the peak rate) should not exceed 2048
214/// bytes/s (`Rp`).
215/// * The period over which to monitor the peak resource usage should be
216/// 0.0625s (Wp).
217///
218/// This is shown in Figure 2 below.
219/// @code
220/// Fig. 2:
221///
222/// ^ Rate (Units per second)
223/// | _____ .
224/// | / B \ .
225/// 2048|---------------------------/-------\--------Rp (Maximum peak rate)
226/// | __ / \ .
227/// | / \ / A2 \ .
228/// | / A1 \ / \ .
229/// 1024|--------/------\ ------/---------------\----Rs (Maximum sustained rate)
230/// | __ / \ / \__.
231/// |__/ \/ \___/ .
232/// | .
233/// --------------------------------------------->
234/// T (seconds)
235/// @endcode
236/// Notice that we can understand the limitations imposed by the rate-limiter
237/// graphically as the maximum area above the respective lines, `Rp` and `Rs`,
238/// that the usage curve to allowed to achieve. In the example above:
239///
240/// * The area above the sustained rate `Rs` (e.g., `A1` or `A2+B`) should
241/// contain no more than 512 bytes (Rs * Ws).
242///
243/// * The area above the peak rate `Rp` should contain no more than 128 bytes
244/// (Rp * Wp).
245///
246/// Further suppose that we have a function, `sendData`, that transmits a
247/// specified amount of data over that network:
248/// @code
249/// /// Send a specified `dataSize` amount of data over the network.
250/// /// Return `true` if data was sent successfully and `false` otherwise.
251/// bool sendData(bsl::size_t dataSize);
252/// @endcode
253/// First, we create a `balb::RateLimiter` object having a sustained rate of
254/// 1024 bytes/s, a sustained-rate time-window of 0.5s
255/// (512 bytes / 1024 bytes/s), a peak-rate of 2048 bytes/s, and a peak-rate
256/// time-window of 0.0625s (128 bytes / 2048 bytes/s):
257/// @code
258/// bsls::Types::Uint64 sustainedRateLimit = 1024;
259/// bsls::TimeInterval sustainedRateWindow(0.5);
260/// bsls::Types::Uint64 peakRateLimit = 2048;
261/// bsls::TimeInterval peakRateWindow(0.0625);
262/// bsls::TimeInterval now = bdlt::CurrentTime::now();
263///
264/// balb::RateLimiter rateLimiter(sustainedRateLimit,
265/// sustainedRateWindow,
266/// peakRateLimit,
267/// peakRateWindow,
268/// now);
269/// @endcode
270/// Note that the rate limiter does not prevent the rate at any instant from
271/// exceeding either the peak-rate or the sustained rate; instead, it prevents
272/// the average rate over the peak-rate time-window from exceeding maximum
273/// peak-rate and the average rate over the sustained-rate time-window from
274/// exceeding the maximum sustained-rate.
275///
276/// Then, we define the size of data to be send, the size of each data chunk,
277/// and a counter of data actually sent:
278/// @code
279/// bsl::size_t sizeOfData = 10 * 1024; // in bytes
280/// bsl::size_t chunkSize = 64; // in bytes
281/// bsl::size_t bytesSent = 0;
282/// @endcode
283/// Now, we send the chunks of data using a loop. For each iteration, we check
284/// whether submitting another byte would exceed the rate limiter's bandwidth
285/// limits. If not, we send an additional chunk of data and submit the number
286/// of bytes sent to the leaky bucket. Note that `submit` is invoked only after
287/// the data has been sent.
288/// @code
289/// while (bytesSent < sizeOfData) {
290/// now = bdlt::CurrentTime::now();
291/// if (!rateLimiter.wouldExceedBandwidth(now)) {
292/// if (true == sendData(chunkSize)) {
293/// rateLimiter.submit(chunkSize);
294/// bytesSent += chunkSize;
295/// }
296/// }
297/// @endcode
298/// Finally, if submitting another byte will cause the rate limiter to exceed
299/// its bandwidth limits, then we wait until the submission will be allowed by
300/// waiting for an amount time returned by the `calculateTimeToSubmit` method:
301/// @code
302/// else {
303/// bsls::TimeInterval timeToSubmit =
304/// rateLimiter.calculateTimeToSubmit(now);
305/// bsls::Types::Uint64 uS = timeToSubmit.totalMicroseconds() +
306/// (timeToSubmit.nanoseconds() % 1000 ? 1 : 0);
307/// bslmt::ThreadUtil::microSleep(static_cast<int>(uS));
308/// }
309/// }
310/// @endcode
311/// Notice that we wait by putting the thread into a sleep state instead of
312/// using busy-waiting to better optimize for multi-threaded applications.
313/// @}
314/** @} */
315/** @} */
316
317/** @addtogroup bal
318 * @{
319 */
320/** @addtogroup balb
321 * @{
322 */
323/** @addtogroup balb_ratelimiter
324 * @{
325 */
326
327#include <balscm_version.h>
328
329#include <balb_leakybucket.h>
330
331#include <bsls_assert.h>
332#include <bsls_timeinterval.h>
333#include <bsls_types.h>
334
335#include <bsl_algorithm.h>
336#include <bsl_climits.h>
337
338#include <bsl_c_limits.h>
339
340
341namespace balb {
342
343 //==================
344 // class RateLimiter
345 //==================
346
347/// This mechanism implements a rate limiter that allows clients to monitor
348/// and control the usage of a resource such that the rate of consumption
349/// stays within configured limits. The behavior of a rate limiter is
350/// determined by four properties: the sustained rate (in units/s), the
351/// sustained-rate time-window (in seconds), the peak rate (in units/s), and
352/// the peak-rate time-window (in seconds). All of these properties can be
353/// specified at construction or using the `setRateLimits` method.
354///
355/// Units can be indicated to a rate limiter as consumed by either
356/// submitting them using the `submit` method. Units can be marked as
357/// reserved, which effectively shorten the sustained-rate time-window and
358/// the peak-rate time-window, by using the `reserve` method.
359///
360/// Whether submitting 1 more unit would exceed the configured limits can be
361/// determined using the `wouldExceedBandwidth` method. The estimated
362/// amount of time to wait before 1 more unit will be allowed to be
363/// submitted can be determined using the `calculateTimeToSubmit` method.
364///
365/// The state of a rate limiter must be updated manually using the
366/// `updateState` method supplying the current time interval. The time
367/// intervals supplied should all refer to the same time origin.
368///
369/// A rate limiter keeps some statistics, including the number of submitted
370/// units, that can be accessed using the `getStatistics` and reset using
371/// the `resetStatistics` method.
372///
373/// This class:
374/// * is *exception* *neutral* (agnostic)
375/// * is *const* *thread-safe*
376/// For terminology see @ref bsldoc_glossary .
377///
378/// See @ref balb_ratelimiter
380
381 // DATA
382 LeakyBucket d_peakRateBucket; // 'balb::LeakyBucket' object for
383 // handling peak load
384
385 LeakyBucket d_sustainedRateBucket; // 'balb::LeakyBucket' object for
386 // handling sustained load
387
388 private:
389 // NOT IMPLEMENTED
390 RateLimiter& operator=(const RateLimiter&);
391 RateLimiter(const RateLimiter&);
392
393 public:
394 // CREATORS
395
396 /// Create a RateLimiter object, having the specified
397 /// `sustainedRateLimit`, the specified `sustainedRateWindow`, the
398 /// specified `peakRateLimit`, the specified `peakRateWindow`, and using
399 /// the specified `currentTime` as the initial `lastUpdateTime`.
400 ///
401 /// \pre The behavior is undefined unless `0 < sustainedRateLimit`,
402 /// `0 < sustainedRateWindow`, `0 < peakRateLimit`,
403 /// `0 < peakRateWindow`, the product of `sustainedRateLimit` and
404 /// `sustainedRateWindow` can be represented by 64-bit unsigned integral
405 /// type, and the product of `peakRateLimit` and `peakRateWindow` can be
406 /// represented by 64-bit unsigned integral type.
411 const bsls::TimeInterval& currentTime);
412
413 /// Destroy this object.
415
416 // CLASS METHODS
417
418 /// Returns `true` if, supposing the specified `sustainedRateLimit`,
419 /// `sustainedRateWindow`, `peakRateLimit`, and `peakRateWindow` are
420 /// used to initialize a `RateLimiter` object, the corresponding query
421 /// methods return the same values. The implementation of `RateLimiter`
422 /// uses `balb::LeakyBucket` objects, and for some combinations of
423 /// values the capacity of the `balb::LeakyBucket` is rounded such that the rederived values differ.
424 ///
425 /// \note Note that this method is most likely
426 /// to return `true` when the product of each corresponding pair of
427 /// limit and window (as a fraction of a second) is integral.
428 static bool
433
434 // MANIPULATORS
435
436 /// Update the state of this rate limiter to the specified
437 /// `currentTime`. Return the estimated time interval that should pass
438 /// from `currentTime` until 1 more unit can be submitted to this rate
439 /// limiter without exceeding its configured limits. The number of
440 /// nanoseconds in the returned time interval is rounded up.
441 ///
442 /// \note Note that a time interval of 0 is returned if 1 or more units can be submitted
443 /// at `currentTime`. Also note that after waiting for the returned
444 /// time interval, clients should typically check again using this
445 /// method, because additional units may have been submitted in the
446 /// interim.
448 const bsls::TimeInterval& currentTime);
449
450 /// Cancel the specified `numUnits` that were previously reserved.
451 ///
452 /// \pre The behavior is undefined unless `numUnits <= unitsReserved()`.
453 void cancelReserved(bsls::Types::Uint64 numUnits);
454
455 /// Reserve the specified `numUnits` for future use by this rate limiter.
456 ///
457 /// \pre The behavior is undefined unless the sum of `numUnits`,
458 /// unused units previously submitted to this rate limiter, and
459 /// `unitsReserved` can be represented by a 64-bit unsigned integral
460 /// type.
461 void reserve(bsls::Types::Uint64 numUnits);
462
463 /// Reset the statistics counter for this rate limiter to 0, and set the
464 /// `lastUpdateTime` of this rate limiter to the specified
465 /// `currentTime`.
466 void reset(const bsls::TimeInterval& currentTime);
467
468 /// Reset the statics collected for this rate limiter by setting the
469 /// number of units used and the number of units submitted to 0, and set
470 /// the `statisticsCollectionStartTime` to the `lastUpdateTime` of this
471 /// leaky bucket.
472 void resetStatistics();
473
474 /// Set the sustained rate of this rate limiter to the specified
475 /// `sustainedRateLimit`, the sustained-rate time-window to the
476 /// specified `sustainedRateWindow`, the peak rate to the specified
477 /// `peakRateLimit` and the peak-rate time-window to the specified `peakRateWindow`.
478 ///
479 /// \pre The behavior is undefined unless
480 /// `0 < sustainedRateLimit`, `0 < sustainedRateWindow`,
481 /// `0 < peakRateLimit`, `0 < peakRateWindow`, the product of
482 /// `sustainedRateLimit` and `sustainedRateWindow` can be represented by
483 /// 64-bit unsigned integral type, and the product of `peakRateLimit`
484 /// and `peakRateWindow` can be represented by 64-bit unsigned integral
485 /// type.
490
491 /// Submit the specified `numUnits` to this rate limiter.
492 ///
493 /// \pre The behavior is undefined unless the sum of `numUnits`, unused units previously
494 /// submitted to this rate limiter, and `unitsReserved` can be
495 /// represented by a 64-bit unsigned integral type.
496 void submit(bsls::Types::Uint64 numUnits);
497
498 /// Submit the specified `numUnits` that were previously reserved.
499 ///
500 /// \pre The behavior is undefined unless `numUnits <= unitsReserved()`.
501 void submitReserved(bsls::Types::Uint64 numUnits);
502
503 /// Set the `lastUpdateTime` of this rate limiter to the specified
504 /// `currentTime`. If the `currentTime` is after `lastUpdateTime`, then
505 /// recalculate number of units available for consumption based on the
506 /// `peakRate`, `sustainedRate` and the time interval between
507 /// `lastUpdateTime` and `currentTime`. If `currentTime` is before
508 /// `statisticsCollectionStartTime`, set it' to `currentTime`.
509 void updateState(const bsls::TimeInterval& currentTime);
510
511 /// Update the state of this rate limiter to the specified
512 /// `currentTime`. Return `true` if submitting 1 unit at the
513 /// `currentTime` would exceed the configured limits, and false
514 /// otherwise.
515 bool wouldExceedBandwidth(const bsls::TimeInterval& currentTime);
516
517 // ACCESSORS
518
519 /// Load, into the specified `submittedUnits` and the specified
520 /// `unusedUnits` respectively, the numbers of submitted units and the
521 /// number of unused units for this rate limiter from the
522 /// `statisticsCollectionStartTime` to the 'lastUpdateTime. The number
523 /// of unused units is the difference between the number of units that
524 /// could have been consumed at the sustained rate and the number of
525 /// units actually submitted for the time period.
526 void getStatistics(bsls::Types::Uint64* submittedUnits,
527 bsls::Types::Uint64* unusedUnits) const;
528
529 /// Return the time when this rate limiter was last updated.
531
532 /// Return the peak rate of this rate limiter.
534
535 /// Return the peak-rate time-period of this rate limiter.
536 ///
537 /// \note Note that this period is generally significantly shorter than
538 /// `sustainedRateWindow`.
540
541 /// Return the time interval when the collection of the statistics (as
542 /// returned by `getStatistics`) started.
544
545 /// Return the sustained rate of this rate limiter.
547
548 /// Return the sustained-rate time-period of this rate limiter.
549 ///
550 /// \note Note that this period is generally significantly longer than the
551 /// `peakRateWindow`.
553
554 /// Return the number of reserved units for this rate limiter.
556};
557
558// ============================================================================
559// INLINE FUNCTION DEFINITIONS
560// ============================================================================
561
562 //------------------
563 // class RateLimiter
564 //------------------
565
566// MANIPULATORS
567inline
569{
570 BSLS_ASSERT(numUnits <= unitsReserved());
571
572 d_peakRateBucket.cancelReserved(numUnits);
573 d_sustainedRateBucket.cancelReserved(numUnits);
574}
575
576inline
578{
579 BSLS_ASSERT_SAFE(numUnits <= ULLONG_MAX - unitsReserved());
580
581 BSLS_ASSERT_SAFE(d_sustainedRateBucket.unitsInBucket() <=
582 ULLONG_MAX - unitsReserved()- numUnits);
583
584 BSLS_ASSERT_SAFE(d_peakRateBucket.unitsInBucket() <=
585 ULLONG_MAX - unitsReserved()- numUnits);
586
587 d_peakRateBucket.reserve(numUnits);
588 d_sustainedRateBucket.reserve(numUnits);
589}
590
591inline
593{
594 d_peakRateBucket.reset(currentTime);
595 d_sustainedRateBucket.reset(currentTime);
596}
597
598inline
600{
601 d_sustainedRateBucket.resetStatistics();
602}
603
604inline
606{
607 BSLS_ASSERT_SAFE(numUnits <=
608 ULLONG_MAX - d_sustainedRateBucket.unitsInBucket());
609
611 ULLONG_MAX - d_sustainedRateBucket.unitsInBucket()- numUnits);
612
613 BSLS_ASSERT_SAFE(numUnits <=
614 ULLONG_MAX - d_peakRateBucket.unitsInBucket());
615
617 ULLONG_MAX - d_peakRateBucket.unitsInBucket()- numUnits);
618
619 d_peakRateBucket.submit(numUnits);
620 d_sustainedRateBucket.submit(numUnits);
621}
622
623inline
625{
626 BSLS_ASSERT(numUnits <= unitsReserved());
627
628 // There is no need to check whether 'numUnits' causes overflow because the
629 // reserved units was already checked by the 'reserve' method.
630
631 d_peakRateBucket.submitReserved(numUnits);
632 d_sustainedRateBucket.submitReserved(numUnits);
633}
634
635inline
637{
638 d_peakRateBucket.updateState(currentTime);
639 d_sustainedRateBucket.updateState(currentTime);
640}
641
642inline
644{
645 return (d_peakRateBucket.wouldOverflow(currentTime) ||
646 d_sustainedRateBucket.wouldOverflow(currentTime));
647}
648
649// ACCESSORS
650inline
652 bsls::Types::Uint64* unusedUnits) const
653{
654 BSLS_ASSERT_SAFE(0 != submittedUnits);
655 BSLS_ASSERT_SAFE(0 != unusedUnits);
656
657 // The statistics is collected from the sustained rate leaky bucket.
658
659 d_sustainedRateBucket.getStatistics(submittedUnits, unusedUnits);
660}
661
662inline
664{
665 return bsl::max(d_sustainedRateBucket.lastUpdateTime(),
666 d_peakRateBucket.lastUpdateTime());
667}
668
669inline
671{
672 return d_peakRateBucket.drainRate();
673}
674
675inline
677{
678 return LeakyBucket::calculateTimeWindow(d_peakRateBucket.drainRate(),
679 d_peakRateBucket.capacity());
680}
681
682inline
687
688inline
690{
691 return d_sustainedRateBucket.drainRate();
692}
693
694inline
696{
697 return LeakyBucket::calculateTimeWindow(d_sustainedRateBucket.drainRate(),
698 d_sustainedRateBucket.capacity());
699}
700
701inline
703{
704 BSLS_ASSERT_SAFE(d_sustainedRateBucket.unitsReserved() ==
705 d_peakRateBucket.unitsReserved());
706
707 return d_sustainedRateBucket.unitsReserved();
708}
709
710} // close package namespace
711
712
713#endif
714
715// ----------------------------------------------------------------------------
716// Copyright 2021 Bloomberg Finance L.P.
717//
718// Licensed under the Apache License, Version 2.0 (the "License");
719// you may not use this file except in compliance with the License.
720// You may obtain a copy of the License at
721//
722// http://www.apache.org/licenses/LICENSE-2.0
723//
724// Unless required by applicable law or agreed to in writing, software
725// distributed under the License is distributed on an "AS IS" BASIS,
726// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
727// See the License for the specific language governing permissions and
728// limitations under the License.
729// ----------------------------- END-OF-FILE ----------------------------------
730
731/** @} */
732/** @} */
733/** @} */
Definition balb_leakybucket.h:421
void submitReserved(bsls::Types::Uint64 numUnits)
Definition balb_leakybucket.h:731
bsls::Types::Uint64 unitsInBucket() const
Return the number of submitted units in this leaky bucket.
Definition balb_leakybucket.h:766
void submit(bsls::Types::Uint64 numUnits)
Definition balb_leakybucket.h:716
void reserve(bsls::Types::Uint64 numUnits)
Definition balb_leakybucket.h:683
bsls::TimeInterval lastUpdateTime() const
Return the time interval when this leaky bucket was last updated.
Definition balb_leakybucket.h:754
void reset(const bsls::TimeInterval &currentTime)
Definition balb_leakybucket.h:697
void getStatistics(bsls::Types::Uint64 *submittedUnits, bsls::Types::Uint64 *unusedUnits) const
bool wouldOverflow(const bsls::TimeInterval &currentTime)
bsls::TimeInterval statisticsCollectionStartTime() const
Definition balb_leakybucket.h:760
void updateState(const bsls::TimeInterval &currentTime)
static bsls::TimeInterval calculateTimeWindow(bsls::Types::Uint64 drainRate, bsls::Types::Uint64 capacity)
bsls::Types::Uint64 unitsReserved() const
Return the number of reserved units in this leaky bucket.
Definition balb_leakybucket.h:772
bsls::Types::Uint64 drainRate() const
Return the drain rate of this leaky bucket.
Definition balb_leakybucket.h:748
bsls::Types::Uint64 capacity() const
Return the capacity of this leaky bucket.
Definition balb_leakybucket.h:742
void cancelReserved(bsls::Types::Uint64 numUnits)
Definition balb_leakybucket.h:670
void resetStatistics()
Definition balb_leakybucket.h:708
Definition balb_ratelimiter.h:379
void setRateLimits(bsls::Types::Uint64 sustainedRateLimit, const bsls::TimeInterval &sustainedRateWindow, bsls::Types::Uint64 peakRateLimit, const bsls::TimeInterval &peakRateWindow)
void reset(const bsls::TimeInterval &currentTime)
Definition balb_ratelimiter.h:592
static bool supportsRateLimitsExactly(bsls::Types::Uint64 sustainedRateLimit, const bsls::TimeInterval &sustainedRateWindow, bsls::Types::Uint64 peakRateLimit, const bsls::TimeInterval &peakRateWindow)
bsls::Types::Uint64 unitsReserved() const
Return the number of reserved units for this rate limiter.
Definition balb_ratelimiter.h:702
void getStatistics(bsls::Types::Uint64 *submittedUnits, bsls::Types::Uint64 *unusedUnits) const
Definition balb_ratelimiter.h:651
bsls::TimeInterval sustainedRateWindow() const
Definition balb_ratelimiter.h:695
~RateLimiter()
Destroy this object.
bsls::TimeInterval statisticsCollectionStartTime() const
Definition balb_ratelimiter.h:683
bsls::TimeInterval lastUpdateTime() const
Return the time when this rate limiter was last updated.
Definition balb_ratelimiter.h:663
bsls::TimeInterval peakRateWindow() const
Definition balb_ratelimiter.h:676
bsls::TimeInterval calculateTimeToSubmit(const bsls::TimeInterval &currentTime)
bool wouldExceedBandwidth(const bsls::TimeInterval &currentTime)
Definition balb_ratelimiter.h:643
void updateState(const bsls::TimeInterval &currentTime)
Definition balb_ratelimiter.h:636
void resetStatistics()
Definition balb_ratelimiter.h:599
void cancelReserved(bsls::Types::Uint64 numUnits)
Definition balb_ratelimiter.h:568
void submitReserved(bsls::Types::Uint64 numUnits)
Definition balb_ratelimiter.h:624
bsls::Types::Uint64 sustainedRateLimit() const
Return the sustained rate of this rate limiter.
Definition balb_ratelimiter.h:689
void submit(bsls::Types::Uint64 numUnits)
Definition balb_ratelimiter.h:605
RateLimiter(bsls::Types::Uint64 sustainedRateLimit, const bsls::TimeInterval &sustainedRateWindow, bsls::Types::Uint64 peakRateLimit, const bsls::TimeInterval &peakRateWindow, const bsls::TimeInterval &currentTime)
void reserve(bsls::Types::Uint64 numUnits)
Definition balb_ratelimiter.h:577
bsls::Types::Uint64 peakRateLimit() const
Return the peak rate of this rate limiter.
Definition balb_ratelimiter.h:670
Definition bsls_timeinterval.h:307
#define BSLS_ASSERT(X)
Definition bsls_assert.h:1976
#define BSLS_ASSERT_SAFE(X)
Definition bsls_assert.h:1917
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
Definition balb_controlmanager.h:144
unsigned long long Uint64
Definition bsls_types.h:139