BDE 4.39.x Production Release
Loading...
Searching...
No Matches
balb_leakybucket.h
Go to the documentation of this file.
1/// @file balb_leakybucket.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// balb_leakybucket.h -*-C++-*-
8#ifndef INCLUDED_BALB_LEAKYBUCKET
9#define INCLUDED_BALB_LEAKYBUCKET
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup balb_leakybucket balb_leakybucket
15/// @brief Provide a mechanism to monitor the consumption rate of a resource.
16/// @addtogroup bal
17/// @{
18/// @addtogroup balb
19/// @{
20/// @addtogroup balb_leakybucket
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#balb_leakybucket-purpose"> Purpose</a>
25/// * <a href="#balb_leakybucket-classes"> Classes </a>
26/// * <a href="#balb_leakybucket-description"> Description </a>
27/// * <a href="#balb_leakybucket-adding-units"> Adding Units </a>
28/// * <a href="#balb_leakybucket-submitting-units"> Submitting Units </a>
29/// * <a href="#balb_leakybucket-reserving-units"> Reserving Units </a>
30/// * <a href="#balb_leakybucket-monitoring-resource-usage"> Monitoring Resource Usage </a>
31/// * <a href="#balb_leakybucket-checking-for-overflow"> Checking for Overflow </a>
32/// * <a href="#balb_leakybucket-modeling-a-network-connection"> Modeling a Network Connection </a>
33/// * <a href="#balb_leakybucket-approximations"> Approximations </a>
34/// * <a href="#balb_leakybucket-sliding-time-window"> Sliding Time-Window </a>
35/// * <a href="#balb_leakybucket-time-synchronization"> Time Synchronization </a>
36/// * <a href="#balb_leakybucket-usage"> Usage </a>
37/// * <a href="#balb_leakybucket-example-1-controlling-network-traffic-generation"> Example 1: Controlling Network Traffic Generation </a>
38///
39/// # Purpose {#balb_leakybucket-purpose}
40/// Provide a mechanism to monitor the consumption rate of a resource.
41///
42/// # Classes {#balb_leakybucket-classes}
43///
44/// - balb::LeakyBucket: a leaky bucket rate monitor
45///
46/// @see balb_ratelimiter
47///
48/// # Description {#balb_leakybucket-description}
49/// This component provides a mechanism, `balb::LeakyBucket`, that
50/// implements a leaky bucket algorithm that allows clients to monitor whether a
51/// resource is being consumed at a particular rate.
52///
53/// The name of this mechanism, leaky bucket, derives from an analogy of pouring
54/// water into a bucket with a hole at the bottom. The maximum rate at which
55/// water will drain out the bucket depends on the size of the hole, and not on
56/// the rate at which water is poured into the bucket. If more water is being
57/// poured into the bucket than being drained, the bucket will eventually
58/// overflow. If the person pouring water into a leaky bucket ensures the
59/// bucket doesn't overflow, then the average rate they pour water will, over
60/// time, be limited by the rate at which water flows out of the bucket. By
61/// analogy, a leaky bucket provides a means to limit the rate of consumption of
62/// some resource (water poured into the bucket) to a configured rate (the size
63/// of the hole in the bucket).
64///
65/// The behavior of a leaky bucket is determined by two properties: the capacity
66/// and the drain rate. The drain rate, measured in `units/s`, is the rate at
67/// which the resource is drained. The capacity, measured in `units`, is the
68/// maximum amount of the resource that the leaky bucket can hold before it
69/// overflows. `unit` is a generic unit of measurement (e.g., bytes, number of
70/// messages, packets, liters, clock cycles, etc.). Note that the drain rate
71/// determines average rate of resource consumption, while the capacity
72/// restricts the time period over which the average actual rate of resource
73/// consumption approaches the drain rate.
74///
75/// ## Adding Units {#balb_leakybucket-adding-units}
76///
77///
78/// Units can be added to a leaky bucket by either submitting them or reserving
79/// them. Submitted units are removed from a leaky bucket at the drain rate,
80/// while reserved units remain unchanged until they are later either cancelled
81/// (removed from the leaky bucket) or submitted.
82///
83/// ### Submitting Units {#balb_leakybucket-submitting-units}
84///
85///
86/// Units can be submitted to a leaky bucket by invoking the `submit` method,
87/// and should be added only after the resource had been consumed.
88///
89/// Figure 1 illustrates a typical workflow for submitting units to a leaky
90/// bucket.
91/// @code
92/// Fig. 1: Capacity = 5 units, Rate = 1 unit / second
93///
94/// Submit 5 Submit 2
95///
96/// 7| | 7| | 7| | 7| |
97/// 6| | 6| | 6| | 6| |
98/// c--5|~~~~~| c--5|-----| c--5|-----| c--5|-----|
99/// 4|~~~~~| 4| | 4| | 4| |
100/// 3|~~~~~| 3| | 3|~~~~~| 3| |
101/// 2|~~~~~| 2| | 2|~~~~~| 2| |
102/// 1|~~~~~| 1|~~~~~| 1|~~~~~| 1| |
103/// +-- --+ +-- --+ +-- --+ +-- --+
104///
105/// Time: t0 t0 + 4s t0 + 4s t0 + 10s
106/// @endcode
107/// Suppose that we have an empty leaky bucket with a capacity of `c = 5 units`
108/// and a drain rate of `d = 1 units/s`. At `t0`, we submit 5 units to the
109/// leaky bucket, bringing the total number of units held up to 5. At 't0 +
110/// 4s', 4 units have been drained from the leaky bucket, bringing the number of
111/// units held down to 1. Finally, at `t0 + 10s`, all units have been drained
112/// from the leaky bucket, making it empty.
113///
114/// Unlike a real-life water bucket, units submitted to a leaky bucket don't
115/// spillover after its capacity has been exceeded, instead the leaky bucket may
116/// hold a number of units beyond its capacity, as examined in Figure 2 below.,
117/// these units are still contained in the leaky bucket.
118///
119/// Figure 2 illustrates what happens if a leaky bucket exceeds its capacity.
120/// This scenario is the same as that in Figure 1, but at time `t0 + 4s`, we
121/// submit 6 units instead of 2.
122/// @code
123/// Fig. 2: Capacity = 5 units, Rate = 1 unit / second
124///
125/// Submit 5 Submit 6
126///
127/// 7| | 7| | 7|~~~~~| 7| |
128/// 6| | 6| | 6|~~~~~| 6| |
129/// c--5|~~~~~| c--5|-----| c--5|~~~~~| c--5|-----|
130/// 4|~~~~~| 4| | 4|~~~~~| 4| |
131/// 3|~~~~~| 3| | 3|~~~~~| 3| |
132/// 2|~~~~~| 2| | 2|~~~~~| 2| |
133/// 1|~~~~~| 1|~~~~~| 1|~~~~~| 1|~~~~~|
134/// +-- --+ +-- --+ +-- --+ +-- --+
135///
136/// Time: t0 t0 + 4s t0 + 4s t0 + 10s
137/// @endcode
138/// At `t0 + 4s`, when the number of units held by the leaky bucket is 1, we
139/// submit 6 more units. This brings the number of units held to 7, which
140/// exceeds the capacity of the leaky bucket. At `t0 + 10s`, 6 units had been
141/// drained from the leaky bucket bring the number of units held down to 1.
142///
143/// ### Reserving Units {#balb_leakybucket-reserving-units}
144///
145///
146/// Units can be reserved for a leaky bucket using the `reserve` method, and
147/// they may be later canceled using the `cancelReserved` method or submitted
148/// using the `submitReserved` method. Unlike submitted units, reserved units
149/// do *not* drain from the leaky bucket; like submitted units, reserved units
150/// count toward the total number of units for the purposes of determining
151/// whether a leaky bucket has exceeded its capacity. Reserving units
152/// effectively decreases the capacity of a leaky bucket. Therefore, the time
153/// interval between reserving units and submitting or canceling the reservation
154/// should be kept as short as possible. For a practical example of using
155/// reserved units, please see @ref balb_reservationguard .
156///
157/// Figure 3 illustrate an example of how reserving units works in a leaky
158/// bucket.
159/// @code
160/// Fig. 3: Capacity = 5 units, Rate = 1 unit / second
161///
162/// Reserve 4 Submit 3 Cancel 1
163/// from reserve from reserve
164///
165/// 7| | 7| | 7| | 7| | 7| |
166/// 6| | 6| | 6| | 6| | 6| |
167/// c--5|-----| c--5|-----| c--5|-----| c--5|-----| c--5|-----|
168/// 4|#####| 4|#####| 4|~~~~~| 4| | 4| |
169/// 3|#####| 3|#####| 3|~~~~~| 3| | 3| |
170/// 2|#####| 2|#####| 2|~~~~~| 2| | 2| |
171/// 1|#####| 1|#####| 1|#####| 1|#####| 1| |
172/// +-- --+ +-- --+ +-- --+ +-- --+ +-- --+
173///
174/// Time: t0 t0 + 5s t0 + 6s t0 + 9s t0 + 10s
175/// @endcode
176/// Suppose that we have an empty leaky bucket with a capacity of `c = 5 units`
177/// and a drain rate of `d = 1 units/s`. At `t0` we reserve 4 units. At 't0 +
178/// 5s', we observe that none of the reserved units are drained from the leaky
179/// bucket. At `t0 + 6s`, we submit 3 of the previously reserved units, which
180/// brings the number of reserved units down to 1 and the number of units held
181/// up to 3. At `t0 + 9s`, we observe that all but the remaining reserved unit
182/// have been drained from the bucket. Finally, at `t0 + 10s`, we cancel the
183/// remaining reserved unit.
184///
185/// ## Monitoring Resource Usage {#balb_leakybucket-monitoring-resource-usage}
186///
187///
188/// The recommended usage of a leaky bucket is to first check whether 1 unit can
189/// be added without causing the leaky bucket to overflow, and if so, consume
190/// the desired amount of the resource. Afterwards, submit the amount of
191/// consumed resource to the leaky bucket.
192///
193/// ### Checking for Overflow {#balb_leakybucket-checking-for-overflow}
194///
195///
196/// A leaky bucket can be queried whether submitting a 1 more unit would cause
197/// it to overflow via the `wouldOverflow` method. This method facilitates the
198/// recommended usage of a leak bucket: check whether 1 more unit can be added
199/// without causing the leaky bucket to overflow, and if so, consume the desired
200/// amount of the resource (which may be more than 1). Compared to the
201/// alternative -- checking whether the desired amount can be submitted without
202/// overflow -- this recommended usage may allow a limited spike in the rate of
203/// actual consumption when the leaky bucket is empty (which is often
204/// acceptable) but is able to sustain a long term average that is actually
205/// closer to the drain rate.
206///
207/// ## Modeling a Network Connection {#balb_leakybucket-modeling-a-network-connection}
208///
209///
210/// The primary use case of leaky bucket is limiting the rate at which data is
211/// written on a network. In this use case, the drain rate of the bucket
212/// corresponds to the *ideal* maximum transmission rate that the client wishes
213/// to enforce on their outgoing connection. Clients may choose to provide a
214/// value related to the physical limitations of their network or any other
215/// arbitrary limit. The function of a leaky bucket's capacity is to limit the
216/// time period over which the average actual transmission rate may exceed the
217/// configured drain rate of the leaky bucket (see `Approximations` section and
218/// `Sliding Time-Window` section).
219///
220/// ## Approximations {#balb_leakybucket-approximations}
221///
222///
223/// Leaky bucket is modeled on a water bucket with a hole, but as a leaky bucket
224/// does not manage any resources, there are several approximations to this
225/// model:
226///
227/// 1. Units are submitted instantaneously to the leaky bucket, whereas the
228/// consumption of a resource occurs over time at a rate that depends on the
229/// nature and speed of the resource.
230/// 2. Leaky bucket simulates the consumption of a resource with a specified
231/// fixed drain rate, but the resource is actually consumed at different
232/// rates over time. This approximation still guarantees that the actual
233/// consumption rate does not exceed the specified drain rate when amortized
234/// over some configured period of time (determined by the capacity and the
235/// drain rate of the bucket), but does not prevent the consumption rate from
236/// spiking above the drain rate for shorter periods of time (see 'Sliding
237/// Time-Window' section).
238///
239/// ## Sliding Time-Window {#balb_leakybucket-sliding-time-window}
240///
241///
242/// One of the properties of the resource pattern created by using a leaky
243/// bucket is an approximation of a sliding time window over which the average
244/// consumption rate is guaranteed to be less than the drain rate. This time
245/// period can be calculated using the leaky bucket's capacity and drain rate,
246/// which can be conveniently performed using the `calculateTimeWindow` class
247/// method.
248///
249/// ## Time Synchronization {#balb_leakybucket-time-synchronization}
250///
251///
252/// Leaky bucket does not utilize an internal timer, so timing must be handled
253/// manually. Clients can specify an initial time interval for the leaky bucket
254/// at construction or using the `reset` method. Whenever the number of units
255/// in a leaky bucket needs to be updated, clients must invoke the `updateState`
256/// method specifying the current time interval. Since leaky bucket cares only
257/// about the elapsed time (not absolute time), the specified time intervals may
258/// be relative to any arbitrary time origin, though all of them must refer to
259/// the same origin. For the sake of consistency, clients are encouraged to use
260/// the unix epoch time (such as the values returned by
261/// `bdlt::CurrentTime::now`).
262///
263/// ## Usage {#balb_leakybucket-usage}
264///
265///
266/// This section illustrates the intended use of this component.
267///
268/// ### Example 1: Controlling Network Traffic Generation {#balb_leakybucket-example-1-controlling-network-traffic-generation}
269///
270///
271/// In some systems, data is processed faster than they are consumed by I/O
272/// interfaces. This could lead to data loss due to the overflowing of the
273/// buffers where data is queued before being processed. In other systems,
274/// generic resources are shared, and their consumption might need to be managed
275/// in order to guarantee quality-of-service (QOS).
276///
277/// Suppose we have a network interface capable of transferring at a rate of
278/// 1024 byte/s and an application wants to transmit 5 KB (5120 bytes) of data
279/// over that network in 20 different 256-bytes data chunks. We want to send
280/// data over this interface and want to ensure the transmission uses on average
281/// less than 50% of the available bandwidth, or 512 byte/s. In this way, other
282/// clients can still reasonably send and receive data using the same network
283/// interface.
284///
285/// Further suppose that we have a function, `sendData`, that transmits a
286/// specified data buffer over that network interface:
287/// @code
288/// bool sendData(const char *buffer, size_t dataSize);
289/// // Send the specified 'buffer' of the specified size 'dataSize' through
290/// // the network interface. Return 'true' if data was sent successfully,
291/// // and 'false' otherwise.
292/// @endcode
293/// First, we create a leaky bucket having a drain rate of 512 bytes/s, a
294/// capacity of 2560 bytes, and a time origin set to the current time (as an
295/// interval from unix epoch). Note that `unit`, the unit of measurement for
296/// leaky bucket, corresponds to `byte` in this example:
297/// @code
298/// bsls::Types::Uint64 rate = 512; // bytes/second
299/// bsls::Types::Uint64 capacity = 2560; // bytes
300/// bsls::TimeInterval now = bdlt::CurrentTime::now();
301/// LeakyBucket bucket(rate, capacity, now);
302/// @endcode
303/// Then, we define a data buffer to be sent, the size of each data chunk, and
304/// the total size of the data to transmit:
305/// @code
306/// char buffer[5120];
307/// unsigned int chunkSize = 256; // in bytes
308/// bsls::Types::Uint64 totalSize = 20 * chunkSize; // in bytes
309/// bsls::Types::Uint64 dataSent = 0; // in bytes
310///
311/// // Load 'buffer'.
312/// // ...
313/// @endcode
314/// Notice that, for the sake of brevity, we elide the loading of `buffer` with
315/// the data to be sent.
316///
317/// Now, we send the chunks of data using a loop. For each iteration, we check
318/// whether submitting another byte would cause the leaky bucket to overflow.
319/// If not, we send an additional chunk of data and submit the number of bytes
320/// sent to the leaky bucket. Note that `submit` is invoked only after the data
321/// has been sent.
322/// @code
323/// char *data = buffer;
324/// while (dataSent < totalSize) {
325/// now = bdlt::CurrentTime::now();
326/// if (!bucket.wouldOverflow(now)) {
327/// if (true == sendData(data, chunkSize)) {
328/// data += chunkSize;
329/// bucket.submit(chunkSize);
330/// dataSent += chunkSize;
331/// }
332/// }
333/// @endcode
334/// Finally, if submitting another byte will cause the leaky bucket to overflow,
335/// then we wait until the submission will be allowed by waiting for an amount
336/// time returned by the `calculateTimeToSubmit` method:
337/// @code
338/// else {
339/// bsls::TimeInterval timeToSubmit = bucket.calculateTimeToSubmit(
340/// now);
341///
342/// // Round up the number of microseconds.
343/// bsls::Types::Uint64 uS = timeToSubmit.totalMicroseconds() +
344/// ((timeToSubmit.nanoseconds() % 1000) ? 1 : 0);
345/// bslmt::ThreadUtil::microSleep(static_cast<int>(uS));
346/// }
347/// }
348/// @endcode
349/// Notice that we wait by putting the thread into a sleep state instead of
350/// using busy-waiting to better optimize for multi-threaded applications.
351/// @}
352/** @} */
353/** @} */
354
355/** @addtogroup bal
356 * @{
357 */
358/** @addtogroup balb
359 * @{
360 */
361/** @addtogroup balb_leakybucket
362 * @{
363 */
364
365#include <balscm_version.h>
366
367#include <bsls_assert.h>
368#include <bsls_timeinterval.h>
369#include <bsls_types.h>
370
371#include <bsl_climits.h>
372
373
374namespace balb {
375
376 //==================
377 // class LeakyBucket
378 //==================
379
380/// This mechanism implements a leaky bucket that allows clients to monitor
381/// whether a resource is being consumed at a particular rate. The behavior
382/// of a leak bucket is determined by two properties: the drain rate (in
383/// units/s) and capacity (in units), both of which can be specified at
384/// construction or using the `setRateAndCapacity` method.
385///
386/// Units can be added to a leaky bucket by either submitting them using the
387/// `submit` method or reserving them using the `reserve` method. Submitted
388/// units are removed from a leaky bucket at the drain rate, while reserved
389/// units stays unaffected in a leaky bucket until they are either cancelled
390/// (removed from the leaky bucket) using the `cancelReserved` method or
391/// submitted using the `submitReserved` method.
392///
393/// Adding units to a leaky bucket will cause it to overflow if after the
394/// units are added, the total number of units in the leaky bucket
395/// (including both submitted and reserved units) exceeds its capacity. A
396/// leaky bucket can be queried whether adding a specified number of units
397/// would cause it to overflow via the `wouldOverflow` method. If
398/// submitting units to a leaky bucket will cause it to overflow, the
399/// estimated amount of time to wait before 1 more units can be submitted
400/// without causing the leaky bucket to overflow can be determined using the
401/// `calculateTimeToSubmit` method.
402///
403/// The state of a leaky bucket must be updated manually using the
404/// `updateState` method supplying the current time interval. The time
405/// intervals supplied should all refer to the same time origin.
406///
407/// A leaky bucket keeps some statistics, including the number of submitted
408/// units, that can be accessed using the `getStatistics` method and reset
409/// using the `resetStatistics` method.
410///
411/// The class invariants are:
412/// * `capacity() > 0`
413/// * `drainRate() > 0`
414///
415/// This class:
416/// * is *exception* *neutral* (agnostic)
417/// * is *const* *thread-safe*
418/// For terminology see @ref bsldoc_glossary .
419///
420/// See @ref balb_leakybucket
422
423 // DATA
424 bsls::Types::Uint64 d_drainRate; // drain rate in units per
425 // second
426
427 bsls::Types::Uint64 d_capacity; // the bucket capacity in units
428
429 bsls::Types::Uint64 d_unitsReserved; // reserved units
430
431 bsls::Types::Uint64 d_unitsInBucket; // number of units currently in
432 // the bucket
433
434 bsls::Types::Uint64 d_fractionalUnitDrainedInNanoUnits;
435 // fractional number of units
436 // that is carried from the
437 // last drain operation
438
439 bsls::TimeInterval d_lastUpdateTime; // time of last drain, updated
440 // via the 'updateState' method
441
442 bsls::TimeInterval d_maxUpdateInterval; // time to drain maximum number
443 // of units
444
445 bsls::Types::Uint64 d_statSubmittedUnits; // submitted unit counter,
446 // number of submitted units
447 // since last reset
448
449 bsls::Types::Uint64 d_statSubmittedUnitsAtLastUpdate;
450 // submitted unit counter saved
451 // during last update
452
453 bsls::TimeInterval d_statisticsCollectionStartTime;
454 // start time for the submitted
455 // unit counter
456
457 private:
458 // NOT IMPLEMENTED
459 LeakyBucket& operator=(const LeakyBucket&);
460 LeakyBucket(const LeakyBucket&);
461
462 public:
463 // CLASS METHODS
464
465 /// Return the capacity of a leaky bucket as the rounded-down product of
466 /// the specified `drainRate` by the specified `timeWindow`. If the result evaluates to 0, return 1.
467 ///
468 /// \pre The behavior is undefined unless
469 /// the product of `drainRate` and `timeWindow` can be represented by a
470 /// 64-bit unsigned integral type.
473 const bsls::TimeInterval& timeWindow);
474
475 /// Return the time interval required to drain the specified `numUnits`
476 /// at the specified `drainRate`, round up the number of nanoseconds in
477 /// the time interval if the specified `ceilFlag` is set to `true`,
478 /// otherwise, round down the number of nanoseconds.
479 ///
480 /// \pre The behavior is undefined unless the number of seconds in the calculated interval
481 /// may be represented by a 64-bit signed integral type.
484 bool ceilFlag);
485
486 /// Return the time interval over which a leaky bucket *approximates* a
487 /// moving-total of submitted units, as the rounded-down ratio between
488 /// the specified `capacity` and the specified `drainRate`. If the
489 /// rounded ratio is 0, return a time interval of 1 nanosecond.
490 ///
491 /// \pre The behavior is undefined unless `drainRate > 0` and
492 /// `capacity / drainRate` can be represented with 64-bit signed
493 /// integral type.
497
498 // CREATORS
499
500 /// Create an empty leaky bucket having the specified `drainRate`, the
501 /// specified `capacity`, and the specified `currentTime` as the initial `lastUpdateTime`.
502 ///
503 /// \pre The behavior is undefined unless `0 < newRate`,
504 /// `0 < newCapacity`, and `LLONG_MIN != currentTime.seconds()`.
507 const bsls::TimeInterval& currentTime);
508
509 /// Destroy this object.
510 ~LeakyBucket();
511
512 // MANIPULATORS
513
514 /// If 1 more unit can be submitted to this leaky bucket without causing
515 /// it to overflow, then return a time interval of 0 immediately.
516 /// Otherwise, first update the state of this leaky bucket to the
517 /// specified `currentTime`. Then, return the estimated time interval
518 /// that should pass from `currentTime` until 1 more unit can be
519 /// submitted to this leaky bucket without causing it to overflow. The
520 /// number of nanoseconds in the returned time interval is rounded up.
521 ///
522 /// \note Note that a time interval of 0 can still be return after the state
523 /// of this leaky bucket has been updated to `currentTime`. Also note
524 /// that after waiting for the returned time interval, clients should
525 /// typically check again using this method, because additional units
526 /// may have been submitted in the interim.
527 ///
528 /// \pre The behavior is undefined unless `LLONG_MIN != currentTime.seconds()` and the total number of
529 /// seconds in the time interval resulting from
530 /// `currentTime - lastUpdateTime()` can be represented with a 64-bit
531 /// signed integer.
533 const bsls::TimeInterval& currentTime);
534
535 /// Cancel the specified `numUnits` that were previously reserved. This
536 /// method reduces the number of reserved units by `numUnits`.
537 ///
538 /// \pre The behavior is undefined unless `numUnits <= unitsReserved()`.
539 void cancelReserved(bsls::Types::Uint64 numUnits);
540
541 /// Reserve the specified `numUnits` for future use by this leaky bucket.
542 ///
543 /// \pre The behavior is undefined unless 'unitsReserved() +
544 /// unitsInBucket() + numUnits' can be represented by a 64-bit unsigned integral type.
545 ///
546 /// \note Note that after this operation, this bucket
547 /// may overflow. Also note that the time interval between the
548 /// invocations of `reserve` and `submitReserved` or `cancelReserved`
549 /// should be kept as short as possible; otherwise, the precision of the
550 /// time interval calculated by `calculateTimeToSubmit` may be
551 /// negatively affected.
552 void reserve(bsls::Types::Uint64 numUnits);
553
554 /// Reset the following statistic counters for this leaky bucket to 0:
555 /// `unitsInBucket`, `unitsReserved`, `submittedUnits`, and
556 /// `unusedUnits`. Set the `lastUpdateTime` and the
557 /// `statisticCollectionStartTime` to the specified `currentTime` of this leaky bucket.
558 ///
559 /// \pre The behavior is undefined unless 'LLONG_MIN !=
560 /// currentTime.seconds()'.
561 void reset(const bsls::TimeInterval& currentTime);
562
563 /// Reset the statics collected for this leaky bucket by setting the
564 /// number of units used and the number of units submitted to 0, and set
565 /// the `statisticsCollectionStartTime` to the `lastUpdateTime` of this
566 /// leaky bucket.
567 void resetStatistics();
568
569 /// Set the drain rate of this leaky bucket to the specified `newRate`
570 /// and the capacity of this leaky bucket to the specified `newCapacity`.
571 ///
572 /// \pre The behavior is undefined unless `0 < newRate` and
573 /// `0 < newCapacity`.
575 bsls::Types::Uint64 newCapacity);
576
577 /// Submit the specified `numUnits` to this leaky bucket.
578 ///
579 /// \pre The behavior is undefined unless `unitsReserved() + unitsInBucket() + numUnits`
580 /// can be represented by a 64-bit unsigned integral type.
581 ///
582 /// \note Note that after this operation, this leaky bucket may overflow.
583 void submit(bsls::Types::Uint64 numUnits);
584
585 /// Submit the specified `numUnits` that were previously reserved. This
586 /// method reduces the number of reserved units by `numUnits` and
587 /// submits `numUnits` to this leaky bucket.
588 ///
589 /// \pre The behavior is undefined unless `numUnits <= unitsReserved()`.
590 void submitReserved(bsls::Types::Uint64 numUnits);
591
592 /// Set the `lastUpdateTime` of this leaky bucket to the specified
593 /// `currentTime`. If `currentTime` is after `lastUpdateTime`, then
594 /// update the `unitsInBucket` of this leaky bucket by subtracting from
595 /// it the number of units drained from `lastUpdateTime` to
596 /// `currentTime`. If `currentTime` is before the
597 /// `statisticsCollectionStartTime` of this leaky bucket, set
598 /// `statisticsCollectionStartTime` to `currentTime`.
599 ///
600 /// \pre The behavior is undefined unless `LLONG_MIN != currentTime.seconds()` and the total
601 /// number of seconds in the time interval resulting from
602 /// `currentTime - lastUpdateTime()` can be represented with a 64-bit
603 /// signed integer.
604 void updateState(const bsls::TimeInterval& currentTime);
605
606 /// Update the state of this this leaky bucket to the specified
607 /// `currentTime`, and return `true` if adding 1 more unit to this leaky
608 /// bucket would cause the total number of units held by this leaky
609 /// bucket to exceed its capacity, and `false` otherwise.
610 ///
611 /// \note Note that this method counts both submitted units and reserved units toward
612 /// the total number of units held by this leaky bucket.
613 ///
614 /// \pre The behavior is undefined unless `LLONG_MIN != currentTime.seconds()` and the
615 /// total number of seconds in the time interval resulting from
616 /// `currentTime - lastUpdateTime()` can be represented with a 64-bit
617 /// signed integer.
618 bool wouldOverflow(const bsls::TimeInterval& currentTime);
619
620 // ACCESSORS
621
622 /// Return the capacity of this leaky bucket.
624
625 /// Return the drain rate of this leaky bucket.
627
628 /// Load, into the specified `submittedUnits` and the specified
629 /// `unusedUnits` respectively, the numbers of submitted units and the
630 /// number of unused units for this leaky bucket from the
631 /// `statisticsCollectionStartTime` to the `lastUpdateTime`. The number
632 /// of unused units is the difference between the number of units that
633 /// could have been consumed and the number of units actually submitted
634 /// for the time period.
635 void getStatistics(bsls::Types::Uint64* submittedUnits,
636 bsls::Types::Uint64* unusedUnits) const;
637
638 /// Return the time interval when this leaky bucket was last updated.
640
641 /// Return the time interval when the collection of the statistics (as
642 /// returned by `getStatistics`) started.
644
645 /// Return the number of submitted units in this leaky bucket.
647
648 /// Return the number of reserved units in this leaky bucket.
650};
651
652// ============================================================================
653// INLINE FUNCTION DEFINITIONS
654// ============================================================================
655
656 //-----------------------
657 // class LeakyBucket
658 //-----------------------
659
660// CREATORS
661inline
663{
664 BSLS_ASSERT_SAFE(0 < d_drainRate);
665 BSLS_ASSERT_SAFE(0 < d_capacity);
666}
667
668// MANIPULATORS
669inline
671{
672 BSLS_ASSERT_SAFE(numUnits <= d_unitsReserved);
673
674 if (numUnits > d_unitsReserved) {
675 d_unitsReserved = 0;
676 }
677 else {
678 d_unitsReserved -= numUnits;
679 }
680}
681
682inline
684{
685 // Check whether adding 'numUnits' causes an unsigned 64-bit integral type
686 // to overflow.
687
688 BSLS_ASSERT_SAFE(numUnits <= ULLONG_MAX - d_unitsReserved);
689
691 d_unitsInBucket <= ULLONG_MAX - d_unitsReserved - numUnits);
692
693 d_unitsReserved += numUnits;
694}
695
696inline
698{
699 BSLS_ASSERT_SAFE(LLONG_MIN != currentTime.seconds());
700
701 d_lastUpdateTime = currentTime;
702 d_unitsInBucket = 0;
703 d_unitsReserved = 0;
705}
706
707inline
709{
710 d_statisticsCollectionStartTime = d_lastUpdateTime;
711 d_statSubmittedUnits = 0;
712 d_statSubmittedUnitsAtLastUpdate = 0;
713}
714
715inline
717{
718 // Check whether adding 'numUnits' causes an unsigned 64-bit integer type
719 // to overflow.
720
721 BSLS_ASSERT_SAFE(numUnits <= ULLONG_MAX - d_unitsInBucket);
722
724 d_unitsReserved <= ULLONG_MAX - d_unitsInBucket - numUnits);
725
726 d_unitsInBucket += numUnits;
727 d_statSubmittedUnits += numUnits;
728}
729
730inline
732{
733 BSLS_ASSERT_SAFE(numUnits <= d_unitsReserved);
734
735 d_unitsReserved -= numUnits;
736
737 submit(numUnits);
738}
739
740// ACCESSORS
741inline
743{
744 return d_capacity;
745}
746
747inline
749{
750 return d_drainRate;
751}
752
753inline
755{
756 return d_lastUpdateTime;
757}
758
759inline
761{
762 return d_statisticsCollectionStartTime;
763}
764
765inline
767{
768 return d_unitsInBucket;
769}
770
771inline
773{
774 return d_unitsReserved;
775}
776
777} // close package namespace
778
779
780#endif
781
782// ----------------------------------------------------------------------------
783// Copyright 2021 Bloomberg Finance L.P.
784//
785// Licensed under the Apache License, Version 2.0 (the "License");
786// you may not use this file except in compliance with the License.
787// You may obtain a copy of the License at
788//
789// http://www.apache.org/licenses/LICENSE-2.0
790//
791// Unless required by applicable law or agreed to in writing, software
792// distributed under the License is distributed on an "AS IS" BASIS,
793// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
794// See the License for the specific language governing permissions and
795// limitations under the License.
796// ----------------------------- END-OF-FILE ----------------------------------
797
798/** @} */
799/** @} */
800/** @} */
Definition balb_leakybucket.h:421
void submitReserved(bsls::Types::Uint64 numUnits)
Definition balb_leakybucket.h:731
~LeakyBucket()
Destroy this object.
Definition balb_leakybucket.h:662
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 setRateAndCapacity(bsls::Types::Uint64 newRate, bsls::Types::Uint64 newCapacity)
void reserve(bsls::Types::Uint64 numUnits)
Definition balb_leakybucket.h:683
LeakyBucket(bsls::Types::Uint64 drainRate, bsls::Types::Uint64 capacity, const bsls::TimeInterval &currentTime)
bsls::TimeInterval lastUpdateTime() const
Return the time interval when this leaky bucket was last updated.
Definition balb_leakybucket.h:754
static bsls::Types::Uint64 calculateCapacity(bsls::Types::Uint64 drainRate, const bsls::TimeInterval &timeWindow)
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)
bsls::TimeInterval calculateTimeToSubmit(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
static bsls::TimeInterval calculateDrainTime(bsls::Types::Uint64 numUnits, bsls::Types::Uint64 drainRate, bool ceilFlag)
void resetStatistics()
Definition balb_leakybucket.h:708
Definition bsls_timeinterval.h:307
BSLS_KEYWORD_CONSTEXPR bsls::Types::Int64 seconds() const
Definition bsls_timeinterval.h:1429
#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