Issue #937284 by chx, chrisdolby, deviantintegral, Berdir, tim.plunkett | hefox: DEADLOCK errors on MergeQuery INSERT due to InnoDB gap locking when condition in SELECT ... FOR UPDATE results in 0 rows.
parent
ddba1d97c7
commit
70e0b16715
|
@ -1,6 +1,8 @@
|
||||||
|
|
||||||
Drupal 7.25, xxxx-xx-xx (development version)
|
Drupal 7.25, xxxx-xx-xx (development version)
|
||||||
-----------------------
|
-----------------------
|
||||||
|
- Fixed a bug in the database API that caused frequent deadlock errors when
|
||||||
|
running merge queries on some servers.
|
||||||
- Performance improvement: Prevent block rehashing from writing blocks to the
|
- Performance improvement: Prevent block rehashing from writing blocks to the
|
||||||
database on every cache clear and cron run when the blocks have not changed.
|
database on every cache clear and cron run when the blocks have not changed.
|
||||||
This fix results in an extra 'saved' key which is added and set to TRUE for
|
This fix results in an extra 'saved' key which is added and set to TRUE for
|
||||||
|
|
|
@ -1606,55 +1606,43 @@ class MergeQuery extends Query implements QueryConditionInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute() {
|
public function execute() {
|
||||||
// Wrap multiple queries in a transaction, if the database supports it.
|
if (!count($this->condition)) {
|
||||||
$transaction = $this->connection->startTransaction();
|
throw new InvalidMergeQueryException(t('Invalid merge query: no conditions'));
|
||||||
try {
|
}
|
||||||
if (!count($this->condition)) {
|
$select = $this->connection->select($this->conditionTable)
|
||||||
throw new InvalidMergeQueryException(t('Invalid merge query: no conditions'));
|
->condition($this->condition);
|
||||||
|
$select->addExpression('1');
|
||||||
|
if (!$select->execute()->fetchField()) {
|
||||||
|
try {
|
||||||
|
$insert = $this->connection->insert($this->table)->fields($this->insertFields);
|
||||||
|
if ($this->defaultFields) {
|
||||||
|
$insert->useDefaults($this->defaultFields);
|
||||||
|
}
|
||||||
|
$insert->execute();
|
||||||
|
return self::STATUS_INSERT;
|
||||||
}
|
}
|
||||||
$select = $this->connection->select($this->conditionTable)
|
catch (Exception $e) {
|
||||||
->condition($this->condition)
|
// The insert query failed, maybe it's because a racing insert query
|
||||||
->forUpdate();
|
// beat us in inserting the same row. Retry the select query, if it
|
||||||
$select->addExpression('1');
|
// returns a row, ignore the error and continue with the update
|
||||||
if (!$select->execute()->fetchField()) {
|
// query below.
|
||||||
try {
|
if (!$select->execute()->fetchField()) {
|
||||||
$insert = $this->connection->insert($this->table)->fields($this->insertFields);
|
throw $e;
|
||||||
if ($this->defaultFields) {
|
|
||||||
$insert->useDefaults($this->defaultFields);
|
|
||||||
}
|
|
||||||
$insert->execute();
|
|
||||||
return MergeQuery::STATUS_INSERT;
|
|
||||||
}
|
}
|
||||||
catch (Exception $e) {
|
|
||||||
// The insert query failed, maybe it's because a racing insert query
|
|
||||||
// beat us in inserting the same row. Retry the select query, if it
|
|
||||||
// returns a row, ignore the error and continue with the update
|
|
||||||
// query below.
|
|
||||||
if (!$select->execute()->fetchField()) {
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($this->needsUpdate) {
|
|
||||||
$update = $this->connection->update($this->table)
|
|
||||||
->fields($this->updateFields)
|
|
||||||
->condition($this->condition);
|
|
||||||
if ($this->expressionFields) {
|
|
||||||
foreach ($this->expressionFields as $field => $data) {
|
|
||||||
$update->expression($field, $data['expression'], $data['arguments']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$update->execute();
|
|
||||||
return MergeQuery::STATUS_UPDATE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception $e) {
|
if ($this->needsUpdate) {
|
||||||
// Something really wrong happened here, bubble up the exception to the
|
$update = $this->connection->update($this->table)
|
||||||
// caller.
|
->fields($this->updateFields)
|
||||||
$transaction->rollback();
|
->condition($this->condition);
|
||||||
throw $e;
|
if ($this->expressionFields) {
|
||||||
}
|
foreach ($this->expressionFields as $field => $data) {
|
||||||
// Transaction commits here where $transaction looses scope.
|
$update->expression($field, $data['expression'], $data['arguments']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$update->execute();
|
||||||
|
return self::STATUS_UPDATE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue