1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-09 16:32:39 +01:00

Guarantee the key_position key is created properly

Summary:
Ref T12987. I was focused on the RefCursor table and overlooked that we need some care on this key.

It's currently possible to run `bin/storage upgrade --no-adjust`, then start Phabricator, and end up with duplicate records in this table. If you try to run `bin/storage adjust` later, it will try to add the unique key but fail. This is unusual for normal installs (they usually do not use `--no-adjust`) but we do it in the cluster and I did this exact thing on `secure`.

Normally, to avoid this, when a new table with a unique key is introduced, we also add a migration to explicitly add that key.

This is mostly harmless in this case. Fix this mistake (force the table to contain only unique rows; add the key) and try using `LOCK TABLES` to make this atomic. If this doesn't cause problems we can use this in similar situations in the future.

The "alter table may unlock things" warning comes from here:

https://dev.mysql.com/doc/refman/5.7/en/lock-tables.html

It seems like it's fine to issue `UNLOCK TABLES` even if you don't have any locks, so I think this script should always do the right thing now, regardless of ALTER TABLE unlocking or not unlocking tables.

Test Plan: Ran `bin/storage upgrade -f`, saw table end up in the right state. I'll also check this on `secure`, where the starting state is a little messier.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T12987

Differential Revision: https://secure.phabricator.com/D18623
This commit is contained in:
epriestley 2017-09-18 11:47:08 -07:00
parent 94ab0c9afb
commit 3ad727ba78

View file

@ -0,0 +1,52 @@
<?php
$table = new PhabricatorRepositoryRefPosition();
$conn = $table->establishConnection('w');
$key_name = 'key_position';
try {
queryfx(
$conn,
'ALTER TABLE %T DROP KEY %T',
$table->getTableName(),
$key_name);
} catch (AphrontQueryException $ex) {
// This key may or may not exist, depending on exactly when the install
// ran previous migrations and adjustments. We're just dropping it if it
// does exist.
// We're doing this first (outside of the lock) because the MySQL
// documentation says "if you ALTER TABLE a locked table, it may become
// unlocked".
}
queryfx(
$conn,
'LOCK TABLES %T WRITE',
$table->getTableName());
$seen = array();
foreach (new LiskMigrationIterator($table) as $position) {
$cursor_id = $position->getCursorID();
$hash = $position->getCommitIdentifier();
// If this is the first copy of this row we've seen, mark it as seen and
// move on.
if (empty($seen[$cursor_id][$hash])) {
$seen[$cursor_id][$hash] = true;
continue;
}
// Otherwise, get rid of this row as it duplicates a row we saw previously.
$position->delete();
}
queryfx(
$conn,
'ALTER TABLE %T ADD UNIQUE KEY %T (cursorID, commitIdentifier)',
$table->getTableName(),
$key_name);
queryfx(
$conn,
'UNLOCK TABLES');