Как сделать процесс в памяти транзакционным? - PullRequest
4 голосов
/ 22 сентября 2009

Я очень хорошо знаком с использованием СУБД транзакций, но как мне убедиться, что изменения, внесенные в мои данные в памяти, будут откатываться в случае сбоя транзакции? Что если я даже не использую базу данных?

Вот надуманный пример:

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;
}

Ответы [ 4 ]

8 голосов
/ 22 сентября 2009

Практически каждый вариант для этого требует одного из трех основных методов:

  1. Сделайте копию ваших данных до внесения изменений, чтобы вернуться к состоянию отката, если оно было прервано.
  2. Работа с копией данных, обновление оригинала при фиксации.
  3. Ведите журнал изменений ваших данных, чтобы отменить их в случае сбоя.

Например, Программная транзакционная память , о которой вы упомянули, следует третьему подходу. Приятно то, что он может работать с данными оптимистично и просто выбрасывать журнал при успешной фиксации.

1 голос
/ 23 сентября 2009

Вы спросили:

«Что если во время коммита произойдет ошибка?»

Это не имеет значения. Вы можете зафиксировать где-нибудь / что-то в памяти и тем временем проверить, если операция прошла успешно. Если это так, вы меняете ссылку на предполагаемый объект (объект A) на тот, где вы зафиксировали (объект B). Тогда вы получаете отказоустойчивые коммиты - ссылка обновляется только при успешном коммите. Изменение ссылки является атомарным.

1 голос
/ 22 сентября 2009

Взгляните на проект Microsoft Research, SXM .

Со страницы Мориса Херлихи можно загрузить документацию и примеры кода.

0 голосов
/ 22 сентября 2009
public void TransactionalMethod()
{
    var items = GetListOfItems();

    try {
        foreach (var item in items)
        {           
            MethodThatMayThrowException(item);

            item.Processed = true;
        }
    }
    catch(Exception ex) {
        foreach (var item in items)
        {
            if (item.Processed) {
                UndoProcessingForThisItem(item);
            }
        }
    }
}

Очевидно, что реализация "Отменить ..." оставлена ​​читателю в качестве упражнения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...