ResultFlag 响应数据返回控制
本文聚焦于 写类请求(insert / update / replace / delete / increase / list 写操作 / batch 写操作 / fieldset / fieldinc 等)成功或失败后,响应包里是否携带记录数据、携带哪份数据 的控制开关,即
ResultFlag。 旧的SetResultFlag接口只能整体控制,无法区分"操作成功"与"操作失败"两个场景;新接口SetResultFlagForSuccess与SetResultFlagForFail拆出了这两个维度,业务可按需精细化设置。
读类请求(get / batchget / fieldget / batchfieldget / traverse 等)默认就会返回数据,不走 ResultFlag。
1. 取值定义
无论 TDR SDK、PB SDK 还是 Go SDK,ResultFlag 都是一个枚举值,含义如下:
| 取值(C++ 宏 / Go 常量) | 数值 | 含义 |
|---|---|---|
TCaplusValueFlag_NOVALUE |
0 | 不返回任何数据(仅返回操作是否成功) |
TCaplusValueFlag_SAMEWITHREQUEST |
1 | 响应包回带请求中携带过来的字段("原样回带") |
TCaplusValueFlag_ALLVALUE |
2 | 返回 tcapsvr 端操作后的所有字段(new value) |
TCaplusValueFlag_ALLOLDVALUE |
3 | 返回 tcapsvr 端操作前的所有字段(old value) |
在 Go SDK 中,这四个常量定义在
tcaplus_protocol_cs包下;C++ 端在tcaplus_protocol_cs::TCaplusValueFlag_*命名空间中。
2. 命令字 × ResultFlag 矩阵
下表汇总了操作成功与操作失败两种情况下,各命令字对每个 ResultFlag 取值的响应数据返回行为。表格内容与 SDK 源码(tcaplus_service_request.h / tcaplus_service_request.cpp)的注释完全一致。
2.1 操作成功时的返回(SetResultFlagForSuccess)
| 命令字 | NOVALUE (0) | SAMEWITHREQUEST (1) | ALLVALUE (2) | ALLOLDVALUE (3) |
|---|---|---|---|---|
INSERT_REQ / BATCH_INSERT_REQ |
不返回数据 | 返回与请求一致的数据 | 返回 insert 后的数据 | 返回空数据(插入前本无数据) |
REPLACE_REQ / BATCH_REPLACE_REQ |
不返回数据 | 返回与请求一致的数据 | 返回 replace 后的数据 | 返回 replace 前的数据;若原本无数据则返回空 |
UPDATE_REQ / BATCH_UPDATE_REQ |
不返回数据 | 返回与请求一致的数据 | 返回 update 后的数据 | 返回 update 前的数据 |
INCREASE_REQ |
不返回数据 | 返回与请求一致的数据 | 返回 increase 后的数据 | 返回 increase 前的数据;若原本无数据则返回空 |
DELETE_REQ / BATCH_DELETE_REQ |
不返回数据 | 返回与请求一致的数据 | 返回空数据(已删除) | 返回 delete 前的数据 |
LIST_ADDAFTER_REQ |
不返回数据 | 不返回数据 | 返回本次插入与本次淘汰的记录¹ | 不返回数据 |
LIST_DELETE_REQ |
不返回数据 | 不返回数据 | 返回空数据 | 返回 listdelete 前的数据 |
LIST_REPLACE_REQ |
不返回数据 | 不返回数据 | 返回 listreplace 后的数据 | 返回 listreplace 前的数据 |
LIST_REPLACE_BATCH_REQ |
返回与请求一致的数据² | 返回与请求一致的数据² | 返回 listreplace 后的数据 | 返回 listreplace 前的数据 |
LIST_DELETE_BATCH_REQ |
不返回数据 | 不返回数据 | 不返回数据 | 返回被成功删除的 index 对应的全部旧数据 |
UPDATE_ITEM_REQ / LIST_UPDATE_ITEM_REQ |
不返回数据 | (受限³)返回与请求一致的数据 | 返回操作后的数据 | 返回操作前的数据 |
PB_FIELD_SET_REQ(PB 部分字段更新) |
不返回数据 | 返回与请求一致的数据 | 返回 fieldset 后的数据 | 返回 fieldset 前的数据 |
注:
LIST_ADDAFTER_REQ在ALLVALUE下,如果"插入+淘汰"两条记录总大小超过 10 MB,只返回插入记录,不再返回淘汰记录。LIST_REPLACE_BATCH_REQ与其他命令略有不同:NOVALUE与SAMEWITHREQUEST都会回带"请求字段"。这是有意为之:批量接口下响应包内每条子记录都带iResult错误码,调用方需要通过响应里的 key / element index 把每条子结果对应回原请求中的某条记录,再结合子记录的iResult判断哪些成功、哪些失败、失败的错误码是什么。回带请求字段就是为了让调用方能稳定地完成这种"逐条对应",即使设了NOVALUE也会保留这份关联信息。UPDATE_ITEM_REQ/LIST_UPDATE_ITEM_REQ服务端对SAMEWITHREQUEST(1)有两条额外校验,触发任一条整个请求即被拒绝并返回SVR_ERR_FAIL_INVALID_RESULT_FLAG:- 旧接口:调用
SetResultFlag(1)整请求直接被拒(成功失败的语义都拒)。 - 新接口:
SetResultFlagForSuccess与SetResultFlagForFail同时都设为SAMEWITHREQUEST(1)整请求被拒;只设其中一个为SAMEWITHREQUEST是合法的。换句话说,若想"成功也按请求回带、失败按服务端旧值回带",请用ForSuccess(1) + ForFail(3)(合法),而不能用ForSuccess(1) + ForFail(1)。
- 旧接口:调用
对
BATCH_INSERT_REQ/BATCH_REPLACE_REQ/BATCH_UPDATE_REQ/BATCH_DELETE_REQ,表中的"不返回数据"只表示不回带 value 数据。proxy 仍会在批量响应中为每条子记录返回 key 与recordResult[i]错误码;即使ResultFlag = NOVALUE(0),调用方也能知道哪个 key 成功、哪个 key 失败。
2.2 操作失败时的返回(SetResultFlagForFail)
操作失败的常见原因包括:condition 不满足、版本号校验失败、记录不存在(update/replace 等)、记录已存在(insert)。
| 命令字 | NOVALUE (0) | SAMEWITHREQUEST (1) | ALLVALUE (2) | ALLOLDVALUE (3) |
|---|---|---|---|---|
INSERT_REQ / BATCH_INSERT_REQ |
不返回数据 | 返回与请求一致的数据 | 不合理场景(已存在或失败,无"操作后"概念) | 取到 tcapsvr 已有数据则返回,否则返回空 |
REPLACE_REQ / BATCH_REPLACE_REQ |
不返回数据 | 返回与请求一致的数据 | 不合理场景 | 取到 tcapsvr 已有数据则返回,否则返回空 |
UPDATE_REQ / BATCH_UPDATE_REQ |
不返回数据 | 返回与请求一致的数据 | 不合理场景 | 取到 tcapsvr 已有数据则返回,否则返回空 |
INCREASE_REQ |
不返回数据 | 返回与请求一致的数据 | 不合理场景 | 取到 tcapsvr 已有数据则返回,否则返回空 |
DELETE_REQ / BATCH_DELETE_REQ |
不返回数据 | 返回与请求一致的数据 | 不合理场景 | 取到 tcapsvr 已有数据则返回,否则返回空 |
LIST_ADDAFTER_REQ |
不返回数据 | 不返回数据 | 不合理场景 | 不返回数据 |
LIST_DELETE_REQ |
不返回数据 | 不返回数据 | 不合理场景 | 取到 tcapsvr 已有数据则返回,否则返回空 |
LIST_REPLACE_REQ |
不返回数据 | 不返回数据 | 不合理场景 | 取到 tcapsvr 已有数据则返回,否则返回空 |
LIST_DELETE_BATCH_REQ |
不返回数据 | 不返回数据 | 不合理场景 | 返回被成功删除的 index 对应的全部旧数据 |
LIST_REPLACE_BATCH_REQ |
返回与请求一致的数据⁴ | 返回与请求一致的数据⁴ | 不合理场景⁴ | 返回与请求一致的数据⁴ |
UPDATE_ITEM_REQ / LIST_UPDATE_ITEM_REQ |
不返回数据 | (受限³)返回与请求一致的数据 | 不合理场景 | 取到 tcapsvr 已有数据则返回,否则返回空 |
PB_FIELD_SET_REQ |
不返回数据 | 返回与请求一致的数据 | 不合理场景 | 取到 tcapsvr 已有数据则返回,否则返回空 |
不合理场景:写请求失败时不存在"操作后的新数据",把
ResultFlag设为ALLVALUE在语义上没有意义,存储层会按"不返回数据"处理;业务应避免对SetResultFlagForFail传2。对
BATCH_INSERT_REQ/BATCH_REPLACE_REQ/BATCH_UPDATE_REQ/BATCH_DELETE_REQ,即使ForFail = NOVALUE(0),proxy 也会把失败子记录的 key 和对应recordResult[i]错误码返回给调用方;NOVALUE只影响是否回带 value 数据。
注:
LIST_REPLACE_BATCH_REQ无论ForFail取何值,都只回带请求字段。这是listReplace语义的有意设计:listReplace本质是 Update 语义,子记录失败的主要原因是 element index 不存在(SVR_ERR_FAIL_INVALID_INDEX)或记录已过期(DB_ERR_RECORD_NOT_EXIST),服务端本就没有"操作前的旧数据"可返回;因此与其返回空,不如统一回带请求字段,让调用方按 key / element index 反查出哪条请求失败了、错误码(elementResult[i])是什么。
2.3 旧 SetResultFlag 的兼容性
SetResultFlag(x) 是历史遗留接口,实际行为与命令字强相关——既不像新接口能严格区分 success / fail,对于不同命令字其能识别的取值范围也不一致。新代码请改用 SetResultFlagForSuccess / SetResultFlagForFail,存量代码不强制改造。
SDK 层(tcaplus_service_request.cpp::SetResultFlag)仅在以下命令字上接受 SetResultFlag 调用,其他命令字直接返回 API_ERR_OPERATION_TYPE_NOT_MATCH:
INSERT_REQ / REPLACE_REQ / INCREASE_REQ / UPDATE_REQ / DELETE_REQ /
LIST_DELETE_REQ / LIST_REPLACE_REQ / LIST_DELETE_BATCH_REQ / LIST_ADDAFTER_REQ /
UPDATE_ITEM_REQ / LIST_UPDATE_ITEM_REQ / PB_BATCH_FIELD_GET_REQ。
下表对照 tcapsvr 实际处理逻辑给出每个命令字旧接口的行为:
| 命令字 | NOVALUE (0) | SAMEWITHREQUEST (1) | ALLVALUE (2) | ALLOLDVALUE (3) |
|---|---|---|---|---|
INSERT_REQ |
不返回数据 | 返回请求数据 | 返回请求数据 | 返回请求数据 |
REPLACE_REQ |
不返回数据 | 返回请求数据 | 返回 svr 端 replace 后数据 | 返回 svr 端 replace 前数据 |
UPDATE_REQ |
不返回数据 | 返回请求数据 | 返回 svr 端 update 后数据 | 返回 svr 端 update 前数据 |
INCREASE_REQ |
不返回数据 | 返回请求数据 | 返回 increase 后数据 | 返回 increase 前数据 |
DELETE_REQ |
不返回数据 | 返回请求数据 | 返回 delete 前数据(与 ALLOLDVALUE 等价,非空) | 返回 delete 前数据 |
LIST_ADDAFTER_REQ |
不返回数据 | 不返回数据 | 返回本次插入 + 被淘汰记录 | 不返回数据 |
LIST_DELETE_REQ |
不返回数据 | 不返回数据 | 返回 svr 端旧值 | 不返回数据 |
LIST_REPLACE_REQ |
不返回数据 | 不返回数据 | 返回 svr 端旧值(无论成功/失败均回带) | 不返回数据 |
LIST_DELETE_BATCH_REQ |
不返回数据 | 不返回数据 | 返回被成功删除 index 的全部旧数据 | 不返回数据 |
UPDATE_ITEM_REQ / LIST_UPDATE_ITEM_REQ |
不返回数据 | 整个请求被拒绝:SVR_ERR_FAIL_INVALID_RESULT_FLAG |
返回操作后数据 | 返回操作前数据 |
PB_BATCH_FIELD_GET_REQ |
仅作 batchfieldget 内部透传,不影响响应数据 |
同左 | 同左 | 同左 |
关键差异提醒:
LIST_*系列旧接口只识别ALLVALUE (2),传 1/3 等价于"什么都不返回"。新接口下这三个取值分别有完整语义。DELETE_REQ旧接口对ALLVALUE与ALLOLDVALUE不做区分,均回带 svr 端"已有数据"(即删除前数据);新接口SetResultFlagForSuccess(ALLVALUE)才会返回空。INSERT_REQ旧接口对 1/2/3 不做区分,均回带请求字段。UPDATE_ITEM_REQ/LIST_UPDATE_ITEM_REQ旧接口不支持SetResultFlag(1),需要"成功失败均按请求回带"的语义请改用新接口的ForSuccess(1)+ForFail(0/2/3)之一。
2.4 不支持 ResultFlag 的命令字
LIST_ADDAFTER_BATCH_REQ(对应 PB SDK 的 ListBatchAdd 接口)不支持ResultFlag 控制响应数据,服务端总是返回和请求一致的数据,让调用方知道哪个元素成功、哪个元素失败。
3. C++ TDR SDK 用法
TDR SDK 的接口定义在头文件 tcaplus_service_request.h 中,直接挂在 TcaplusService::TcaplusServiceRequest 上。
3.1 接口
// 旧接口:单次设置,会影响 success / fail 两个场景(语义不准确,仅作兼容)
int TcaplusServiceRequest::SetResultFlag(char result_flag);
// 新接口:分别设置成功与失败时的返回数据
int TcaplusServiceRequest::SetResultFlagForSuccess(char result_flag);
int TcaplusServiceRequest::SetResultFlagForFail (char result_flag);
返回值:
0(TcapErrCode::GEN_ERR_SUC) 设置成功。<0失败:通常是请求未初始化(API_ERR_PARAMETER_INVALID)或当前命令字不支持设置 ResultFlag(API_ERR_OPERATION_TYPE_NOT_MATCH)。
3.2 典型示例:Update 请求要求"成功回带新值、失败回带服务端老值"
#include "tcaplus/tcaplus_service_api.h"
using namespace TcaplusService;
using tcaplus_protocol_cs::TCaplusValueFlag_ALLVALUE;
using tcaplus_protocol_cs::TCaplusValueFlag_ALLOLDVALUE;
TcaplusServiceRequest* req = api.CreateRequest(TCAPLUS_API_UPDATE_REQ);
if (NULL == req) { /* handle error */ }
// 业务希望:
// 1) 更新成功时,返回 tcapsvr 端 update 后的完整数据(用于一并刷新本地缓存)
// 2) 更新失败时(例如 condition 不匹配),把服务端老数据捎回来,便于业务做合并/重试
req->SetResultFlagForSuccess(TCaplusValueFlag_ALLVALUE);
req->SetResultFlagForFail (TCaplusValueFlag_ALLOLDVALUE);
// ... SetTableName / record->SetKey / record->SetData / SetCondition ...
api.SendRequest(req);
3.3 典型示例:BatchGet 想拿到失败子记录的 key
SetResultFlag(>0) 在 batch_get 请求上有一个特殊语义——使 SDK 在 batch 响应中也返回失败子记录的 key,方便业务定位哪些 key 失败:
using tcaplus_protocol_cs::TCaplusValueFlag_SAMEWITHREQUEST;
// ...
TcaplusServiceRequest* req = api.CreateRequest(TCAPLUS_API_BATCH_GET_REQ);
req->SetResultFlag(TCaplusValueFlag_SAMEWITHREQUEST);
// 任意非 NOVALUE 值(SAMEWITHREQUEST / ALLVALUE / ALLOLDVALUE 均可)都会触发
// "失败 key 一并返回",对 BATCH_GET 而言三者等价;这里选 SAMEWITHREQUEST(1) 仅为示意。
// ...
该路径走的是旧接口
SetResultFlag,新接口不影响此行为。
4. C++ PB SDK 用法
PB SDK 的入口在头文件 tcaplus_async_pb_api.h(异步,类 TcaplusAsyncPbApi)
4.1 总览:PB SDK 对 ResultFlag 的处理模型
几个关键事实 —— 业务开发前请先理解:
- PB SDK 内部统一使用新接口
SetResultFlagForSuccess/SetResultFlagForFail。 - 用户能改默认值的入口有三类,由接口类型决定走哪一类:
| 入口 | 适用接口 | 设置方式 | 能否区分 success / fail |
|---|---|---|---|
A. SetMessageOption(msg, ...) |
单记录写(Add / Set / Update / Del / UpdateItem / FieldInc / FieldSet),以及 List 单条 / batch 写(ListAddAfter / ListReplace / ListUpdateItem / ListDel / ListBatchDel) |
在 msg(List 系列为 req.m_pMsg 或 req.m_vecMsg 中任一 msg)上挂 MESSAGE_OPTION_RESULT_FLAG_FOR_SUCCESS / ..._FOR_FAIL |
✔ |
B. 函数参数 int resultFlag |
4 参数 Update(Update(msg, op, cond, resultFlag, cb, ttl))、批量写(BatchAdd(msgs, resultFlag, cb) / BatchSet(msgs, resultFlag, cb) / BatchUpdate(msgs, resultFlag, [op, cond,] cb) / BatchDelete(msgs, resultFlag, [cond,] cb)) |
接口签名直接收 resultFlag |
视接口而定,详见 §4.3 |
C. Request 结构成员 m_nResultFlag |
ListBatchUpdate(req, cb) 一个接口 |
给 req.m_nResultFlag 赋值 |
仅 success(fail 仍走 A 兜底),详见 §4.4 |
| 特例:完全不支持 | ListBatchAdd(req, cb) 一个接口 |
— | 无法设置,完整规则见 §2.4 |
4.2 入口 A:SetMessageOption(推荐,单记录写都用它)
把 MESSAGE_OPTION_RESULT_FLAG_FOR_SUCCESS / ..._FOR_FAIL 挂到 msg 上,SDK 在发请求前会覆盖该接口的默认值。
#include "tcaplus_kv_api.h"
#include "tcaplus_async_pb_api.h"
using namespace NS_TCAPLUS_PROTOBUF_API;
// 取值是字符串 "0"/"1"/"2"/"3",分别对应 NOVALUE / SAMEWITHREQUEST / ALLVALUE / ALLOLDVALUE
g_stApi.SetMessageOption(*msg, MESSAGE_OPTION_RESULT_FLAG_FOR_SUCCESS, std::string("3")); // 成功时回带操作前数据
g_stApi.SetMessageOption(*msg, MESSAGE_OPTION_RESULT_FLAG_FOR_FAIL, std::string("3")); // 失败时也回带服务端已有数据
g_stApi.Set(msg, &cb); // 异步:回调里读取 msg 即可拿到对应数据
读回当前生效值用
GetMessageOption(*msg, MESSAGE_OPTION_RESULT_FLAG_FOR_SUCCESS, &out),返回字符串形式的"0"~"3"。
适用接口与默认行为(节选自头文件 doxygen 注释):
- 单记录写接口:option 挂在直接传入的
msg参数上 ——SetMessageOption(*msg, ..., ...)。 - List 单条写接口:签名是
(req, cb),没有独立msg参数也没有int resultFlag参数,option 挂在req.m_pMsg上 ——SetMessageOption(*req.m_pMsg, ..., ...)。 - List batch 写接口(
ListBatchDel):req 内是m_vecMsg(vector< pair<Message*, int> >,每条 msg 含 index),option 挂在任一条 msg 上即对整批生效 ——SetMessageOption(*req.m_vecMsg[0].first, ..., ...)(机制同 §4.3 batch 接口的 FillMessageOption 覆盖)。ListBatchAdd不在此列,见 §2.4。
| 接口 | 默认 ForSuccess | 默认 ForFail | 说明 |
|---|---|---|---|
Add(msg, cb, ttl) |
ALLVALUE(2) |
NOVALUE(0) |
默认插入成功时通过 OnRecv(msgs) 回带服务端写入后的完整记录 |
Set(msg, cb, ttl) |
ALLVALUE(2) |
NOVALUE(0) |
upsert 语义:成功时回带操作后的记录(可由 version 判断 insert 还是 update) |
Set(msg, op, cond, cb, ttl) |
ALLVALUE(2) |
NOVALUE(0) |
同上,附加数组 operation / 行级 condition |
Del(msg, cb) |
ALLOLDVALUE(3) |
NOVALUE(0) |
默认成功时通过 OnRecv 回带删除前的旧记录 |
Del(msg, cond, cb) |
ALLOLDVALUE(3) |
NOVALUE(0) |
同上,附加行级 condition |
UpdateItem(msg, op, cond, cb) |
ALLVALUE(2) |
NOVALUE(0) |
数组级 PUSH / POP;默认成功时回带 update 后的记录 |
FieldInc(...)(4 个重载) |
NOVALUE(0) |
NOVALUE(0) |
SDK 不主动设置默认值;增量结果通过 OnRecv(msg, dottedpaths, ...) 回调本身的字段返回,一般无需依赖 ResultFlag |
FieldSet(...)(2 个重载) |
NOVALUE(0) |
NOVALUE(0) |
同上 |
ListAddAfter(req, cb) |
ALLVALUE(2) |
NOVALUE(0) |
默认回带"本次插入 + 被淘汰元素";..._FOR_SUCCESS, "0" 可关闭成功回带 |
ListReplace(req, cb) |
ALLVALUE(2) |
NOVALUE(0) |
默认回带 replace 后记录;..._FOR_SUCCESS, "3" 改为回带 replace 前记录;fail 槽位需显式开 |
ListUpdateItem(req, cb) |
ALLVALUE(2) |
NOVALUE(0) |
同 ListReplace |
ListDel(req, cb) |
ALLOLDVALUE(3) |
NOVALUE(0) |
默认回带删除前记录;..._FOR_SUCCESS, "0" 可关闭 |
ListBatchDel(req, cb) |
ALLOLDVALUE(3) |
NOVALUE(0) |
默认逐 index 回带旧记录;..._FOR_SUCCESS, "0" 可改为只返回成功删除的 index 列表 |
使用要点(下文 *msg 在 List 系列接口上请替换为 *req.m_pMsg):
- 想让某个接口"成功也不返回数据"以省带宽(典型场景:日志类、计数类写入),唯一办法是
SetMessageOption(*msg, MESSAGE_OPTION_RESULT_FLAG_FOR_SUCCESS, std::string("0"))。 - 想在失败时拿到服务端已有数据(典型场景:condition 不满足、version 不匹配),必须显式
SetMessageOption(*msg, MESSAGE_OPTION_RESULT_FLAG_FOR_FAIL, std::string("3"))——SDK 不会主动设 fail 槽位。
典型示例:ListReplace 让失败也回带服务端旧数据
NS_TCAPLUS_PROTOBUF_API::ListReplaceRequest req;
// ... 填充 req.m_pMsg / req.m_nElemIndex ...
// 默认:成功时回带 replace 后的记录,失败时不返回。
// 现在改为:成功时回带 replace 前的记录,失败时也回带服务端已有数据。
g_stApi.SetMessageOption(*req.m_pMsg, MESSAGE_OPTION_RESULT_FLAG_FOR_SUCCESS, std::string("3"));
g_stApi.SetMessageOption(*req.m_pMsg, MESSAGE_OPTION_RESULT_FLAG_FOR_FAIL, std::string("3"));
g_stApi.ListReplace(req, &cb);
4.3 入口 B:函数参数 int resultFlag(批量写 / 带 operation 的 Update)
下列接口签名本身就有 int resultFlag 参数:
int Update(Message* msg, const string& operation, const string& condition,
int resultFlag, TcaplusPbCallback* cb, uint64_t ttl = 0);
int BatchAdd (vector<Message*>* msgs, int resultFlag, TcaplusPbCallback* cb);
int BatchDelete(vector<Message*>* msgs, int resultFlag, TcaplusPbCallback* cb);
int BatchDelete(vector<Message*>* msgs, int resultFlag, const string& condition, TcaplusPbCallback* cb);
int BatchUpdate(vector<Message*>* msgs, int resultFlag, TcaplusPbCallback* cb);
int BatchUpdate(vector<Message*>* msgs, int resultFlag, const string& operation,
const string& condition, TcaplusPbCallback* cb);
int BatchSet (vector<Message*>* msgs, int resultFlag, TcaplusPbCallback* cb);
参数 resultFlag 在 SDK 内的落点:
| PB SDK 接口 | resultFlag 怎么落到 ForSuccess / ForFail |
|---|---|
BatchAdd(msgs, resultFlag, cb) |
仅赋给 ForSuccess,Fail 槽位保持 NOVALUE |
BatchDelete(msgs, resultFlag, [cond,] cb) |
同时赋给 ForSuccess 与 ForFail(两者同值) |
BatchUpdate(msgs, resultFlag, [op, cond,] cb) |
同 BatchDelete |
BatchSet(msgs, resultFlag, cb) |
同 BatchDelete |
Update(msg, op, cond, resultFlag, cb, ttl) |
仅赋给 ForSuccess,Fail 槽位保持 NOVALUE |
💡 强烈推荐:用
SetMessageOption精细化控制 success / fail 两个槽位不论是
BatchAdd这种"fail 槽位接口未灌"的情况,还是BatchDelete/Update/Set"success 与 fail 同灌"的限制,都建议通过SetMessageOption在任意一个msgs[i]上挂MESSAGE_OPTION_RESULT_FLAG_FOR_SUCCESS/MESSAGE_OPTION_RESULT_FLAG_FOR_FAIL来精细化控制:
- 覆盖语义:batch 接口的 ResultFlag 槽位是 request 级别(整个 batch 共用一份),SDK 内部循环
msgs时调FillMessageOption,把 msg 上的 option 写回 request 级别槽位——这一步发生在参数resultFlag赋值之后,因此会覆盖参数路径的设置。只需在msgs[0]上挂一次,整批生效。- 表达力更强:success / fail 是对称独立的两条通道,可让两个槽位取不同值——这是参数
resultFlag在BatchDelete/Update/Set上做不到的(参数同灌导致两者必然同值)。- 可完全替代参数
resultFlag:把 success 与 fail 都通过 option 设上,参数resultFlag传任意占位值(如0)即可,最终生效的是 option 的两个值。⚠️ 不要在不同 msg 上挂不同的值——
FillMessageOption按msgs顺序遍历覆盖,最后一个被遍历到的非默认值会覆盖前面所有设置,行为反直觉。请只在msgs[0]上挂一次,避免歧义。
典型示例:BatchUpdate 成功不回带、失败回带旧数据
std::vector<google::protobuf::Message*> msgs;
// ... 填充 msgs ...
// BatchUpdate 内部会把 resultFlag 同时灌入 success 与 fail(两者同值),
// 这里通过 msg-level option 把 fail 槽位单独覆盖回 ALLOLDVALUE。
// 注:fail 槽位是 request 级别的,只挂在 msgs[0] 上即可对整批生效,
// 无需 for-loop 给每个 msg 都设。
g_stApi.SetMessageOption(*msgs[0], MESSAGE_OPTION_RESULT_FLAG_FOR_FAIL, std::string("3"));
int ret = api.BatchUpdate(&msgs,
/*resultFlag=*/0, // success = NOVALUE,不回带成功子记录
&cb);
// 最终下发到协议层:ForSuccess = NOVALUE(0),ForFail = ALLOLDVALUE(3)
典型示例:完全走 SetMessageOption,不依赖函数参数 resultFlag
std::vector<google::protobuf::Message*> msgs;
// ... 填充 msgs ...
// 在任意一条 msg 上同时挂 _FOR_SUCCESS 与 _FOR_FAIL,对整批生效。
// 这两个 option 会覆盖 BatchUpdate 函数参数 resultFlag 灌入的 success / fail 槽位,
// 因此参数 resultFlag 传任意占位值(这里用 0)都可以。
g_stApi.SetMessageOption(*msgs[0], MESSAGE_OPTION_RESULT_FLAG_FOR_SUCCESS, std::string("2")); // 成功回带操作后数据
g_stApi.SetMessageOption(*msgs[0], MESSAGE_OPTION_RESULT_FLAG_FOR_FAIL, std::string("3")); // 失败回带服务端旧数据
int ret = api.BatchUpdate(&msgs, /*resultFlag=*/0 /* 占位,会被上面两次 SetMessageOption 覆盖 */, &cb);
// 最终下发到协议层:ForSuccess = ALLVALUE(2),ForFail = ALLOLDVALUE(3)
典型示例:Update(msg, op, cond, resultFlag, ...) 自定义 success,并补 fail
// 成功时回带操作后的新数据,失败时回带服务端已有的旧数据
g_stApi.SetMessageOption(*msg, MESSAGE_OPTION_RESULT_FLAG_FOR_FAIL, std::string("3"));
g_stApi.Update(msg,
/*operation=*/"", /*condition=*/"",
/*resultFlag=*/TCaplusValueFlag_ALLVALUE,
&cb);
4.4 入口 C:Request 结构成员 m_nResultFlag(仅 ListBatchUpdate)
PB SDK 中唯一走这个入口的接口是 ListBatchUpdate:
int ListBatchUpdate(NS_TCAPLUS_PROTOBUF_API::ListBatchUpdateRequest &req,
TcaplusPbCallback *cb);
| Request 字段 | 取值 | 控制的槽位 | 备注 |
|---|---|---|---|
req.m_nResultFlag |
TCaplusValueFlag_*(NOVALUE / SAMEWITHREQUEST / ALLVALUE / ALLOLDVALUE) |
仅 ForSuccess | 用户必须自己填,未填等同 NOVALUE(成功不回带数据) |
req.m_nResultFlag 在 SDK 内部经 request->SetResultFlagForSuccess(req.m_nResultFlag) 直接落到协议层 success 槽位。Fail 槽位 SDK 不会主动赋值,如需失败也回带服务端数据,必须通过入口 A 在 req.m_pMsg 上补一刀:SetMessageOption(*req.m_pMsg, MESSAGE_OPTION_RESULT_FLAG_FOR_FAIL, ...)。
典型示例:ListBatchUpdate 选择回带模式
NS_TCAPLUS_PROTOBUF_API::ListBatchUpdateRequest req;
// ... 填充 req ...
// 入口 C:控制 ForSuccess —— 想拿到每条元素的旧值
req.m_nResultFlag = tcaplus_protocol_cs::TCaplusValueFlag_ALLOLDVALUE;
// 入口 A(兜底):如需失败也回带服务端数据,挂在 req.m_pMsg 上
g_stApi.SetMessageOption(*req.m_pMsg, MESSAGE_OPTION_RESULT_FLAG_FOR_FAIL, std::string("3"));
g_stApi.ListBatchUpdate(req, &cb);
💡 与 §4.3 批量写接口同理,你也可以完全用入口 A 替代入口 C:在
req.m_pMsg上挂MESSAGE_OPTION_RESULT_FLAG_FOR_SUCCESS即可覆盖req.m_nResultFlag的设置——SDK 内部FillMessageOption调用发生在SetResultFlagForSuccess(req.m_nResultFlag)之后,msg-level option 会覆盖入口 C 写入的值。其它 List 写接口(
ListAddAfter/ListReplace/ListUpdateItem/ListDel/ListBatchDel)走入口 A,默认值与设置示例见 §4.2;ListBatchAdd是特例,不走任何入口(完整规则见 §2.4);List 读接口(ListGetAll/ListQuery/ListGet/ListTraverse等)由 SDK 内部按命令字写死合理 ResultFlag,业务一般无需调整。
4.5 PB SDK 写接口默认值速查表
未做任何用户设置时 PB SDK 内部下发给协议层的 ForSuccess / ForFail:
| PB SDK 接口 | 默认 ForSuccess | 默认 ForFail | 用户覆盖入口 |
|---|---|---|---|
Add(msg, cb, ttl) |
ALLVALUE(2) |
NOVALUE(0) |
A. SetMessageOption(msg, ...) |
Set(msg, cb, ttl) / Set(msg, op, cond, cb, ttl) |
ALLVALUE(2) |
NOVALUE(0) |
A |
Update(msg, cb, ttl) |
ALLVALUE(2)(写死,A 改不动 success) |
NOVALUE(0) |
success:改用 Update(msg, "", "", resultFlag, cb, ttl);fail:A |
Update(msg, op, cond, resultFlag, cb, ttl) |
resultFlag 参数 |
NOVALUE(0) |
B(参数)+ A(仅 fail 槽位) |
Del(msg, cb) / Del(msg, cond, cb) |
ALLOLDVALUE(3) |
NOVALUE(0) |
A |
UpdateItem(msg, op, cond, cb) |
ALLVALUE(2) |
NOVALUE(0) |
A |
FieldInc(...) / FieldSet(...) |
NOVALUE(0) |
NOVALUE(0) |
A(实际很少需要) |
BatchAdd(msgs, resultFlag, cb) |
resultFlag 参数 |
NOVALUE(0) |
B(参数)+ A(仅 fail 槽位) |
BatchDelete(msgs, resultFlag, ...) |
resultFlag 参数 |
resultFlag 参数(同值) |
B(参数同灌 success+fail)+ A(覆盖 fail 槽位) |
BatchUpdate(msgs, resultFlag, ...) |
同 BatchDelete |
同 BatchDelete |
同 BatchDelete |
BatchSet(msgs, resultFlag, cb) |
同 BatchDelete |
同 BatchDelete |
同 BatchDelete |
ListAddAfter(req, cb) |
ALLVALUE(2) |
NOVALUE(0) |
A(针对 req.m_pMsg) |
ListReplace(req, cb) |
ALLVALUE(2) |
NOVALUE(0) |
A |
ListUpdateItem(req, cb) |
ALLVALUE(2) |
NOVALUE(0) |
A |
ListDel(req, cb) |
ALLOLDVALUE(3) |
NOVALUE(0) |
A |
ListBatchAdd(req, cb) |
不支持设置,规则见 §2.4 | 同左 | — |
ListBatchDel(req, cb) |
ALLOLDVALUE(3) |
NOVALUE(0) |
A |
ListBatchUpdate(req, cb) |
req.m_nResultFlag |
NOVALUE(0) |
C(req.m_nResultFlag) |
四条原则:
- Fail 槽位除
Batch*Delete/Update/Set外,全部默认NOVALUE。要让失败也回带数据,必须显式SetMessageOption(*msg, MESSAGE_OPTION_RESULT_FLAG_FOR_FAIL, ...)。Update(msg, cb, ttl)无法用SetMessageOption改 success 槽位——它内部直接调Update(msg, "", "", ALLVALUE, cb, ttl)把 ForSuccess 写死。要灵活控制 success,请改用 §4.3 的 4 参数Update。ListBatchAdd(req, cb)不支持 ResultFlag,详见 §2.4。- PB SDK 用户无须调老接口
SetResultFlag:所有写接口的 ResultFlag 控制都已落到新接口(success / fail 双槽位),老接口仅存在于 TDR SDK(§3)的兼容路径中。
5. Go SDK 用法
Go SDK 提供两套写请求接口形态:Raw 是底层 Request 用法,Option-based 是对底层 Request 的高层封装。两者的设置入口不同,但最终都落到同一套 ResultFlag 语义——Go SDK 仓库 tcaplus-go-api 的 example/ 目录里有完整覆盖:
| 路径 | 接口形态 | 示例目录 | 设置方式 |
|---|---|---|---|
| A. Raw(底层 Request) | client.NewRequest(zone, table, cmd.TcaplusApi...Req) 构造 req 对象 → client.Do(req) 或 client.SendRequest(req) |
example/TDR/sync、example/TDR/async、example/PB/sync、example/PB/async |
在 req 上直接调用 SetResultFlagForSuccess / SetResultFlagForFail / 旧的 SetResultFlag |
| B. Option-based(高层 Client.Do用法,推荐用法) | client.DoXxx(msg, opt) / client.DoXxx(table, data, opt) |
example/TDR/sync2.0、example/PB/sync2.0 |
在 option.TDROpt 或 option.PBOpt 结构里填 ResultFlag / ResultFlagForSuccess / ResultFlagForFail 字段 |
接口契约定义在
request/request.go与protocol/option/option.go。Option-based 接口内部仍会创建并设置底层 Request,最终落到协议层同一个chFlag字段。
5.1 Raw 接口(底层 Request)
接口签名(来自 request/request.go):
// 旧接口,仅作兼容,flag 必须是 0/1/2/3
SetResultFlag(flag int) error
// 新接口,result_flag 取 0/1/2/3 对应 NOVALUE/SAMEWITHREQUEST/ALLVALUE/ALLOLDVALUE
SetResultFlagForSuccess(result_flag byte) int
SetResultFlagForFail (result_flag byte) int
返回值:
- 新接口返回
int:terror.GEN_ERR_SUC(0) 成功,terror.ParameterInvalid等表示失败。 - 旧接口返回
error:参数非法或当前请求类型不支持时返回*terror.ErrorCode。
典型示例 1:TDR Insert 成功时回带 server 端 insert 后的完整数据
// 摘自 example/TDR/sync/insert.go
req, err := client.NewRequest(ZoneId, TableName, cmd.TcaplusApiInsertReq)
if err != nil { /* ... */ }
// 0=只返回成功与否, 1=返回与请求一致, 2=返回操作后所有字段, 3=返回操作前所有字段
req.SetResultFlagForSuccess(2)
rec, _ := req.AddRecord(0)
// ... rec.SetData(data) ...
resp, err := client.Do(req, 2*time.Second)
典型示例 2:批量 Update 失败时回带 server 端老数据
req, _ := client.NewRequest(ZoneId, TableName, cmd.TcaplusApiBatchUpdateReq)
// 批量构造记录 ...
req.SetResultFlagForSuccess(0) // 成功不需要数据
req.SetResultFlagForFail(3) // 失败回带老数据,便于业务合并
resp, err := client.Do(req, 2*time.Second)
典型示例 3:使用旧 SetResultFlag(仅兼容,新代码不推荐)
// 摘自 example/TDR/sync/delete.go
req, _ := client.NewRequest(ZoneId, TableName, cmd.TcaplusApiDeleteReq)
// 旧接口:删除成功后返回 tcaplus 端的旧数据,默认为 0
if err := req.SetResultFlag(3); err != nil {
fmt.Printf("SetResultFlag failed %v\n", err.Error())
return
}
Go SDK 写类 request 对
flag做了严格校验(!= 0 && != 1 && != 2 && != 3会返回ParameterInvalid),不要传超出 0~3 的值。SetResultFlag对不支持的命令字(如BatchInsert/Replace类的部分 batch 命令)会返回OperationTypeNotMatch,行为与 §2.3 描述一致。
5.2 Option-based 接口(option.TDROpt / option.PBOpt,推荐用法)
option 包同时提供三个字段(来源:protocol/option/option.go):
type TDROpt struct {
// ... 其他字段 ...
// 推荐使用 ResultFlagForSuccess / ResultFlagForFail;ResultFlag 有历史包袱,某些场景并不准确
ResultFlag byte // 旧接口
ResultFlagForSuccess byte // 新接口:成功时返回的数据
ResultFlagForFail byte // 新接口:失败时返回的数据
}
type PBOpt struct {
// ... 其他字段 ...
ResultFlag byte
ResultFlagForSuccess byte
ResultFlagForFail byte
// PB FieldUpdate 专用:默认 false 时只返回更新的部分字段;设为 true 才让 ResultFlagForSuccess 生效
SupportFieldResultFlagForSuccess bool
SupportFieldResultFlagForFail bool
}
包级常量(与 C++ 端一致):
const (
TcaplusResultFlagNoValue byte = 0
TcaplusResultFlagSameWithRequest byte = 1
TcaplusResultFlagAllNewValue byte = 2
TcaplusResultFlagAllOldValue byte = 3
)
典型示例 1:PB Insert 成功时返回操作后完整数据
// 摘自 example/PB/sync2.0/insert.go
msg := &tcaplusservice.GamePlayers{ /* ... */ }
opt := &option.PBOpt{
ResultFlag: option.TcaplusResultFlagAllNewValue, // 等价于 SetResultFlagForSuccess(2)
}
if err := client.DoInsert(msg, opt); err != nil { /* ... */ }
fmt.Println(opt.Version) // opt.Version 也会被 SDK 回填
典型示例 2:TDR Increase 区分成功 / 失败两个 flag
// 摘自 example/TDR/sync2.0/increase.go
opt := &option.TDROpt{
ResultFlagForSuccess: option.TcaplusResultFlagAllNewValue, // 成功回带 inc 后值
ResultFlagForFail: option.TcaplusResultFlagAllOldValue, // 失败回带 svr 端老值
IncField: []option.IncFieldInfo{
{FieldName: "level", IncData: int32(2), Operation: cmd.TcaplusApiOpPlus},
},
}
if err := client.DoIncrease("table_generic", data, opt); err != nil { /* ... */ }
典型示例 3:PB FieldUpdate 必须显式打开支持开关
opt := &option.PBOpt{
ResultFlagForSuccess: option.TcaplusResultFlagAllNewValue,
// 必须置 true 才会按 ResultFlagForSuccess 返回完整记录,否则只返回更新过的字段
SupportFieldResultFlagForSuccess: true,
}
if err := client.DoFieldUpdate(msg, opt); err != nil { /* ... */ }
6. 与其他特性的关系
condition不匹配:返回错误码COMMON_ERR_CONDITION_NOT_MATCHED,此时走SetResultFlagForFail的逻辑。如希望拿到当时的服务端数据,请把ForFail设为ALLOLDVALUE。详见 条件过滤和更新功能。- 版本检查失败(乐观锁):同上,按"失败"分支处理。
- batch 类请求:每条记录的结果各自适用上述矩阵;总错误码与子错误码的处理见 批量操作接口的相关说明。
- TTL:
SetTtl类写请求同样支持 ResultFlag,可用于在续期成功后立即拿到含新 ttl 的数据。
7. 常见问题(FAQ)
Q1:为什么我设置了 ALLVALUE 却没拿到数据?
- 当前请求是失败场景,而你设置的是
SetResultFlagForSuccess。请同时设置SetResultFlagForFail(ALLOLDVALUE)才能拿到 server 端老数据。 - 或者命令字本身在该 flag 下就不返回数据(见 §2 矩阵中标灰的格子),如
DELETE+ALLVALUE。
Q2:旧的 SetResultFlag(1) 与新的 SetResultFlagForSuccess(1) + SetResultFlagForFail(1) 等价吗?
不完全等价。旧接口对部分命令字(特别是 LIST_* 系列)的成功/失败行为合并处理,且没有"操作前老数据"的概念。强烈建议新代码统一改用新接口。
Q3:SetResultFlagForFail(ALLVALUE=2) 会怎样?
存储层把它当作"不合理场景",行为等同于不返回数据。请不要这样设置。
Q4:PB SDK 中我没调过任何 ResultFlag,为什么响应里有/没有数据?
PB SDK 对每个高层接口有内置默认值(详见 §4.3 与 tcaplus_async_pb_api.cpp 的具体调用点)。如想要完全确定的行为,请显式设置。
Q5:SetMessageOption(MESSAGE_OPTION_RESULT_FLAG_FOR_SUCCESS, ...) 的值用字符串还是数字?
PB SDK 的 SetMessageOption 第三参数是 std::string,所以要传字符串形式的 "0" / "1" / "2" / "3",SDK 内部会 atoi 后写到 MSGOPTION::result_flag_for_success。
8. 参考代码位置
- C++ TDR API 头文件:
tcaplus_service_request.h(接口注释最完整) - C++ TDR API 实现:
tcaplus_cpp_api/tcaplus/service_api/tcaplus_service_request.cpp::SetResultFlagForSuccess / SetResultFlagForFail / SetResultFlag - C++ PB API:
tcaplus_cpp_api/tcaplus/protobuf_api/tcaplus_async_pb_api.{h,cpp}、tcaplus_kv_api.h(MSGOPTION、MESSAGE_OPTION_RESULT_FLAG_FOR_SUCCESS/FAIL) - Go SDK:
tcaplus-go-api/request/request.go(接口契约),各request/*_req.go文件提供具体实现