Атомные операции в лазурной таблице хранения

Я ищу для создания счетчика просмотров страницы в azure table storage. Если говорят, что два пользователя одновременно заходят на страницу и текущее значение на странице Page100s = 100, гарантировано ли, что страницаПросмотр = 102 после операции обновления?

Ответы

Ответ 1

Ответ зависит от того, как вы реализуете свой счетчик.: -)

В хранилище таблиц нет оператора "increment", поэтому вам нужно будет прочитать текущее значение (100) и обновить его до нового значения (101). В хранилище таблиц используется оптимистичный concurrency, поэтому, если вы делаете то, что приходит естественно при использовании клиентской библиотеки хранилища .NET, скорее всего, вы увидите исключение, когда два процесса попытались сделать это одновременно. Это будет поток:

  • Процесс A считывает значение PageViews и получает 100.
  • Процесс B считывает значение PageViews и получает 100.
  • Процесс A создает условное обновление для PageViews, что означает "установить PageViews на 101, если он в настоящее время 100". Это удается.
  • Процесс B выполняет те же операции и терпит неудачу, поскольку предварительное условие (PageViews == 100) является ложным.

Очевидное, что нужно сделать, когда вы получаете ошибку, - это повторить процесс. (Прочитайте текущее значение, которое теперь составляет 101, и обновите его до 102.) Это всегда (в конечном итоге) приведет к тому, что ваш счетчик будет иметь правильное значение.

Существуют и другие возможности, и мы сделали целый эпизод Cloud Cover о том, как реализовать действительно масштабируемый счетчик: http://channel9.msdn.com/Shows/Cloud+Cover/Cloud-Cover-Episode-43-Scalable-Counters-with-Windows-Azure.

То, что описано в этом видео, вероятно, будет излишним, если столкновения маловероятны. I.e., если ваш коэффициент попадания один в секунду, нормальный шаблон "читать, увеличивать, записывать" будет безопасным и эффективным. Если, с другой стороны, вы получаете 1000 ударов в секунду, вам нужно сделать что-то умнее.

ИЗМЕНИТЬ

Просто хотел прояснить, кто читает это, чтобы понять оптимистичный concurrency... условная операция на самом деле не установлена ​​ "PageViews до 101, если она в настоящее время 100". Это больше похоже на "set PageViews до 101, если он не изменился с тех пор, как я в последний раз смотрел на него". (Это достигается с помощью ETag, который вернулся в HTTP-запрос.)

Ответ 2

Вы также можете пересмотреть часть "count". Почему бы не превратить это в двухэтапный процесс?

Шаг 1 - Просмотр просмотров страницы

Каждый раз, когда кто-то просматривает страницу, добавьте запись в таблицу (позвоните на нее PageViews). Информация, которую вы добавили в один из этих магазинов, будет следующей:

  • PartitionKey = PageName
  • RowKey = Случайный GUID

После нескольких просмотров у вас будет что-то вроде этого:

  • MyPage.aspx - someGuid
  • MyPage.aspx - someGuid
  • SomePage.aspx - someGuid
  • MyPage.aspx - someGuid

Шаг 2 - просмотр просмотров страницы

Теперь мы хотим получить все эти записи, посчитать их, увеличить счетчик и удалить все записи. Предположим, что у вас работает несколько работающих. Оба ваших работника будут иметь цикл, который будет работать от 1 до 10 минут. Каждый раз, когда истекает рабочее время, он принимает аренду на блобе, если аренда еще не была сделана (это всегда должно быть одним и тем же блобом, вы можете использовать AutoRenewLease).

Первый рабочий, получающий блокировку, может идти вперед и делать подсчет:

  • Получить все записи из таблицы PageViewRecordings или из кеша
  • Подсчитать все виды страниц на страницу
  • Обновить счетчик где-то
  • Удалите записи, которые были учтены при подсчете

Проблема в том, что очень сложно превратить это в идемпотентный процесс. Что произойдет, если ваш экземпляр завершится с ошибкой между счетом и удалением? У вас будет увеличенный счетчик страниц, но поскольку элементы не были удалены, они будут добавлены к общему счету в следующий раз при их обработке.

Вот почему я бы предложил следующее. В той же таблице (PageViews) вы также будете записывать общие просмотры страниц в том же разделе. Но данные будут немного разными (это будет одна запись в этом разделе, содержащая общее количество):

  • PartitionKey = PageName
  • RowKey = Guid.Empty(просто не используйте случайный указатель, таким образом мы знаем разницу между записанным просмотром страницы и записью, содержащей общее количество).
  • Count = Текущее количество просмотров страницы

Это вполне возможно, потому что хранилище таблиц меньше. И почему мы это делаем? Поскольку у нас есть транзакции, если мы ограничимся одной и той же таблицей + разделом с maxmium из 100 объектов. Что мы можем сделать с этим?

  • Используя Take, мы получаем 100 записей из этой таблицы + раздела.
  • Первая запись, которую мы получим, это запись счетчика. Зачем? Поскольку его rowkey является Guid.Empty и сортировка лексикографическая
  • Подсчитайте эти записи (-1, потому что первая запись не является просмотром страницы, это просто наш счетчик заполнителей)
  • Обновить свойство Count записи счетчика
  • Удалить 99 (или менее) других записей
  • SaveChanges с использованием пакета.
  • Повторяйте до тех пор, пока не останется только одна запись (запись счетчика).

И каждые X минут ваши работники будут видеть, нет ли аренды на блобе, получить аренду и перезапустить процесс.

Является ли этот ответ достаточно ясным или мне нужно добавить код?

Ответ 3

Я согласился с тем же вопросом. С помощью библиотеки Azure python я разрабатываю простой счетчик с использованием eTag и If-Match вместо блокировки. Основная идея заключается в том, чтобы повторить попытку увеличить счетчик до тех пор, пока обновление не будет успешно запущено по определенным критериям, что не является дополнительным обновлением. Если запрос обновлений тяжелый, необходимо вызвать осколок.

https://github.com/flyakite/simple-scalable-datastore/blob/master/datastore/azuretable.py

Ответ 4

При использовании веб-сайтов Azure очередные опции Azure Queues и WebJobs. В одном сценарии, хотя я на самом деле собираюсь принять подход к sharding, и WebJobs периодически обновляет агрегаты. Таблица таблиц хранения Azure таблицы UserPageView с PartitionKey = User и RowKey = Страница. Два одновременных пользователя с одним и тем же идентификатором пользователя не будут разрешены.