This is an automated email from the ASF dual-hosted git repository.

ashishtiwari 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 7d5aeafdb feat: add max pending entries option to batch-processor 
(#12338)
7d5aeafdb is described below

commit 7d5aeafdb442151667be95ad4ebc37cd3b1fa6bb
Author: Ashish Tiwari <[email protected]>
AuthorDate: Mon Jun 23 11:10:17 2025 +0530

    feat: add max pending entries option to batch-processor (#12338)
---
 apisix/plugins/kafka-logger.lua          |  16 ++++-
 apisix/utils/batch-processor-manager.lua |  35 ++++++++-
 apisix/utils/batch-processor.lua         |   6 +-
 t/plugin/kafka-logger3.t                 | 120 +++++++++++++++++++++++++++++++
 4 files changed, 170 insertions(+), 7 deletions(-)

diff --git a/apisix/plugins/kafka-logger.lua b/apisix/plugins/kafka-logger.lua
index adeec2921..75510f52c 100644
--- a/apisix/plugins/kafka-logger.lua
+++ b/apisix/plugins/kafka-logger.lua
@@ -19,6 +19,7 @@ local core     = require("apisix.core")
 local log_util = require("apisix.utils.log-util")
 local producer = require ("resty.kafka.producer")
 local bp_manager_mod = require("apisix.utils.batch-processor-manager")
+local plugin = require("apisix.plugin")
 
 local math     = math
 local pairs    = pairs
@@ -140,7 +141,12 @@ local metadata_schema = {
     properties = {
         log_format = {
             type = "object"
-        }
+        },
+        max_pending_entries = {
+            type = "integer",
+            description = "maximum number of pending entries in the batch 
processor",
+            minimum = 1,
+        },
     },
 }
 
@@ -246,6 +252,9 @@ end
 
 
 function _M.log(conf, ctx)
+    local metadata = plugin.plugin_metadata(plugin_name)
+    local max_pending_entries = metadata and metadata.value and
+                                metadata.value.max_pending_entries or nil
     local entry
     if conf.meta_format == "origin" then
         entry = log_util.get_req_original(ctx, conf)
@@ -255,7 +264,7 @@ function _M.log(conf, ctx)
         entry = log_util.get_log_entry(plugin_name, conf, ctx)
     end
 
-    if batch_processor_manager:add_entry(conf, entry) then
+    if batch_processor_manager:add_entry(conf, entry, max_pending_entries) then
         return
     end
 
@@ -307,10 +316,11 @@ function _M.log(conf, ctx)
         end
 
         core.log.info("send data to kafka: ", data)
+
         return send_kafka_data(conf, data, prod)
     end
 
-    batch_processor_manager:add_entry_to_new_processor(conf, entry, ctx, func)
+    batch_processor_manager:add_entry_to_new_processor(conf, entry, ctx, func, 
max_pending_entries)
 end
 
 
diff --git a/apisix/utils/batch-processor-manager.lua 
b/apisix/utils/batch-processor-manager.lua
index a8122773d..4e97bd617 100644
--- a/apisix/utils/batch-processor-manager.lua
+++ b/apisix/utils/batch-processor-manager.lua
@@ -29,6 +29,7 @@ function _M.new(name)
     return setmetatable({
         stale_timer_running = false,
         buffers = {},
+        total_pushed_entries = 0,
         name = name,
     }, mt)
 end
@@ -85,7 +86,25 @@ do
 end
 
 
-function _M:add_entry(conf, entry)
+local function total_processed_entries(self)
+    local processed_entries = 0
+    for _, log_buffer in pairs(self.buffers) do
+        processed_entries = processed_entries + log_buffer.processed_entries
+    end
+    return processed_entries
+end
+
+function _M:add_entry(conf, entry, max_pending_entries)
+    if max_pending_entries then
+        local total_processed_entries_count = total_processed_entries(self)
+        if self.total_pushed_entries - total_processed_entries_count > 
max_pending_entries then
+            core.log.error("max pending entries limit exceeded. discarding 
entry.",
+                           " total_pushed_entries: ", 
self.total_pushed_entries,
+                           " total_processed_entries: ", 
total_processed_entries_count,
+                           " max_pending_entries: ", max_pending_entries)
+            return
+        end
+    end
     check_stale(self)
 
     local log_buffer = self.buffers[conf]
@@ -94,11 +113,22 @@ function _M:add_entry(conf, entry)
     end
 
     log_buffer:push(entry)
+    self.total_pushed_entries = self.total_pushed_entries + 1
     return true
 end
 
 
-function _M:add_entry_to_new_processor(conf, entry, ctx, func)
+function _M:add_entry_to_new_processor(conf, entry, ctx, func, 
max_pending_entries)
+    if max_pending_entries then
+        local total_processed_entries_count = total_processed_entries(self)
+        if self.total_pushed_entries - total_processed_entries_count > 
max_pending_entries then
+            core.log.error("max pending entries limit exceeded. discarding 
entry.",
+                           " total_pushed_entries: ", 
self.total_pushed_entries,
+                           " total_processed_entries: ", 
total_processed_entries_count,
+                           " max_pending_entries: ", max_pending_entries)
+            return
+        end
+    end
     check_stale(self)
 
     local config = {
@@ -120,6 +150,7 @@ function _M:add_entry_to_new_processor(conf, entry, ctx, 
func)
 
     log_buffer:push(entry)
     self.buffers[conf] = log_buffer
+    self.total_pushed_entries = self.total_pushed_entries + 1
     return true
 end
 
diff --git a/apisix/utils/batch-processor.lua b/apisix/utils/batch-processor.lua
index cd831f2ad..eabee4f9e 100644
--- a/apisix/utils/batch-processor.lua
+++ b/apisix/utils/batch-processor.lua
@@ -91,6 +91,7 @@ function execute_func(premature, self, batch)
             core.log.error("Batch Processor[", self.name, "] failed to process 
entries [",
                             #batch.entries + 1 - first_fail, "/", 
#batch.entries ,"]: ", err)
             batch.entries = slice_batch(batch.entries, first_fail)
+            self.processed_entries = self.processed_entries + first_fail - 1
         else
             core.log.error("Batch Processor[", self.name,
                            "] failed to process entries: ", err)
@@ -101,13 +102,14 @@ function execute_func(premature, self, batch)
             schedule_func_exec(self, self.retry_delay,
                                batch)
         else
+            self.processed_entries = self.processed_entries + #batch.entries
             core.log.error("Batch Processor[", self.name,"] exceeded ",
                            "the max_retry_count[", batch.retry_count,
                            "] dropping the entries")
         end
         return
     end
-
+    self.processed_entries = self.processed_entries + #batch.entries
     core.log.debug("Batch Processor[", self.name,
                    "] successfully processed the entries")
 end
@@ -170,12 +172,12 @@ function batch_processor:new(func, config)
         last_entry_t = 0,
         route_id = config.route_id,
         server_addr = config.server_addr,
+        processed_entries = 0
     }
 
     return setmetatable(processor, batch_processor_mt)
 end
 
-
 function batch_processor:push(entry)
     -- if the batch size is one then immediately send for processing
     if self.batch_max_size == 1 then
diff --git a/t/plugin/kafka-logger3.t b/t/plugin/kafka-logger3.t
new file mode 100644
index 000000000..82c8248c6
--- /dev/null
+++ b/t/plugin/kafka-logger3.t
@@ -0,0 +1,120 @@
+#
+# 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);
+no_long_string();
+no_root_location();
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    if (!$block->request) {
+        $block->set_value("request", "GET /t");
+    }
+});
+
+run_tests;
+
+__DATA__
+
+=== TEST 1: should drop entries
+--- extra_yaml_config
+plugins:
+  - kafka-logger
+--- config
+location /t {
+    content_by_lua_block {
+        local http = require "resty.http"
+        local httpc = http.new()
+        local data = {
+            {
+                input = {
+                    plugins = {
+                        ["kafka-logger"] = {
+                            broker_list = {
+                                ["127.0.0.1"] = 1234
+                            },
+                            kafka_topic = "test2",
+                            producer_type = "sync",
+                            timeout = 1,
+                            batch_max_size = 1,
+                            required_acks = 1,
+                            meta_format = "origin",
+                            max_retry_count = 10,
+                        }
+                    },
+                    upstream = {
+                        nodes = {
+                            ["127.0.0.1:1980"] = 1
+                        },
+                        type = "roundrobin"
+                    },
+                    uri = "/hello",
+                },
+            },
+        }
+
+        local t = require("lib.test_admin").test
+
+        -- Set plugin metadata
+        local metadata = {
+            log_format = {
+                host = "$host",
+                ["@timestamp"] = "$time_iso8601",
+                client_ip = "$remote_addr"
+            },
+            max_pending_entries = 1
+        }
+
+        local code, body = t('/apisix/admin/plugin_metadata/kafka-logger', 
ngx.HTTP_PUT, metadata)
+        if code >= 300 then
+            ngx.status = code
+            ngx.say(body)
+            return
+        end
+
+        -- Create route
+        local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, 
data[1].input)
+        if code >= 300 then
+            ngx.status = code
+            ngx.say(body)
+            return
+        end
+
+        local uri = "http://127.0.0.1:"; .. ngx.var.server_port .. "/hello"
+        httpc:request_uri(uri, {
+            method = "GET",
+            keepalive_timeout = 1,
+            keepalive_pool = 1,
+        })
+        httpc:request_uri(uri, {
+            method = "GET",
+            keepalive_timeout = 1,
+            keepalive_pool = 1,
+        })
+        httpc:request_uri(uri, {
+            method = "GET",
+            keepalive_timeout = 1,
+            keepalive_pool = 1,
+        })
+        ngx.sleep(2)
+    }
+}
+--- error_log
+max pending entries limit exceeded. discarding entry
+--- timeout: 5

Reply via email to