Skip to content

Commit 4f7398a

Browse files
authored
refactor(meta): add Lua API documentation and benchmarking tools (#18458)
* refactor(meta): move Lua functions to metactl namespace Move all Lua functions from global scope to metactl namespace to prevent conflicts with other Lua libraries: - metactl.new_grpc_client() replaces new_grpc_client() - metactl.spawn() replaces spawn() - metactl.sleep() replaces sleep() - metactl.NULL replaces NULL * docs(meta): add Lua API documentation and benchmarking tools Add comprehensive documentation for the metactl Lua runtime API, including all available functions, client methods, and usage patterns. Add benchmark script and runner for performance testing of concurrent meta operations. - Complete API documentation with examples and best practices - Benchmark script with configurable concurrent workers - Python test runner with meta service setup automation * chore: add README to benchmark dir
1 parent 419a0c2 commit 4f7398a

File tree

13 files changed

+616
-59
lines changed

13 files changed

+616
-59
lines changed

โ€Žlicenserc.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ excludes = [
1313
# licensed under Elastic License 2.0
1414
"src/binaries/query/ee_main.rs",
1515
"src/meta/binaries/meta/ee_main.rs",
16+
"src/meta/control/lua_util.lua",
1617
"src/meta/ee",
1718
"src/query/ee",
1819
"src/bendsave",

โ€Žsrc/meta/control/LUA_API.md

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
# Databend Meta Control Lua API Documentation
2+
3+
This document describes the Lua runtime API available in `databend-metactl lua` command.
4+
5+
## Overview
6+
7+
The Lua runtime provides access to meta service operations through the `metactl` namespace. All functions and utilities are available in the global `metactl` table.
8+
9+
## Core Functions
10+
11+
### metactl.new_grpc_client(address)
12+
13+
Creates a new gRPC client for communicating with the meta service.
14+
15+
**Parameters:**
16+
- `address` (string): The gRPC address of the meta service (e.g., "127.0.0.1:9191")
17+
18+
**Returns:**
19+
- A gRPC client object with methods for meta operations
20+
21+
**Example:**
22+
```lua
23+
local client = metactl.new_grpc_client("127.0.0.1:9191")
24+
```
25+
26+
### metactl.spawn(function)
27+
28+
Spawns an asynchronous task that runs concurrently.
29+
30+
**Parameters:**
31+
- `function`: A Lua function to execute asynchronously
32+
33+
**Returns:**
34+
- A task handle that can be awaited with `join()`
35+
36+
**Example:**
37+
```lua
38+
local task = metactl.spawn(function()
39+
metactl.sleep(1.0)
40+
print("Task completed")
41+
end)
42+
task:join()
43+
```
44+
45+
### metactl.sleep(seconds)
46+
47+
Asynchronously sleeps for the specified duration.
48+
49+
**Parameters:**
50+
- `seconds` (number): Duration to sleep in seconds (supports fractional values)
51+
52+
**Example:**
53+
```lua
54+
metactl.sleep(0.5) -- Sleep for 500ms
55+
metactl.sleep(2.0) -- Sleep for 2 seconds
56+
```
57+
58+
### metactl.to_string(value)
59+
60+
Converts any Lua value to a human-readable string representation.
61+
62+
**Parameters:**
63+
- `value`: Any Lua value (nil, boolean, number, string, table, etc.)
64+
65+
**Returns:**
66+
- String representation of the value
67+
68+
**Features:**
69+
- Handles nested tables recursively
70+
- Detects and converts byte vectors to strings
71+
- Sorts table keys for consistent output
72+
- Handles NULL values from meta service
73+
- Escapes quotes in strings
74+
- Single-line output format
75+
76+
**Example:**
77+
```lua
78+
print(metactl.to_string({key="value", data={1,2,3}}))
79+
-- Output: {"data"={1,2,3},"key"="value"}
80+
81+
print(metactl.to_string(metactl.NULL))
82+
-- Output: NULL
83+
```
84+
85+
## Constants
86+
87+
### metactl.NULL
88+
89+
A special constant representing NULL values returned from the meta service.
90+
91+
**Example:**
92+
```lua
93+
local result, err = client:get("nonexistent_key")
94+
if result == metactl.NULL then
95+
print("Key not found")
96+
end
97+
```
98+
99+
## gRPC Client Methods
100+
101+
The client object returned by `metactl.new_grpc_client()` provides these methods:
102+
103+
### client:get(key)
104+
105+
Retrieves a value from the meta service.
106+
107+
**Parameters:**
108+
- `key` (string): The key to retrieve
109+
110+
**Returns:**
111+
- `result`: The retrieved value or `metactl.NULL` if not found
112+
- `error`: Error message string if operation failed, nil otherwise
113+
114+
**Example:**
115+
```lua
116+
local client = metactl.new_grpc_client("127.0.0.1:9191")
117+
local result, err = client:get("my_key")
118+
if err then
119+
print("Error:", err)
120+
else
121+
print("Value:", metactl.to_string(result))
122+
end
123+
```
124+
125+
### client:upsert(key, value)
126+
127+
Inserts or updates a key-value pair in the meta service.
128+
129+
**Parameters:**
130+
- `key` (string): The key to upsert
131+
- `value` (string): The value to store
132+
133+
**Returns:**
134+
- `result`: Operation result containing sequence number and other metadata
135+
- `error`: Error message string if operation failed, nil otherwise
136+
137+
**Example:**
138+
```lua
139+
local client = metactl.new_grpc_client("127.0.0.1:9191")
140+
local result, err = client:upsert("my_key", "my_value")
141+
if err then
142+
print("Error:", err)
143+
else
144+
print("Upsert result:", metactl.to_string(result))
145+
end
146+
```
147+
148+
## Task Handling
149+
150+
### task:join()
151+
152+
Waits for a spawned task to complete and returns its result.
153+
154+
**Returns:**
155+
- The return value of the spawned function
156+
157+
**Example:**
158+
```lua
159+
local task = metactl.spawn(function()
160+
return "task result"
161+
end)
162+
163+
local result = task:join()
164+
print(result) -- Output: task result
165+
```
166+
167+
## Usage Patterns
168+
169+
### Basic Key-Value Operations
170+
171+
```lua
172+
local client = metactl.new_grpc_client("127.0.0.1:9191")
173+
174+
-- Store a value
175+
local upsert_result, err = client:upsert("config/timeout", "30")
176+
if not err then
177+
print("Stored successfully:", metactl.to_string(upsert_result))
178+
end
179+
180+
-- Retrieve a value
181+
local get_result, err = client:get("config/timeout")
182+
if not err then
183+
if get_result == metactl.NULL then
184+
print("Key not found")
185+
else
186+
print("Retrieved:", metactl.to_string(get_result))
187+
end
188+
end
189+
```
190+
191+
### Concurrent Operations
192+
193+
```lua
194+
local client = metactl.new_grpc_client("127.0.0.1:9191")
195+
196+
local task1 = metactl.spawn(function()
197+
client:upsert("key1", "value1")
198+
print("Task 1 completed")
199+
end)
200+
201+
local task2 = metactl.spawn(function()
202+
metactl.sleep(0.1)
203+
local result, _ = client:get("key1")
204+
print("Task 2 got:", metactl.to_string(result))
205+
end)
206+
207+
task1:join()
208+
task2:join()
209+
```
210+
211+
### Error Handling
212+
213+
```lua
214+
local client = metactl.new_grpc_client("127.0.0.1:9191")
215+
216+
local result, err = client:get("some_key")
217+
if err then
218+
print("Operation failed:", err)
219+
return
220+
end
221+
222+
if result == metactl.NULL then
223+
print("Key does not exist")
224+
else
225+
print("Key value:", metactl.to_string(result))
226+
end
227+
```
228+
229+
## Data Types
230+
231+
The meta service returns structured data that can be processed using `metactl.to_string()`:
232+
233+
```lua
234+
-- Typical get response structure:
235+
{
236+
"data" = "actual_value", -- The stored value as byte array
237+
"seq" = 1 -- Sequence number
238+
}
239+
240+
-- Typical upsert response structure:
241+
{
242+
"result" = {
243+
"data" = "stored_value",
244+
"seq" = 1
245+
}
246+
}
247+
```
248+
249+
## Best Practices
250+
251+
1. **Always check for errors**: Both `get` and `upsert` operations can fail
252+
2. **Handle NULL values**: Use `metactl.NULL` comparison for missing keys
253+
3. **Use concurrent operations**: Leverage `metactl.spawn()` for parallel processing
254+
4. **Format output**: Use `metactl.to_string()` for readable data display
255+
5. **Clean resource usage**: Ensure tasks are properly joined
256+
257+
## Examples
258+
259+
See the test files in `tests/metactl/subcommands/` for comprehensive usage examples:
260+
- `cmd_lua_grpc.py` - Basic gRPC operations
261+
- `cmd_lua_spawn_grpc.py` - Concurrent gRPC operations
262+
- `cmd_lua_spawn_concurrent.py` - Task spawning patterns

โ€Žtests/metactl/lua_util.lua renamed to โ€Žsrc/meta/control/lua_util.lua

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,36 @@
1+
-- Copyright 2021 Datafuse Labs
2+
--
3+
-- Licensed under the Apache License, Version 2.0 (the "License");
4+
-- you may not use this file except in compliance with the License.
5+
-- You may obtain a copy of the License at
6+
--
7+
-- http://www.apache.org/licenses/LICENSE-2.0
8+
--
9+
-- Unless required by applicable law or agreed to in writing, software
10+
-- distributed under the License is distributed on an "AS IS" BASIS,
11+
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
-- See the License for the specific language governing permissions and
13+
-- limitations under the License.
14+
115
-- Function to check if a value is NULL (mlua NULL constant)
2-
function is_null(value)
3-
return value == NULL
16+
local function is_null(value)
17+
return value == metactl.NULL
418
end
519

620
-- Function to normalize NULL values to nil for easier handling
7-
function normalize_value(value)
21+
local function normalize_value(value)
822
if is_null(value) then
923
return nil
1024
end
1125
return value
1226
end
1327

1428
-- Function to check if a table represents a bytes vector and convert it to string
15-
function bytes_vector_to_string(value)
29+
local function bytes_vector_to_string(value)
1630
-- Check if table represents a bytes vector (integer indices starting from 1, u8 values)
1731
local max_index = 0
1832
local min_index = math.huge
19-
33+
2034
-- First pass: check if all keys are integers and find range
2135
for k, v in pairs(value) do
2236
if type(k) ~= "number" or k ~= math.floor(k) or k < 1 then
@@ -28,7 +42,7 @@ function bytes_vector_to_string(value)
2842
max_index = math.max(max_index, k)
2943
min_index = math.min(min_index, k)
3044
end
31-
45+
3246
-- Check if indices are consecutive starting from 1
3347
if min_index == 1 then
3448
local expected_length = max_index
@@ -41,7 +55,7 @@ function bytes_vector_to_string(value)
4155
if max_index > 1048576 then
4256
return '"<bytes vector too large: ' .. max_index .. ' bytes>"'
4357
end
44-
58+
4559
-- Convert bytes vector to string
4660
local chars = {}
4761
for i = 1, max_index do
@@ -50,16 +64,16 @@ function bytes_vector_to_string(value)
5064
return '"' .. table.concat(chars) .. '"'
5165
end
5266
end
53-
67+
5468
return nil -- Not a valid bytes vector
5569
end
5670

5771
-- Function to convert a value or table into a single-line string recursively
58-
function to_string(value)
72+
local function to_string(value)
5973
if value == nil then
6074
return "nil"
6175
end
62-
76+
6377
if is_null(value) then
6478
return "NULL"
6579
end
@@ -83,7 +97,7 @@ function to_string(value)
8397
if bytes_string then
8498
return bytes_string
8599
end
86-
100+
87101
-- Regular table processing
88102
local result = "{"
89103
local keys = {}
@@ -117,7 +131,7 @@ function to_string(value)
117131
local first = true
118132
for _, k in ipairs(keys) do
119133
local v = value[k]
120-
134+
121135
-- Skip nil and NULL values
122136
if v ~= nil and not is_null(v) then
123137
if not first then
@@ -143,3 +157,6 @@ function to_string(value)
143157
-- Handle function, userdata, thread
144158
return "<" .. type(value) .. ">"
145159
end
160+
161+
-- Register to_string function in metactl namespace
162+
metactl.to_string = to_string

0 commit comments

Comments
ย (0)