Странное поведение с begin_with и двоичным столбцом в DynamoDB

Резюме

При запросе ключа двоичного диапазона с использованием begins_with некоторые результаты не возвращаются, даже если они начинаются с запрашиваемого значения. Похоже, это происходит только с определенными значениями и только в DynamoDB-local, а не в версии DynamoDB, размещенной на AWS.

Вот суть, которую вы можете воспроизвести, чтобы воспроизвести проблему: 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-local у меня есть заметил какое-то странное поведение

Я вставил 20 элементов в свою таблицу вот так:

table.put_item(
    Item={
        'user_id': user_id.bytes,
        'project_id_item_id': project_id.bytes + item_id.bytes
    }
)              

Каждый элемент имеет одинаковый user_id и тот же project_id с другим item_id.

Когда я пытаюсь запросить те же данные обратно, иногда (возможно, в 1 из 5 раз, когда я запускаю тест), я получаю обратно только некоторые элементы:

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_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

Наконец, самая странная часть - я могу попытаться сопоставить меньшее количество байтов идентификатора проекта, и я начинаю видеть все 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 совпадений.

Кто-нибудь знает, что здесь может происходить? В документации указано, что выражение begin_with работает с двоичными данными. Я совершенно не понимаю, что может пойти не так. Интересно, делает ли DynamoDB-local что-то вроде внутреннего преобразования двоичных данных в строки для выполнения сравнений, и некоторые из этих двоичных шаблонов не конвертируются правильно.

Похоже, это может быть связано с UUID project_id. Если я жестко запрограммирую его на 76761923-aeba-4edf-9fcc-b9eeb5f80cc4 в тесте, я могу каждый раз пропускать элементы.


person Pete Baughman    schedule 03.05.2020    source источник


Ответы (1)


Это может быть ошибка шестилетней давности в DynamoDB local I оставлю этот вопрос открытым на случай, если у кого-то будет больше информации, и я обновлю этот ответ, если смогу узнать больше информации от Amazon.

Изменить. По состоянию на 23 июня им удалось воспроизвести проблему, и она находится в очереди на исправление в следующем выпуске.

2-е изменение: по состоянию на 4 августа они изучают проблему, и в ближайшее время будет выпущено исправление.

person Pete Baughman    schedule 03.05.2020
comment
отличная работа на форуме AWS, пингуем их, пока не исправили :) - person pangon; 05.07.2021