aboutsummaryrefslogtreecommitdiffstats
path: root/src/libutil
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@rspamd.com>2024-12-07 15:43:55 +0000
committerVsevolod Stakhov <vsevolod@rspamd.com>2024-12-07 15:43:55 +0000
commit5d6581cfe0647f2ac9d3bbf170ee6ef5ff6cf39e (patch)
tree80b00c901521d8b3a95822ffa338c6573d76483a /src/libutil
parentd2a8b3d26c71554e3dda7d7b8f4fe1344587d40a (diff)
downloadrspamd-5d6581cfe0647f2ac9d3bbf170ee6ef5ff6cf39e.tar.gz
rspamd-5d6581cfe0647f2ac9d3bbf170ee6ef5ff6cf39e.zip
[Feature] Improve address rotation algorithm
We apply the following algorithm: 1) Get the current element and it's AF 2) If the next element has the same AF, then we just move to the next element 3) If the next element has different AF, then we should find the next element with the same AF 4) If we cannot find such element, then we return the next element (switching AF)
Diffstat (limited to 'src/libutil')
-rw-r--r--src/libutil/upstream.c56
1 files changed, 49 insertions, 7 deletions
diff --git a/src/libutil/upstream.c b/src/libutil/upstream.c
index 49570dec0..87121f9d3 100644
--- a/src/libutil/upstream.c
+++ b/src/libutil/upstream.c
@@ -1038,16 +1038,58 @@ rspamd_upstream_dtor(struct upstream *up)
rspamd_inet_addr_t *
rspamd_upstream_addr_next(struct upstream *up)
{
- unsigned int idx, next_idx;
+ unsigned int idx = up->addrs.cur, next_idx = up->addrs.cur, cur_af,
+ min_errors, min_errors_idx;
struct upstream_addr_elt *e1, *e2;
- do {
- idx = up->addrs.cur;
- next_idx = (idx + 1) % up->addrs.addr->len;
- e1 = g_ptr_array_index(up->addrs.addr, idx);
+ /*
+ * We apply the following algorithm:
+ * 1) Get the current element and it's AF
+ * 2) If the next element has the same AF, then we just move to the next element
+ * 3) If the next element has different AF, then we should find the next element with the same AF
+ * 4) If we cannot find such element, then we return the next element (switching AF)
+ */
+
+ e1 = g_ptr_array_index(up->addrs.addr, up->addrs.cur);
+ cur_af = rspamd_inet_address_get_af(e1->addr);
+ min_errors = e1->errors;
+ min_errors_idx = idx;
+
+ for (;;) {
+ unsigned int new_af;
+ next_idx = (next_idx + 1) % up->addrs.addr->len;
e2 = g_ptr_array_index(up->addrs.addr, next_idx);
- up->addrs.cur = next_idx;
- } while (e2->errors > e1->errors);
+
+ if (e2->errors < min_errors) {
+ min_errors = e2->errors;
+ min_errors_idx = next_idx;
+ }
+
+ if (next_idx == idx) {
+ /* We did a full circle, so we have to select something else */
+ if (e2->errors == 0) {
+ /* No errors on the current address, so we can use it */
+ }
+ else {
+ /* We have some errors, so we had to select the address with the lowest err count */
+ next_idx = min_errors_idx;
+ }
+
+ /* Always stop on full circle */
+ break;
+ }
+
+ new_af = rspamd_inet_address_get_af(e2->addr);
+
+ if (cur_af == new_af && e2->errors <= e1->errors) {
+ /* Same AF */
+ up->addrs.cur = next_idx;
+ return e2->addr;
+ }
+ }
+
+ e2 = g_ptr_array_index(up->addrs.addr, next_idx);
+ up->addrs.cur = next_idx;
return e2->addr;
}