内部模块的交互都是使用protobuf,所有有部分数据是存protobuf的bytes到couchbase里面,但是某天发现从coucbase里面取protobuf的binary数据后客户端解析失败:

1
2
3
Caused by: com.couchbase.client.java.error.TranscodingException: Flags (0x0) indicate non-binary document for id 12307971, could not decode.
at com.couchbase.client.java.transcoder.BinaryTranscoder.doDecode(BinaryTranscoder.java:38) ~[imhttp-v1.jar:na]
at

使用的couchbase java sdk版本是2.2.6 对应代码如下

1
2
3
4
5
6
7
8
9
@Override
protected BinaryDocument doDecode(String id, ByteBuf content, long cas, int expiry, int flags,
ResponseStatus status) throws Exception {
if (!TranscoderUtils.hasBinaryFlags(flags)) {
throw new TranscodingException("Flags (0x" + Integer.toHexString(flags) + ") indicate non-binary " +
"document for id " + id + ", could not decode.");
}
return BinaryDocument.create(id, expiry, content, cas);
}

查看couchbase解析response的源代码后发现flags是memcache协议中的一个字段

关于flags

memcached 协议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| 0x81 | 0x00 | 0x00 | 0x05 |
+---------------+---------------+---------------+---------------+
4| 0x04 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
8| 0x00 | 0x00 | 0x00 | 0x09 |
+---------------+---------------+---------------+---------------+
12| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
16| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
20| 0x00 | 0x00 | 0x00 | 0x01 |
+---------------+---------------+---------------+---------------+
24| 0xde | 0xad | 0xbe | 0xef |
+---------------+---------------+---------------+---------------+
28| 0x48 ('H') | 0x65 ('e') | 0x6c ('l') | 0x6c ('l') |
+---------------+---------------+---------------+---------------+
32| 0x6f ('o') | 0x57 ('W') | 0x6f ('o') | 0x72 ('r') |
+---------------+---------------+---------------+---------------+
36| 0x6c ('l') | 0x64 ('d') |
+---------------+---------------+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Total 38 bytes (24 byte header, 4 byte extras, 5 byte key
and 5 byte value)
Field (offset) (value)
Magic (0) : 0x81
Opcode (1) : 0x00
Key length (2,3) : 0x0005
Extra length (4) : 0x04
Data type (5) : 0x00
Status (6,7) : 0x0000
Total body (8-11) : 0x00000009
Opaque (12-15): 0x00000000
CAS (16-23): 0x0000000000000001
Extras :
Flags (24-27): 0xdeadbeef
Key (28-32): The textual string: "Hello"
Value (33-37): The textual string: "World"

文档中没详细解释flags是什么用的,但是根据上面校验代码看,应该是是一个存储类型的校验字段。

所以只能继续看源码,查看源码后发现flags的值在add set replace的时候客户端就已经生成了,但是出错的那些数据都是没有flags字段的,由于这部分数据都是c sdk负责写的,所以怀疑是c sdk写数据的时候没有生成这个flags

c sdk写数据的抓包结果

所以这个问题是各个sdk行为不一致引起的,所以解决方法就是删掉那段校验的代码,重新打包就可以了

备注:关于部分sdk的行为

c sdk的请求flags为0,也就是没有设置,get response也不校验flags的请求,所以无报错
python sdk add请求有设置flags的,get response不校验flags,所以也无报错
java sdk add 请求有设置flags, get response强校验flags,所以解析失败