Skip to content

Commit 30f302f

Browse files
committed
proxy: tune slot cache freeing algorithm
Main goals for slot-cache-freeing algorithm: 1. Avoid hard limits on free slots. Prevents freeing/allocating slots in tight loops. 2. Don't hang onto slot memory forever after temporary traffic bursts. Something like a PID controller to tune "Free slot limit" would run afoul of rule 1: if we have slightly too many free sometimes we can end up alloc/freeing during heavily pipelined traffic. Best case until the PID controller adjusts. This might still be useful for finding a target number of free slots in the future so I may revisit this. In the meantime the current algorithm can be made less aggressive by giving it a concept of clock time. It would decide on when to free things after every N requests are processed; now it will wait until things are "too free" for at least a minute before adjusting. This should prevent wobbles from brusty traffic. If slot counts do still wobble with this adjustment, the allocations should be far enough apart (60 seconds) to be generally amortized.
1 parent bc7ef4a commit 30f302f

File tree

2 files changed

+28
-11
lines changed

2 files changed

+28
-11
lines changed

proxy.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,7 @@ struct mcp_funcgen_s {
661661
bool closed; // the hook holding this fgen has been replaced
662662
bool ready; // if we're locked down or not.
663663
bool is_router; // if this fgen is actually a router object.
664+
struct timespec free_waiter; // must be "too free" for this much time
664665
mcp_rcontext_t **list;
665666
struct mcp_rqueue_s *queue_list;
666667
char name[FGEN_NAME_MAXLEN+1]; // string name for the generator.

proxy_luafgen.c

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,11 @@ static void mcp_rcontext_cleanup(lua_State *L, mcp_funcgen_t *fgen, mcp_rcontext
135135
// require fewer test rounds for unit tests.
136136
#define FGEN_FREE_PRESSURE_MAX 100
137137
#define FGEN_FREE_PRESSURE_DROP 10
138+
#define FGEN_FREE_WAIT 0
138139
#else
139-
#define FGEN_FREE_PRESSURE_MAX 4000
140-
#define FGEN_FREE_PRESSURE_DROP 100
140+
#define FGEN_FREE_PRESSURE_MAX 5000
141+
#define FGEN_FREE_PRESSURE_DROP 200
142+
#define FGEN_FREE_WAIT 60 // seconds.
141143
#endif
142144
static void _mcplib_funcgen_cache(mcp_funcgen_t *fgen, mcp_rcontext_t *rctx) {
143145
bool do_cache = true;
@@ -148,23 +150,29 @@ static void _mcplib_funcgen_cache(mcp_funcgen_t *fgen, mcp_rcontext_t *rctx) {
148150
// - If free rctx are less than half of total, reduce pressure.
149151
// - If pressure is too high, immediately free the rctx, then drop the
150152
// pressure slightly.
153+
// - If pressure is too high, and has been for more than FGEN_FREE_WAIT
154+
// seconds, immediately free the rctx, then drop the pressure slightly.
151155
//
152156
// This should allow bursty traffic to avoid spinning on alloc/frees,
153-
// while one-time bursts will slowly free slots back down to a minimum of
154-
// 1.
157+
// while one-time bursts will slowly free slots back down to a min of 1.
155158
if (fgen->free > fgen->total/2 - 1) {
156159
if (fgen->free_pressure++ > FGEN_FREE_PRESSURE_MAX) {
160+
struct timespec now;
161+
clock_gettime(CLOCK_REALTIME, &now);
162+
if (fgen->free_waiter.tv_sec == 0) {
163+
fgen->free_waiter.tv_sec = now.tv_sec + FGEN_FREE_WAIT;
164+
}
165+
166+
if (now.tv_sec >= fgen->free_waiter.tv_sec) {
167+
do_cache = false;
168+
}
169+
// check again in a little while.
157170
fgen->free_pressure -= FGEN_FREE_PRESSURE_DROP;
158-
// do not cache the rctx
159-
assert(fgen->self_ref);
160-
lua_State *L = fgen->thread->L;
161-
lua_rawgeti(L, LUA_REGISTRYINDEX, fgen->self_ref);
162-
mcp_rcontext_cleanup(L, fgen, rctx, lua_absindex(L, -1));
163-
lua_pop(L, 1); // drop fgen
164-
do_cache = false;
165171
}
166172
} else {
167173
fgen->free_pressure >>= 1;
174+
// must be too-free for a full wait period before releasing.
175+
fgen->free_waiter.tv_sec = 0;
168176
}
169177

170178
if (do_cache) {
@@ -178,6 +186,13 @@ static void _mcplib_funcgen_cache(mcp_funcgen_t *fgen, mcp_rcontext_t *rctx) {
178186
}
179187
fgen->list[fgen->free] = rctx;
180188
fgen->free++;
189+
} else {
190+
// do not cache the rctx
191+
assert(fgen->self_ref);
192+
lua_State *L = fgen->thread->L;
193+
lua_rawgeti(L, LUA_REGISTRYINDEX, fgen->self_ref);
194+
mcp_rcontext_cleanup(L, fgen, rctx, lua_absindex(L, -1));
195+
lua_pop(L, 1); // drop fgen
181196
}
182197

183198
// we're closed and every outstanding request slot has been
@@ -385,6 +400,7 @@ mcp_rcontext_t *mcp_funcgen_get_rctx(lua_State *L, int fgen_ref, mcp_funcgen_t *
385400
if (fgen->free == 0) {
386401
// reset free pressure so we try to keep the rctx cached
387402
fgen->free_pressure = 0;
403+
fgen->free_waiter.tv_sec = 0;
388404
// TODO (perf): pre-create this c closure somewhere hidden.
389405
lua_pushcclosure(L, _mcplib_funcgen_gencall, 0);
390406
// pull in the funcgen object

0 commit comments

Comments
 (0)