СМ. АЛЬТЕРНАТИВНОЕ РЕШЕНИЕ НИЖЕ - ответ на комментарий OP с просьбой сохранить порядок списка в индексе.
Одно решение
- Разделите таблицу по списку
- Сохранять каждый элемент списка как отдельный элемент
- Сохранять отдельный элемент
sortOrder
с номером версии
Плюсы:
- Элемент
sortOrder
может действовать как блокировка списка (через номер версии), решая ваше состояние гонки. - Для изменения списка требуется обновить только один элемент (элемент
sortOrder
) - Для вставки или добавления нового элемента в ваш список требуется обновить только два элемента (новый элемент и элемент
sortOrder
)
Минусы:
- При запросе элементов списка они не будут возвращены в правильном порядке сортировки. Вам нужно будет использовать атрибут
order
элемента sortOrder
для сортировки возвращаемых элементов каждый раз.
Эта сортировка может выполняться на стороне сервера (например, в лямбда-функции перед возвратом результаты запроса вашему клиенту) или на стороне клиента (в коде внешнего интерфейса). Сортировка относительно короткого списка довольно эффективна, и ваш вариант использования звучит так, как будто это, вероятно, не вызовет проблемы. Тем не менее, вы должны учитывать ваши ожидаемые шаблоны доступа и потребности в производительности.
Пример таблицы:
partion key | sort key | version | order | item_details
list#1 | sortOrder | 2 | [item#UUID1, item#UUID2]
list#1 | item#UUID1 | | | foo
list#1 | item#UUID2 | | | bar
list#2 | sortOrder | 1 | [item#UUID3]
list#2 | item#UUID3 | | | baz
Чтобы СОЗДАТЬ (вставить) новый элемент:
get_item
список sortOrder
элемент put_item
новый элемент update_item
sortOrder
с увеличенной версией и новый order
атрибут, при условии, что ожидаемый номер версии все еще существует - Если условие не выполняется (потому что другой клиент изменил список, так как вы сделали исходный
get_item
), вы можете удалить новый элемент, который вы вставили, а затем начать сначала.
ЧИТАТЬ (перечислить) элементы в списке
query
все элементы в списке, включая sortOrder
- отсортируйте
item
s с помощью атрибута sortOrder
order
- Примечание: возможно, другой клиент добавил элемент, который еще не обновил
sortOrder
. В зависимости от ваших потребностей вы можете проигнорировать новый элемент или повторить попытку до тех пор, пока элементы в order
не совпадут с фактическими возвращенными элементами
ОБНОВЛЕНИЕ элемента в списке
УДАЛЕНИЕ элемента в списке
get_item
список sortOrder
элемент update_item
sortOrder
с увеличенной версией и новым атрибутом order
, при условии, что ожидаемый номер версии все еще существует delete_item
элемент, который будет удалено
Для СОРТИРОВКИ существующего списка
update_item
элемент sortOrder
с условием от ожидаемого version
. Ожидаемое значение version
было бы с момента последнего запроса клиентом элементов списка
Другой подход: TransactWriteItems
Если важно, чтобы элементы хранились в отсортированном порядке, вы можете использовать TransactWriteItems
, что позволит вам записывать до 25 элементов за одну транзакцию. Если ваши списки дел не превышают 25 элементов, это было бы более простым решением с более производительным чтением списков за счет менее эффективных вставок и сортировок. Ограничение в 25 элементов - основная проблема с этим.
АЛЬТЕРНАТИВНОЕ РЕШЕНИЕ
Согласно вашему комментарию, другое решение - отсортировать список с помощью ключа сортировки. Вам все равно потребуется блокировка для каждого списка, но процесс будет немного отличаться.
Пример таблицы:
partition key | sort key | itemID | version | item_details | TTL | lock_key
list#1 | lock | | 2 | | 888500 | xyz123
list#1 | 000001 | UUID1 | | foo
list#1 | 000002 | UUID2 | | bar
list#2 | lock | | 1 | | 888001 | abc456
list#2 | 000001 | UUID3 | | baz
Чтобы получить блокировку для списка, клиент должен:
- обновить:
-
TTL
до настоящего момента + интервал (например, 10 секунд) lock_key
(в случайную строку, предоставленную клиентом) - увеличить
version
число
- при условии:
-
TTL
<сейчас (т.е. срок действия любой предыдущей блокировки истек) </li>
Чтобы обновить sh блокировку , уже полученную клиентом (то есть, если приближается 10-секундный предел):
- обновление:
-
TTL
до настоящего момента + интервал (например, еще 10 секунд) - увеличить
version
- при условии:
-
lock_key
== ожидаемый lock_key
(то есть срок его действия не истек, и он не был получен другим клиентом)
Блокировку необходимо будет получить при вставке или добавлении элементов или обновлении порядок сортировки. Если вы просто обновляете другой (не ключ сортировки) атрибут (ы) элемента, вам не обязательно нужна блокировка.
Если вы читаете список, вы можете оптимистично запросить без получение блокировки, но вы должны прочитать блокировку до и после, чтобы убедиться, что она не была получена за это время, так как это может привести к несогласованному списку, и в этом случае вы должны повторить попытку. Это причина для атрибута version
в lock
.
Итак:
При вставке, добавлении или изменении порядка сортировки:
- получить блокировку списка
- записать элемент (ы), включая обновление нового ключа сортировки для каждого перемещаемого элемента
- refre sh блокировка списка, если вам нужно больше время
При чтении
- читать блокировку
- если TTL> сейчас
- идет обновление прогресс, поэтому подождите, пока не истечет TTL, и повторите попытку
- иначе, если TTL <сейчас: <ul>
- чтение элементов списка (т.е. запрос)
- чтение блокировки снова. Если версия была изменена, запрошенные элементы могут быть несовместимы, поэтому начните заново. Если он не изменился, то клиент может быть уверен, что возвращенные результаты согласованы.
Проблема с использованием блокировок заключается в том, что пока список обновляется, другие клиенты не будут уметь читать последовательно. В зависимости от вашего варианта использования это может быть приемлемо. Если нет, вы можете создать слой кэша, который согласован с транзакциями. Однако реализация кеша, вероятно, выходит за рамки вашего вопроса:)
Все это предполагает, что ваши списки дел не ограничены 25 элементами, в противном случае вы можете использовать TransactWriteItems
, как упоминалось.