From 44068d7821a4b354d5a5321268e620dce57ff932 Mon Sep 17 00:00:00 2001 From: Callum Date: Thu, 28 Mar 2024 13:20:16 +0000 Subject: [PATCH] :sparkles: new operational mode - percent with CPU --- modules/load_balancer/lb_data.c | 29 +++++++++++++++++++++ modules/load_balancer/lb_data.h | 15 ++++++++--- modules/load_balancer/load_balancer.c | 36 ++++++++++++++++++++++----- 3 files changed, 70 insertions(+), 10 deletions(-) diff --git a/modules/load_balancer/lb_data.c b/modules/load_balancer/lb_data.c index 1648779683b..fcef998cf3d 100644 --- a/modules/load_balancer/lb_data.c +++ b/modules/load_balancer/lb_data.c @@ -308,6 +308,10 @@ int add_lb_dsturi( struct lb_data *data, int id, int group, char *uri, fs_url = r->fs_url; dst->rmap[i].max_load = initial_fs_load; dst->rmap[i].fs_enabled = 1; + + dst->rmap[i].current_sessions = 0; + dst->rmap[i].max_sessions = 0; + dst->rmap[i].cpu_idle = 100; } else { dst->rmap[i].max_load = r->val; } @@ -424,6 +428,11 @@ static int get_dst_load(struct lb_resource **res, unsigned int res_no, if( flags & LB_FLAGS_RELATIVE ) { if( dst->rmap[l].max_load ) av = 100 - (100 * lb_dlg_binds.get_profile_size(res[k]->profile, &dst->profile_id) / dst->rmap[l].max_load); + } else if( flags & LB_FLAGS_PERCENT_WITH_CPU ) { + /* generate score based on the percentage of channels occupied, reduced by CPU idle factor */ + if( dst->rmap[l].current_sessions && dst->rmap[l].max_sessions && dst->rmap[l].cpu_idle ) { + av = ( 100 - ( 100 * ( dst->rmap[l].current_sessions + dst->rmap[l].sessions_since_last_heartbeat ) / dst->rmap[l].max_sessions ) ) * dst->rmap[l].cpu_idle + } } else { av = dst->rmap[l].max_load - lb_dlg_binds.get_profile_size(res[k]->profile, &dst->profile_id); } @@ -758,6 +767,7 @@ int lb_route(struct sip_msg *req, int group, struct lb_res_str_list *rl, if( it_d->group == group ) { if( (dst_bitmap_cur[i] & (1 << j)) && ((it_d->flags & LB_DST_STAT_DSBL_FLAG) == 0) ) { + if( (dst_bitmap_cur[i] & (1 << j)) && ((it_d->flags & LB_DST_STAT_DSBL_FLAG) == 0) ) { /* valid destination (group & resources & status) */ cnt_aval_dst++; if( get_dst_load(res_cur, res_cur_n, it_d, flags, &it_l) ) { @@ -818,11 +828,30 @@ int lb_route(struct sip_msg *req, int group, struct lb_res_str_list *rl, if( dst != NULL ) { + LM_DBG("%s call of LB - winning destination %d <%.*s> selected " "for LB set with free=%d\n", (reuse ? "sequential" : "initial"), dst->id, dst->uri.len, dst->uri.s, load ); + if ( flags & LB_FLAGS_PERCENT_WITH_CPU ) { + + // find all resources used by this call, increment on each + for( k=0 ; krmap_no ; l++ ) { + if( res_cur[k] == dst->rmap[l].resource ) { + dst->rmap[l].sessions_since_last_heartbeat++; + + LM_DBG("Incrementing sess since last HB for winning destination %d <%.*s> " + "(sessions_since_last_heartbeat=%d)\n", + dst->id, dst->uri.len, dst->uri.s, dst->sessions_since_last_heartbeat ); + + break; // exit the loop + } + } + } + } + /* add to the profiles */ for( i=0 ; iprofile_id, diff --git a/modules/load_balancer/lb_data.h b/modules/load_balancer/lb_data.h index 71d822d39e2..8c78214101d 100644 --- a/modules/load_balancer/lb_data.h +++ b/modules/load_balancer/lb_data.h @@ -33,10 +33,11 @@ #include "../freeswitch/fs_api.h" #include "lb_parser.h" -#define LB_FLAGS_RELATIVE (1<<0) /* do relative versus absolute estimation. default is absolute */ -#define LB_FLAGS_NEGATIVE (1<<1) /* do not skip negative loads. default to skip */ -#define LB_FLAGS_RANDOM (1<<2) /* pick a random destination among all selected dsts with equal load */ -#define LB_FLAGS_DEFAULT 0 +#define LB_FLAGS_RELATIVE (1<<0) /* do relative versus absolute estimation. default is absolute */ +#define LB_FLAGS_NEGATIVE (1<<1) /* do not skip negative loads. default to skip */ +#define LB_FLAGS_RANDOM (1<<2) /* pick a random destination among all selected dsts with equal load */ +#define LB_FLAGS_PERCENT_WITH_CPU (1<<3) /* score as percentage of max sessions used + CPU util factor */ +#define LB_FLAGS_DEFAULT 0 #define LB_DST_PING_DSBL_FLAG (1<<0) #define LB_DST_PING_PERM_FLAG (1<<1) @@ -62,6 +63,12 @@ struct lb_resource_map { struct lb_resource *resource; unsigned int max_load; + unsigned int max_sessions; /* Raw data from HEARTBEART */ + unsigned int current_sessions; /* Raw data from HEARTBEART */ + unsigned int cpu_idle; /* Raw data from HEARTBEART */ + + unsigned int sessions_since_last_heartbeat; /* Count of sessions allocated since last FS heartbeat */ + int fs_enabled; }; diff --git a/modules/load_balancer/load_balancer.c b/modules/load_balancer/load_balancer.c index 4059d8b8809..dd5cffe1b3e 100644 --- a/modules/load_balancer/load_balancer.c +++ b/modules/load_balancer/load_balancer.c @@ -556,9 +556,21 @@ static int w_lb_start(struct sip_msg *req, int *grp_no, for( f=flstr->s ; fs+flstr->len ; f++ ) { switch( *f ) { case 'r': + if( flags & LB_FLAGS_PERCENT_WITH_CPU ) { + LM_ERR("flags c & r are mutually exclusive (r)\n"); + return -5; + } flags |= LB_FLAGS_RELATIVE; LM_DBG("using relative versus absolute estimation\n"); break; + case 'c': + if( flags & LB_FLAGS_RELATIVE ) { + LM_ERR("flags c & r are mutually exclusive (c)\n"); + return -5; + } + flags |= LB_FLAGS_PERCENT_WITH_CPU; + LM_DBG("using percentage of max sessions with CPU factor estimation \n"); + break; case 'n': flags |= LB_FLAGS_NEGATIVE; LM_DBG("do not skip negative loads\n"); @@ -798,12 +810,24 @@ static void lb_update_max_loads(unsigned int ticks, void *param) dst->rmap[ri].resource->profile, &dst->profile_id); old = dst->rmap[ri].max_load; - /* - * The normal case. OpenSIPS sees, at _most_, the same number - * of sessions as FreeSWITCH does. Any differences must be - * subtracted from the remote "max sessions" value - */ - if (psz < dst->fs_sock->stats.max_sess) { + if ( flags & LB_FLAGS_PERCENT_WITH_CPU ) { + /* + * In this mode we capture the raw values and use these in each LB calculation. This + * means we do not use profile counting in the load calculation. This is suitable for + * architectures where many unreplicated OpenSIPs instances feed calls into the same pool + * of FreeSWITCH instances. + */ + dst->rmap[ri].max_sessions = dst->fs_sock->stats.max_sess; + dst->rmap[ri].current_sessions = dst->fs_sock->stats.sess; + dst->rmap[ri].cpu_idle = dst->fs_sock->stats.id_cpu / (float)100; + /* reset sessions since last heartbeat counter */ + dst->rmap[ri].sessions_since_last_heartbeat = 0; + } else if (psz < dst->fs_sock->stats.max_sess) { + /* + * The normal case. OpenSIPS sees, at _most_, the same number + * of sessions as FreeSWITCH does. Any differences must be + * subtracted from the remote "max sessions" value + */ dst->rmap[ri].max_load = (dst->fs_sock->stats.id_cpu / (float)100) * (dst->fs_sock->stats.max_sess -