Как проверить транзакции MySQL? - PullRequest
13 голосов
/ 10 августа 2010

У меня есть вопрос о тестировании запросов в транзакции.Я использую транзакции MySQL уже довольно давно, и каждый раз, когда я делаю это, я использую что-то вроде:

$doCommit = true;
$error = "";
mysql_query("BEGIN");

/* repeat this part with the different queries in the transaction
   this often involves updating of and inserting in multiple tables */
$query = "SELECT, UPDATE, INSERT, etc";
$result = mysql_query($query);
if(!$result){
    $error .= mysql_error() . " in " . $query . "<BR>";
    $doCommit = false;
}
/* end of repeating part */

if($doCommit){
    mysql_query("COMMIT");
} else {
    echo $error;
    mysql_query("ROLLBACK");
}

Теперь часто случается, что я хочу проверить свою транзакцию, поэтому я меняю *От 1004 * до mysql_query("ROLLBACK");, но я могу себе представить, что это не очень хороший способ проверить подобные вещи.Обычно нереально скопировать каждую таблицу в temp_table, обновить и вставить в эти таблицы, а затем удалить их (например, потому что таблицы могут быть очень большими).Конечно, когда код поступает в производство, обработка ошибок (а не просто распечатка ошибок) ставится на место.

Какой лучший способ сделать такие вещи?

Ответы [ 3 ]

10 голосов
/ 18 августа 2010

Прежде всего, в вашей реализации есть ошибка.В случае ошибки запроса текущая транзакция автоматически откатывается и затем закрывается.Поэтому, поскольку вы продолжаете выполнять запросы, они не будут находиться в транзакции (они будут переданы в БД).Затем, когда вы выполните Rollback, он молча потерпит неудачу.Из документов MySQL :

Rolling back can be a slow operation that may occur implicitly without the user 
having explicitly asked for it (for example, when an error occurs).

Явная команда ROLLBACK должна использоваться только в том случае, если в приложении определено, что необходимо выполнить откат (по причинам, отличным от ошибки запроса),Например, если вы вычитаете средства со счета, вы бы явно откатились, если бы обнаружили, что у пользователя недостаточно средств для завершения обмена ...

Что касается тестирования транзакций,Я делаю копию базы данных.Я создаю новую базу данных и устанавливаю набор «фиктивных данных».Затем я запускаю все тесты с помощью автоматизированного инструмента.Инструмент фактически фиксирует транзакции и принудительно выполняет откат, а также проверяет, поддерживается ли ожидаемое состояние базы данных на протяжении всех тестов.Так как сложнее запрограммированно узнать конечное состояние из транзакции, если у вас есть неизвестный ввод для транзакции, тестирование живых (или даже скопированных из живых) данных не будет легким.Вы можете сделать это (и должны), но не зависите от этих результатов, чтобы определить, работает ли ваша система.Используйте эти результаты для создания новых тестовых случаев для автоматизированного тестера ...

3 голосов
/ 23 августа 2010

Может быть, вы могли бы изменить свой первый пример и использовать какой-нибудь класс оболочки доступа к БД?

В этом классе-обертке вы можете иметь переменную $ normalCommit = true; и метод SetCommitMode (), который устанавливает эту переменную $ normalCommit. И у вас есть метод Commit (), который фиксирует if ($ normalCommit == true) Или даже есть переменная $ failTransaction, которая вызывает mysql_query ("ROLLBACK"); если вы хотите (чтобы вы могли пройти / провалить много последовательных тестов).

Затем, когда вы запускаете тест, вы можете установить где-нибудь в файле тестового кода: $ MyDBClass-> SetCommitMode (ложь); или же $ MyDBClass-> RollBackNextOperation (истина); перед операцией, которую вы хотите провалить, и она просто провалится. Таким образом, код, который вы тестируете, не будет содержать тех проверок на неудачу / фиксацию, только класс DB будет содержать их.

И обычно ТОЛЬКО тестовый код (особенно если вы выполняете модульное тестирование) должен вызывать эти методы SetCommitMode и RollBackNextOperation, поэтому вы случайно не оставляете эти вызовы в рабочем коде.

Или вы можете передать некоторые сумасшедшие данные в ваш метод (если вы тестируете метод), например, отрицательные переменные для сохранения в полях UNSIGNED, и тогда ваша транзакция завершится неудачей на 100%, если ваш код не выполнит коммит после такого SQL ошибка (но не должна).

2 голосов
/ 17 августа 2010

Обычно я использую что-то вроде (я использую pdo для моего примера):

$db->beginTransaction();
try {
  $db->exec('INSERT/DELETE/UPDATE');
  $db->commit();
}
catch (PDOException $e) {
  $db->rollBack();
  // rethrow the error or
}

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

function my_exception_handler($exception) {
  if($exception instanceof PDOException) {
    // assuming you have a registry class
    Registry::get('database')->rollBack();
  }
}
...