BDE 4.14.0 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
467 /// result evaluates to 0, return 1. The behavior is undefined unless
468 /// the product of `drainRate` and `timeWindow` can be represented by a
469 /// 64-bit unsigned integral type.
472 const bsls::TimeInterval& timeWindow);
473
474 /// Return the time interval required to drain the specified `numUnits`
475 /// at the specified `drainRate`, round up the number of nanoseconds in
476 /// the time interval if the specified `ceilFlag` is set to `true`,
477 /// otherwise, round down the number of nanoseconds. The behavior is
478 /// undefined unless the number of seconds in the calculated interval
479 /// may be represented by a 64-bit signed integral type.
482 bool ceilFlag);
483
484 /// Return the time interval over which a leaky bucket *approximates* a
485 /// moving-total of submitted units, as the rounded-down ratio between
486 /// the specified `capacity` and the specified `drainRate`. If the
487 /// rounded ratio is 0, return a time interval of 1 nanosecond. The
488 /// behavior is undefined unless `drainRate > 0` and
489 /// `capacity / drainRate` can be represented with 64-bit signed
490 /// integral type.
494
495 // CREATORS
496
497 /// Create an empty leaky bucket having the specified `drainRate`, the
498 /// specified `capacity`, and the specified `currentTime` as the initial
499 /// `lastUpdateTime`. The behavior is undefined unless `0 < newRate`,
500 /// `0 < newCapacity`, and `LLONG_MIN != currentTime.seconds()`.
503 const bsls::TimeInterval& currentTime);
504
505 /// Destroy this object.
506 ~LeakyBucket();
507
508 // MANIPULATORS
509
510 /// If 1 more unit can be submitted to this leaky bucket without causing
511 /// it to overflow, then return a time interval of 0 immediately.
512 /// Otherwise, first update the state of this leaky bucket to the
513 /// specified `currentTime`. Then, return the estimated time interval
514 /// that should pass from `currentTime` until 1 more unit can be
515 /// submitted to this leaky bucket without causing it to overflow. The
516 /// number of nanoseconds in the returned time interval is rounded up.
517 /// Note that a time interval of 0 can still be return after the state
518 /// of this leaky bucket has been updated to `currentTime`. Also note
519 /// that after waiting for the returned time interval, clients should
520 /// typically check again using this method, because additional units
521 /// may have been submitted in the interim. The behavior is undefined
522 /// unless `LLONG_MIN != currentTime.seconds()` and the total number of
523 /// seconds in the time interval resulting from
524 /// `currentTime - lastUpdateTime()` can be represented with a 64-bit
525 /// signed integer.
527 const bsls::TimeInterval& currentTime);
528
529 /// Cancel the specified `numUnits` that were previously reserved. This
530 /// method reduces the number of reserved units by `numUnits`. The
531 /// behavior is undefined unless `numUnits <= unitsReserved()`.
532 void cancelReserved(bsls::Types::Uint64 numUnits);
533
534 /// Reserve the specified `numUnits` for future use by this leaky
535 /// bucket. The behavior is undefined unless 'unitsReserved() +
536 /// unitsInBucket() + numOfUnits' can be represented by a 64-bit
537 /// unsigned integral type. Note that after this operation, this bucket
538 /// may overflow. Also note that the time interval between the
539 /// invocations of `reserve` and `submitReserved` or `cancelReserved`
540 /// should be kept as short as possible; otherwise, the precision of the
541 /// time interval calculated by `calculateTimeToSubmit` may be
542 /// negatively affected.
543 void reserve(bsls::Types::Uint64 numUnits);
544
545 /// Reset the following statistic counters for this leaky bucket to 0:
546 /// `unitsInBucket`, `unitsReserved`, `submittedUnits`, and
547 /// `unusedUnits`. Set the `lastUpdateTime` and the
548 /// `statisticCollectionStartTime` to the specified `currentTime` of
549 /// this leaky bucket. The behavior is undefined unless 'LLONG_MIN !=
550 /// currentTime.seconds()'.
551 void reset(const bsls::TimeInterval& currentTime);
552
553 /// Reset the statics collected for this leaky bucket by setting the
554 /// number of units used and the number of units submitted to 0, and set
555 /// the `statisticsCollectionStartTime` to the `lastUpdateTime` of this
556 /// leaky bucket.
557 void resetStatistics();
558
559 /// Set the drain rate of this leaky bucket to the specified `newRate`
560 /// and the capacity of this leaky bucket to the specified
561 /// `newCapacity`. The behavior is undefined unless `0 < newRate` and
562 /// `0 < newCapacity`.
564 bsls::Types::Uint64 newCapacity);
565
566 /// Submit the specified `numUnits` to this leaky bucket. The behavior
567 /// is undefined unless `unitsReserved() + unitsInBucket() + numUnits`
568 /// can be represented by a 64-bit unsigned integral type. Note that
569 /// after this operation, this leaky bucket may overflow.
570 void submit(bsls::Types::Uint64 numUnits);
571
572 /// Submit the specified `numUnits` that were previously reserved. This
573 /// method reduces the number of reserved units by `numUnits` and
574 /// submits `numUnits` to this leaky bucket. The behavior is undefined
575 /// unless `numUnits <= unitsReserved()`.
576 void submitReserved(bsls::Types::Uint64 numUnits);
577
578 /// Set the `lastUpdateTime` of this leaky bucket to the specified
579 /// `currentTime`. If `currentTime` is after `lastUpdateTime`, then
580 /// update the `unitsInBucket` of this leaky bucket by subtracting from
581 /// it the number of units drained from `lastUpdateTime` to
582 /// `currentTime`. If `currentTime` is before the
583 /// `statisticsCollectionStartTime` of this leaky bucket, set
584 /// `statisticsCollectionStartTime` to `currentTime`. The behavior is
585 /// undefined unless `LLONG_MIN != currentTime.seconds()` and the total
586 /// number of seconds in the time interval resulting from
587 /// `currentTime - lastUpdateTime()` can be represented with a 64-bit
588 /// signed integer.
589 void updateState(const bsls::TimeInterval& currentTime);
590
591 /// Update the state of this this leaky bucket to the specified
592 /// `currentTime`, and return `true` if adding 1 more unit to this leaky
593 /// bucket would cause the total number of units held by this leaky
594 /// bucket to exceed its capacity, and `false` otherwise. Note that
595 /// this method counts both submitted units and reserved units toward
596 /// the total number of units held by this leaky bucket. The behavior
597 /// is undefined unless `LLONG_MIN != currentTime.seconds()` and the
598 /// total number of seconds in the time interval resulting from
599 /// `currentTime - lastUpdateTime()` can be represented with a 64-bit
600 /// signed integer.
601 bool wouldOverflow(const bsls::TimeInterval& currentTime);
602
603 // ACCESSORS
604
605 /// Return the capacity of this leaky bucket.
607
608 /// Return the drain rate of this leaky bucket.
610
611 /// Load, into the specified `submittedUnits` and the specified
612 /// `unusedUnits` respectively, the numbers of submitted units and the
613 /// number of unused units for this leaky bucket from the
614 /// `statisticsCollectionStartTime` to the `lastUpdateTime`. The number
615 /// of unused units is the difference between the number of units that
616 /// could have been consumed and the number of units actually submitted
617 /// for the time period.
618 void getStatistics(bsls::Types::Uint64* submittedUnits,
619 bsls::Types::Uint64* unusedUnits) const;
620
621 /// Return the time interval when this leaky bucket was last updated.
623
624 /// Return the time interval when the collection of the statistics (as
625 /// returned by `getStatistics`) started.
627
628 /// Return the number of submitted units in this leaky bucket.
630
631 /// Return the number of reserved units in this leaky bucket.
633};
634
635// ============================================================================
636// INLINE FUNCTION DEFINITIONS
637// ============================================================================
638
639 //-----------------------
640 // class LeakyBucket
641 //-----------------------
642
643// CREATORS
644inline
646{
647 BSLS_ASSERT_SAFE(0 < d_drainRate);
648 BSLS_ASSERT_SAFE(0 < d_capacity);
649}
650
651// MANIPULATORS
652inline
654{
655 BSLS_ASSERT_SAFE(numUnits <= d_unitsReserved);
656
657 if (numUnits > d_unitsReserved) {
658 d_unitsReserved = 0;
659 }
660 else {
661 d_unitsReserved -= numUnits;
662 }
663}
664
665inline
667{
668 // Check whether adding 'numUnits' causes an unsigned 64-bit integral type
669 // to overflow.
670
671 BSLS_ASSERT_SAFE(numUnits <= ULLONG_MAX - d_unitsReserved);
672
674 d_unitsInBucket <= ULLONG_MAX - d_unitsReserved - numUnits);
675
676 d_unitsReserved += numUnits;
677}
678
679inline
681{
682 BSLS_ASSERT_SAFE(LLONG_MIN != currentTime.seconds());
683
684 d_lastUpdateTime = currentTime;
685 d_unitsInBucket = 0;
686 d_unitsReserved = 0;
688}
689
690inline
692{
693 d_statisticsCollectionStartTime = d_lastUpdateTime;
694 d_statSubmittedUnits = 0;
695 d_statSubmittedUnitsAtLastUpdate = 0;
696}
697
698inline
700{
701 // Check whether adding 'numUnits' causes an unsigned 64-bit integer type
702 // to overflow.
703
704 BSLS_ASSERT_SAFE(numUnits <= ULLONG_MAX - d_unitsInBucket);
705
707 d_unitsReserved <= ULLONG_MAX - d_unitsInBucket - numUnits);
708
709 d_unitsInBucket += numUnits;
710 d_statSubmittedUnits += numUnits;
711}
712
713inline
715{
716 BSLS_ASSERT_SAFE(numUnits <= d_unitsReserved);
717
718 d_unitsReserved -= numUnits;
719
720 submit(numUnits);
721}
722
723// ACCESSORS
724inline
726{
727 return d_capacity;
728}
729
730inline
732{
733 return d_drainRate;
734}
735
736inline
738{
739 return d_lastUpdateTime;
740}
741
742inline
744{
745 return d_statisticsCollectionStartTime;
746}
747
748inline
750{
751 return d_unitsInBucket;
752}
753
754inline
756{
757 return d_unitsReserved;
758}
759
760} // close package namespace
761
762
763#endif
764
765// ----------------------------------------------------------------------------
766// Copyright 2021 Bloomberg Finance L.P.
767//
768// Licensed under the Apache License, Version 2.0 (the "License");
769// you may not use this file except in compliance with the License.
770// You may obtain a copy of the License at
771//
772// http://www.apache.org/licenses/LICENSE-2.0
773//
774// Unless required by applicable law or agreed to in writing, software
775// distributed under the License is distributed on an "AS IS" BASIS,
776// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
777// See the License for the specific language governing permissions and
778// limitations under the License.
779// ----------------------------- END-OF-FILE ----------------------------------
780
781/** @} */
782/** @} */
783/** @} */
Definition balb_leakybucket.h:421
void submitReserved(bsls::Types::Uint64 numUnits)
Definition balb_leakybucket.h:714
~LeakyBucket()
Destroy this object.
Definition balb_leakybucket.h:645
bsls::Types::Uint64 unitsInBucket() const
Return the number of submitted units in this leaky bucket.
Definition balb_leakybucket.h:749
void submit(bsls::Types::Uint64 numUnits)
Definition balb_leakybucket.h:699
void setRateAndCapacity(bsls::Types::Uint64 newRate, bsls::Types::Uint64 newCapacity)
void reserve(bsls::Types::Uint64 numUnits)
Definition balb_leakybucket.h:666
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:737
static bsls::Types::Uint64 calculateCapacity(bsls::Types::Uint64 drainRate, const bsls::TimeInterval &timeWindow)
void reset(const bsls::TimeInterval &currentTime)
Definition balb_leakybucket.h:680
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:743
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:755
bsls::Types::Uint64 drainRate() const
Return the drain rate of this leaky bucket.
Definition balb_leakybucket.h:731
bsls::Types::Uint64 capacity() const
Return the capacity of this leaky bucket.
Definition balb_leakybucket.h:725
void cancelReserved(bsls::Types::Uint64 numUnits)
Definition balb_leakybucket.h:653
static bsls::TimeInterval calculateDrainTime(bsls::Types::Uint64 numUnits, bsls::Types::Uint64 drainRate, bool ceilFlag)
void resetStatistics()
Definition balb_leakybucket.h:691
Definition bsls_timeinterval.h:301
BSLS_KEYWORD_CONSTEXPR bsls::Types::Int64 seconds() const
Definition bsls_timeinterval.h:1354
#define BSLS_ASSERT_SAFE(X)
Definition bsls_assert.h:1762
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
Definition balb_controlmanager.h:133
unsigned long long Uint64
Definition bsls_types.h:137