summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Faulet <cfaulet@haproxy.com>2022-06-22 15:28:16 +0200
committerChristopher Faulet <cfaulet@haproxy.com>2022-06-22 18:33:27 +0200
commitaa55640b8c3704de79b576337605d36d93fadfc3 (patch)
tree840b52adfd9766da6798af9538a67a556e260e29
parentdbbdb25f1c0a96094c4e357105264053644d9255 (diff)
downloadhaproxy-aa55640b8c3704de79b576337605d36d93fadfc3.tar.gz
MINOR: freq_ctr: Add a function to get events excess over the current period
freq_ctr_overshoot_period() function may be used to retrieve the excess of events over the current period for a givent frequency counter, ignoring the history. It is a way compare the "current rate" (the number of events over the current period) to a given rate and estimate the excess of events. It may be used to safely add new events, especially at the begining of the current period for a frequency counter with large period. This way, it is possible to smoothly add events during the whole period without quickly consuming all the quota at the beginning of the period and waiting for the next one to be able to add new events.
-rw-r--r--include/haproxy/freq_ctr.h1
-rw-r--r--src/freq_ctr.c58
2 files changed, 59 insertions, 0 deletions
diff --git a/include/haproxy/freq_ctr.h b/include/haproxy/freq_ctr.h
index e0f50809d..be9c9d894 100644
--- a/include/haproxy/freq_ctr.h
+++ b/include/haproxy/freq_ctr.h
@@ -29,6 +29,7 @@
/* exported functions from freq_ctr.c */
ullong freq_ctr_total(const struct freq_ctr *ctr, uint period, int pend);
+int freq_ctr_overshoot_period(const struct freq_ctr *ctr, uint period, uint freq);
/* Update a frequency counter by <inc> incremental units. It is automatically
* rotated if the period is over. It is important that it correctly initializes
diff --git a/src/freq_ctr.c b/src/freq_ctr.c
index 54aa78f02..9d9ab3bfa 100644
--- a/src/freq_ctr.c
+++ b/src/freq_ctr.c
@@ -96,6 +96,64 @@ ullong freq_ctr_total(const struct freq_ctr *ctr, uint period, int pend)
return past * remain + (curr + pend) * period;
}
+/* Returns the excess of events (may be negative) over the current period for
+ * target frequency <freq>. It returns 0 if the counter is in the future. The
+ * result considers the position of the current time within the current period.
+ *
+ * The caller may safely add new events if result is negative or null.
+ */
+int freq_ctr_overshoot_period(const struct freq_ctr *ctr, uint period, uint freq)
+{
+ uint curr, old_curr;
+ uint tick, old_tick;
+ int elapsed;
+
+ tick = HA_ATOMIC_LOAD(&ctr->curr_tick);
+ curr = HA_ATOMIC_LOAD(&ctr->curr_ctr);
+
+ while (1) {
+ if (tick & 0x1) // change in progress
+ goto redo0;
+
+ old_tick = tick;
+ old_curr = curr;
+
+ /* now let's load the values a second time and make sure they
+ * did not change, which will indicate it was a stable reading.
+ */
+
+ tick = HA_ATOMIC_LOAD(&ctr->curr_tick);
+ if (tick & 0x1) // change in progress
+ goto redo0;
+
+ if (tick != old_tick)
+ goto redo1;
+
+ curr = HA_ATOMIC_LOAD(&ctr->curr_ctr);
+ if (curr != old_curr)
+ goto redo2;
+
+ /* all values match between two loads, they're stable, let's
+ * quit now.
+ */
+ break;
+ redo0:
+ tick = HA_ATOMIC_LOAD(&ctr->curr_tick);
+ redo1:
+ curr = HA_ATOMIC_LOAD(&ctr->curr_ctr);
+ redo2:
+ __ha_cpu_relax();
+ };
+
+ elapsed = HA_ATOMIC_LOAD(&global_now_ms) - tick;
+ if (unlikely(elapsed < 0)) {
+ /* The counter is in the future, there is no overshoot */
+ return 0;
+ }
+
+ return curr - div64_32((uint64_t)elapsed * freq, period);
+}
+
/*
* Local variables:
* c-indent-level: 8