MEDIUM: peers: Use true states for the peer applets as seen from outside

This patch is a cleanup of the recent change about the relation between a
peer and the applet used to deal with I/O. Three flags was introduced to
reflect the peer applet state as seen from outside (from the sync task in
fact). Using flags instead of true states was in fact a bad idea. This work
but it is confusing. Especially because it was mixed with LEARN and TEACH
peer flags.

So, now, to make it clearer, we are now using a dedicated state for this
purpose. From the outside, the peer may be in one of the following state
with respects of its applet:

 * the peer has no applet, it is stopped (PEER_APP_ST_STOPPED).

 * the peer applet was created with a validated connection from the protocol
   perspective. But the sync task must synchronized it with the peers
   section. It is in starting state (PEER_APP_ST_STARTING).

 * The starting starting was acknowledged by the sync task, the peer applet
   can start to process messages. It is in running state
   (PEER_APP_ST_RUNNING).

 * The last peer applet was released and the associated connection
   closed. But the sync task must synchronized it with the peers section. It
   is in stopping state (PEER_APP_ST_STOPPING).

Functionnaly speaking, there is no true change here. But it should be easier
to understand now.

In addition to these changes, __process_peer_state() function was renamed
sync_peer_app_state().
This commit is contained in:
Christopher Faulet 2024-04-24 17:57:29 +02:00
parent 229755d8f5
commit ea9bd6d075
2 changed files with 62 additions and 31 deletions

View File

