Странный результат при запросе GSI Dynamodb с несуществующим стартовым ключом. Ошибка или особенность?

Я запрашиваю глобальный вторичный индекс с начальным ключом, которого не существует, и вижу некоторые странные результаты. Это ошибка ddb или (не?) Задокументированное поведение? Есть ли обходные пути?

У меня есть таблица, в которой основной hashKey является «id», а ShopIndex GSI - «магазином». Оба без rangeKeys.

Когда я запрашиваю, используя начальный ключ "id", который не существует, я ожидаю получить пустой ответ с правильным последним оцененным ключом, поскольку нет результатов для возврата после недопустимого начального ключа. .

Однако то, что я вижу, - это, казалось бы, случайные результаты.

Пример кода:

Этот фрагмент возвращает один элемент из индекса. Не первый результат. Не последний.

const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB();
dynamodb.query({
    TableName: 'products',
    IndexName: 'ShopIndex',
    Limit: 1,
    ExpressionAttributeValues: {
        ':shop': { S: 'shop_KgHqp62taEV' }
    },
    KeyConditionExpression: 'shop = :shop',
    ExclusiveStartKey: {
        id: { S: 'doesnotexist' },
        shop: { S: 'shop_KgHqp62taEV' },
    },
}, (err, result) => {
    if (err) throw err;
    console.log('result', JSON.stringify(result, null, 2));
});

Если я полностью удалю ключ запуска, он вернет другой элемент.

Если я добавлю его обратно и установлю ScanIndexForward: false, он вернет третий другой элемент.

Если я удалю ключ запуска И установлю ScanIndexForward: false, он вернет четвертый другой элемент.

Wtf.

Насколько я могу судить, нет другого способа обнаружить это, кроме как найти "id" и подтвердить его существование, прежде чем пытаться использовать его в качестве стартового ключа?

Я пропустил это в документации, или это еще одна батарейка, не включенная в комплект, а также мина, которую мне нужно обойти?


person Cory Mawhorter    schedule 21.06.2018    source источник
comment
Возможно ли, что KeyConditionExpression: 'shop = :shop' не делает то, что вы намереваетесь, а вместо этого всегда верно?   -  person Michael - sqlbot    schedule 22.06.2018
comment
Я так не думаю, потому что другие операции работают и с возвращаемыми данными нет ничего плохого. почти уверен, что это просто недокументированная ошибка. FWIW я переключился на выполнение чтения перед всеми запросами, чтобы подтвердить, что первичный индекс существует, перед попыткой запроса. раздражает, но работает.   -  person Cory Mawhorter    schedule 23.06.2018
comment
Но вы тестируете Limit: 1,, что означает, что вы потенциально маскируете неправильное поведение, которое всегда присутствует, и вы, по сути, получаете правильный результат случайно. Что происходит с большим пределом, когда запись существует? Если вы получите как правильные, так и неправильные ответы, это должно подтвердить это.   -  person Michael - sqlbot    schedule 23.06.2018
comment
большие пределы ведут себя одинаково - всего ›1 результат относительно этого первого случайного объекта. это определенно кажется ошибкой, учитывая в конечном итоге непротиворечивый характер ddb. если я создам - ​​›запрос, используя этот результат в качестве начального ключа, а он еще не согласован, я получу случайные результаты. это ошибка. он, вероятно, должен потерпеть неудачу для несуществующих стартовых ключей так же, как и попытка прочитать несуществующий идентификатор.   -  person Cory Mawhorter    schedule 07.07.2018
comment
Что произойдет, если вы не используете ограничение 1, а запись все же существует? Если вы получите как правильные, так и неправильные ответы, это будет иначе, чем если бы вы получили только правильный ответ.   -  person Michael - sqlbot    schedule 07.07.2018
comment
О ... подождите ... перечитав вопрос, я предположил, что вы на самом деле не сказали. У этих случайных записей одинаковое значение shop?   -  person Michael - sqlbot    schedule 07.07.2018
comment
@ Michael-sqlbot да, они все одинаковые. с / без ограничения демонстрирует такое же странное поведение. реализация работает достаточно хорошо во все остальное время. проверка стартового ключа перед его использованием также оказалась довольно хорошим обходным путем (по крайней мере, в небольших масштабах).   -  person Cory Mawhorter    schedule 15.09.2018
comment
В этом случае проблема в ваших ожиданиях. Значение вашего начального ключа занимает место, где оно принадлежит в индексе, даже если значение фактически не присутствует в индексе. Так же, как фамилия Jozxyqk занимает место в телефонном справочнике, даже если ни у кого в городе нет этого имени. Вы сканируете из этого места, поэтому изменение порядка приводит к изменению результатов. Такое поведение кажется совершенно правильным.   -  person Michael - sqlbot    schedule 15.09.2018
comment
это было бы правдой, если бы Jozxyqk существовал в книге. но я говорю о том, что это не, и вместо этого возвращается случайная страница из книги. определенно неправильное поведение для несогласованного БД.   -  person Cory Mawhorter    schedule 16.09.2018
comment
Именно в этом суть ошибки вашего предположения. Каждая запись действительно где-то принадлежит, даже если эта запись в настоящее время не существует, даже если ее никогда не было. У него все еще есть место, и это место, где вы просите DynamoDB начать сканирование - место, где эта запись будет найдена, если она есть. Не имеет значения, существует ли эта запись на самом деле прямо сейчас. Это может быть разрыв шириной всего в 0 записей, но можно сказать, что он все равно существует. Сканирование вперед или назад из этого места дает наблюдаемый вами результат.   -  person Michael - sqlbot    schedule 16.09.2018


Ответы (1)


Это особенность!

В вашей таблице у вас есть больше ids для того же shop.

Представьте себе следующий пример:

id shop 41 A 22 A 93 A 34 A

Элементы находятся в памяти в таком порядке: 41, 22, 93, 34.

Когда вы запрашиваете один элемент без ExclusiveStartKey, вы получите 41 (первый отсканированный).

Когда вы говорите, что начальный ключ (т.е. последний оцененный ключ) - 93, вы получите следующий: 34.

Когда вы говорите, что начальный ключ 93, но ScanIndexForward: false, вы посмотрите назад и получите 22.

Чтобы лучше понять, запустите запросы без Limit: 1. Вы должны заметить разницу в результатах.

Итак, это определенно особенность! Очень важный, потому что с этими функциями и ключом диапазона вы можете выполнять замечательные запросы. Я сделал! ;)

person Costin    schedule 24.06.2018
comment
это отличное объяснение стартовых ключей, но это не совсем то, о чем мой пост. мой пост об использовании стартового ключа для идентификатора, который не существует в данных. используя ваш пример таблицы, использование начального ключа 100 вернет, казалось бы, случайный результат (вместо 41 или 34) - person Cory Mawhorter; 07.07.2018
comment
@ Костин, вопрос в следующем: почему ExpressionAttributeValues: { ':shop': { S: 'shop_KgHqp62taEV' } }, и KeyConditionExpression: 'shop = :shop', не приводят к тому, что ничего не возвращается? - person Michael - sqlbot; 07.07.2018