Skip to content

THREESCALE-12258 Stream response back when using proxy#1572

Open
tkan145 wants to merge 3 commits into3scale:masterfrom
tkan145:THREESCALE-12258
Open

THREESCALE-12258 Stream response back when using proxy#1572
tkan145 wants to merge 3 commits into3scale:masterfrom
tkan145:THREESCALE-12258

Conversation

@tkan145
Copy link
Contributor

@tkan145 tkan145 commented Feb 17, 2026

What

THREESCALE-12258

In the proxy code, we call httpc:proxy_response(res), lua-resty-http then call sock:receive(max_chunk_size). When Content-Length header exist but no "max_chunk_size" passes in, the function will try to read the chunk with the size equal to value of Content-Lenght header (see here). Every iteration of the loop, proxy_response will then allocates a string with the size of "Content-Length" on the Lua/LuaJIT GC heap.

These strings are short-lived (passed to ngx_print and then discarded), but LuaJIT's GC doesn't immediately free them (read more here). With a large response body, the dead strings pile up. LuaJIT's GC is incremental and may not keep up, causing peak memory to be much higher than the actual response body size.

Before

Version Memory - 130Mb payload After request is finished CPU
2.16 0.563Gib 0.298Gib 5.23m
2.15.3 0.718Gib 0.433Gib 9.36m
2.14 0.714Gib 0.432Gib 6m
2.13 0.715GiB 0.440Gib 7m

After

Version Memory - 130Mb payload After request is finished CPU
2.16 0.173Gib 0.042Gib 5.23m
2.15 0.173Gib 0.043Gib 9.36m

NOTE: we can see even after this patch, the response is still fully buffered in memory.

When running on a local machine, I found that responses were sent almost instantly and memory usage remained stable at around ~60MB throughout the request.

However, when running in a OCP cluster and sending a request from outside the cluster, the response appeared to be buffered and only sent after the entire response had been downloaded.

request with 30M payload
apicast-memory

Requests to the same gateway via a different pod don't seem to cache the response, so I suspect there's something funny with the OCP routing/ingress.

We can call ngx.flush(true) immediately following nginx.print to flush the buffer. This essentially achieves the same result as setting proxy_buffering to off. However, I remain uncertain about the merits of this idea.

The response via our Lua code using proxy policies will have the header Transfer-Encoding: chunked (we strip the Content-Length header here)

via nginx

< HTTP/1.1 200 OK                        
< Server: openresty                      
< Date: Wed, 11 Mar 2026 02:18:10 GMT    
< Content-Type: application/octet-stream 
< Content-Length: 100000000              
< Connection: keep-alive                 
< Access-Control-Allow-Credentials: true 
< Access-Control-Allow-Origin: *         

via proxy policies

< HTTP/1.1 200 OK                        
< Server: openresty                      
< Content-Type: application/octet-stream 
< Transfer-Encoding: chunked             
< Connection: keep-alive                 
< Date: Wed, 11 Mar 2026 02:16:52 GMT    
< Access-Control-Allow-Origin: *         
< Access-Control-Allow-Credentials: true 

Verification steps

  • Checkout this branch
  • Build a new runtime-image
make runtime-image IMAGE_NAME=apicast-test
  • Get inside dev-env
cd dev-environments/https-proxy-upstream-tlsv1.3

Modify docker-compose.yaml as follow

diff --git a/dev-environments/https-proxy-upstream-tlsv1.3/docker-compose.yml b/dev-environments/https-proxy-upstream-tlsv1.3/docker-compose.yml
index 25a49c52..e583db50 100644                                                                                                                 
--- a/dev-environments/https-proxy-upstream-tlsv1.3/docker-compose.yml                                                                          
+++ b/dev-environments/https-proxy-upstream-tlsv1.3/docker-compose.yml                                                                          
@@ -42,6 +42,6 @@ services:                                                                                                                     
     volumes:                                                                                                                                   
       - ./cert/example.com.pem:/etc/pki/example.com.pem                                                                                        
   two.upstream:                                                                                                                                
-    image: quay.io/kuadrant/authorino-examples:talker-api                                                                                      
+    image: quay.io/an_tran/httpbingo:latest                                                                                                    
     expose:                                                                                                                                    
       - "8080"                                                                                                                                 
  • Generate certs
make certs
  • First run with the latest image
make gateway IMAGE_NAME=quay.io/3scale/apicast:latest
  • In another terminal run
docker stats
  • Send a request
curl --resolve get.example.com:8080:127.0.0.1 -v "http://get.example.com:8080/bytes/100000000?user_key="
?user_key=123"

Take note of the memory usage

  • Stop the gateway
CTRL-C
  • Start the gateway again but with new image
make gateway IMAGE_NAME=apicast-test
  • Send the request again and check the memory usage, you should see lower memory consumption this time

@tkan145 tkan145 force-pushed the THREESCALE-12258 branch 6 times, most recently from 8b73bc8 to bfdef91 Compare March 11, 2026 01:44
@tkan145 tkan145 marked this pull request as ready for review March 11, 2026 04:38
@tkan145 tkan145 requested a review from a team as a code owner March 11, 2026 04:38
@tkan145 tkan145 force-pushed the THREESCALE-12258 branch 4 times, most recently from d7462ac to 01becde Compare March 11, 2026 07:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant