摘要
使用begins_with
查询二进制范围键时,即使某些结果以所查询的值开头,也不会返回。这似乎只在特定值下发生,而且只在DynamoDB本地版本中发生,而不是在AWS托管的DynamoDB版本中。
以下是您可以运行的重现该问题的要点:https://gist.github.com/pbaughman/922db7b51f7f82bbd9634949d71f846b
详细信息
我有一个DynamoDB表,具有以下模式:
user_id - Primary Key - binary - Contains 16 byte UUID
project_id_item_id - Sort Key - binary - 32 bytes - two UUIDs concatinated
在使用dynamodb本地docker图像本地运行我的单元测试时,我观察到的一些奇怪行为
我在桌子上插入了20个项目,如下所示:
table.put_item(
Item={
'user_id': user_id.bytes,
'project_id_item_id': project_id.bytes + item_id.bytes
}
)
每个项目具有相同的user_id
和具有不同item_id
的相同project_id
。
当我试图重新查询相同的数据时,有时(可能是我运行测试的五分之一(我只得到一些项目:
table.query(
KeyConditionExpression=
Key('user_id').eq(user_id.bytes) &
Key('project_id_item_id').begins_with(project_id.bytes))
)
# Only returns 14 items
如果我从KeyConditionExpression中删除第二个条件,我会得到所有20个项。
如果我运行扫描而不是查询,并使用相同的条件表达式,我会得到所有20个项目的
table.scan(
FilterExpression=
Key('user_id').eq(user_id.bytes) &
Key('project_id_item_id').begins_with(project_id.bytes))
)
# 20 items are returned
如果我打印表中每个项目的project_id_id_item_id,我可以看到它们都以相同的project_id:开头
[i['project_id_item_id'].value.hex() for i in table.scan()['Items']]
# Result:
|---------Project Id-----------|
['76761923aeba4edf9fccb9eeb5f80cc40604481b26c84c73b63308dd588a4df1',
'76761923aeba4edf9fccb9eeb5f80cc40ec926452c294c909befa772b86e2175',
'76761923aeba4edf9fccb9eeb5f80cc460ff943b36ec44518175525d6eb30480',
'76761923aeba4edf9fccb9eeb5f80cc464e427afe84d49a5b3f890f9d25ee73b',
'76761923aeba4edf9fccb9eeb5f80cc466f3bfd77b14479a8977d91af1a5fa01',
'76761923aeba4edf9fccb9eeb5f80cc46cd5b7dec9514714918449f8b49cbe4e',
'76761923aeba4edf9fccb9eeb5f80cc47d89f44aae584c1c9da475392cb0a085',
'76761923aeba4edf9fccb9eeb5f80cc495f85af4d1f142608fae72e23f54cbfb',
'76761923aeba4edf9fccb9eeb5f80cc496374432375a498b937dec3177d95c1a',
'76761923aeba4edf9fccb9eeb5f80cc49eba93584f964d13b09fdd7866a5e382',
'76761923aeba4edf9fccb9eeb5f80cc4a6086f1362224115b7376bc5a5ce66b8',
'76761923aeba4edf9fccb9eeb5f80cc4b5c6872aa1a84994b6f694666288b446',
'76761923aeba4edf9fccb9eeb5f80cc4be07cd547d804be4973041cfd1529734',
'76761923aeba4edf9fccb9eeb5f80cc4c48daab011c449f993f061da3746a660',
'76761923aeba4edf9fccb9eeb5f80cc4d09bc44973654f39b95a91eb3e291c68',
'76761923aeba4edf9fccb9eeb5f80cc4d0edda3d8c6643ad8e93afe2f1b518d4',
'76761923aeba4edf9fccb9eeb5f80cc4d8d1f6f4a85e47d78e2d06ec1938ee2a',
'76761923aeba4edf9fccb9eeb5f80cc4dc7323adfa35423fba15f77facb9a41b',
'76761923aeba4edf9fccb9eeb5f80cc4f948fb40873b425aa644f220cdcb5d4b',
'76761923aeba4edf9fccb9eeb5f80cc4fc7f0583f593454d92a8a266a93c6fcd']
作为健全性检查,以下是我在查询中使用的project_id:
print(project_id)
76761923-aeba-4edf-9fcc-b9eeb5f80cc4 # Matches what's returned by scan above
最后,最奇怪的是,我可以尝试匹配更少字节的项目ID,然后我开始看到所有20个项目,然后是零个项目,再看到所有20项:
hash_key = Key('hash_key').eq(hash_key)
for n in range(1,17):
short_key = project_id.bytes[:n]
range_key = Key('project_id_item_id').begins_with(short_key)
count = table.query(KeyConditionExpression=hash_key & range_key)['Count']
print("If I only query for 0x{:32} I find {} items".format(short_key.hex(), count))
获取我:
If I only query for 0x76 I find 20 items
If I only query for 0x7676 I find 20 items
If I only query for 0x767619 I find 20 items
If I only query for 0x76761923 I find 20 items
If I only query for 0x76761923ae I find 20 items
If I only query for 0x76761923aeba I find 20 items
If I only query for 0x76761923aeba4e I find 20 items
If I only query for 0x76761923aeba4edf I find 0 items
If I only query for 0x76761923aeba4edf9f I find 20 items
If I only query for 0x76761923aeba4edf9fcc I find 0 items
If I only query for 0x76761923aeba4edf9fccb9 I find 20 items
If I only query for 0x76761923aeba4edf9fccb9ee I find 0 items
If I only query for 0x76761923aeba4edf9fccb9eeb5 I find 20 items
If I only query for 0x76761923aeba4edf9fccb9eeb5f8 I find 20 items
If I only query for 0x76761923aeba4edf9fccb9eeb5f80c I find 20 items
If I only query for 0x76761923aeba4edf9fccb9eeb5f80cc4 I find 15 items
我完全被这种模式惊呆了。如果我正在搜索的范围键是8、10或12字节长,我将得不到匹配项。如果它是16字节长,我会得到少于20个但多于0个匹配。
有人知道这里会发生什么吗?文档指出begins_withi表达式适用于二进制数据。我完全不知道会出什么问题。我想知道DynamoDB local是否正在做一些事情,比如在内部将二进制数据转换为字符串来进行比较,而其中一些二进制模式没有正确转换。
看起来它可能与project_id UUID有关。如果我在测试中把它硬编码到76761923-aeba-4edf-9fcc-b9eeb5f80cc4
,我可以让它每次都错过项目。
这可能是DynamoDB本地中一个六年前的错误。如果有人有更多的见解,我会保留这个问题,如果我能从亚马逊找到更多信息,我会更新这个答案。
编辑:截至6月23日,他们已经成功复制了该问题,并将在未来的版本中进行修复。
第二次编辑:截至8月4日,他们正在调查该问题,不久将发布修复程序