From af7418bdfa8b4c867a0d4b99708e2709ec681343 Mon Sep 17 00:00:00 2001 From: An Tran Date: Thu, 27 Aug 2026 15:58:43 +1000 Subject: [PATCH] perf(proxy/upstream_connection): setup configuration in init phase Previously, the configuration for these two policies was set up in the export() phase. This phase only runs once, when the PolicyChain is built at configuration load time. In PR #1485, we moved those parts to the rewrite phase and caused a small allocation per request. This PR eliminates that by moving the configuration construction to the init phase. --- CHANGELOG.md | 1 + .../src/apicast/policy/http_proxy/proxy.lua | 23 +++++++++++-------- .../upstream_connection.lua | 12 ++++++---- spec/policy/http_proxy/http_proxy_spec.lua | 11 +++++++++ .../upstream_connection_spec.lua | 11 +++++++++ 5 files changed, 43 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 433a0e2df..e91fbed0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Routing Policy: eliminate unnecessary TemplateString allocation per request [PR #1585](https://github.com/3scale/APIcast/pull/1585) - Headers Policy: don't render template string when delete header [PR #1586](https://github.com/3scale/APIcast/pull/1586) - 3scale Batcher Policy: replace regex with string operations [PR #1583](https://github.com/3scale/APIcast/pull/1583) +- Proxy/Upstream Connection: setup configuration in init phase - [PR #1602](https://github.com/3scale/APIcast/pull/1602) ### Fixed - Correct FAPI header to `x-fapi-interaction-id` [PR #1557](https://github.com/3scale/APIcast/pull/1557) [THREESCALE-11957](https://issues.redhat.com/browse/THREESCALE-11957) diff --git a/gateway/src/apicast/policy/http_proxy/proxy.lua b/gateway/src/apicast/policy/http_proxy/proxy.lua index 5e9d6f4fd..592d23a1c 100644 --- a/gateway/src/apicast/policy/http_proxy/proxy.lua +++ b/gateway/src/apicast/policy/http_proxy/proxy.lua @@ -8,6 +8,10 @@ local new = _M.new local proxies = {"http", "https"} +local function find_proxy(self, scheme) + return self.proxies[scheme] +end + function _M.new(config) local self = new(config) self.proxies = {} @@ -27,22 +31,21 @@ function _M.new(config) end self.proxies[proto] = val or self.all_proxy end - return self -end - -local function find_proxy(self, scheme) - return self.proxies[scheme] -end -function _M:rewrite(context) - -- APIcast reads this flag in the access phase, that's why we need to set it - -- in rewrite phase. - context.get_http_proxy = function(uri) + self.get_http_proxy = function(uri) if not uri.scheme then return nil end return find_proxy(self, uri.scheme) end + + return self +end + +function _M:rewrite(context) + -- APIcast reads this flag in the access phase, that's why we need to set it + -- in rewrite phase. + context.get_http_proxy = self.get_http_proxy end return _M diff --git a/gateway/src/apicast/policy/upstream_connection/upstream_connection.lua b/gateway/src/apicast/policy/upstream_connection/upstream_connection.lua index 1b1d66110..cecbae09b 100644 --- a/gateway/src/apicast/policy/upstream_connection/upstream_connection.lua +++ b/gateway/src/apicast/policy/upstream_connection/upstream_connection.lua @@ -15,15 +15,17 @@ function _M.new(config) self.send_timeout = tonumber(config.send_timeout) self.read_timeout = tonumber(config.read_timeout) - return self -end - -function _M:rewrite(context) - context.upstream_connection_opts = { + self.upstream_connection_opts = { connect_timeout = self.connect_timeout, send_timeout = self.send_timeout, read_timeout = self.read_timeout } + + return self +end + +function _M:rewrite(context) + context.upstream_connection_opts = self.upstream_connection_opts end return _M diff --git a/spec/policy/http_proxy/http_proxy_spec.lua b/spec/policy/http_proxy/http_proxy_spec.lua index ef989c35f..b3e716a0b 100644 --- a/spec/policy/http_proxy/http_proxy_spec.lua +++ b/spec/policy/http_proxy/http_proxy_spec.lua @@ -46,6 +46,17 @@ describe('HTTP proxy policy', function() assert.is_nil(context.get_http_proxy(http_uri)) end) + it("reuses the same get_http_proxy function across requests instead of allocating a new one", function() + local proxy = proxy_policy.new({ all_proxy = all_proxy_val }) + + local context_a = {} + local context_b = {} + proxy:rewrite(context_a) + proxy:rewrite(context_b) + + assert.equal(context_a.get_http_proxy, context_b.get_http_proxy) + end) + describe("get_http_proxy callback", function() local proxy = proxy_policy.new({ all_proxy = all_proxy_val, diff --git a/spec/policy/upstream_connection/upstream_connection_spec.lua b/spec/policy/upstream_connection/upstream_connection_spec.lua index 59987c7c5..e99482167 100644 --- a/spec/policy/upstream_connection/upstream_connection_spec.lua +++ b/spec/policy/upstream_connection/upstream_connection_spec.lua @@ -23,5 +23,16 @@ describe('Upstream connection policy', function() assert.same(config_timeouts, context.upstream_connection_opts) end) + + it('reuses the same table across requests instead of allocating a new one', function() + local policy = UpstreamConnectionPolicy.new({ connect_timeout = 1, send_timeout = 2, read_timeout = 3 }) + + local context_a = {} + local context_b = {} + policy:rewrite(context_a) + policy:rewrite(context_b) + + assert.equal(context_a.upstream_connection_opts, context_b.upstream_connection_opts) + end) end) end)