Coverage Report

Created: 2026-08-13 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/haproxy/src/lb_ss.c
Line
Count
Source
1
/*
2
 * sticky load-balancing
3
 *
4
 * Copyright 2024 HAProxy Technologies
5
 *
6
 * This program is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 2 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19
 */
20
21
#include <haproxy/api.h>
22
#include <haproxy/backend.h>
23
#include <haproxy/lb_ss.h>
24
#include <haproxy/list.h>
25
#include <haproxy/server-t.h>
26
27
/* This function elects a new stick server for proxy px.
28
 *
29
 * The lbprm's lock must be held.
30
 */
31
static void recalc_server_ss(struct proxy *px)
32
0
{
33
0
  struct server *cur, *first;
34
0
  int flag;
35
36
0
  if (!px->lbprm.tot_used)
37
0
    return; /* no server */
38
39
  /* here we *know* that we have some servers */
40
0
  if (px->srv_act)
41
0
    flag = 0;
42
0
  else
43
0
    flag = SRV_F_BACKUP;
44
45
0
  first = NULL;
46
47
0
  list_for_each_entry(cur, &px->servers, el_px) {
48
0
    if ((cur->flags & SRV_F_BACKUP) == flag &&
49
0
        srv_willbe_usable(cur)) {
50
0
      first = cur;
51
0
      break;
52
0
    }
53
0
  }
54
0
  px->lbprm.ss.srv = first;
55
0
}
56
57
/* this function updates the stick server according to server <srv>'s new state.
58
 *
59
 * The server's lock must be held. The lbprm's lock will be used.
60
 */
61
static void ss_set_server_status_down(struct server *srv)
62
0
{
63
0
  struct proxy *p = srv->proxy;
64
65
0
  if (!srv_lb_status_changed(srv))
66
0
    return;
67
68
0
  if (srv_willbe_usable(srv))
69
0
    goto out_update_state;
70
71
0
  HA_RWLOCK_WRLOCK(LBPRM_LOCK, &p->lbprm.lock);
72
73
0
  if (!srv_currently_usable(srv))
74
    /* server was already down */
75
0
    goto out_update_backend;
76
77
0
  if (srv->flags & SRV_F_BACKUP) {
78
0
    p->lbprm.tot_wbck -= srv->cur_eweight;
79
0
    p->srv_bck--;
80
0
  } else {
81
0
    p->lbprm.tot_wact -= srv->cur_eweight;
82
0
    p->srv_act--;
83
0
  }
84
0
  if (srv == p->lbprm.ss.srv) {
85
    /* sticked server is down, elect a new server
86
     * that we will be sticking on.
87
     */
88
0
    recalc_server_ss(p);
89
0
  }
90
91
0
 out_update_backend:
92
  /* check/update tot_used, tot_weight */
93
0
  update_backend_weight(p);
94
0
  HA_RWLOCK_WRUNLOCK(LBPRM_LOCK, &p->lbprm.lock);
95
96
0
 out_update_state:
97
0
  srv_lb_commit_status(srv);
98
0
}
99
100
/* This function updates the stick server according to server <srv>'s new state.
101
 *
102
 * The server's lock must be held. The lbprm's lock will be used.
103
 */
104
static void ss_set_server_status_up(struct server *srv)
105
0
{
106
0
  struct proxy *p = srv->proxy;
107
108
0
  if (!srv_lb_status_changed(srv))
109
0
    return;
110
111
0
  if (!srv_willbe_usable(srv))
112
0
    goto out_update_state;
113
114
0
  HA_RWLOCK_WRLOCK(LBPRM_LOCK, &p->lbprm.lock);
115
116
0
  if (srv_currently_usable(srv))
117
    /* server was already up */
118
0
    goto out_update_backend;
119
120
0
  if (srv->flags & SRV_F_BACKUP) {
121
0
    p->lbprm.tot_wbck += srv->next_eweight;
122
0
    p->srv_bck++;
123
0
  } else {
124
0
    p->lbprm.tot_wact += srv->next_eweight;
125
0
    p->srv_act++;
126
0
  }
127
0
  if (!p->lbprm.ss.srv ||
128
0
      ((p->lbprm.ss.srv->flags & SRV_F_BACKUP) && !(srv->flags & SRV_F_BACKUP))) {
129
    /* we didn't have a server or were sticking on a backup server,
130
     * but now we have an active server, let's switch to it
131
     */
132
0
    p->lbprm.ss.srv = srv;
133
0
  }
134
135
0
 out_update_backend:
136
  /* check/update tot_used, tot_weight */
137
0
  update_backend_weight(p);
138
0
  HA_RWLOCK_WRUNLOCK(LBPRM_LOCK, &p->lbprm.lock);
139
140
0
 out_update_state:
141
0
  srv_lb_commit_status(srv);
142
0
}
143
144
/* This function is responsible for preparing sticky LB algorithm.
145
 * It should be called only once per proxy, at config time.
146
 */
147
static int init_server_ss(struct proxy *p)
148
0
{
149
0
  struct server *srv;
150
151
0
  if (LIST_ISEMPTY(&p->servers))
152
0
    return 0;
153
154
0
  list_for_each_entry(srv, &p->servers, el_px) {
155
0
    srv->next_eweight = 1; /* ignore weights, all servers have the same weight */
156
0
    srv_lb_commit_status(srv);
157
0
  }
158
159
  /* recounts servers and their weights */
160
0
  recount_servers(p);
161
0
  update_backend_weight(p);
162
0
  recalc_server_ss(p);
163
0
  return 0;
164
0
}
165
166
/*
167
 * This function returns the server that we're sticking on. If any server
168
 * is found, it will be returned. If no valid server is found, NULL is
169
 * returned.
170
 *
171
 * The lbprm's lock will be used.
172
 */
173
struct server *ss_get_server(struct proxy *px)
174
0
{
175
0
  struct server *srv = NULL;
176
177
0
  HA_RWLOCK_RDLOCK(LBPRM_LOCK, &px->lbprm.lock);
178
0
  srv = px->lbprm.ss.srv;
179
0
  HA_RWLOCK_RDUNLOCK(LBPRM_LOCK, &px->lbprm.lock);
180
0
  return srv;
181
0
}
182
183
static struct lb_ops lb_ss_ops = {ILH,
184
  .map = {
185
    { .mask = BE_LB_KIND | BE_LB_PARM, .match = BE_LB_KIND_SA | BE_LB_SA_SS },
186
    { 0, 0 }
187
  },
188
  .algo_prop              = BE_LB_PROP_DYN,
189
  .proxy_init             = init_server_ss,
190
  .set_server_status_up   = ss_set_server_status_up,
191
  .set_server_status_down = ss_set_server_status_down,
192
};
193
194
INITCALL1(STG_REGISTER, lb_ops_register, &lb_ss_ops);