@@ -8,10 +8,15 @@ namespace NewLife.NovaDb.Server;
88/// <remarks>
99/// 控制器方法通过 Remoting RPC 暴露为远程接口。
1010/// 路由格式:Kv/{方法名},如 Kv/Set、Kv/Get。
11- /// 所有方法入参均为 IPacket,通过 SpanReader 直接读取二进制参数,跳过 JSON 序列化。
12- /// 返回值也为 IPacket,通过 SpanWriter 写入 ArrayPacket,跳过 JSON 序列化。
13- /// Remoting 框架检测到 IsPacketParameter/IsPacketReturn 后自动走原生二进制通道。
11+ /// 所有方法入参均为 IPacket(Remoting 检测到 IsPacketParameter=True 后直接传原始消息体),
12+ /// 通过 SpanReader 直接读取二进制参数,跳过 JSON 序列化。
13+ /// 返回值也为 IPacket(IsPacketReturn=True),通过 SpanWriter 写入 ArrayPacket,
14+ /// 或直接返回 KvStore 的 IOwnerPacket(网络层发送后自动释放回池)。
1415/// 控制器实例由 Remoting 框架按请求创建,通过静态字段共享引擎。
16+ /// 二进制协议说明:
17+ /// EncodedString = EncodedInt(UTF8字节数) + UTF8字节
18+ /// EncodedInt = 变长整数(1-5 字节,最高位 1 表示后续还有字节)
19+ /// NullableBytes = EncodedInt(-1)=null / EncodedInt(0)=空 / EncodedInt(n)+n字节
1520/// </remarks>
1621internal class KvController : IApi
1722{
@@ -35,9 +40,15 @@ internal class KvController : IApi
3540 return SharedServer ? . GetKvStore ( tableName ) ;
3641 }
3742
38- /// <summary>KV 设置键值对(IPacket 二进制协议)</summary>
39- /// <param name="data">请求包:[tableName][key][value(nullable)][ttlSeconds]</param>
40- /// <returns>响应包:[1B: 0=false, 1=true]</returns>
43+ /// <summary>KV 设置键值对</summary>
44+ /// <param name="data">
45+ /// 请求包:
46+ /// [EncodedString tableName] 表名
47+ /// [EncodedString key] 键
48+ /// [NullableBytes value] 值(null 或任意字节)
49+ /// [Int32 ttlSeconds] 过期秒数,0=永不过期(4字节小端)
50+ /// </param>
51+ /// <returns>响应包:[1B: 0=失败, 1=成功]</returns>
4152 public IPacket Set ( IPacket data )
4253 {
4354 var ( tableName , key , value , ttlSeconds ) = KvPacket . DecodeSet ( data ) ;
@@ -49,22 +60,37 @@ public IPacket Set(IPacket data)
4960 return KvPacket . EncodeBoolean ( true ) ;
5061 }
5162
52- /// <summary>KV 获取值(IPacket 二进制协议,跳过 Base64 与 JSON 开销)</summary>
53- /// <param name="data">请求包:[tableName][key]</param>
54- /// <returns>响应包:[1B 0=notfound] 或 [1B 1=found][value bytes]</returns>
63+ /// <summary>KV 获取值(跳过 Base64 与 JSON 开销,直接返回原始字节包)</summary>
64+ /// <param name="data">
65+ /// 请求包:
66+ /// [EncodedString tableName] 表名
67+ /// [EncodedString key] 键
68+ /// </param>
69+ /// <returns>
70+ /// 响应包:
71+ /// 键存在时返回存储的原始字节(ArrayPacket);
72+ /// 键不存在或存储未初始化时返回空包(Length=0)
73+ /// </returns>
5574 public IPacket Get ( IPacket data )
5675 {
5776 var ( tableName , key ) = KvPacket . DecodeTableKey ( data ) ;
5877 var store = GetStore ( tableName ) ;
59- if ( store == null ) return KvPacket . EncodeNullableValue ( null ) ;
78+ if ( store == null ) return KvPacket . EncodeEmpty ( ) ;
6079
6180 using var pk = store . Get ( key ) ;
62- return KvPacket . EncodeNullableValue ( pk ? . ReadBytes ( ) ) ;
81+ if ( pk == null ) return KvPacket . EncodeEmpty ( ) ;
82+
83+ // GetSpan() 直接读取底层缓冲区,避免 ReadBytes() 分配中间字节数组,再包装 IPacket 的双重分配
84+ return new ArrayPacket ( pk . GetSpan ( ) . ToArray ( ) ) ;
6385 }
6486
65- /// <summary>KV 删除键(IPacket 二进制协议)</summary>
66- /// <param name="data">请求包:[tableName][key]</param>
67- /// <returns>响应包:[1B: 0=false, 1=true]</returns>
87+ /// <summary>KV 删除键</summary>
88+ /// <param name="data">
89+ /// 请求包:
90+ /// [EncodedString tableName] 表名
91+ /// [EncodedString key] 键
92+ /// </param>
93+ /// <returns>响应包:[1B: 0=键不存在/失败, 1=删除成功]</returns>
6894 public IPacket Delete ( IPacket data )
6995 {
7096 var ( tableName , key ) = KvPacket . DecodeTableKey ( data ) ;
@@ -74,9 +100,13 @@ public IPacket Delete(IPacket data)
74100 return KvPacket . EncodeBoolean ( store . Delete ( key ) ) ;
75101 }
76102
77- /// <summary>KV 检查键是否存在(IPacket 二进制协议)</summary>
78- /// <param name="data">请求包:[tableName][key]</param>
79- /// <returns>响应包:[1B: 0=false, 1=true]</returns>
103+ /// <summary>KV 检查键是否存在</summary>
104+ /// <param name="data">
105+ /// 请求包:
106+ /// [EncodedString tableName] 表名
107+ /// [EncodedString key] 键
108+ /// </param>
109+ /// <returns>响应包:[1B: 0=不存在, 1=存在]</returns>
80110 public IPacket Exists ( IPacket data )
81111 {
82112 var ( tableName , key ) = KvPacket . DecodeTableKey ( data ) ;
@@ -86,9 +116,13 @@ public IPacket Exists(IPacket data)
86116 return KvPacket . EncodeBoolean ( store . Exists ( key ) ) ;
87117 }
88118
89- /// <summary>按通配符模式删除键(IPacket 二进制协议)</summary>
90- /// <param name="data">请求包:[tableName][pattern]</param>
91- /// <returns>响应包:[4B Int32: 删除数量]</returns>
119+ /// <summary>按通配符模式删除键</summary>
120+ /// <param name="data">
121+ /// 请求包:
122+ /// [EncodedString tableName] 表名
123+ /// [EncodedString pattern] 通配符模式(* 匹配任意字符,? 匹配单个字符)
124+ /// </param>
125+ /// <returns>响应包:[Int32 删除数量](4字节小端)</returns>
92126 public IPacket DeleteByPattern ( IPacket data )
93127 {
94128 var ( tableName , pattern ) = KvPacket . DecodeDeleteByPattern ( data ) ;
@@ -98,39 +132,57 @@ public IPacket DeleteByPattern(IPacket data)
98132 return KvPacket . EncodeInt32 ( store . DeleteByPattern ( pattern ) ) ;
99133 }
100134
101- /// <summary>获取缓存项总数(IPacket 二进制协议)</summary>
102- /// <param name="data">请求包:[tableName]</param>
103- /// <returns>响应包:[4B Int32: 总数]</returns>
135+ /// <summary>获取缓存项总数</summary>
136+ /// <param name="data">
137+ /// 请求包:
138+ /// [EncodedString tableName] 表名
139+ /// </param>
140+ /// <returns>响应包:[Int32 总数](4字节小端)</returns>
104141 public IPacket GetCount ( IPacket data )
105142 {
106143 var tableName = KvPacket . DecodeTableOnly ( data ) ;
107144 var store = GetStore ( tableName ) ;
108145 return KvPacket . EncodeInt32 ( store ? . Count ?? 0 ) ;
109146 }
110147
111- /// <summary>获取所有缓存键(IPacket 二进制协议)</summary>
112- /// <param name="data">请求包:[tableName]</param>
113- /// <returns>响应包:[4B count][key1...][keyN]</returns>
148+ /// <summary>获取所有缓存键</summary>
149+ /// <param name="data">
150+ /// 请求包:
151+ /// [EncodedString tableName] 表名
152+ /// </param>
153+ /// <returns>
154+ /// 响应包:
155+ /// [Int32 count] 键数量(4字节小端)
156+ /// [EncodedString key1] ... 各键字符串(count 个)
157+ /// </returns>
114158 public IPacket GetAllKeys ( IPacket data )
115159 {
116160 var tableName = KvPacket . DecodeTableOnly ( data ) ;
117161 var store = GetStore ( tableName ) ;
118162 return KvPacket . EncodeStringArray ( store ? . GetAllKeys ( ) . ToArray ( ) ?? [ ] ) ;
119163 }
120164
121- /// <summary>清空所有缓存项(IPacket 二进制协议)</summary>
122- /// <param name="data">请求包:[tableName]</param>
123- /// <returns>空响应包</returns>
165+ /// <summary>清空所有缓存项</summary>
166+ /// <param name="data">
167+ /// 请求包:
168+ /// [EncodedString tableName] 表名
169+ /// </param>
170+ /// <returns>空响应包(Length=0)</returns>
124171 public IPacket Clear ( IPacket data )
125172 {
126173 var tableName = KvPacket . DecodeTableOnly ( data ) ;
127174 GetStore ( tableName ) ? . Clear ( ) ;
128175 return KvPacket . EncodeEmpty ( ) ;
129176 }
130177
131- /// <summary>设置缓存项有效期(IPacket 二进制协议)</summary>
132- /// <param name="data">请求包:[tableName][key][ttlSeconds]</param>
133- /// <returns>响应包:[1B: 0=false, 1=true]</returns>
178+ /// <summary>设置缓存项有效期</summary>
179+ /// <param name="data">
180+ /// 请求包:
181+ /// [EncodedString tableName] 表名
182+ /// [EncodedString key] 键
183+ /// [Int32 ttlSeconds] 新的过期秒数(4字节小端)
184+ /// </param>
185+ /// <returns>响应包:[1B: 0=键不存在/失败, 1=设置成功]</returns>
134186 public IPacket SetExpire ( IPacket data )
135187 {
136188 var ( tableName , key , ttlSeconds ) = KvPacket . DecodeSetExpire ( data ) ;
@@ -140,9 +192,16 @@ public IPacket SetExpire(IPacket data)
140192 return KvPacket . EncodeBoolean ( store . SetExpiration ( key , TimeSpan . FromSeconds ( ttlSeconds ) ) ) ;
141193 }
142194
143- /// <summary>获取缓存项剩余有效期(IPacket 二进制协议)</summary>
144- /// <param name="data">请求包:[tableName][key]</param>
145- /// <returns>响应包:[8B Double: TTL 秒数]</returns>
195+ /// <summary>获取缓存项剩余有效期</summary>
196+ /// <param name="data">
197+ /// 请求包:
198+ /// [EncodedString tableName] 表名
199+ /// [EncodedString key] 键
200+ /// </param>
201+ /// <returns>
202+ /// 响应包:[Double TTL秒数](8字节小端)
203+ /// 正数=剩余秒数,负数=键不存在或永不过期
204+ /// </returns>
146205 public IPacket GetExpire ( IPacket data )
147206 {
148207 var ( tableName , key ) = KvPacket . DecodeTableKey ( data ) ;
@@ -152,9 +211,14 @@ public IPacket GetExpire(IPacket data)
152211 return KvPacket . EncodeDouble ( store . GetTtl ( key ) . TotalSeconds ) ;
153212 }
154213
155- /// <summary>原子递增(整数,IPacket 二进制协议)</summary>
156- /// <param name="data">请求包:[tableName][key][Int64 delta]</param>
157- /// <returns>响应包:[8B Int64: 更新后的值]</returns>
214+ /// <summary>原子递增(整数)</summary>
215+ /// <param name="data">
216+ /// 请求包:
217+ /// [EncodedString tableName] 表名
218+ /// [EncodedString key] 键
219+ /// [Int64 delta] 变化量(8字节小端,可为负数)
220+ /// </param>
221+ /// <returns>响应包:[Int64 更新后的值](8字节小端)</returns>
158222 public IPacket Increment ( IPacket data )
159223 {
160224 var ( tableName , key , delta ) = KvPacket . DecodeIncrement ( data ) ;
@@ -164,9 +228,14 @@ public IPacket Increment(IPacket data)
164228 return KvPacket . EncodeInt64 ( store . Inc ( key , delta ) ) ;
165229 }
166230
167- /// <summary>原子递增(浮点,IPacket 二进制协议)</summary>
168- /// <param name="data">请求包:[tableName][key][Double delta]</param>
169- /// <returns>响应包:[8B Double: 更新后的值]</returns>
231+ /// <summary>原子递增(浮点)</summary>
232+ /// <param name="data">
233+ /// 请求包:
234+ /// [EncodedString tableName] 表名
235+ /// [EncodedString key] 键
236+ /// [Double delta] 变化量(8字节小端,可为负数)
237+ /// </param>
238+ /// <returns>响应包:[Double 更新后的值](8字节小端)</returns>
170239 public IPacket IncrementDouble ( IPacket data )
171240 {
172241 var ( tableName , key , delta ) = KvPacket . DecodeIncrementDouble ( data ) ;
@@ -176,9 +245,19 @@ public IPacket IncrementDouble(IPacket data)
176245 return KvPacket . EncodeDouble ( store . IncDouble ( key , delta ) ) ;
177246 }
178247
179- /// <summary>搜索匹配的键(IPacket 二进制协议)</summary>
180- /// <param name="data">请求包:[tableName][pattern][offset][count]</param>
181- /// <returns>响应包:[4B count][key1...][keyN]</returns>
248+ /// <summary>搜索匹配的键</summary>
249+ /// <param name="data">
250+ /// 请求包:
251+ /// [EncodedString tableName] 表名
252+ /// [EncodedString pattern] 搜索模式
253+ /// [Int32 offset] 偏移量(4字节小端)
254+ /// [Int32 count] 最大返回数量,-1=不限(4字节小端)
255+ /// </param>
256+ /// <returns>
257+ /// 响应包:
258+ /// [Int32 count] 匹配键数量(4字节小端)
259+ /// [EncodedString key1] ... 各键字符串(count 个)
260+ /// </returns>
182261 public IPacket Search ( IPacket data )
183262 {
184263 var ( tableName , pattern , offset , count ) = KvPacket . DecodeSearch ( data ) ;
@@ -188,9 +267,21 @@ public IPacket Search(IPacket data)
188267 return KvPacket . EncodeStringArray ( store . Search ( pattern , offset , count ) . ToArray ( ) ) ;
189268 }
190269
191- /// <summary>批量获取键值对(IPacket 二进制协议,跳过 Base64 与 JSON 开销)</summary>
192- /// <param name="data">请求包:[tableName][keyCount][key1...][keyN]</param>
193- /// <returns>响应包:[count][key1][value1Flag][value1Len][value1]...[keyN][valueNFlag]</returns>
270+ /// <summary>批量获取键值对(跳过 Base64 与 JSON 开销)</summary>
271+ /// <param name="data">
272+ /// 请求包:
273+ /// [EncodedString tableName] 表名
274+ /// [Int32 keyCount] 键数量(4字节小端)
275+ /// [EncodedString key1] ... 各键字符串(keyCount 个)
276+ /// </param>
277+ /// <returns>
278+ /// 响应包:
279+ /// [Int32 count] 键数量(4字节小端,与请求一致)
280+ /// 对每个键,依次写入:
281+ /// [EncodedString key] 键字符串
282+ /// [1B flag] 0=键不存在/null, 1=有值
283+ /// [if flag=1: EncodedInt(valueLen)][valueLen 字节 value]
284+ /// </returns>
194285 public IPacket GetAll ( IPacket data )
195286 {
196287 var ( tableName , keys ) = KvPacket . DecodeGetAll ( data ) ;
@@ -208,9 +299,17 @@ public IPacket GetAll(IPacket data)
208299 }
209300 }
210301
211- /// <summary>批量设置键值对(IPacket 二进制协议)</summary>
212- /// <param name="data">请求包:[tableName][ttlSeconds][count][key1][value1]...[keyN][valueN]</param>
213- /// <returns>响应包:[4B Int32: 设置的键个数]</returns>
302+ /// <summary>批量设置键值对</summary>
303+ /// <param name="data">
304+ /// 请求包:
305+ /// [EncodedString tableName] 表名
306+ /// [Int32 ttlSeconds] 过期秒数,0=永不过期(4字节小端)
307+ /// [Int32 count] 键值对数量(4字节小端)
308+ /// 对每个键值对,依次写入:
309+ /// [EncodedString key] 键
310+ /// [NullableBytes value] 值(null 或任意字节)
311+ /// </param>
312+ /// <returns>响应包:[Int32 成功设置的键个数](4字节小端)</returns>
214313 public IPacket SetAll ( IPacket data )
215314 {
216315 var ( tableName , values , ttlSeconds ) = KvPacket . DecodeSetAll ( data ) ;
0 commit comments