Как смоделировать вручную отсортированный список дел в DynamoDb? - PullRequest
0 голосов
/ 08 мая 2020

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

Мои мысли на данный момент:

Если мы используем список в одна запись Dynamo для хранения данных, сортировка проста, однако:

  • Обновление элемента означает обновление всего списка элементов.
  • Размер списка привязан к 400 КБ

Если мы сохраняем каждый элемент как запись в DynamoDb, нам нужно будет указывать значение «веса» каждый раз, когда мы сортируем или сохраняем элемент, что может вызвать состояние гонки. Один, который приходит на ум, - это при сохранении нового элемента:

  • прочитать последний индекс элемента -Answer: 21
  • (записать элемент с индексом 21, но из другой параллельной процедуры )
  • записать новый элемент в индекс 21. -Ошибка: 21 взята

1 Ответ

1 голос
/ 09 мая 2020

СМ. АЛЬТЕРНАТИВНОЕ РЕШЕНИЕ НИЖЕ - ответ на комментарий OP с просьбой сохранить порядок списка в индексе.

Одно решение

  1. Разделите таблицу по списку
  2. Сохранять каждый элемент списка как отдельный элемент
  3. Сохранять отдельный элемент 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 не совпадут с фактическими возвращенными элементами

ОБНОВЛЕНИЕ элемента в списке

  • update_item на элементе

УДАЛЕНИЕ элемента в списке

  • 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, как упоминалось.

...