Нет. CouchDB использует модель «оптимистичного параллелизма». Проще говоря, это просто означает, что вы отправляете версию документа вместе со своим обновлением, и CouchDB отклоняет изменение, если текущая версия документа не соответствует отправленной вами.
На самом деле это обманчиво просто. Вы можете переосмыслить многие обычные сценарии на основе транзакций для CouchDB. Тем не менее, при изучении CouchDB вам нужно как бы выбросить свои знания в области СУБД. Полезно подходить к проблемам с более высокого уровня, а не пытаться приспособить Couch к миру, основанному на SQL.
Отслеживание инвентаря
Обозначенная вами проблема в первую очередь связана с инвентаризацией. Если у вас есть документ, описывающий элемент, и он включает поле для «доступного количества», вы можете решить проблемы параллелизма следующим образом:
- Получите документ, обратите внимание на
_rev
свойство, которое CouchDB отправляет вместе
- Уменьшите значение поля количества, если оно больше нуля
- Отправьте обновленный документ обратно, используя
_rev
свойство
- Если
_rev
совпадает с текущим сохраненным номером, готово!
- Если есть конфликт (когда
_rev
не совпадает), получите самую новую версию документа
В этом случае следует подумать о двух возможных сценариях сбоя. Если количество в самой последней версии документа равно 0, вы обрабатываете ее так же, как и в РСУБД, и предупреждаете пользователя, что он фактически не может купить то, что хотел купить. Если последняя версия документа имеет количество больше 0, вы просто повторяете операцию с обновленными данными и начинаете с самого начала. Это заставляет вас выполнять немного больше работы, чем это сделала бы РСУБД, и может немного раздражать, если есть частые конфликтующие обновления.
Итак, ответ, который я только что дал, предполагает, что вы собираетесь делать что-то в CouchDB почти так же, как в РСУБД. Я мог бы подойти к этой проблеме несколько иначе:
Я бы начал с основного документа продукта, который включает все данные дескриптора (имя, изображение, описание, цену и т. Д.). Затем я бы добавил документ «инвентарный билет» для каждого конкретного экземпляра с полями для product_key
и claimed_by
. Если вы продаете модель молотка, и у вас есть 20 штук на продажу, у вас могут быть документы с ключами, такими как hammer-1
, hammer-2
и т.д., для представления каждого доступного молотка.
Затем я создал представление, которое дает мне список доступных молотков с функцией уменьшения, которая позволяет мне видеть «общее». Они полностью сняты с манжеты, но должны дать вам представление о том, как будет выглядеть рабочий вид.
карта
function(doc)
{
if (doc.type == 'inventory_ticket' && doc.claimed_by == null ) {
emit(doc.product_key, { 'inventory_ticket' :doc.id, '_rev' : doc._rev });
}
}
Это дает мне список доступных «билетов» по ключам продукта. Я мог бы взять группу из них, когда кто-то хочет купить молоток, а затем итеративно отправлять обновления (используя id
и _rev
), пока я не востребую один (ранее заявленные билеты приведут к ошибке обновления).
Уменьшить
function (keys, values, combine) {
return values.length;
}
Эта функция сокращения просто возвращает общее количество невостребованных inventory_ticket
предметов, поэтому вы можете определить, сколько «молотков» доступно для покупки.
Предостережения
Это решение представляет собой примерно 3,5 минуты на обдумывание конкретной проблемы, которую вы представили. Могут быть способы сделать это лучше! Тем не менее, это значительно снижает количество конфликтующих обновлений и снижает необходимость реагировать на конфликт с помощью нового обновления. В соответствии с этой моделью у вас не будет нескольких пользователей, пытающихся изменить данные в первичной записи продукта. В самом худшем случае у вас будет несколько пользователей, пытающихся потребовать один билет, и если вы захватили несколько из них, вы просто переходите к следующему билету и повторите попытку.
Ссылка: https://wiki.apache.org/couchdb/Frequent_asked_questions#How_do_I_use_transactions_with_CouchDB.3F
Расширяя ответ мистера Курта. Для многих сценариев вам не нужно выкупать биржевые билеты по порядку. Вместо выбора первого билета вы можете случайным образом выбрать из оставшихся билетов. Учитывая большое количество билетов и большое количество одновременных запросов, вы значительно снизите конкуренцию по этим билетам по сравнению со всеми, кто пытается получить первый билет.
источник
Шаблон проектирования для спокойных транзакций - создать «напряжение» в системе. В популярном примере использования транзакции по банковскому счету вы должны убедиться, что обновили общую сумму для обоих задействованных счетов:
Сканирование на предмет натяжения должно выполняться в бэкэнд-процессе для всех «натянутых документов», чтобы время натяжения в системе было коротким. В приведенном выше примере будет кратковременное ожидаемое несоответствие, когда первая учетная запись была обновлена, а вторая еще не обновлена. Это нужно учитывать так же, как вы будете иметь дело с возможной согласованностью, если ваш Couchdb является распределенным.
Другая возможная реализация полностью исключает необходимость транзакций: просто сохраните документы напряжения и оцените состояние вашей системы, оценив каждый задействованный документ напряжения. В приведенном выше примере это будет означать, что общая сумма для учетной записи определяется только как сумма значений в документах транзакции, в которых задействована эта учетная запись. В Couchdb вы можете очень хорошо смоделировать это как представление карты / уменьшения.
источник
Нет, CouchDB обычно не подходит для транзакционных приложений, потому что он не поддерживает атомарные операции в кластерной / реплицированной среде.
CouchDB пожертвовал транзакционными возможностями в пользу масштабируемости. Чтобы иметь атомарные операции, вам нужна центральная система координации, которая ограничивает вашу масштабируемость.
Если вы можете гарантировать, что у вас есть только один экземпляр CouchDB или что каждый, модифицирующий конкретный документ, подключается к одному и тому же экземпляру CouchDB, вы можете использовать систему обнаружения конфликтов для создания своего рода атомарности с использованием методов, описанных выше, но если вы позже масштабируете до кластера или используйте размещенный сервис, такой как Cloudant, он сломается, и вам придется переделывать эту часть системы.
Итак, я предлагаю использовать что-то другое, кроме CouchDB для остатков на вашем счете, так будет намного проще.
источник
Как ответ на проблему OP, Couch, вероятно, здесь не лучший выбор. Использование представлений - отличный способ отслеживать инвентарь, но ограничение до 0 более или менее невозможно. Проблема заключается в состоянии гонки, когда вы читаете результат представления, решаете, что можете использовать элемент «молоток-1», а затем пишете документ для его использования. Проблема в том, что нет атомарного способа написать документ только для использования молотка, если в результате просмотра имеется> 0 молотков-1. Если все 100 пользователей одновременно запрашивают представление и видят 1 молоток-1, все они могут написать документ, чтобы использовать молоток 1, в результате чего получится -99 молотков-1. На практике состояние гонки будет довольно небольшим - действительно небольшим, если ваша БД работает под управлением localhost. Но после масштабирования и создания внешнего сервера или кластера БД проблема станет гораздо более заметной.
Обновление ответа MrKurt (возможно, оно просто датировано, или он мог не знать о некоторых функциях CouchDB)
Представление - это хороший способ управлять такими вещами, как балансы / инвентарь в CouchDB.
Вам не нужно использовать docid и rev в представлении. Вы получаете и то, и другое бесплатно, когда получаете результаты просмотра. Их использование - особенно в подробном формате, таком как словарь, - просто увеличит ваш обзор излишне большим.
Простое представление для отслеживания остатков инвентаря должно выглядеть примерно так (тоже не в моей голове)
А функция уменьшения еще проще
При этом используется встроенная функция сокращения, которая просто суммирует значения всех строк с соответствующими ключами.
В этом представлении любой документ может иметь член "InventoryChange", который сопоставляет product_key с изменением их общего инвентаря. т.е.
Добавил бы 10 hammer_1234 и 25 saw_4321.
Сжечь 5 молотков из инвентаря.
В этой модели вы никогда не обновляете данные, а только добавляете. Это означает, что нет возможности для конфликтов обновлений. Уходят все транзакционные проблемы обновления данных :)
Еще одна приятная особенность этой модели заключается в том, что ЛЮБОЙ документ в БД может как добавлять, так и вычитать элементы из инвентаря. Эти документы могут содержать любые другие данные. У вас может быть документ «Отгрузка» с кучей данных о дате и времени получения, складе, принимающем сотруднике и т. Д., И пока этот документ определяет InventoryChange, он будет обновлять инвентарь. Как и документ "Продажа", документ "DamagedItem" и т. Д. Глядя на каждый документ, они читаются очень четко. А вид выполняет всю тяжелую работу.
источник
На самом деле, вы можете в некотором роде. Взгляните на HTTP Document API и прокрутите вниз до заголовка «Изменить несколько документов с помощью одного запроса».
По сути, вы можете создать / обновить / удалить кучу документов в одном почтовом запросе к URI / {dbname} / _ bulk_docs, и все они либо будут успешными, либо все не удастся. Однако документ предупреждает, что это поведение может измениться в будущем.
РЕДАКТИРОВАТЬ: как и ожидалось, начиная с версии 0.9 объемная документация больше не работает таким образом.
источник
Просто используйте легкое решение SQlite для транзакций, и когда транзакция будет успешно завершена, реплицируйте ее и отметьте ее реплицируемой в SQLite.
Таблица SQLite
Вы также можете удалить транзакции, которые успешно реплицируются.
источник