Я очень хорошо знаком с использованием СУБД транзакций, но как мне убедиться, что изменения, внесенные в мои данные в памяти, будут откатываться в случае сбоя транзакции? Что если я даже не использую базу данных?
Вот надуманный пример:
public void TransactionalMethod()
{
var items = GetListOfItems();
foreach (var item in items)
{
MethodThatMayThrowException(item);
item.Processed = true;
}
}
В моем примере я мог бы захотеть откатить изменения, внесенные в элементы списка, но как мне это сделать?
Мне известна «программная транзакционная память», но я мало что знаю об этом, и она кажется довольно экспериментальной. Мне также известна концепция «компенсируемых транзакций», но это влечет за собой накладные расходы при написании кода do / undo.
Subversion, похоже, имеет дело с ошибками обновления рабочей копии, заставляя вас выполнить команду "cleanup".
Есть идеи?
UPDATE
Рид Копси предлагает отличный ответ , в том числе:
Работа над копией данных, обновление оригинала при фиксации.
Это поднимает мой вопрос еще на один уровень - что, если во время коммита произойдет ошибка ? Мы так часто думаем о коммите как о немедленной операции, но в действительности он может вносить много изменений в большое количество данных. Что произойдет, если во время применения коммита есть такие вещи, как OutOfMemoryException
s?
С другой стороны, если выбрать вариант отката, что произойдет, если во время отката возникнет исключение ? Я понимаю, что в Oracle RDBMS есть концепция сегментов отката, журналов UNDO и других вещей, но при условии, что сериализации на диск не существует (если не сериализовано на диск, этого не произошло, а сбой означает, что вы можете исследовать эти журналы). и оправиться от этого), это действительно возможно?
ОБНОВЛЕНИЕ 2:
ответ от Alex сделал хорошее предложение: а именно, что каждый обновляет другой объект, тогда фаза фиксации просто меняет ссылку на текущий объект на новый объект. Он пошел дальше и предположил, что объект, который вы изменяете, фактически является списком измененных объектов.
Я понимаю, что он говорит (я думаю), и я хочу сделать вопрос более сложным в результате:
Как, учитывая этот сценарий, вы справляетесь с блокировкой? Представьте, что у вас есть список клиентов:
var customers = new Dictionary<CustomerKey, Customer>();
Теперь, вы хотите внести изменения в некоторых этих клиентов, как применить эти изменения без блокировки и замены всего списка? Например:
var customerTx = new Dictionary<CustomerKey, Customer>();
foreach (var customer in customers.Values)
{
var updatedCust = customer.Clone();
customerTx.Add(GetKey(updatedCust), updatedCust);
if (CalculateRevenueMightThrowException(customer) >= 10000)
{
updatedCust.Preferred = true;
}
}
Как мне зафиксировать? Это (предложение Алекса) будет означать блокировку всех клиентов при замене списка ссылок:
lock (customers)
{
customers = customerTx;
}
Принимая во внимание, что если я перебираю цикл, изменяя ссылку в исходном списке, она не является атомарной, а не справляется с проблемой «что, если она сломается на полпути»:
foreach (var kvp in customerTx)
{
customers[kvp.Key] = kvp.Value;
}