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

baoyuan 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 18884bcad feat: add ksuid algorithm on request-id plugin (#12573)
18884bcad is described below

commit 18884bcad48771e5e05586db4c3932cdb6afc6e0
Author: YH <[email protected]>
AuthorDate: Fri Sep 5 15:20:48 2025 +0800

    feat: add ksuid algorithm on request-id plugin (#12573)
---
 apisix-master-0.rockspec             |   1 +
 apisix/plugins/request-id.lua        |   7 +-
 docs/en/latest/plugins/request-id.md |  55 ++++++++++++-
 docs/zh/latest/plugins/request-id.md |  55 ++++++++++++-
 t/plugin/request-id3.t               | 154 +++++++++++++++++++++++++++++++++++
 5 files changed, 269 insertions(+), 3 deletions(-)

diff --git a/apisix-master-0.rockspec b/apisix-master-0.rockspec
index abc5f3a76..83f6309ec 100644
--- a/apisix-master-0.rockspec
+++ b/apisix-master-0.rockspec
@@ -40,6 +40,7 @@ dependencies = {
     "lua-resty-balancer = 0.04",
     "lua-resty-ngxvar = 0.5.2",
     "lua-resty-jit-uuid = 0.0.7",
+    "lua-resty-ksuid = 1.0.1",
     "lua-resty-worker-events = 1.0.0",
     "lua-resty-healthcheck-api7 = 3.2.0",
     "api7-lua-resty-jwt = 0.2.5",
diff --git a/apisix/plugins/request-id.lua b/apisix/plugins/request-id.lua
index dac3162db..d9569302a 100644
--- a/apisix/plugins/request-id.lua
+++ b/apisix/plugins/request-id.lua
@@ -19,6 +19,7 @@ local ngx = ngx
 local core = require("apisix.core")
 local uuid = require("resty.jit-uuid")
 local nanoid = require("nanoid")
+local ksuid = require("resty.ksuid")
 local math_random = math.random
 local str_byte = string.byte
 local ffi = require "ffi"
@@ -32,7 +33,7 @@ local schema = {
         include_in_response = {type = "boolean", default = true},
         algorithm = {
             type = "string",
-            enum = {"uuid", "nanoid", "range_id"},
+            enum = {"uuid", "nanoid", "range_id", "ksuid"},
             default = "uuid"
         },
         range_id = {
@@ -87,6 +88,10 @@ local function get_request_id(conf)
         return get_range_id(conf.range_id)
     end
 
+    if conf.algorithm == "ksuid" then
+        return ksuid.generate()
+    end
+
     return uuid()
 end
 
diff --git a/docs/en/latest/plugins/request-id.md 
b/docs/en/latest/plugins/request-id.md
index 3f5fb398b..3d998ac21 100644
--- a/docs/en/latest/plugins/request-id.md
+++ b/docs/en/latest/plugins/request-id.md
@@ -40,7 +40,7 @@ The `request-id` Plugin adds a unique ID to each request 
proxied through APISIX,
 | ------------------- | ------- | -------- | -------------- | 
------------------------------- | 
---------------------------------------------------------------------- |
 | header_name         | string  | False    | "X-Request-Id" |                  
               | Name of the header that carries the request unique ID. Note 
that if a request carries an ID in the `header_name` header, the Plugin will 
use the header value as the unique ID and will not overwrite it with the 
generated ID.                                 |
 | include_in_response | boolean | False    | true           |                  
               | If true, include the generated request ID in the response 
header, where the name of the header is the `header_name` value. |
-| algorithm           | string  | False    | "uuid"         | 
["uuid","nanoid","range_id"] | Algorithm used for generating the unique ID. 
When set to `uuid` , the Plugin generates a universally unique identifier. When 
set to `nanoid`, the Plugin generates a compact, URL-safe ID. When set to 
`range_id`, the Plugin generates a sequential ID with specific parameters.      
           |
+| algorithm           | string  | False    | "uuid"         | 
["uuid","nanoid","range_id","ksuid"] | Algorithm used for generating the unique 
ID. When set to `uuid` , the Plugin generates a universally unique identifier. 
When set to `nanoid`, the Plugin generates a compact, URL-safe ID. When set to 
`range_id`, the Plugin generates a sequential ID with specific parameters. When 
set to `ksuid`, the Plugin generates a sequential ID with timestamp and random 
number.                  |
 | range_id      | object | False | |   | Configuration for generating a 
request ID using the `range_id` algorithm.  |
 | range_id.char_set      | string | False | 
"abcdefghijklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ0123456789" | minimum 
length 6 | Character set used for the `range_id` algorithm. |
 | range_id.length    | integer | False | 16             | >=6 | Length of the 
generated ID for the `range_id` algorithm. |
@@ -243,6 +243,59 @@ You should receive an `HTTP/1.1 200 OK` response and see 
the response includes t
 X-Request-Id: kepgHWCH2ycQ6JknQKrX2
 ```
 
+### Use `ksuid` Algorithm
+
+The following example demonstrates how to configure `request-id` on a Route 
and use the `ksuid` algorithm to generate the request ID.
+
+Create a Route with the `request-id` Plugin as such:
+
+```shell
+curl "http://127.0.0.1:9180/apisix/admin/routes"; -X PUT \
+  -H "X-API-KEY: ${ADMIN_API_KEY}" \
+  -d '{
+    "id": "request-id-route",
+    "uri": "/anything",
+    "plugins": {
+      "request-id": {
+        "algorithm": "ksuid"
+      }
+    },
+    "upstream": {
+      "type": "roundrobin",
+      "nodes": {
+        "httpbin.org:80": 1
+      }
+    }
+  }'
+```
+
+Send a request to the Route:
+
+```shell
+curl -i "http://127.0.0.1:9080/anything";
+```
+
+You should receive an `HTTP/1.1 200 OK` response and see the response includes 
the `X-Request-Id` header with an ID generated using the `ksuid` algorithm:
+
+```text
+X-Request-Id: 325ghCANEKjw6Jsfejg5p6QrLYB
+```
+
+If the 
[ksuid](https://github.com/segmentio/ksuid?tab=readme-ov-file#command-line-tool)
 is installed, this ID can be viewed through `ksuid -f inspect 
325ghCANEKjw6Jsfejg5p6QrLYB`:
+
+``` text
+REPRESENTATION:
+
+    String: 325ghCANEKjw6Jsfejg5p6QrLYB
+    Raw: 15430DBBD7F68AD7CA0AE277772AB36DDB1A3C13
+
+COMPONENTS:
+
+    Time: 2025-09-01 16:39:23 +0800 CST
+    Timestamp: 356715963
+    Payload: D7F68AD7CA0AE277772AB36DDB1A3C13
+```
+
 ### Attach Request ID Globally and on a Route
 
 The following example demonstrates how to configure `request-id` as a global 
Plugin and on a Route to attach two IDs.
diff --git a/docs/zh/latest/plugins/request-id.md 
b/docs/zh/latest/plugins/request-id.md
index 7747fc70a..371319a77 100644
--- a/docs/zh/latest/plugins/request-id.md
+++ b/docs/zh/latest/plugins/request-id.md
@@ -36,7 +36,7 @@ description: request-id 插件为通过 APISIX 代理的每个请求添加一个
 | ------------------- | ------- | -------- | -------------- | ------ | 
------------------------------ |
 | header_name | string | 否 | "X-Request-Id" | | 携带请求唯一 ID 的标头的名称。请注意,如果请求在 
`header_name` 标头中携带 ID,则插件将使用标头值作为唯一 ID,并且不会用生成的 ID 覆盖它。|
 | include_in_response | 布尔值 | 否 | true | | 如果为 true,则将生成的请求 ID 
包含在响应标头中,其中标头的名称是 `header_name` 值。|
-| algorithm | string | 否 | "uuid" | ["uuid","nanoid","range_id"] | 用于生成唯一 ID 
的算法。设置为 `uuid` 时,插件会生成一个通用唯一标识符。设置为 `nanoid` 时,插件会生成一个紧凑的、URL 安全的 ID。设置为 
`range_id` 时,插件会生成具有特定参数的连续 ID。|
+| algorithm | string | 否 | "uuid" | ["uuid","nanoid","range_id","ksuid"] | 
用于生成唯一 ID 的算法。设置为 `uuid` 时,插件会生成一个通用唯一标识符。设置为 `nanoid` 时,插件会生成一个紧凑的、URL 安全的 
ID。设置为 `range_id` 时,插件会生成具有特定参数的连续 ID。设置为 `ksuid` 时,插件会生成具有时间戳和随机值的连续 ID。|
 | range_id | object | 否 | | |使用 `range_id` 算法生成请求 ID 的配置。|
 | range_id.char_set | string | 否 | 
"abcdefghijklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ0123456789" | 最小长度 6 | 用于 
`range_id` 算法的字符集。|
 | range_id.length | integer | 否 | 16 | >=6 | 用于 `range_id` 算法的生成的 ID 的长度。|
@@ -239,6 +239,59 @@ curl -i "http://127.0.0.1:9080/anything";
 X-Request-Id: kepgHWCH2ycQ6JknQKrX2
 ```
 
+### 使用 `ksuid` 算法
+
+以下示例演示如何在路由上配置 `request-id` 并使用 `ksuid` 算法生成请求 ID。
+
+使用 `request-id` 插件创建路由,如下所示:
+
+```shell
+curl "http://127.0.0.1:9180/apisix/admin/routes"; -X PUT \
+  -H "X-API-KEY: ${ADMIN_API_KEY}" \
+  -d '{
+    "id": "request-id-route",
+    "uri": "/anything",
+    "plugins": {
+      "request-id": {
+        "algorithm": "ksuid"
+      }
+    },
+    "upstream": {
+      "type": "roundrobin",
+      "nodes": {
+        "httpbin.org:80": 1
+      }
+    }
+  }'
+```
+
+向路由发送请求:
+
+```shell
+curl -i "http://127.0.0.1:9080/anything";
+```
+
+您应该收到一个 `HTTP/1.1 200 OK` 响应,并看到响应包含 `X-Request-Id` 标头,其中的 ID 使用 `ksuid` 算法生成:
+
+```text
+X-Request-Id: 325ghCANEKjw6Jsfejg5p6QrLYB
+```
+
+如果装有[ksuid](https://github.com/segmentio/ksuid?tab=readme-ov-file#command-line-tool)命令工具,此
 ID 可以通过`ksuid -f inspect 325ghCANEKjw6Jsfejg5p6QrLYB`查看:
+
+``` text
+REPRESENTATION:
+
+    String: 325ghCANEKjw6Jsfejg5p6QrLYB
+    Raw: 15430DBBD7F68AD7CA0AE277772AB36DDB1A3C13
+
+COMPONENTS:
+
+    Time: 2025-09-01 16:39:23 +0800 CST
+    Timestamp: 356715963
+    Payload: D7F68AD7CA0AE277772AB36DDB1A3C13
+```
+
 ### 全局和在路由上附加请求 ID
 
 以下示例演示如何将 `request-id` 配置为全局插件并在路由上附加两个 ID。
diff --git a/t/plugin/request-id3.t b/t/plugin/request-id3.t
new file mode 100644
index 000000000..a377d6e81
--- /dev/null
+++ b/t/plugin/request-id3.t
@@ -0,0 +1,154 @@
+#
+# 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';
+
+worker_connections(1024);
+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: check config with algorithm ksuid
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "plugins": {
+                            "request-id": {
+                                "algorithm": "ksuid"
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1982": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/opentracing"
+                }]]
+                )
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 2: hit
+--- request
+GET /opentracing
+--- error_log
+X-Request-Id
+--- no_error_log
+[error]
+
+
+
+=== TEST 3: add plugin with algorithm ksuid
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local http = require "resty.http"
+            local v = {}
+            local ids = {}
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                       "plugins": {
+                            "request-id": {
+                                "algorithm": "ksuid"
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1982": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/opentracing"
+                }]]
+                )
+            if code >= 300 then
+                ngx.say("algorithm ksuid is error")
+            end
+            for i = 1, 180 do
+                local th = assert(ngx.thread.spawn(function()
+                    local httpc = http.new()
+                    local uri = "http://127.0.0.1:"; .. ngx.var.server_port .. 
"/opentracing"
+                    local res, err = httpc:request_uri(uri,
+                        {
+                            method = "GET",
+                            headers = {
+                                ["Content-Type"] = "application/json",
+                            }
+                        }
+                    )
+                    if not res then
+                        ngx.log(ngx.ERR, err)
+                        return
+                    end
+                    local id = res.headers["X-Request-Id"]
+                    if not id then
+                        return -- ignore if the data is not synced yet.
+                    end
+                    if #id ~= 27 then
+                        ngx.say(id)
+                        ngx.say("incorrect length for id")
+                        return
+                    end
+                    local start, en = string.find(id, '[a-zA-Z0-9]*')
+                    if start ~= 1 or en ~= 27 then
+                        ngx.say("incorrect char set for id")
+                        ngx.say(id)
+                        return
+                    end
+                    if ids[id] == true then
+                        ngx.say("ids not unique")
+                        return
+                    end
+                    ids[id] = true
+                end, i))
+                table.insert(v, th)
+            end
+            for i, th in ipairs(v) do
+                ngx.thread.wait(th)
+            end
+            ngx.say("true")
+        }
+    }
+--- wait: 5
+--- response_body
+true

Reply via email to