ResultFlag 响应数据返回控制

本文聚焦于 写类请求(insert / update / replace / delete / increase / list 写操作 / batch 写操作 / fieldset / fieldinc 等)成功或失败后,响应包里是否携带记录数据、携带哪份数据 的控制开关,即 ResultFlag。 旧的 SetResultFlag 接口只能整体控制,无法区分"操作成功"与"操作失败"两个场景;新接口 SetResultFlagForSuccessSetResultFlagForFail 拆出了这两个维度,业务可按需精细化设置。

读类请求(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 前的数据

注:

  1. LIST_ADDAFTER_REQALLVALUE 下,如果"插入+淘汰"两条记录总大小超过 10 MB,返回插入记录,不再返回淘汰记录。
  2. LIST_REPLACE_BATCH_REQ 与其他命令略有不同: NOVALUESAMEWITHREQUEST 都会回带"请求字段"。这是有意为之:批量接口下响应包内每条子记录都带 iResult 错误码,调用方需要通过响应里的 key / element index 把每条子结果对应回原请求中的某条记录,再结合子记录的 iResult 判断哪些成功、哪些失败、失败的错误码是什么。回带请求字段就是为了让调用方能稳定地完成这种"逐条对应",即使设了 NOVALUE 也会保留这份关联信息。
  3. UPDATE_ITEM_REQ / LIST_UPDATE_ITEM_REQ 服务端对 SAMEWITHREQUEST(1) 有两条额外校验,触发任一条整个请求即被拒绝并返回 SVR_ERR_FAIL_INVALID_RESULT_FLAG
    • 旧接口:调用 SetResultFlag(1) 整请求直接被拒(成功失败的语义都拒)。
    • 新接口SetResultFlagForSuccessSetResultFlagForFail 同时都设为 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 在语义上没有意义,存储层会按"不返回数据"处理;业务应避免对 SetResultFlagForFail2

BATCH_INSERT_REQ / BATCH_REPLACE_REQ / BATCH_UPDATE_REQ / BATCH_DELETE_REQ,即使 ForFail = NOVALUE(0),proxy 也会把失败子记录的 key 和对应 recordResult[i] 错误码返回给调用方;NOVALUE 只影响是否回带 value 数据。

注:

  1. 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_MATCHINSERT_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 内部透传,不影响响应数据 同左 同左 同左

关键差异提醒

  1. LIST_* 系列旧接口只识别 ALLVALUE (2),传 1/3 等价于"什么都不返回"。新接口下这三个取值分别有完整语义。
  2. DELETE_REQ 旧接口对 ALLVALUEALLOLDVALUE 不做区分,均回带 svr 端"已有数据"(即删除前数据);新接口 SetResultFlagForSuccess(ALLVALUE) 才会返回
  3. INSERT_REQ 旧接口对 1/2/3 不做区分,均回带请求字段。
  4. 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 的处理模型

几个关键事实 —— 业务开发前请先理解:

  1. PB SDK 内部统一使用新接口 SetResultFlagForSuccess / SetResultFlagForFail
  2. 用户能改默认值的入口有三类,由接口类型决定走哪一类
入口 适用接口 设置方式 能否区分 success / fail
A. SetMessageOption(msg, ...) 单记录写Add / Set / Update / Del / UpdateItem / FieldInc / FieldSet),以及 List 单条 / batch 写ListAddAfter / ListReplace / ListUpdateItem / ListDel / ListBatchDel msg(List 系列为 req.m_pMsgreq.m_vecMsg 中任一 msg)上挂 MESSAGE_OPTION_RESULT_FLAG_FOR_SUCCESS / ..._FOR_FAIL
B. 函数参数 int resultFlag 4 参数 UpdateUpdate(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_vecMsgvector< 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) 同时赋给 ForSuccessForFail(两者同值)
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 是对称独立的两条通道,可让两个槽位取不同值——这是参数 resultFlagBatchDelete/Update/Set 上做不到的(参数同灌导致两者必然同值)。
  • 可完全替代参数 resultFlag:把 success 与 fail 都通过 option 设上,参数 resultFlag 传任意占位值(如 0)即可,最终生效的是 option 的两个值。

⚠️ 不要在不同 msg 上挂不同的值——FillMessageOptionmsgs 顺序遍历覆盖,最后一个被遍历到的非默认值会覆盖前面所有设置,行为反直觉。请只在 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

四条原则

  1. Fail 槽位除 Batch*Delete/Update/Set 外,全部默认 NOVALUE。要让失败也回带数据,必须显式 SetMessageOption(*msg, MESSAGE_OPTION_RESULT_FLAG_FOR_FAIL, ...)
  2. Update(msg, cb, ttl) 无法用 SetMessageOption 改 success 槽位——它内部直接调 Update(msg, "", "", ALLVALUE, cb, ttl) 把 ForSuccess 写死。要灵活控制 success,请改用 §4.3 的 4 参数 Update
  3. ListBatchAdd(req, cb) 不支持 ResultFlag,详见 §2.4。
  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-apiexample/ 目录里有完整覆盖:

路径 接口形态 示例目录 设置方式
A. Raw(底层 Request) client.NewRequest(zone, table, cmd.TcaplusApi...Req) 构造 req 对象 → client.Do(req)client.SendRequest(req) example/TDR/syncexample/TDR/asyncexample/PB/syncexample/PB/async req 上直接调用 SetResultFlagForSuccess / SetResultFlagForFail / 旧的 SetResultFlag
B. Option-based(高层 Client.Do用法,推荐用法) client.DoXxx(msg, opt) / client.DoXxx(table, data, opt) example/TDR/sync2.0example/PB/sync2.0 option.TDROptoption.PBOpt 结构里填 ResultFlag / ResultFlagForSuccess / ResultFlagForFail 字段

接口契约定义在 request/request.goprotocol/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

返回值:

  • 新接口返回 intterror.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 类请求:每条记录的结果各自适用上述矩阵;总错误码与子错误码的处理见 批量操作接口的相关说明
  • TTLSetTtl 类写请求同样支持 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.hMSGOPTIONMESSAGE_OPTION_RESULT_FLAG_FOR_SUCCESS/FAIL
  • Go SDK:tcaplus-go-api/request/request.go(接口契约),各 request/*_req.go 文件提供具体实现

results matching ""

    No results matching ""