Skip to content

Commit c073abc

Browse files
committed
feat: support readdir in batch
ignore grpc test case fix test case resume ci.yml try to fix old unit test code
1 parent bbcaf8a commit c073abc

File tree

7 files changed

+215
-24
lines changed

7 files changed

+215
-24
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
- version: 3.4.0
2323
conf: Procfile-single-enable-mtls
2424

25-
runs-on: "ubuntu-20.04"
25+
runs-on: "ubuntu-22.04"
2626
env:
2727
OPENRESTY_PREFIX: "/usr/local/openresty"
2828
AUTH_ENDPOINT_V3: "127.0.0.1:12379"
@@ -41,7 +41,7 @@ jobs:
4141
go-version: "1.17"
4242

4343
- name: get dependencies
44-
run: sudo apt install -y cpanminus build-essential libncurses5-dev libreadline-dev libssl-dev perl
44+
run: sudo apt install -y cpanminus build-essential libncurses5-dev libreadline-dev libssl-dev perl libpcre3-dev libpcre3
4545

4646
- name: Setup Lua
4747
uses: leafo/[email protected]

api_v3.md

Lines changed: 91 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,28 @@
11
API V3
22
======
33

4-
* [Methods](#methods)
5-
* [new](#new)
6-
* [get](#get)
7-
* [set](#set)
8-
* [setnx](#setnx)
9-
* [setx](#setx)
10-
* [delete](#delete)
11-
* [watch](#watch)
12-
* [watchcancel](#watchcancel)
13-
* [readdir](#readdir)
14-
* [watchdir](#watchdir)
15-
* [rmdir](#rmdir)
16-
* [txn](#txn)
17-
* [version](#version)
18-
* [grant](#grant)
19-
* [revoke](#revoke)
20-
* [keepalive](#keepalive)
21-
* [timetolive](#timetolive)
22-
* [leases](#leases)
4+
- [API V3](#api-v3)
5+
- [Method](#method)
6+
- [new](#new)
7+
- [get](#get)
8+
- [set](#set)
9+
- [setnx](#setnx)
10+
- [setx](#setx)
11+
- [delete](#delete)
12+
- [watch](#watch)
13+
- [watchcancel](#watchcancel)
14+
- [readdir](#readdir)
15+
- [watchdir](#watchdir)
16+
- [rmdir](#rmdir)
17+
- [txn](#txn)
18+
- [version](#version)
19+
- [grant](#grant)
20+
- [revoke](#revoke)
21+
- [keepalive](#keepalive)
22+
- [timetolive](#timetolive)
23+
- [leases](#leases)
24+
- [nextkey](#nextkey)
25+
- [rangeend](#rangeend)
2326

2427
Method
2528
======
@@ -358,3 +361,72 @@ To retrieve lease information.
358361
To list all existing leases.
359362

360363
[Back to TOP](#api-v3)
364+
365+
### nextkey
366+
367+
`syntax: next_key = etcd.nextkey(key:string)`
368+
369+
- `key`: etcd path to key
370+
371+
return next path to key, only used when `sort_order='ASCEND' and sort_target='KEY'`.
372+
373+
[Back to TOP](#api-v3)
374+
375+
376+
### rangeend
377+
378+
`syntax: range_end = etcd.rangeend(key:string)`
379+
380+
- `key`: etcd path to key
381+
382+
return range end path to key.
383+
384+
When using etcd's `cli:readdir`, if the value in this path is too large (by default: exceeding 4MB), you will encounter the following error:
385+
386+
```
387+
rpc error: code = ResourceExhausted desc = grpc: received message larger than max (8653851 vs. 4194304).
388+
```
389+
390+
Therefore, we need to read this directory in batches.
391+
392+
The code example :
393+
394+
``` lua
395+
local cli, _ = etcd.new([option:table])
396+
397+
local res, err
398+
local start_key = '/path/to/dir'
399+
local last_key = start_key
400+
local range_end = cli.rangeend(start_key)
401+
local data = {}
402+
while(1) do
403+
res, err = cli:readdir(last_key, {
404+
timeout = 3000,
405+
range_end = range_end,
406+
revision = -1,
407+
limit = 20, -- batch size
408+
sort_order = 'ASCEND',
409+
sort_target = 'KEY',
410+
})
411+
-- error handle
412+
if err or not res then
413+
break
414+
end
415+
-- collect res.body
416+
table.insert(data, res.body)
417+
local last_kv_key = res.body.kvs[#res.body.kvs].key
418+
last_key = cli.nextkey(last_key)
419+
if not res.body.more then
420+
break
421+
end
422+
end
423+
424+
for _, body in ipairs(data) do
425+
-- handle data
426+
end
427+
```
428+
429+
[Back to TOP](#api-v3)
430+
431+
432+

lib/resty/etcd/utils.lua

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ local select = select
99
local ipairs = ipairs
1010
local pairs = pairs
1111
local type = type
12+
local str_char = string.char
13+
1214

1315

1416
if not http.tls_handshake then
@@ -125,4 +127,10 @@ local function is_empty_str(input_str)
125127
end
126128
_M.is_empty_str = is_empty_str
127129

130+
--- @param key string
131+
--- @return string
132+
_M.next_key = function(key)
133+
return key .. str_char(0)
134+
end
135+
128136
return _M

lib/resty/etcd/v3.lua

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -952,7 +952,8 @@ local function request_chunk(self, method, path, opts, timeout)
952952
end
953953
end
954954

955-
955+
--- @param key string
956+
--- @return string
956957
local function get_range_end(key)
957958
if #key == 0 then
958959
return str_char(0)
@@ -1319,7 +1320,7 @@ function _M.readdir(self, key, opts)
13191320

13201321
key = utils.get_real_key(self.key_prefix, key)
13211322

1322-
attr.range_end = get_range_end(key)
1323+
attr.range_end = opts and opts.range_end or get_range_end(key)
13231324
attr.revision = opts and opts.revision
13241325
attr.limit = opts and opts.limit
13251326
attr.sort_order = opts and opts.sort_order
@@ -1689,6 +1690,9 @@ end
16891690

16901691
end -- do
16911692

1693+
_M.rangeend = get_range_end
1694+
_M.nextkey = utils.next_key
1695+
16921696

16931697
local implemented_grpc_methods = {
16941698
grant = true,

t/v3/grpc/txn.t

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ __DATA__
8888
check_res(data, err, "ddd")
8989
}
9090
}
91+
--- request
92+
GET /t
93+
--- no_error_log
94+
[error]
9195
--- response_body
9296
checked val as expect: abc
9397
checked val as expect: ddd
@@ -118,6 +122,10 @@ checked val as expect: ddd
118122
check_res(data, err, "abc")
119123
}
120124
}
125+
--- request
126+
GET /t
127+
--- no_error_log
128+
[error]
121129
--- response_body
122130
checked val as expect: abc
123131
checked val as expect: abc
@@ -145,6 +153,10 @@ checked val as expect: abc
145153
check_res(data, err, "aaa", 200)
146154
}
147155
}
156+
--- request
157+
GET /t
158+
--- no_error_log
159+
[error]
148160
--- response_body
149161
checked val as expect: aaa
150162
@@ -201,6 +213,10 @@ checked val as expect: aaa
201213
check_res(data, err, '{"k":"ddd"}')
202214
}
203215
}
216+
--- request
217+
GET /t
218+
--- no_error_log
219+
[error]
204220
--- response_body
205221
checked val as expect: {"k":"abc"}
206222
checked val as expect: {"k":"ddd"}

t/v3/key.t

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,97 @@ GET /t
113113
ok
114114
115115
116+
=== TEST 2-2: readdir(key) in batch
117+
--- http_config eval: $::HttpConfig
118+
--- config
119+
location /t {
120+
content_by_lua_block {
121+
local tab_nkeys = require "table.nkeys"
122+
local etcd, err = require "resty.etcd" .new({protocol = "v3"})
123+
ngx.say('new')
124+
check_res(etcd, err)
125+
126+
local res, err = etcd:set("/batch/dir", "abc")
127+
ngx.say('set1')
128+
check_res(res, err)
129+
130+
local res, err = etcd:set("/batch/dir/a", "abca")
131+
ngx.say('set2')
132+
check_res(res, err)
133+
134+
local res, err = etcd:set("/batch/dir/b", "abcb")
135+
ngx.say('set3')
136+
check_res(res, err)
137+
138+
139+
local start_key = '/batch/dir'
140+
local last_key = start_key
141+
local range_end = etcd.rangeend(start_key)
142+
local data = {}
143+
local kvs = {}
144+
local times = 0
145+
while(1) do
146+
times = times + 1
147+
res, err = etcd:readdir(last_key, {
148+
timeout = 5000,
149+
range_end = range_end,
150+
revision = -1,
151+
limit = 1,
152+
sort_order = 'ASCEND',
153+
sort_target = 'KEY',
154+
})
155+
ngx.say('while')
156+
check_res(res, err)
157+
158+
table.insert(data, res.body)
159+
160+
local last_kv_key = res.body.kvs[#res.body.kvs].key
161+
last_key = etcd.nextkey(last_kv_key)
162+
163+
if not res.body.more then
164+
break
165+
end
166+
167+
if times > 4 then
168+
ngx.say("too many times")
169+
ngx.exit(500)
170+
return
171+
end
172+
173+
end
174+
175+
for _, body in ipairs(data) do
176+
for _, kv in ipairs(body.kvs) do
177+
table.insert(kvs, kv)
178+
end
179+
end
180+
181+
if tab_nkeys(kvs) == 3 and times == 3 then
182+
ngx.say("ok")
183+
ngx.exit(200)
184+
else
185+
ngx.say(string.format("failed len(kvs):%s, times:%s", tab_nkeys(kvs), times))
186+
ngx.exit(500)
187+
end
188+
189+
}
190+
}
191+
--- request
192+
GET /t
193+
--- no_error_log
194+
[error]
195+
--- response_body
196+
new
197+
set1
198+
set2
199+
set3
200+
while
201+
while
202+
while
203+
ok
204+
--- timeout: 5
205+
206+
116207

117208
=== TEST 3: watch(key)
118209
--- http_config eval: $::HttpConfig

t/v3/tls.t

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ GET /t
103103
--- no_error_log
104104
[error]
105105
--- response_body eval
106-
qr/18: self signed certificate/
106+
qr/self-signed certificate/
107107
108108
109109

0 commit comments

Comments
 (0)