This is an automated email from the ASF dual-hosted git repository.
AlinsRan 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 a64f2baa0 fix(standalone): stop aborting stream connections before the
first config arrives (#13855)
a64f2baa0 is described below
commit a64f2baa06b1c7d55e53051f3430f39bf9f4024c
Author: AlinsRan <[email protected]>
AuthorDate: Fri Aug 28 08:50:46 2026 +0800
fix(standalone): stop aborting stream connections before the first config
arrives (#13855)
---
apisix/admin/standalone.lua | 10 +++++--
apisix/stream/router/ip_port.lua | 8 +++++-
t/admin/standalone-stream.t | 57 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 72 insertions(+), 3 deletions(-)
diff --git a/apisix/admin/standalone.lua b/apisix/admin/standalone.lua
index 46f9a8001..7e69b5902 100644
--- a/apisix/admin/standalone.lua
+++ b/apisix/admin/standalone.lua
@@ -333,7 +333,10 @@ function _M.init_worker()
-- due to the event module can not broadcast events between http and
stream subsystems,
-- we need to poll the shared dict to keep the config in sync
- local last_modified_per_worker
+ -- The timestamp only has second resolution, so two updates landing in the
+ -- same second are indistinguishable by it and the later one would never
+ -- reach this worker. The digest changes with the content, so compare both.
+ local last_modified_per_worker, digest_per_worker
timer_every(1, function ()
if not exiting() then
local config, err = get_config()
@@ -343,9 +346,12 @@ function _M.init_worker()
end
else
local last_modified = config[METADATA_LAST_MODIFIED]
- if last_modified_per_worker ~= last_modified then
+ local digest = config[METADATA_DIGEST]
+ if last_modified_per_worker ~= last_modified
+ or digest_per_worker ~= digest then
update_config(config)
last_modified_per_worker = last_modified
+ digest_per_worker = digest
end
end
end
diff --git a/apisix/stream/router/ip_port.lua b/apisix/stream/router/ip_port.lua
index 45e8e5531..1757824f9 100644
--- a/apisix/stream/router/ip_port.lua
+++ b/apisix/stream/router/ip_port.lua
@@ -151,7 +151,13 @@ do
local _, cur_svc_ver = service_mod.services()
if router_ver ~= user_routes.conf_version
or service_ver ~= cur_svc_ver then
- local err = create_router(user_routes.values)
+ -- `values` is nil until the config source has delivered
+ -- /stream_routes for the first time, which in standalone mode can
+ -- happen after this subsystem is already accepting connections.
+ -- Treat that as "no stream route" instead of indexing a nil table:
+ -- the error would be thrown before router_ver is assigned, so
every
+ -- later connection would take this branch and abort again.
+ local err = create_router(user_routes.values or {})
if err then
return false, "failed to create router: " .. err
end
diff --git a/t/admin/standalone-stream.t b/t/admin/standalone-stream.t
new file mode 100644
index 000000000..27c90814e
--- /dev/null
+++ b/t/admin/standalone-stream.t
@@ -0,0 +1,57 @@
+#
+# 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');
+no_long_string();
+no_root_location();
+no_shuffle();
+
+add_block_preprocessor(sub {
+ my ($block) = @_;
+
+ if (!defined $block->yaml_config) {
+ $block->set_value("yaml_config", <<'_EOC_');
+deployment:
+ role: traditional
+ role_traditional:
+ config_provider: yaml
+ admin:
+ admin_key:
+ - name: admin
+ key: edd1c9f034335f136f87ad84b625c8f1
+ role: admin
+_EOC_
+ }
+
+ $block->set_value("stream_enable", 1);
+
+ if (!$block->stream_request) {
+ $block->set_value("stream_request", "mmm");
+ }
+});
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: stream connection before the first config is pushed
+--- error_log
+not hit any route
+--- no_error_log
+attempt to index local 'tab'