From 6f142de8cca7706d83b288fc78b3f6b522dc696f Mon Sep 17 00:00:00 2001 From: Abhishek Choudhary Date: Thu, 1 Jan 2026 15:51:58 +0545 Subject: [PATCH 1/3] fix: maintain node_version for independent upstream (#1130) Signed-off-by: Nic Signed-off-by: Abhishek Choudhary Co-authored-by: Abhishek Choudhary --- apisix/utils/upstream.lua | 13 ++- t/node/healthchecker-independent-upstream.t | 115 ++++++++++++++++++++ 2 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 t/node/healthchecker-independent-upstream.t diff --git a/apisix/utils/upstream.lua b/apisix/utils/upstream.lua index 49e8a18a1075..19bdd1a57b71 100644 --- a/apisix/utils/upstream.lua +++ b/apisix/utils/upstream.lua @@ -16,10 +16,11 @@ -- local core = require("apisix.core") local ipmatcher = require("resty.ipmatcher") -local ngx_now = ngx.now local ipairs = ipairs local type = type local tostring = tostring +local resource = require("apisix.resource") + local _M = {} @@ -117,11 +118,15 @@ function _M.parse_domain_in_up(up) return up end - if not up.orig_modifiedIndex then - up.orig_modifiedIndex = up.modifiedIndex + local nodes_ver = resource.get_nodes_ver(up.value.resource_key) + if not nodes_ver then + nodes_ver = 0 end - up.modifiedIndex = up.orig_modifiedIndex .. "#" .. ngx_now() + nodes_ver = nodes_ver + 1 + up.value._nodes_ver = nodes_ver up.value.nodes = new_nodes + resource.set_nodes_ver_and_nodes(up.value.resource_key, nodes_ver, new_nodes) + core.log.info("resolve upstream which contain domain: ", core.json.delay_encode(up, true)) return up diff --git a/t/node/healthchecker-independent-upstream.t b/t/node/healthchecker-independent-upstream.t new file mode 100644 index 000000000000..7abe169bcf77 --- /dev/null +++ b/t/node/healthchecker-independent-upstream.t @@ -0,0 +1,115 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX 'no_plan'; + +repeat_each(1); +log_level('info'); +worker_connections(256); +no_root_location(); +no_shuffle(); + +run_tests(); + +__DATA__ + +=== TEST 1: using route with an upstream id reference should also trigger healthcheck_manager +--- extra_init_by_lua + local utils = require("apisix.core.utils") + local count = 0 + utils.dns_parse = function (domain) -- mock: DNS parser + + count = count + 1 + if domain == "test1.com" then + return {address = "127.0.0." .. count} + end + if domain == "test2.com" then + return {address = "127.0.0." .. count+100} + end + + error("unknown domain: " .. domain) + end +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/upstreams/1', + ngx.HTTP_PUT, + [[{ + "nodes": { + "test1.com:1980": 1, + "test2.com:1980": 1 + }, + "type": "roundrobin", + "desc": "new upstream", + "checks": { + "active": { + "http_path": "/status", + "healthy": { + "interval": 1, + "successes": 4 + }, + "unhealthy": { + "interval": 1, + "http_failures": 1 + } + } + } + }]] + ) + + if code >= 300 then + ngx.status = code + return + end + + code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "uri": "/hello", + "upstream_id": "1" + }]] + ) + + if code >= 300 then + ngx.status = code + return + end + + for _, _ in ipairs({1, 2, 3, 4, 5}) do + code, body = t('/hello', ngx.HTTP_GET) + if code >= 300 then + ngx.status = code + return + end + ngx.sleep(1) + end + ngx.say(body) + } + } +--- timeout: 10 +--- request +GET /t +--- response_body +passed +--- grep_error_log eval +qr/create new checker: table: / +--- grep_error_log_out +create new checker: table: +create new checker: table: +create new checker: table: +create new checker: table: +create new checker: table: From 8cffd80ace2e19de40371ef0877a963699cd3e5f Mon Sep 17 00:00:00 2001 From: Abhishek Choudhary Date: Fri, 2 Jan 2026 09:53:48 +0545 Subject: [PATCH 2/3] trailing whitespace Signed-off-by: Abhishek Choudhary --- t/node/healthchecker-independent-upstream.t | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/t/node/healthchecker-independent-upstream.t b/t/node/healthchecker-independent-upstream.t index 7abe169bcf77..58778c08ef6d 100644 --- a/t/node/healthchecker-independent-upstream.t +++ b/t/node/healthchecker-independent-upstream.t @@ -31,7 +31,7 @@ __DATA__ local utils = require("apisix.core.utils") local count = 0 utils.dns_parse = function (domain) -- mock: DNS parser - + count = count + 1 if domain == "test1.com" then return {address = "127.0.0." .. count} @@ -108,8 +108,8 @@ passed --- grep_error_log eval qr/create new checker: table: / --- grep_error_log_out -create new checker: table: -create new checker: table: -create new checker: table: -create new checker: table: -create new checker: table: +create new checker: table: +create new checker: table: +create new checker: table: +create new checker: table: +create new checker: table: From d469ed0463b44fb0fc7f91832a54b8a402777a7c Mon Sep 17 00:00:00 2001 From: Abhishek Choudhary Date: Wed, 7 Jan 2026 15:48:40 +0545 Subject: [PATCH 3/3] trailing space Signed-off-by: Abhishek Choudhary --- t/node/healthchecker-independent-upstream.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/node/healthchecker-independent-upstream.t b/t/node/healthchecker-independent-upstream.t index 58778c08ef6d..957aef2c4e52 100644 --- a/t/node/healthchecker-independent-upstream.t +++ b/t/node/healthchecker-independent-upstream.t @@ -106,7 +106,7 @@ GET /t --- response_body passed --- grep_error_log eval -qr/create new checker: table: / +qr/create new checker: table:/ --- grep_error_log_out create new checker: table: create new checker: table: