Транзакции в modx
Добрый вечер! Как следует из заголовка топика, вопрос будет по транзакциям, а именно одновременное изменения одних данных несколькими пользователями.
Я опишу вопрос максимально упращено, главное понять принцип.
Есть таблица (допустим class = VotesTable) проголосовавших, для упрощения вопроса, она имеет 2 поля. Первое id(автоинкремент) и votes(голоса-integer).
Для примера одна запись:
id = 10
votes = 33
Есть страница с формой и кнопкой плюс. Соответственно нажимая на плюс в поле votes плюсуется единица к значению находящееся в поле votes.
Обработчики упращенный:
1 Первый пользователь получил 33 голоса.
2 Второй пользователь получил 33 голоса.
3. Первый увеличил на 1 получил 34 и записал новое значение в таблицу
4. Второй увеличил на 1 и записал 34 в таблицу.
А реально должно получится 35. Как решаются такие задачи?
ЗЫ:
Хорошо, с транзакциями диалога не получается, может быть подскажите как на xPDO составить такой запрос
Есть таблица (допустим class = VotesTable) проголосовавших, для упрощения вопроса, она имеет 2 поля. Первое id(автоинкремент) и votes(голоса-integer).
Для примера одна запись:
id = 10
votes = 33
Есть страница с формой и кнопкой плюс. Соответственно нажимая на плюс в поле votes плюсуется единица к значению находящееся в поле votes.
Обработчики упращенный:
$c =$modx->newQuery('VotesTable');
$c->where(array(
'id' => 10,
));
//Получили объект
$item = $modx->getObject('VotesTable', $c);
//Берем текущее кол-во голосов
$count = $item->get('votes');
//Плюсуем +1
$count +=1;
//Сохраняем новое значение
$item->set('votes', $count);
$item->save();
Вот и самый главный вопрос. В modx есть$modx->beginTransaction();
$modx->rollback();
$modx->commit();
Как их использовать(где раставить) при одновременной операции несколькими пользователями, что бы каждый пользователь получал актуальное значение votes, а не получилось так:1 Первый пользователь получил 33 голоса.
2 Второй пользователь получил 33 голоса.
3. Первый увеличил на 1 получил 34 и записал новое значение в таблицу
4. Второй увеличил на 1 и записал 34 в таблицу.
А реально должно получится 35. Как решаются такие задачи?
ЗЫ:
Хорошо, с транзакциями диалога не получается, может быть подскажите как на xPDO составить такой запрос
UPDATE VotesTable SET votes = votes+ 1 WHERE id = 10
Пока получается только так, но это запрос пишит всегда единицу$c = $modx->newQuery('VotesTable');
$c->command('update');
$c->set(array(
'votes ' => 1
));
$c->prepare();
print $c->toSQL();
Комментарии: 14
может быть подскажите как на xPDO составить такой запрос
$modx->exec("UPDATE VotesTable SET votes = votes+ 1 WHERE id = 10");
Спасибо, буду так использовать. На PDO я знал такой вариант записи, думал есть какой то хитрый вариант на xPDO.
Вы не совсем понимаете суть транзакций. Их, как правило, используют для одновременного изменения нескольких записей. И если одну из операций выполнить не удалось, то нужно откатить уже проведенные. В вашем примере они не причем.
1. Сначала получить количество голосов, увеличить на 1 и записать в переменную.
2. Выполнить тот запрос, который указан в конце вашего поста с подстановкой переменной из первого шага.
А можно сделать на PDO, как выше написал Василий.
П.С. Кстати, можно упростить ваш пример. Вместо
Хорошо, с транзакциями диалога не получается, может быть подскажите как на xPDO составить такой запросНа xPDO такой запрос можно сделать в 2 этапа:
UPDATE VotesTable SET votes = votes+ 1 WHERE id = 10
1. Сначала получить количество голосов, увеличить на 1 и записать в переменную.
2. Выполнить тот запрос, который указан в конце вашего поста с подстановкой переменной из первого шага.
А можно сделать на PDO, как выше написал Василий.
П.С. Кстати, можно упростить ваш пример. Вместо
$c =$modx->newQuery('VotesTable');
$c->where(array(
'id' => 10,
));
//Получили объект
$item = $modx->getObject('VotesTable', $c);
достаточно сделать так$id = 10;
$item = $modx->getObject('VotesTable', $id);
Вы не совсем понимаете суть транзакций. Их, как правило, используют для одновременного изменения нескольких записей. И если одну из операций выполнить не удалось, то нужно откатить уже проведенные. В вашем примере они не причем.Не я понимаю, что транзакция имеет 2 варианта, либо полностью выполняется, либо ничего не выполняет. Я думал что там есть какой то механизм блокировки для работы с одной записью.
1. Сначала получить количество голосов, увеличить на 1 и записать в переменную.В том то и дело, что теоретически, между получением и записью, другой пользователь тоже получит старое значение и увеличит. Практически получится увеличение на единицу, а должно на две единицы. Это не вариант.
П.С. Кстати, можно упростить ваш пример. ВместоДа, знаю...))) Просто привык так писать…
достаточно сделать такМожно еще короче....)
$id = 10;
$item = $modx->getObject('VotesTable', $id);
$item = $modx->getObject('VotesTable', 10);
В том то и дело, что теоретически, между получением и записью, другой пользователь тоже получит старое значение и увеличит. Практически получится увеличение на единицу, а должно на две единицы. Это не вариант.Теоретически да, но практически вряд ли. Разница между этими операциями составляет доли секунды. Вероятность, что кто-то попадет как раз между, крайне мала. Думаю, что даже если постараться нажать одновременно с кем-то, то все равно не получится.
Ну а в случае использования PDO для таких страхов вообще нет почвы.
А если ещё учесть, что MyISAM транзакций вообще не поддерживает, а на InnoDB modx хоть и заведётся, но никто не гарантирует стабильной работы, то можно считать, что в modx транзакций нет вообще.
А вот эти методы (rollback, commit) — это просто обёрки над одноимёнными стандартными pdo-statement методами
А вот эти методы (rollback, commit) — это просто обёрки над одноимёнными стандартными pdo-statement методами
а на InnoDB modx хоть и заведётся, но никто не гарантирует стабильной работы,С InnoDB засада… foreach($modx->getIterator($class) as $object){...} уходит в бесконечность. Пока не копал почему, но косяк такой имеется.
Я хотел привести ссылку на твою давнюю статью, где ты описывал процесс перехода на innodb, да что-то не нашёл (а сейчас newpg вообще редиректит на modxclub), в веб-архиве тоже не нашёл.
Поэтому не помню чем там дело кончилось :)
Но сам факт того, что валится итератор — это, конечно, печаль (как бэ намекает на то, что фокусы и магия неизбежны).
Ну и раз уж тема зашла, — там от авторов ничего не слышно по поводу поддержки innodb в modx 3?)
Поэтому не помню чем там дело кончилось :)
Но сам факт того, что валится итератор — это, конечно, печаль (как бэ намекает на то, что фокусы и магия неизбежны).
Ну и раз уж тема зашла, — там от авторов ничего не слышно по поводу поддержки innodb в modx 3?)
Ниже ответил на часть ваших вопросов.
Ну и раз уж тема зашла, — там от авторов ничего не слышно по поводу поддержки innodb в modx 3?)Вряд ли что-то будет. Мы как-то с Джейсоном Ковардом разговаривали, и он выразил свое мнение, что innoDB переоценены, и что он не видит в них особого смысла. Есть там еще один момент важный, о котором я сейчас напишу в отдельной статье. Этот момент очень сильно мешает использованию innoDB, но у нас с Сергеем Прохоровым уже есть ответ на это счет, который вполне возможно будет отправлен в ядро, если Джейсон примет.
Статья Сергея Прохорова о валидации и транзауциях: modxclub.ru/topics/validacziya-obektov-tranzakczii-1837.html
Какая-то бессмысленная там статья.
MyISAM не поддерживает транзакций. И php никаким образом не может заставить эти транзакции работать, потому что это уровень базы данных — если мускул не умеет, то php тут бессилен.
Вызов функций $modx->beginTransaction(), $modx->commit() и $modx->rollBack() не даёт ровным счётом ничего, ибо это просто обёртки над стандартными одноимёнными функциями встроенного в php PDO-класса.
Сейчас лень проверять, но, если мне не изменяет память (больше года назад с этим разбирался, не помню уже), ни одна из этих функций не возвращает false, если идёт работа с нетранзакционными таблицами (т.е. с MyISAM) и ведут они себя просто как заглушки, не влияя на ход выполнения программы и не выбрасывая никаких исключений или ошибок.
Как мы выяснили выше, xPDO умеет работать только с MyISAM. Транзакции есть в InnoDB.
Автор в той статье не уточняет — научил ли он работать modx с innodb или нет. Если бы научил, подозреваю, что статья была бы именно об этом, а не о транзакциях.
Вывод? Какая-то бессмысленная там статья.
MyISAM не поддерживает транзакций. И php никаким образом не может заставить эти транзакции работать, потому что это уровень базы данных — если мускул не умеет, то php тут бессилен.
Вызов функций $modx->beginTransaction(), $modx->commit() и $modx->rollBack() не даёт ровным счётом ничего, ибо это просто обёртки над стандартными одноимёнными функциями встроенного в php PDO-класса.
Сейчас лень проверять, но, если мне не изменяет память (больше года назад с этим разбирался, не помню уже), ни одна из этих функций не возвращает false, если идёт работа с нетранзакционными таблицами (т.е. с MyISAM) и ведут они себя просто как заглушки, не влияя на ход выполнения программы и не выбрасывая никаких исключений или ошибок.
Как мы выяснили выше, xPDO умеет работать только с MyISAM. Транзакции есть в InnoDB.
Автор в той статье не уточняет — научил ли он работать modx с innodb или нет. Если бы научил, подозреваю, что статья была бы именно об этом, а не о транзакциях.
Вывод? Какая-то бессмысленная там статья.
По вашим просьбам в статье дописано, что все указанное работает именно на инно.
Алексей, чтобы вы понимали, Сергей работает со мной не первый год, и его статьи бессмысленными называть то же самое, что и мои.
И еще раз: описанное Сергеем — это не домыслы, а длительная и конкретная практика на крупном и очень сложном проекте. И я тоже использовал транзакции на отдельных проектах, ибо иногда без них просто никуда.
MyISAM не поддерживает транзакций. И php никаким образом не может заставить эти транзакции работать, потому что это уровень базы данных — если мускул не умеет, то php тут бессиленКак правильно выше написал Михаил — надо просто переключить таблицы в innoDB, и транзакции заработают.
Вызов функций $modx->beginTransaction(), $modx->commit() и $modx->rollBack() не даёт ровным счётом ничего, ибо это просто обёртки над стандартными одноимёнными функциями встроенного в php PDO-класса.Вызов этих функций именно то и дает, что выполняет SQL-запросы в текущей сессии на старт транзакций и т.п., ибо как и с другими запросами на это требуется авторизация на MySQL-сервере, обращение к БД и т.п. За это xPDO (в качестве PDO-обертки и отвечает, ибо подсасывает указанные в конфиги настройки соединения и т.п.).
Как мы выяснили выше, xPDO умеет работать только с MyISAM.Где я там говорил, что xPDO не умеет работать с innoDB??? Я сказал, что есть один локальный баг. Но метод xPDO::getIterator() мало где используется, так что вы вполне можете сделать свои innoDB-таблицы и просто не вызывать в работе с ними этот метод. В остальном все работает.
И еще раз: описанное Сергеем — это не домыслы, а длительная и конкретная практика на крупном и очень сложном проекте. И я тоже использовал транзакции на отдельных проектах, ибо иногда без них просто никуда.
Как правильно ниже написал ВсеволодДико извиняюсь! Воеводский Михаил. Бывают у меня сбои с именами.
И получается, что он уже выше со своим комментарием.
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.