Skip to content

Commit 65c6084

Browse files
committed
fix(proxy-wasm) improve instance recycling robustness
* Allow for recycling the root instance (not managed by isolation modes) * Allow for smoother recycling of http instances (including when the root instance has trapped) * Also recycle trapped instances in the busy queue * Less verbose context creation of fresh and/or recycled instances starting in atypical steps (e.g. on_response_headers).
1 parent a999747 commit 65c6084

File tree

12 files changed

+137
-66
lines changed

12 files changed

+137
-66
lines changed

src/common/proxy_wasm/ngx_proxy_wasm.c

Lines changed: 70 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,8 @@ ngx_proxy_wasm_ctx(ngx_uint_t *filter_ids, size_t nfilters,
327327
pwexec->pool = pool;
328328
pwexec->filter = filter;
329329
pwexec->parent = pwctx;
330+
pwexec->root_id = filter->id;
331+
330332
pwexec->log = ngx_pcalloc(pwexec->pool, sizeof(ngx_log_t));
331333
if (pwexec->log == NULL) {
332334
return NULL;
@@ -344,14 +346,12 @@ ngx_proxy_wasm_ctx(ngx_uint_t *filter_ids, size_t nfilters,
344346
pwexec->log_ctx.pwexec = pwexec;
345347
pwexec->log_ctx.orig_log = log;
346348

347-
ictx = ngx_proxy_wasm_get_instance(filter, pwstore, log);
349+
ictx = ngx_proxy_wasm_get_instance(filter, pwstore, pwexec, log);
348350
if (ictx == NULL) {
349351
return NULL;
350352
}
351353

352354
pwexec->ictx = ictx;
353-
pwexec->id = ictx->next_id++;
354-
pwexec->root_id = filter->id;
355355

356356
ngx_wasm_assert(pwexec->index + 1 == pwctx->pwexecs.nelts);
357357

@@ -461,7 +461,7 @@ ngx_proxy_wasm_action2rc(ngx_proxy_wasm_ctx_t *pwctx,
461461
filter = pwexec->filter;
462462
action = pwctx->action;
463463

464-
dd("action: %d", action);
464+
dd("enter (pwexec: %p, action: %d)", pwexec, action);
465465

466466
if (pwexec->ecode) {
467467
if (!pwexec->ecode_logged
@@ -611,7 +611,6 @@ ngx_proxy_wasm_run_step(ngx_proxy_wasm_exec_t *pwexec,
611611
ngx_proxy_wasm_instance_t *ictx, ngx_proxy_wasm_step_e step)
612612
{
613613
ngx_int_t rc;
614-
ngx_proxy_wasm_err_e ecode;
615614
ngx_proxy_wasm_action_e action = NGX_PROXY_WASM_ACTION_CONTINUE;
616615
ngx_proxy_wasm_ctx_t *pwctx = pwexec->parent;
617616
ngx_proxy_wasm_filter_t *filter = pwexec->filter;
@@ -657,12 +656,6 @@ ngx_proxy_wasm_run_step(ngx_proxy_wasm_exec_t *pwexec,
657656
#ifdef NGX_WASM_RESPONSE_TRAILERS
658657
case NGX_PROXY_WASM_STEP_RESP_TRAILERS:
659658
#endif
660-
ecode = ngx_proxy_wasm_on_start(ictx, filter, 0);
661-
if (ecode != NGX_PROXY_WASM_ERR_NONE) {
662-
pwexec->ecode = ecode;
663-
goto done;
664-
}
665-
666659
rc = filter->subsystem->resume(pwexec, step, &action);
667660
break;
668661
case NGX_PROXY_WASM_STEP_LOG:
@@ -1177,7 +1170,6 @@ ngx_proxy_wasm_init_abi(ngx_proxy_wasm_filter_t *filter)
11771170
static ngx_int_t
11781171
ngx_proxy_wasm_start_filter(ngx_proxy_wasm_filter_t *filter)
11791172
{
1180-
ngx_proxy_wasm_err_e ecode;
11811173
ngx_proxy_wasm_instance_t *ictx;
11821174

11831175
ngx_wasm_assert(filter->loaded);
@@ -1190,24 +1182,12 @@ ngx_proxy_wasm_start_filter(ngx_proxy_wasm_filter_t *filter)
11901182
return NGX_OK;
11911183
}
11921184

1193-
ictx = ngx_proxy_wasm_get_instance(filter, filter->store, filter->log);
1185+
ictx = ngx_proxy_wasm_get_instance(filter, filter->store, NULL,
1186+
filter->log);
11941187
if (ictx == NULL) {
11951188
return NGX_ERROR;
11961189
}
11971190

1198-
/*
1199-
* update instance log
1200-
* FFI-injected filters have a valid log while the instance's
1201-
* might be outdated.
1202-
*/
1203-
ngx_wavm_instance_set_data(ictx->instance, ictx, filter->log);
1204-
1205-
ecode = ngx_proxy_wasm_on_start(ictx, filter, 1);
1206-
if (ecode != NGX_PROXY_WASM_ERR_NONE) {
1207-
filter->ecode = ecode;
1208-
return NGX_ERROR;
1209-
}
1210-
12111191
filter->started = 1;
12121192

12131193
return NGX_OK;
@@ -1216,12 +1196,16 @@ ngx_proxy_wasm_start_filter(ngx_proxy_wasm_filter_t *filter)
12161196

12171197
ngx_proxy_wasm_instance_t *
12181198
ngx_proxy_wasm_get_instance(ngx_proxy_wasm_filter_t *filter,
1219-
ngx_proxy_wasm_store_t *store, ngx_log_t *log)
1199+
ngx_proxy_wasm_store_t *store, ngx_proxy_wasm_exec_t *pwexec,
1200+
ngx_log_t *log)
12201201
{
12211202
ngx_queue_t *q;
12221203
ngx_pool_t *pool;
12231204
ngx_wavm_module_t *module = filter->module;
1224-
ngx_proxy_wasm_instance_t *ictx = NULL;
1205+
ngx_proxy_wasm_instance_t *ictx;
1206+
ngx_proxy_wasm_err_e ecode;
1207+
1208+
dd("enter (pwexec: %p)", pwexec);
12251209

12261210
if (store == NULL) {
12271211
dd("no store, jump to create");
@@ -1250,7 +1234,7 @@ ngx_proxy_wasm_get_instance(ngx_proxy_wasm_filter_t *filter,
12501234
}
12511235

12521236
if (ictx->module == filter->module) {
1253-
dd("reuse instance");
1237+
dd("reuse busy instance");
12541238
ngx_wasm_assert(ictx->nrefs);
12551239
goto reuse;
12561240
}
@@ -1262,17 +1246,29 @@ ngx_proxy_wasm_get_instance(ngx_proxy_wasm_filter_t *filter,
12621246
{
12631247
ictx = ngx_queue_data(q, ngx_proxy_wasm_instance_t, q);
12641248

1249+
if (ictx->instance->trapped) {
1250+
ngx_proxy_wasm_log_error(NGX_LOG_DEBUG, log, 0,
1251+
"\"%V\" filter freeing trapped instance "
1252+
"(ictx: %p, store: %p)",
1253+
filter->name, ictx, store);
1254+
1255+
q = ngx_queue_next(&ictx->q);
1256+
ngx_proxy_wasm_release_instance(ictx, 1);
1257+
continue;
1258+
}
1259+
12651260
if (ictx->module == filter->module) {
1261+
dd("reuse free instance");
12661262
ngx_wasm_assert(ictx->nrefs == 0);
12671263
ngx_queue_remove(&ictx->q);
12681264
goto reuse;
12691265
}
12701266
}
12711267

1272-
dd("create instance in store: %p", store);
1273-
12741268
create:
12751269

1270+
dd("create instance in store: %p", store);
1271+
12761272
ictx = ngx_pcalloc(pool, sizeof(ngx_proxy_wasm_instance_t));
12771273
if (ictx == NULL) {
12781274
goto error;
@@ -1302,26 +1298,63 @@ ngx_proxy_wasm_get_instance(ngx_proxy_wasm_filter_t *filter,
13021298

13031299
goto done;
13041300

1305-
error:
1306-
1307-
return NULL;
1308-
13091301
reuse:
13101302

13111303
ngx_proxy_wasm_log_error(NGX_LOG_DEBUG, log, 0,
13121304
"\"%V\" filter reusing instance "
13131305
"(ictx: %p, nrefs: %d, store: %p)",
13141306
filter->name, ictx, ictx->nrefs + 1, store);
13151307

1308+
goto done;
1309+
13161310
done:
13171311

13181312
if (store && !ictx->nrefs) {
13191313
ngx_queue_insert_tail(&store->busy, &ictx->q);
13201314
}
13211315

1316+
if (pwexec) {
1317+
if (pwexec->root_id != NGX_PROXY_WASM_ROOT_CTX_ID) {
1318+
ngx_wasm_assert(pwexec->id == 0);
1319+
pwexec->id = ictx->next_id++;
1320+
}
1321+
1322+
if (pwexec->ecode) {
1323+
/* recycled instance */
1324+
pwexec->ecode = NGX_PROXY_WASM_ERR_NONE;
1325+
pwexec->ecode_logged = 0;
1326+
}
1327+
}
1328+
13221329
ictx->nrefs++;
1330+
ictx->pwexec = pwexec;
1331+
1332+
/**
1333+
* update instance log
1334+
* FFI-injected filters have a valid log while the instance's
1335+
* might be outdated.
1336+
*/
1337+
ngx_wavm_instance_set_data(ictx->instance, ictx, log);
1338+
1339+
/* create wasm context (root/http) */
1340+
1341+
ecode = ngx_proxy_wasm_on_start(ictx, filter, pwexec == NULL);
1342+
if (ecode != NGX_PROXY_WASM_ERR_NONE) {
1343+
if (pwexec) {
1344+
pwexec->ecode = ecode;
1345+
1346+
} else {
1347+
filter->ecode = ecode;
1348+
}
1349+
1350+
goto error;
1351+
}
13231352

13241353
return ictx;
1354+
1355+
error:
1356+
1357+
return NULL;
13251358
}
13261359

13271360

@@ -1421,6 +1454,8 @@ ngx_proxy_wasm_on_start(ngx_proxy_wasm_instance_t *ictx,
14211454
ngx_wavm_instance_t *instance = ictx->instance;
14221455
wasm_val_vec_t *rets;
14231456

1457+
dd("enter (pwexec: %p, ictx: %p)", pwexec, ictx);
1458+
14241459
rexec = ngx_proxy_wasm_root_lookup(ictx, filter->id);
14251460
if (rexec == NULL) {
14261461
rexec = ngx_pcalloc(ictx->pool, sizeof(ngx_proxy_wasm_exec_t));

src/common/proxy_wasm/ngx_proxy_wasm.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ ngx_wavm_ptr_t ngx_proxy_wasm_alloc(ngx_proxy_wasm_exec_t *pwexec, size_t size);
398398
void ngx_proxy_wasm_store_destroy(ngx_proxy_wasm_store_t *store);
399399
ngx_proxy_wasm_instance_t *ngx_proxy_wasm_get_instance(
400400
ngx_proxy_wasm_filter_t *filter, ngx_proxy_wasm_store_t *store,
401-
ngx_log_t *log);
401+
ngx_proxy_wasm_exec_t *pwexec, ngx_log_t *log);
402402
void ngx_proxy_wasm_release_instance(ngx_proxy_wasm_instance_t *ictx,
403403
unsigned sweep);
404404

src/common/proxy_wasm/ngx_proxy_wasm_properties.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,6 @@ ngx_proxy_wasm_properties_get_ngx(ngx_proxy_wasm_ctx_t *pwctx,
721721
hash = hash_str(name.data, name.len);
722722

723723
rctx = (ngx_http_wasm_req_ctx_t *) pwctx->data;
724-
725724
if (rctx == NULL || rctx->fake_request) {
726725
ngx_wavm_log_error(NGX_LOG_ERR, pwctx->log, NULL,
727726
"cannot get ngx properties outside of a request");
@@ -861,8 +860,8 @@ static ngx_int_t
861860
ngx_proxy_wasm_properties_get_host(ngx_proxy_wasm_ctx_t *pwctx,
862861
ngx_str_t *path, ngx_str_t *value)
863862
{
864-
host_props_node_t *hpn;
865863
uint32_t hash;
864+
host_props_node_t *hpn;
866865
#ifdef NGX_WASM_HTTP
867866
ngx_http_wasm_req_ctx_t *rctx = pwctx->data;
868867

@@ -893,9 +892,9 @@ static ngx_int_t
893892
ngx_proxy_wasm_properties_set_host(ngx_proxy_wasm_ctx_t *pwctx,
894893
ngx_str_t *path, ngx_str_t *value)
895894
{
896-
host_props_node_t *hpn;
897895
uint32_t hash;
898896
unsigned new_entry = 1;
897+
host_props_node_t *hpn;
899898
#ifdef NGX_WASM_HTTP
900899
ngx_http_wasm_req_ctx_t *rctx = pwctx->data;
901900

src/common/proxy_wasm/ngx_proxy_wasm_util.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,8 @@ ngx_proxy_wasm_filter_tick_handler(ngx_event_t *ev)
212212
return;
213213
}
214214

215-
ictx = ngx_proxy_wasm_get_instance(filter, filter->store, filter->log);
215+
ictx = ngx_proxy_wasm_get_instance(filter, filter->store, pwexec,
216+
filter->log);
216217
if (ictx == NULL) {
217218
ngx_wasm_log_error(NGX_LOG_ERR, log, 0,
218219
"tick_handler: no instance");
@@ -246,6 +247,8 @@ ngx_proxy_wasm_filter_tick_handler(ngx_event_t *ev)
246247
pwexec->ev->data = pwexec;
247248
pwexec->ev->log = log;
248249

250+
dd("scheduling next tick in %ld", pwexec->tick_period);
251+
249252
ngx_add_timer(pwexec->ev, pwexec->tick_period);
250253
}
251254

src/wasm/vm/ngx_wavm.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1461,7 +1461,7 @@ ngx_wavm_log_error(ngx_uint_t level, ngx_log_t *log, ngx_wrt_err_t *e,
14611461
wasm_byte_vec_delete(&trapmsg);
14621462

14631463
#if (NGX_WASM_HAVE_V8 || NGX_WASM_HAVE_WASMER)
1464-
if (ctx->instance) {
1464+
if (ctx && ctx->instance) {
14651465
wasm_trap_trace(e->trap, &trace);
14661466

14671467
if (trace.size > 0) {

t/03-proxy_wasm/003-on_tick.t

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,31 @@ qr/.*?(\[error\]|Uncaught RuntimeError: |\s+)tick_period already set.*
152152
[stub2]
153153
[stub3]
154154
--- must_die
155+
156+
157+
158+
=== TEST 6: proxy_wasm - on_tick with trap
159+
Should recycle the tick instance
160+
Should not prevent http context/instance from starting
161+
--- skip_no_debug: 7
162+
--- wasm_modules: on_phases
163+
--- config
164+
location /t {
165+
proxy_wasm on_phases 'tick_period=200 on_tick=trap';
166+
return 200;
167+
}
168+
--- response_body
169+
--- grep_error_log eval: qr/\[(info|crit)].*?on_tick.*/
170+
--- grep_error_log_out eval
171+
qr/.*?
172+
\[info\] .*? on_tick 200.*
173+
\[crit\] .*? panicked at 'on_tick trap'.*
174+
.*?
175+
\[info\] .*? on_tick 200.*
176+
\[crit\] .*? panicked at 'on_tick trap'.*/
177+
--- error_log
178+
filter 1/1 resuming "on_request_headers" step
179+
filter 1/1 resuming "on_response_headers" step
180+
--- no_error_log
181+
[emerg]
182+
[alert]

t/03-proxy_wasm/007-on_http_instance_isolation.t

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,11 @@ should use an instance per stream
137137
#0 on_vm_start[^#*]*
138138
#0 on_configure[^#*]*
139139
\*\d+ .*? filter new instance[^#*]*
140+
#0 on_configure[^#*]*
140141
\*\d+ .*? filter reusing instance[^#*]*
141-
\*\d+ .*? filter 1\/2 resuming "on_request_headers" step in "rewrite" phase[^#*]*
142142
#0 on_configure[^#*]*
143+
\*\d+ .*? filter 1\/2 resuming "on_request_headers" step in "rewrite" phase[^#*]*
143144
\*\d+ .*? filter 2\/2 resuming "on_request_headers" step in "rewrite" phase[^#*]*
144-
#0 on_configure[^#*]*
145145
\*\d+ .*? filter 1\/2 resuming "on_response_headers" step in "header_filter" phase[^#*]*
146146
\*\d+ .*? filter 2\/2 resuming "on_response_headers" step in "header_filter" phase[^#*]*
147147
\*\d+ .*? filter 1\/2 resuming "on_response_body" step in "body_filter" phase[^#*]*
@@ -156,11 +156,11 @@ should use an instance per stream
156156
\*\d+ .*? filter freeing context #\d+ \(2\/2\)[^#*]*
157157
\*\d+ .*? freeing "hostcalls" instance in "main" vm[^#*]*\Z/,
158158
qr/\A\*\d+ .*? filter new instance[^#*]*
159+
#0 on_configure[^#*]*
159160
\*\d+ .*? filter reusing instance[^#*]*
160-
\*\d+ .*? filter 1\/2 resuming "on_request_headers" step in "rewrite" phase[^#*]*
161161
#0 on_configure[^#*]*
162+
\*\d+ .*? filter 1\/2 resuming "on_request_headers" step in "rewrite" phase[^#*]*
162163
\*\d+ .*? filter 2\/2 resuming "on_request_headers" step in "rewrite" phase[^#*]*
163-
#0 on_configure[^#*]*
164164
\*\d+ .*? filter 1\/2 resuming "on_response_headers" step in "header_filter" phase[^#*]*
165165
\*\d+ .*? filter 2\/2 resuming "on_response_headers" step in "header_filter" phase[^#*]*
166166
\*\d+ .*? filter 1\/2 resuming "on_response_body" step in "body_filter" phase[^#*]*

0 commit comments

Comments
 (0)