Транзакции в modx

Добрый вечер! Как следует из заголовка топика, вопрос будет по транзакциям, а именно одновременное изменения одних данных несколькими пользователями.
Я опишу вопрос максимально упращено, главное понять принцип.
Есть таблица (допустим 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();
Владимир Бабусенко
21 августа 2015, 19:43
modx.pro
4 594
0

Комментарии: 14

Василий Наумкин
22 августа 2015, 02:24
0
может быть подскажите как на xPDO составить такой запрос
$modx->exec("UPDATE VotesTable SET votes = votes+ 1 WHERE id = 10");
    Владимир Бабусенко
    22 августа 2015, 11:29
    0
    Спасибо, буду так использовать. На PDO я знал такой вариант записи, думал есть какой то хитрый вариант на xPDO.
    Сергей Шлоков
    22 августа 2015, 07:45
    +1
    Вы не совсем понимаете суть транзакций. Их, как правило, используют для одновременного изменения нескольких записей. И если одну из операций выполнить не удалось, то нужно откатить уже проведенные. В вашем примере они не причем.
    Хорошо, с транзакциями диалога не получается, может быть подскажите как на xPDO составить такой запрос
    UPDATE VotesTable SET votes = votes+ 1 WHERE id = 10
    На xPDO такой запрос можно сделать в 2 этапа:
    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);
      Владимир Бабусенко
      22 августа 2015, 11:38
      0
      Вы не совсем понимаете суть транзакций. Их, как правило, используют для одновременного изменения нескольких записей. И если одну из операций выполнить не удалось, то нужно откатить уже проведенные. В вашем примере они не причем.
      Не я понимаю, что транзакция имеет 2 варианта, либо полностью выполняется, либо ничего не выполняет. Я думал что там есть какой то механизм блокировки для работы с одной записью.
      1. Сначала получить количество голосов, увеличить на 1 и записать в переменную.
      В том то и дело, что теоретически, между получением и записью, другой пользователь тоже получит старое значение и увеличит. Практически получится увеличение на единицу, а должно на две единицы. Это не вариант.
      П.С. Кстати, можно упростить ваш пример. Вместо
      Да, знаю...))) Просто привык так писать…
      достаточно сделать так
      $id = 10;
      $item = $modx->getObject('VotesTable', $id);
      Можно еще короче....)
      $item = $modx->getObject('VotesTable', 10);
        Сергей Шлоков
        22 августа 2015, 12:05
        0
        В том то и дело, что теоретически, между получением и записью, другой пользователь тоже получит старое значение и увеличит. Практически получится увеличение на единицу, а должно на две единицы. Это не вариант.
        Теоретически да, но практически вряд ли. Разница между этими операциями составляет доли секунды. Вероятность, что кто-то попадет как раз между, крайне мала. Думаю, что даже если постараться нажать одновременно с кем-то, то все равно не получится.
        Ну а в случае использования PDO для таких страхов вообще нет почвы.
      Алексей Карташов
      22 августа 2015, 10:52
      0
      А если ещё учесть, что MyISAM транзакций вообще не поддерживает, а на InnoDB modx хоть и заведётся, но никто не гарантирует стабильной работы, то можно считать, что в modx транзакций нет вообще.
      А вот эти методы (rollback, commit) — это просто обёрки над одноимёнными стандартными pdo-statement методами
        Fi1osof
        23 августа 2015, 15:25
        +1
        а на InnoDB modx хоть и заведётся, но никто не гарантирует стабильной работы,
        С InnoDB засада… foreach($modx->getIterator($class) as $object){...} уходит в бесконечность. Пока не копал почему, но косяк такой имеется.
          Алексей Карташов
          24 августа 2015, 22:12
          0
          Я хотел привести ссылку на твою давнюю статью, где ты описывал процесс перехода на innodb, да что-то не нашёл (а сейчас newpg вообще редиректит на modxclub), в веб-архиве тоже не нашёл.
          Поэтому не помню чем там дело кончилось :)

          Но сам факт того, что валится итератор — это, конечно, печаль (как бэ намекает на то, что фокусы и магия неизбежны).

          Ну и раз уж тема зашла, — там от авторов ничего не слышно по поводу поддержки innodb в modx 3?)
            Fi1osof
            26 августа 2015, 13:34
            0
            Ниже ответил на часть ваших вопросов.

            Ну и раз уж тема зашла, — там от авторов ничего не слышно по поводу поддержки innodb в modx 3?)
            Вряд ли что-то будет. Мы как-то с Джейсоном Ковардом разговаривали, и он выразил свое мнение, что innoDB переоценены, и что он не видит в них особого смысла. Есть там еще один момент важный, о котором я сейчас напишу в отдельной статье. Этот момент очень сильно мешает использованию innoDB, но у нас с Сергеем Прохоровым уже есть ответ на это счет, который вполне возможно будет отправлен в ядро, если Джейсон примет.
              Fi1osof
              26 августа 2015, 18:08
              0
          Воеводский Михаил
          24 августа 2015, 23:36
          0
          Статья Сергея Прохорова о валидации и транзауциях: modxclub.ru/topics/validacziya-obektov-tranzakczii-1837.html
            Алексей Карташов
            25 августа 2015, 01:28
            0
            Какая-то бессмысленная там статья.

            MyISAM не поддерживает транзакций. И php никаким образом не может заставить эти транзакции работать, потому что это уровень базы данных — если мускул не умеет, то php тут бессилен.
            Вызов функций $modx->beginTransaction(), $modx->commit() и $modx->rollBack() не даёт ровным счётом ничего, ибо это просто обёртки над стандартными одноимёнными функциями встроенного в php PDO-класса.

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

            Как мы выяснили выше, xPDO умеет работать только с MyISAM. Транзакции есть в InnoDB.
            Автор в той статье не уточняет — научил ли он работать modx с innodb или нет. Если бы научил, подозреваю, что статья была бы именно об этом, а не о транзакциях.

            Вывод? Какая-то бессмысленная там статья.
              Воеводский Михаил
              25 августа 2015, 09:59
              +1
              По вашим просьбам в статье дописано, что все указанное работает именно на инно.
                Fi1osof
                26 августа 2015, 13:30
                0
                Алексей, чтобы вы понимали, Сергей работает со мной не первый год, и его статьи бессмысленными называть то же самое, что и мои.
                MyISAM не поддерживает транзакций. И php никаким образом не может заставить эти транзакции работать, потому что это уровень базы данных — если мускул не умеет, то php тут бессилен
                Как правильно выше написал Михаил — надо просто переключить таблицы в innoDB, и транзакции заработают.
                Вызов функций $modx->beginTransaction(), $modx->commit() и $modx->rollBack() не даёт ровным счётом ничего, ибо это просто обёртки над стандартными одноимёнными функциями встроенного в php PDO-класса.
                Вызов этих функций именно то и дает, что выполняет SQL-запросы в текущей сессии на старт транзакций и т.п., ибо как и с другими запросами на это требуется авторизация на MySQL-сервере, обращение к БД и т.п. За это xPDO (в качестве PDO-обертки и отвечает, ибо подсасывает указанные в конфиги настройки соединения и т.п.).
                Как мы выяснили выше, xPDO умеет работать только с MyISAM.
                Где я там говорил, что xPDO не умеет работать с innoDB??? Я сказал, что есть один локальный баг. Но метод xPDO::getIterator() мало где используется, так что вы вполне можете сделать свои innoDB-таблицы и просто не вызывать в работе с ними этот метод. В остальном все работает.
                И еще раз: описанное Сергеем — это не домыслы, а длительная и конкретная практика на крупном и очень сложном проекте. И я тоже использовал транзакции на отдельных проектах, ибо иногда без них просто никуда.
                  Fi1osof
                  26 августа 2015, 13:49
                  0
                  Как правильно ниже написал Всеволод
                  Дико извиняюсь! Воеводский Михаил. Бывают у меня сбои с именами.
                  И получается, что он уже выше со своим комментарием.
              Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
              15