@ -34,6 +34,13 @@
#include <haproxy/stick_table-t.h> #include <haproxy/stick_table-t.h>
#include <haproxy/thread-t.h> #include <haproxy/thread-t.h>
/* peer state with respects of its applet, as seen from outside */
enum peer_app_state {
PEER_APP_ST_STOPPED = 0, /* The peer has no applet */
PEER_APP_ST_STARTING, /* The peer has an applet with a validated connection but sync task must ack it first */
PEER_APP_ST_RUNNING, /* The starting state was processed by the sync task and the peer can process messages */
PEER_APP_ST_STOPPING, /* The peer applet was released but the sync task must ack it before switching the peer in STOPPED state */
};
struct shared_table { struct shared_table {
struct stktable *table; /* stick table to sync */ struct stktable *table; /* stick table to sync */
@ -52,6 +59,7 @@ struct shared_table {
struct peer { struct peer {
int local; /* proxy state */ int local; /* proxy state */
enum peer_app_state appstate; /* peer app state */
__decl_thread(HA_SPINLOCK_T lock); /* lock used to handle this peer section */ __decl_thread(HA_SPINLOCK_T lock); /* lock used to handle this peer section */
char *id; char *id;
struct { struct {

View File

@ -98,10 +98,7 @@
#define PEER_F_LEARN_NOTUP2DATE 0x00000200 /* Learn from peer finished but peer is not up to date */ #define PEER_F_LEARN_NOTUP2DATE 0x00000200 /* Learn from peer finished but peer is not up to date */
#define PEER_F_LEARN_PROCESS 0x00000400 /* Learn from peer was started */ #define PEER_F_LEARN_PROCESS 0x00000400 /* Learn from peer was started */
#define PEER_F_LEARN_FINISHED 0x00000800 /* Learn from peer fully finished */ #define PEER_F_LEARN_FINISHED 0x00000800 /* Learn from peer fully finished */
/* unused : 0x00001000 */ /* unused : 0x00001000..0x00008000 */
#define PEER_F_ST_CONNECTED 0x00002000 /* Used to set a peer in connected state. */
/* unused : 0x00004000 */
#define PEER_F_ST_RELEASED 0x00008000 /* Used to set a peer in released state. */
#define PEER_F_RESYNC_REQUESTED 0x00010000 /* A resnyc was explicitly requested */ #define PEER_F_RESYNC_REQUESTED 0x00010000 /* A resnyc was explicitly requested */
#define PEER_F_WAIT_SYNCTASK_ACK 0x00020000 /* Stop all processing waiting for the sync task acknowledgement when the applet state changes */ #define PEER_F_WAIT_SYNCTASK_ACK 0x00020000 /* Stop all processing waiting for the sync task acknowledgement when the applet state changes */
@ -113,7 +110,6 @@
#define PEER_TEACH_RESET ~(PEER_F_TEACH_PROCESS|PEER_F_TEACH_FINISHED) /* PEER_F_TEACH_COMPLETE should never be reset */ #define PEER_TEACH_RESET ~(PEER_F_TEACH_PROCESS|PEER_F_TEACH_FINISHED) /* PEER_F_TEACH_COMPLETE should never be reset */
#define PEER_LEARN_RESET ~(PEER_F_LEARN_ASSIGN|PEER_F_LEARN_PROCESS|PEER_F_LEARN_FINISHED|PEER_F_LEARN_NOTUP2DATE) #define PEER_LEARN_RESET ~(PEER_F_LEARN_ASSIGN|PEER_F_LEARN_PROCESS|PEER_F_LEARN_FINISHED|PEER_F_LEARN_NOTUP2DATE)
#define PEER_STATE_RESET ~(PEER_F_ST_CONNECTED|PEER_F_ST_RELEASED)
#define PEER_RESYNC_TIMEOUT 5000 /* 5 seconds */ #define PEER_RESYNC_TIMEOUT 5000 /* 5 seconds */
@ -505,6 +501,22 @@ static const char *statuscode_str(int statuscode)
} }
} }
static const char *peer_app_state_str(enum peer_app_state appstate)
{
switch (appstate) {
case PEER_APP_ST_STOPPED:
return "STOPPED";
case PEER_APP_ST_STARTING:
return "STARTING";
case PEER_APP_ST_RUNNING:
return "RUNNING";
case PEER_APP_ST_STOPPING:
return "STOPPING";
default:
return "UNKNOWN";
}
}
/* This function encode an uint64 to 'dynamic' length format. /* This function encode an uint64 to 'dynamic' length format.
The encoded value is written at address *str, and the The encoded value is written at address *str, and the
caller must assure that size after *str is large enough. caller must assure that size after *str is large enough.
@ -1079,9 +1091,9 @@ void __peer_session_deinit(struct peer *peer)
/* reset teaching flags to 0 */ /* reset teaching flags to 0 */
peer->flags &= PEER_TEACH_RESET; peer->flags &= PEER_TEACH_RESET;
/* Mark peer as released */ /* Mark the peer as stopping and wait for the sync task */
peer->flags &= PEER_STATE_RESET; peer->flags |= PEER_F_WAIT_SYNCTASK_ACK;
peer->flags |= (PEER_F_ST_RELEASED|PEER_F_WAIT_SYNCTASK_ACK); peer->appstate = PEER_APP_ST_STOPPING;
task_wakeup(peers->sync_task, TASK_WOKEN_MSG); task_wakeup(peers->sync_task, TASK_WOKEN_MSG);
} }
@ -2892,8 +2904,9 @@ static inline void init_connected_peer(struct peer *peer, struct peers *peers)
peer->flags |= PEER_F_TEACH_PROCESS; peer->flags |= PEER_F_TEACH_PROCESS;
} }
peer->flags &= PEER_STATE_RESET; /* Mark the peer as starting and wait the sync task */
peer->flags |= (PEER_F_ST_CONNECTED|PEER_F_WAIT_SYNCTASK_ACK); peer->flags |= PEER_F_WAIT_SYNCTASK_ACK;
peer->appstate = PEER_APP_ST_STARTING;
} }
/* /*
@ -3288,6 +3301,21 @@ static struct appctx *peer_session_create(struct peers *peers, struct peer *peer
return NULL; return NULL;
} }
/* Clear LEARN flags to a given peer, dealing with aborts if it was assigned for
* learning. In this case, the resync timeout is re-armed.
*/
static void clear_peer_learning_status(struct peer *peer)
{
if (peer->flags & PEER_F_LEARN_ASSIGN) {
/* unassign current peer for learning */
peer->peers->flags &= ~PEERS_F_RESYNC_ASSIGN;
peer->peers->flags |= (peer->local ? PEERS_F_RESYNC_LOCALABORT : PEERS_F_RESYNC_REMOTEABORT);
/* reschedule a resync */
peer->peers->resync_timeout = tick_add(now_ms, MS_TO_TICKS(5000));
}
peer->flags &= PEER_LEARN_RESET;
}
static void __process_peer_learn_status(struct peers *peers, struct peer *peer) static void __process_peer_learn_status(struct peers *peers, struct peer *peer)
{ {
struct peer *ps; struct peer *ps;
@ -3345,22 +3373,17 @@ static void __process_peer_learn_status(struct peers *peers, struct peer *peer)
appctx_wakeup(peer->appctx); appctx_wakeup(peer->appctx);
} }
static void __process_peer_state(struct peers *peers, struct peer *peer) /* Synchronise the peer applet state with its associated peers section. This
* function handles STARTING->RUNNING and STOPPING->STOPPED transitions.
*/
static void sync_peer_app_state(struct peers *peers, struct peer *peer)
{ {
/* Check peer state. Order is important */ if (peer->appstate == PEER_APP_ST_STOPPING) {
if (peer->flags & (PEER_F_ST_RELEASED|PEER_F_ST_CONNECTED)) { clear_peer_learning_status(peer);
if (peer->flags & PEER_F_LEARN_ASSIGN) { peer->appstate = PEER_APP_ST_STOPPED;
/* unassign current peer for learning */
peers->flags &= ~PEERS_F_RESYNC_ASSIGN;
peers->flags |= (peer->local ? PEERS_F_RESYNC_LOCALABORT : PEERS_F_RESYNC_REMOTEABORT);
/* reschedule a resync */
peers->resync_timeout = tick_add(now_ms, MS_TO_TICKS(5000));
}
peer->flags &= PEER_LEARN_RESET;
} }
if (peer->flags & PEER_F_ST_CONNECTED) { else if (peer->appstate == PEER_APP_ST_STARTING) {
peer->flags &= PEER_LEARN_RESET; clear_peer_learning_status(peer);
if (peer->local & appctx_is_back(peer->appctx)) { if (peer->local & appctx_is_back(peer->appctx)) {
/* if local peer has accepted the connection (appctx is /* if local peer has accepted the connection (appctx is
* on the backend side), flag it to learn a lesson and * on the backend side), flag it to learn a lesson and
@ -3391,10 +3414,9 @@ static void __process_peer_state(struct peers *peers, struct peer *peer)
peers->flags |= PEERS_F_RESYNC_REMOTEASSIGN; peers->flags |= PEERS_F_RESYNC_REMOTEASSIGN;
} }
} }
peer->appstate = PEER_APP_ST_RUNNING;
appctx_wakeup(peer->appctx); appctx_wakeup(peer->appctx);
} }
peer->flags &= PEER_STATE_RESET;
} }
static void __process_running_peer_sync(struct task *task, struct peers *peers, unsigned int state) static void __process_running_peer_sync(struct task *task, struct peers *peers, unsigned int state)
@ -3432,7 +3454,7 @@ static void __process_running_peer_sync(struct task *task, struct peers *peers,
HA_SPIN_LOCK(PEER_LOCK, &ps->lock); HA_SPIN_LOCK(PEER_LOCK, &ps->lock);
__process_peer_learn_status(peers, ps); __process_peer_learn_status(peers, ps);
__process_peer_state(peers, ps); sync_peer_app_state(peers, ps);
/* Peer changes, if any, were now ack by the sync task. Unblock /* Peer changes, if any, were now ack by the sync task. Unblock
* the peer (any wakeup should already be performed, no need to * the peer (any wakeup should already be performed, no need to
@ -3526,7 +3548,7 @@ static void __process_running_peer_sync(struct task *task, struct peers *peers,
ps->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + ha_random() % 2000)); ps->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + ha_random() % 2000));
ps->heartbeat = TICK_ETERNITY; ps->heartbeat = TICK_ETERNITY;
peer_session_forceshutdown(ps); peer_session_forceshutdown(ps);
__process_peer_state(peers, ps); sync_peer_app_state(peers, ps);
ps->no_hbt++; ps->no_hbt++;
} }
} }
@ -3577,7 +3599,7 @@ static void __process_stopping_peer_sync(struct task *task, struct peers *peers,
HA_SPIN_LOCK(PEER_LOCK, &ps->lock); HA_SPIN_LOCK(PEER_LOCK, &ps->lock);
__process_peer_learn_status(peers, ps); __process_peer_learn_status(peers, ps);
__process_peer_state(peers, ps); sync_peer_app_state(peers, ps);
/* Peer changes, if any, were now ack by the sync task. Unblock /* Peer changes, if any, were now ack by the sync task. Unblock
* the peer (any wakeup should already be performed, no need to * the peer (any wakeup should already be performed, no need to
@ -3593,7 +3615,7 @@ static void __process_stopping_peer_sync(struct task *task, struct peers *peers,
ps->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + ha_random() % 2000)); ps->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + ha_random() % 2000));
if (ps->appctx) { if (ps->appctx) {
peer_session_forceshutdown(ps); peer_session_forceshutdown(ps);
__process_peer_state(peers, ps); sync_peer_app_state(peers, ps);
} }
} }
@ -4006,11 +4028,12 @@ static int peers_dump_peer(struct buffer *msg, struct appctx *appctx, struct pee
struct shared_table *st; struct shared_table *st;
addr_to_str(&peer->srv->addr, pn, sizeof pn); addr_to_str(&peer->srv->addr, pn, sizeof pn);
chunk_appendf(msg, " %p: id=%s(%s,%s) addr=%s:%d last_status=%s", chunk_appendf(msg, " %p: id=%s(%s,%s) addr=%s:%d app_state=%s last_status=%s",
peer, peer->id, peer, peer->id,
peer->local ? "local" : "remote", peer->local ? "local" : "remote",
peer->appctx ? "active" : "inactive", peer->appctx ? "active" : "inactive",
pn, peer->srv->svc_port, pn, peer->srv->svc_port,
peer_app_state_str(peer->appstate),
statuscode_str(peer->statuscode)); statuscode_str(peer->statuscode));
chunk_appendf(msg, " last_hdshk=%s\n", chunk_appendf(msg, " last_hdshk=%s\n",