help request: Evaluating the reasonableness of APISIX’s performance overhead through load testing
- Dominant language
- Lua
- Stars
- 17.1k
- Forks
- 2.9k
- Avg merge
- 3d 16h
- Merged PRs (30d)
- 63
Description
### Description
Evaluating the reasonableness of APISIX’s performance overhead through load testing
Hello, recently I ran load tests on APISIX and wanted to measure the performance overhead introduced by its Lua framework. Below is my load-testing approach.
### Configuration:
single process, single worker enabled.
### Goal:
mainly to evaluate the overhead in the access_by_lua_block phase.
Load-test comparison:
### Comment out the access_by_lua_block phase.
In the content phase, use the ngx.say API to return “hello”.
Remove proxy_pass, and eliminate overhead from SSL and other phases such as header_filter, body_filter, log_filter, etc.
### Keep the access_by_lua_block phase.
In the content phase, use the ngx.say API to return “hello”.
Remove proxy_pass, and eliminate overhead from SSL and other phases such as header_filter, body_filter, log_filter, etc.
Config file: as follows.
```
master_process on;
worker_processes 1;
# main configuration snippet starts
# main configuration snippet ends
error_log logs/error.log warn;
pid logs/nginx.pid;
worker_rlimit_nofile 20480;
events {
accept_mutex off;
worker_connections 10620;
}
worker_rlimit_core 16G;
worker_shutdown_timeout 240s;
env APISIX_PROFILE;
env PATH; # for searching external plugin runner's binary
# reserved environment variables for configuration
env APISIX_DEPLOYMENT_ETCD_HOST;
env GCP_SERVICE_ACCOUNT;
thread_pool grpc-client-nginx-module threads=1;
lua {
lua_shared_dict prometheus-cache 10m;
lua_shared_dict nacos 10m;
}
http {
....;
server {
http2 on;
listen 0.0.0.0:9080 default_server reuseport;
listen 0.0.0.0:9443 ssl default_server reuseport;
server_name _;
ssl_certificate cert/ssl_PLACE_HOLDER.crt;
ssl_certificate_key cert/ssl_PLACE_HOLDER.key;
ssl_session_cache shared:SSL:20m;
ssl_session_timeout 10m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers on;
ssl_session_tickets off;
proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
# opentelemetry_set_ngx_var starts
set $opentelemetry_context_traceparent '';
set $opentelemetry_trace_id '';
set $opentelemetry_span_id '';
# opentelemetry_set_ngx_var ends
# zipkin_set_ngx_var starts
# zipkin_set_ngx_var ends
# http server configuration snippet starts
# http server configuration snippet ends
access_log off;
location = /apisix/nginx_status {
allow 127.0.0.0/24;
deny all;
access_log off;
stub_status;
}
# ssl_client_hello_by_lua_block {
# apisix.ssl_client_hello_phase()
# }
#
# ssl_certificate_by_lua_block {
# apisix.ssl_phase()
# }
proxy_ssl_name $upstream_host;
proxy_ssl_server_name on;
location / {
set $upstream_mirror_host '';
set $upstream_mirror_uri '';
set $upstream_upgrade '';
set $upstream_connection '';
set $upstream_scheme 'http';
set $upstream_host $http_host;
set $upstream_uri '';
set $ctx_ref '';
# http server location configuration snippet starts
# http server location configuration snippet ends
set $llm_content_risk_level '';
set $apisix_upstream_response_time $upstream_response_time;
set $request_type 'traditional_http';
set $request_llm_model '';
set $llm_time_to_first_token '0';
set $llm_model '';
set $llm_prompt_tokens '0';
set $llm_completion_tokens '0';
set $apisix_request_id $request_id;
lua_error_log_request_id $apisix_request_id;
# access_by_lua_block {
# apisix.http_access_phase()
# }
proxy_http_version 1.1;
proxy_set_header Host $upstream_host;
proxy_set_header Upgrade $upstream_upgrade;
proxy_set_header Connection $upstream_connection;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass_header Date;
### the following x-forwarded-* headers is to send to upstream server
set $var_x_forwarded_proto $scheme;
set $var_x_forwarded_host $host;
set $var_x_forwarded_port $server_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $var_x_forwarded_proto;
proxy_set_header X-Forwarded-Host $var_x_forwarded_host;
proxy_set_header X-Forwarded-Port $var_x_forwarded_port;
### the following configuration is to cache response content from upstream server
set $upstream_cache_zone off;
set $upstream_cache_key '';
set $upstream_cache_bypass '';
set $upstream_no_cache '';
proxy_cache $upstream_cache_zone;
proxy_cache_valid any 10s;
proxy_cache_min_uses 1;
proxy_cache_methods GET HEAD POST;
proxy_cache_lock_timeout 5s;
proxy_cache_use_stale off;
proxy_cache_key $upstream_cache_key;
proxy_no_cache $upstream_no_cache;
proxy_cache_bypass $upstream_cache_bypass;
content_by_lua_block {
ngx.say("benchmark")
}
#proxy_pass $upstream_scheme://apisix_backend$upstream_uri;
# mirror /proxy_mirror;
# header_filter_by_lua_block {
# apisix.http_header_filter_phase()
# }
#
# body_filter_by_lua_block {
# apisix.http_body_filter_phase()
# }
#
# log_by_lua_block {
# apisix.http_log_phase()
# }
}
}
```
Load-test results:
With access_by_lua_block commented out:
```
root@885e85bad68d:/usr/local/apisix/wrk# ./wrk -t4 -c200 -d30s -H "Host: test2.abc.com" http://127.0.0.1:9080/get --latency
Running 30s test @ http://127.0.0.1:9080/get
4 threads and 200 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 5.17ms 6.78ms 298.00ms 99.35%
Req/Sec 10.39k 846.65 22.23k 83.33%
Latency Distribution
50% 4.76ms
75% 5.01ms
90% 5.22ms
99% 6.59ms
1241296 requests in 30.01s, 222.55MB read
Requests/sec: 41363.43
Transfer/sec: 7.42MB
```
With access_by_lua_block kept:
```
root@885e85bad68d:/usr/local/apisix/wrk# ./wrk -t4 -c200 -d30s -H "Host: test2.abc.com" http://127.0.0.1:9080/get --latency
Running 30s test @ http://127.0.0.1:9080/get
4 threads and 200 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 18.97ms 45.68ms 977.12ms 98.01%
Req/Sec 3.66k 537.12 7.75k 78.33%
Latency Distribution
50% 12.77ms
75% 14.30ms
90% 18.52ms
99% 265.48ms
437710 requests in 30.08s, 78.48MB read
Requests/sec: 14549.67
Transfer/sec: 2.61MB
```
Preliminary conclusion: QPS drops by about `65%.`
I’d like to ask whether this level of performance overhead is considered reasonable, and whether it falls within the normal framework overhead behavior.
### Environment
- APISIX version (run `apisix version`):
```
- root@885e85bad68d:/usr/local/apisix/wrk# apisix version
/usr/local/openresty//luajit/bin/luajit /usr/local/apisix/apisix/cli/apisix.lua version
3.15.0
```
- Operating system (run `uname -a`):
```
Linux 885e85bad68d 4.18.0-553.80.1.el8_10.x86_64 #1 SMP Fri Oct 24 09:31:30 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
```
- OpenResty / Nginx version (run `openresty -V` or `nginx -V`):
```
root@885e85bad68d:/usr/local/apisix/wrk# openresty -v
nginx version: openresty/1.27.1.2
```
- etcd version, if relevant (run `curl http://127.0.0.1:9090/v1/server_info`):
- APISIX Dashboard version, if relevant:
- Plugin runner version, for issues related to plugin runners:
- LuaRocks version, for installation issues (run `luarocks --version`):
Contributor guide
Research direction
Start by reproducing the two configurations around access_by_lua_block and apisix.http_access_phase() with the provided wrk command and APISIX 3.15.0 environment. Compare the latency and request-rate results, then inspect the access-phase entry point and its documented execution path. Done means establishing whether the measured overhead is expected or identifying a specific, reproducible performance problem with supporting measurements.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- lua, nginx
- Domain
- backend, performance
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 32/100