This is an automated email from the ASF dual-hosted git repository.
shreemaan-abhishek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new d5ea736a7d fix(etcd): check availability before starting watcher
(#13934)
d5ea736a7d is described below
commit d5ea736a7d1219b7730e20fe067b2449485d6f5b
Author: Shreemaan Abhishek <[email protected]>
AuthorDate: Mon Sep 14 18:37:25 2026 +0800
fix(etcd): check availability before starting watcher (#13934)
---
apisix/core/config_etcd.lua | 61 ++++++++++++++++++++------------------
t/core/etcd-watch-start-revision.t | 31 +++++++++++++++++++
2 files changed, 63 insertions(+), 29 deletions(-)
diff --git a/apisix/core/config_etcd.lua b/apisix/core/config_etcd.lua
index 7f41dd7a0a..eebc726511 100644
--- a/apisix/core/config_etcd.lua
+++ b/apisix/core/config_etcd.lua
@@ -128,6 +128,29 @@ local function produce_res(res, err)
end
+local function wait_for_etcd_available(etcd_cli, prefix)
+ while true do
+ local res, err = etcd_cli:get(prefix)
+ if not res then
+ log.error("etcd get: ", err)
+ ngx_sleep(3)
+ elseif not (res.body and res.body.header and res.body.header.revision)
then
+ log.error("etcd response missing header.revision")
+ ngx_sleep(3)
+ else
+ local rev = tonumber(res.body.header.revision)
+ if not rev then
+ log.error("etcd response has invalid header.revision: ",
+ tostring(res.body.header.revision))
+ ngx_sleep(3)
+ else
+ return rev
+ end
+ end
+ end
+end
+
+
local function do_run_watch(premature)
if premature then
return
@@ -147,35 +170,15 @@ local function do_run_watch(premature)
error("failed to create etcd instance: " .. string(err))
end
- -- Watch from the revision the preloaded configuration was read at, so
- -- that everything written after that snapshot is still delivered. The
- -- revision cannot be read back from loaded_configuration here:
- -- core.config.new() removes each entry as it consumes it, so the table
- -- is empty once every preloaded type has been registered -- and then
- -- the fallback below would start the watch at the current revision and
- -- drop every write made since the snapshot.
- local rev = loaded_configuration_rev or 0
-
- if rev == 0 then
- while true do
- local res, err = watch_ctx.cli:get(watch_ctx.prefix)
- if not res then
- log.error("etcd get: ", err)
- ngx_sleep(3)
- elseif not (res.body and res.body.header and
res.body.header.revision) then
- log.error("etcd response missing header.revision")
- ngx_sleep(3)
- else
- rev = tonumber(res.body.header.revision)
- if not rev then
- log.error("etcd response has invalid header.revision:
",
- tostring(res.body.header.revision))
- ngx_sleep(3)
- else
- break
- end
- end
- end
+ -- Config objects take watch_ctx.started as permission to wait only for
+ -- events from the main watcher. Do not publish that state until etcd
+ -- has answered at least one request. The returned revision is only the
+ -- fallback when no configuration was preloaded: using a newer revision
+ -- in place of the snapshot revision would skip intervening writes.
+ local current_rev = wait_for_etcd_available(watch_ctx.cli,
watch_ctx.prefix)
+ local rev = loaded_configuration_rev
+ if not rev or rev == 0 then
+ rev = current_rev
end
watch_ctx.rev = rev + 1
diff --git a/t/core/etcd-watch-start-revision.t
b/t/core/etcd-watch-start-revision.t
index 28eaeecd00..3f5eb70f14 100644
--- a/t/core/etcd-watch-start-revision.t
+++ b/t/core/etcd-watch-start-revision.t
@@ -45,6 +45,9 @@ deployment:
prefix: "/apisix-watch-start-revision"
host:
- "http://127.0.0.1:2379"
+--- extra_yaml_config
+nginx_config:
+ worker_processes: 1
--- extra_init_by_lua_start
-- io.popen reports nothing about how curl fared, so the etcd response is
-- checked instead: a silently failing write here would look exactly like
@@ -89,6 +92,32 @@ deployment:
-- after the snapshot was taken, before any worker starts watching
_G.etcd_put_for_test("/apisix-watch-start-revision/routes/1",
'{"uri":"/hello","upstream":{"type":"roundrobin","nodes":{"127.0.0.1:1980":1}}}')
+--- extra_init_worker_by_lua
+ -- http_init_worker has created the configuration objects and scheduled the
+ -- main watcher, but nginx timers cannot run until this phase returns. Wrap
+ -- the cached watcher client now to verify that initialising the watcher
+ -- checks etcd availability even when the snapshot supplied its revision.
+ local config_etcd = require("apisix.core.config_etcd")
+ local get_etcd
+ for i = 1, 256 do
+ local name, value = debug.getupvalue(config_etcd.new, i)
+ if not name then
+ break
+ end
+ if name == "get_etcd" then
+ get_etcd = value
+ break
+ end
+ end
+ assert(get_etcd, "get_etcd upvalue not found")
+
+ local etcd_cli = assert(get_etcd())
+ local original_get = etcd_cli.get
+ _G.watch_availability_checks = 0
+ etcd_cli.get = function(self, ...)
+ _G.watch_availability_checks = _G.watch_availability_checks + 1
+ return original_get(self, ...)
+ end
--- config
location /t {
content_by_lua_block {
@@ -108,9 +137,11 @@ deployment:
end
ngx.say("uri: ", route.value.uri)
+ ngx.say("availability checks: ", _G.watch_availability_checks)
}
}
--- request
GET /t
--- response_body
uri: /hello
+availability checks: 1