Как избежать потери данных при параллельном коммите сессий?

Есть такая ситуация (пользователь открыл две разных страницы в разных вкладках)

1. сделали запрос 1, открыли сессию, делаем что-то долгодумающее
2. сделали запрос 2, открыли сессию
3. запрос 2 добавил в сессию какие-то данные
4. запрос 1 закончил свою долгоиграющую операцию, добавил данные в сессию (этих данных нет в запросе 2)
5. запрос 1 сдох, делает коммит в файл. Данных много, коммит будет долгий
6. запрос 2 сдох, делает коммит, но он не идет, файл занят запросом 1
7. запрос 1 закончил коммит, разлочили файл
8. запрос 2 получил доступ к файлу, и перезаписал его. Таким образом мы потеряли данные от 1 запроса

как ее разруливать?

upd: имеется в виду своя реализация сессий на похапэ. Но я думаю, что это будет актуально и для других ЯП

1 ответов


Поднял рукописи.
Это похоже на проблемы при работе двух транзакций с одной БД. Я думаю, что можно провести параллелизм с сессией и пхп (извините, но на пхп я не пишу).
Итак:
В БД имеется несколько тупиковых ситуаций: фантомы, "грязные" данные, и т.п. Думаю, что вышеописанный случай относится к проблемам несовместного анализа.

Пролог: фраза из вопроса "коммит в файл" - команда commit в sql, "сессия" - похоже на работу транзакции с данными, "запрос" - транзакция (мои личные предположения).

Цитата:
  "Эффект собственно несовместимого анализа также отличается от предыдущих примеров тем, что в   смеси присутствуют две транзакции - одна длинная, другая короткая.

  Длинная транзакция выполняет некоторый анализ по всей таблице, например, подсчитывает общую   сумму денег на счетах клиентов банка для главного бухгалтера. Пусть на всех счетах находятся   одинаковые суммы, например, по $100. Короткая транзакция в этот момент выполняет перевод $50 с   одного счета на другой так, что общая сумма по всем счетам не меняется.
  http://rghost.ru/2935061/image.png
(Извиняйте, но таблицу не сумел заделать)
  Хотя транзакция B все сделала правильно - деньги переведены без потери, но в результате   транзакция A подсчитала неверную общую сумму.

  Т.к. транзакции по переводу денег идут обычно непрерывно, то в данной ситуации следует ожидать,   что главный бухгалтер никогда не узнает, сколько же денег в банке."

Вывод: (так же из рукописей)

Конфликты между транзакциями

Анализ проблем параллелизма показывает, что если не предпринимать специальных мер, то при работе в смеси нарушается свойство (И) транзакций - изолированность.

Очевидно, что транзакции не мешают друг другу, если они обращаются к разным данным или выполняются в разное время.

Транзакции называются конкурирующими, если они пересекаются по времени и обращаются к одним и тем же данным.

В результате конкуренции за данными между транзакциями возникают конфликты доступа к данным. Различают следующие виды конфликтов:

1) W-W (Запись - Запись). Первая транзакция изменила объект и не закончилась. Вторая транзакция пытается изменить этот объект. Результат - потеря обновления.

2) R-W (Чтение - Запись). Первая транзакция прочитала объект и не закончилась. Вторая транзакция пытается изменить этот объект. Результат - несовместимый анализ (неповторяемое считывание).

3) W-R (Запись - Чтение). Первая транзакция изменила объект и не закончилась. Вторая транзакция пытается прочитать этот объект. Результат - чтение "грязных" данных.

Конфликты типа R-R (Чтение - Чтение) отсутствуют, т.к. данные при чтении не изменяются.

Другие проблемы параллелизма (фантомы и собственно несовместимый анализ) являются более сложными, т.к. принципиальное отличие их в том, что они не могут возникать при работе с одним объектом. Для возникновения этих проблем требуется, чтобы транзакции работали с целыми наборами данных.

График запуска набора транзакций называется последовательным, если транзакции выполняются строго по очереди, т.е. элементарные операции транзакций не чередуются друг с другом.

Если график запуска набора транзакций содержит чередующиеся элементарные операции транзакций, то такой график называется чередующимся.

При выполнении последовательного графика гарантируется, что транзакции выполняются правильно.

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

График запуска транзакции называется верным (сериализуемым), если он эквивалентен какому-либо последовательному графику.

Задача обеспечения изолированной работы пользователей не сводится просто к нахождению правильных (сериальных) графиков запусков транзакций. Второй частью задачи является достижение оптимального графика - т.е. графика с максимальной эффективностью выполнения транзакций. Для этого сначала нужно уточнить понятие "оптимальность".

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

Это время складывается из следующих компонентов:

1) Время ожидания начала транзакции - то время, которое проходит от момента, когда транзакция возникла до момента, когда началась реально выполняться ее первая элементарная операция.

2) Сумма времен выполнения элементарных операций транзакции.

3) Сумма времен всех элементарных операций других транзакций, вклинившихся между элементарными операциями транзакции.

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

Т.к. транзакции не мешают друг другу, если они обращаются к разным данным или выполняются в разное время, то имеется два способа разрешить конкуренцию между поступающими в произвольные моменты транзакциями:

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

2) Предоставить конкурирующим транзакциям "разные" экземпляры данных (т.е. обеспечить, чтобы конкурирующие транзакции работали с разными версиями данными).

Первый метод - "притормаживание" транзакций - реализуется путем использованием блокировок различных видов или метода временных меток.

Второй метод - предоставление разных версий данных - реализуется путем использованием данных из журнала транзакций.

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

Отправил в ответ, т. к. в комментарий не может уместить в себе такое количество символов.


Используйте блокировки. При попытке внести какие-либо изменения, необходимо заблокировать ресурс для остальных пользователей либо даже для того же пользователя, но в другой сессии. В вашем случае можно воспользоваться каким-либо объектом синхронизации. Например, мютексом. То есть, будет так:

1. сделали запрос 1, открыли сессию,
1.1. Попробовали захватить мютекс. Успешно сохранили мютекс в сессии.
1.2. Делаем что-то долгодумающее

2. сделали запрос 2, открыли сессию
2.1. Попробовали захватить мютекс. Получили отказ. Ждём освобождения мютекса.
3. запрос 2 добавил в сессию какие-то данные
4. запрос 1 закончил свою долгоиграющую операцию, добавил данные в сессию (этих данных нет в запросе 2)
5. запрос 1 сдох, делает коммит в файл. Данных много, коммит будет долгий
6. запрос 2 сдох, делает коммит, но он не идет, файл занят запросом 1
7. запрос 1 закончил коммит, разлочили файл
7.1. Освободили мютекс.
8. запрос 2 получил доступ к файлу, и перезаписал его. Таким образом мы потеряли данные от 1 запроса

Дальше всё зависит от реализации.

Опишите более детально задачу, платформу, на которой ведётся разработка. Наверняка есть и более простые решения.