mirror of
https://we.phorge.it/source/phorge.git
synced 2025-03-26 11:10:16 +01:00
Summary: Certain types of things we should be storing in edges (notably, Task X depends on Task Y) should always be acyclic. Allow `PhabricatorEdgeEditor` to enforce this, since we can't correctly enforce it outside of the editor without being vulnerable to races. Each edge type can be marked acyclic. If an edge type is acyclic, we perform additional steps when writing new edges of that type: - We acquire a global lock on the edge type before performing any reads or writes. This ensures we can't produce a cycle as a result of a race where two edits add edges which independently do not produce a cycle, but do produce a cycle when combined. - After performing writes but before committing transactions, we load the edge graph for each acyclic type and verify that it is, in fact, acyclic. If we detect cycles, we abort the edit. - When we're done, we release the edge type locks. This is a relatively high-complexity change, but gives us a simple way to flag an edge type as acyclic in a robust way. Test Plan: Ran unit tests. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T1162 Differential Revision: https://secure.phabricator.com/D2940
69 lines
2.2 KiB
PHP
69 lines
2.2 KiB
PHP
<?php
|
|
|
|
/*
|
|
* Copyright 2012 Facebook, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants {
|
|
|
|
const TABLE_NAME_EDGE = 'edge';
|
|
const TABLE_NAME_EDGEDATA = 'edgedata';
|
|
|
|
const TYPE_TASK_HAS_COMMIT = 1;
|
|
const TYPE_COMMIT_HAS_TASK = 2;
|
|
|
|
const TYPE_TEST_NO_CYCLE = 9000;
|
|
|
|
public static function getInverse($edge_type) {
|
|
static $map = array(
|
|
self::TYPE_TASK_HAS_COMMIT => self::TYPE_COMMIT_HAS_TASK,
|
|
self::TYPE_COMMIT_HAS_TASK => self::TYPE_TASK_HAS_COMMIT,
|
|
);
|
|
|
|
return idx($map, $edge_type);
|
|
}
|
|
|
|
public static function shouldPreventCycles($edge_type) {
|
|
static $map = array(
|
|
self::TYPE_TEST_NO_CYCLE => true,
|
|
);
|
|
return isset($map[$edge_type]);
|
|
}
|
|
|
|
public static function establishConnection($phid_type, $conn_type) {
|
|
static $class_map = array(
|
|
PhabricatorPHIDConstants::PHID_TYPE_TASK => 'ManiphestTask',
|
|
PhabricatorPHIDConstants::PHID_TYPE_CMIT => 'PhabricatorRepository',
|
|
PhabricatorPHIDConstants::PHID_TYPE_DREV => 'DifferentialRevision',
|
|
PhabricatorPHIDConstants::PHID_TYPE_FILE => 'PhabricatorFile',
|
|
PhabricatorPHIDConstants::PHID_TYPE_USER => 'PhabricatorUser',
|
|
PhabricatorPHIDConstants::PHID_TYPE_PROJ => 'PhabricatorProject',
|
|
PhabricatorPHIDConstants::PHID_TYPE_MLST =>
|
|
'PhabricatorMetaMTAMailingList',
|
|
PhabricatorPHIDConstants::PHID_TYPE_TOBJ => 'HarbormasterObject',
|
|
);
|
|
|
|
$class = idx($class_map, $phid_type);
|
|
|
|
if (!$class) {
|
|
throw new Exception(
|
|
"Edges are not available for objects of type '{$phid_type}'!");
|
|
}
|
|
|
|
return newv($class, array())->establishConnection($conn_type);
|
|
}
|
|
|
|
|
|
}
|