diff --git a/bin/garbage b/bin/garbage new file mode 120000 index 0000000000..417438c09c --- /dev/null +++ b/bin/garbage @@ -0,0 +1 @@ +../scripts/setup/manage_garbage.php \ No newline at end of file diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 7c927cd290..cb661a9bd9 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -151,7 +151,7 @@ return array( 'rsrc/css/phui/phui-two-column-view.css' => '39ecafb1', 'rsrc/css/phui/phui-workboard-view.css' => '6704d68d', 'rsrc/css/phui/phui-workpanel-view.css' => 'adec7699', - 'rsrc/css/sprite-login.css' => '1ebb9bf9', + 'rsrc/css/sprite-login.css' => '60e8560e', 'rsrc/css/sprite-main-header.css' => 'f07bbb87', 'rsrc/css/sprite-menu.css' => '9dd65b92', 'rsrc/css/sprite-projects.css' => 'e5ad842a', @@ -310,8 +310,8 @@ return array( 'rsrc/image/people/washington.png' => '40dd301c', 'rsrc/image/phrequent_active.png' => 'a466a8ed', 'rsrc/image/phrequent_inactive.png' => 'bfc15a69', - 'rsrc/image/sprite-login-X2.png' => 'a4bf0a98', - 'rsrc/image/sprite-login.png' => '5f4d0069', + 'rsrc/image/sprite-login-X2.png' => 'e3991e37', + 'rsrc/image/sprite-login.png' => '03d5af29', 'rsrc/image/sprite-main-header.png' => '3673af44', 'rsrc/image/sprite-menu-X2.png' => 'cfd8fca5', 'rsrc/image/sprite-menu.png' => 'd7a99faa', @@ -817,7 +817,7 @@ return array( 'releeph-request-differential-create-dialog' => '8d8b92cd', 'releeph-request-typeahead-css' => '667a48ae', 'setup-issue-css' => 'db7e9c40', - 'sprite-login-css' => '1ebb9bf9', + 'sprite-login-css' => '60e8560e', 'sprite-main-header-css' => 'f07bbb87', 'sprite-menu-css' => '9dd65b92', 'sprite-projects-css' => 'e5ad842a', diff --git a/resources/sprite/login_1x/Asana.png b/resources/sprite/login_1x/Asana.png index 41292fc322..9b2fc85239 100644 Binary files a/resources/sprite/login_1x/Asana.png and b/resources/sprite/login_1x/Asana.png differ diff --git a/resources/sprite/login_2x/Asana.png b/resources/sprite/login_2x/Asana.png index d84c3dffaf..6000ddc1bb 100644 Binary files a/resources/sprite/login_2x/Asana.png and b/resources/sprite/login_2x/Asana.png differ diff --git a/resources/sprite/manifest/login.json b/resources/sprite/manifest/login.json index 3fc9c318b4..a75d2fa69e 100644 --- a/resources/sprite/manifest/login.json +++ b/resources/sprite/manifest/login.json @@ -9,7 +9,7 @@ "login-Asana": { "name": "login-Asana", "rule": ".login-Asana", - "hash": "f8d322843355da1abce614983044c0f8" + "hash": "cfc35b62afb103c5d484a715071e3cc3" }, "login-Bitbucket": { "name": "login-Bitbucket", @@ -131,6 +131,6 @@ 1, 2 ], - "header": "\/**\n * @provides sprite-login-css\n * @generated\n *\/\n\n.sprite-login {\n background-image: url(\/rsrc\/image\/sprite-login.png);\n background-repeat: no-repeat;\n}\n\n@media\nonly screen and (min-device-pixel-ratio: 1.5),\nonly screen and (-webkit-min-device-pixel-ratio: 1.5),\nonly screen and (min-resolution: 1.5dppx) {\n .sprite-login {\n background-image: url(\/rsrc\/image\/sprite-login-X2.png);\n background-size: {X}px {Y}px;\n }\n}\n", + "header": "/**\n * @provides sprite-login-css\n * @generated\n */\n\n.sprite-login {\n background-image: url(/rsrc/image/sprite-login.png);\n background-repeat: no-repeat;\n}\n\n@media\nonly screen and (min-device-pixel-ratio: 1.5),\nonly screen and (-webkit-min-device-pixel-ratio: 1.5),\nonly screen and (min-resolution: 1.5dppx) {\n .sprite-login {\n background-image: url(/rsrc/image/sprite-login-X2.png);\n background-size: {X}px {Y}px;\n }\n}\n", "type": "standard" } diff --git a/resources/sql/autopatches/20150928.drydock.rexpire.1.sql b/resources/sql/autopatches/20150928.drydock.rexpire.1.sql new file mode 100644 index 0000000000..9321b9c0e1 --- /dev/null +++ b/resources/sql/autopatches/20150928.drydock.rexpire.1.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_drydock.drydock_resource + ADD until INT UNSIGNED; diff --git a/resources/sql/autopatches/20150930.drydock.log.1.sql b/resources/sql/autopatches/20150930.drydock.log.1.sql new file mode 100644 index 0000000000..e84859b718 --- /dev/null +++ b/resources/sql/autopatches/20150930.drydock.log.1.sql @@ -0,0 +1,25 @@ +TRUNCATE {$NAMESPACE}_drydock.drydock_log; + +ALTER TABLE {$NAMESPACE}_drydock.drydock_log + DROP resourceID; + +ALTER TABLE {$NAMESPACE}_drydock.drydock_log + DROP leaseID; + +ALTER TABLE {$NAMESPACE}_drydock.drydock_log + DROP message; + +ALTER TABLE {$NAMESPACE}_drydock.drydock_log + ADD blueprintPHID VARBINARY(64); + +ALTER TABLE {$NAMESPACE}_drydock.drydock_log + ADD resourcePHID VARBINARY(64); + +ALTER TABLE {$NAMESPACE}_drydock.drydock_log + ADD leasePHID VARBINARY(64); + +ALTER TABLE {$NAMESPACE}_drydock.drydock_log + ADD type VARCHAR(64) NOT NULL COLLATE {$COLLATE_TEXT}; + +ALTER TABLE {$NAMESPACE}_drydock.drydock_log + ADD data LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20151001.drydock.rname.1.sql b/resources/sql/autopatches/20151001.drydock.rname.1.sql new file mode 100644 index 0000000000..3dbd66c579 --- /dev/null +++ b/resources/sql/autopatches/20151001.drydock.rname.1.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_drydock.drydock_resource + DROP name; diff --git a/resources/sql/autopatches/20151002.dashboard.status.1.sql b/resources/sql/autopatches/20151002.dashboard.status.1.sql new file mode 100644 index 0000000000..09b19ef1d8 --- /dev/null +++ b/resources/sql/autopatches/20151002.dashboard.status.1.sql @@ -0,0 +1,2 @@ +UPDATE {$NAMESPACE}_dashboard.dashboard + SET status = 'active' WHERE status = 'open'; diff --git a/resources/sql/autopatches/20151002.harbormaster.bparam.1.sql b/resources/sql/autopatches/20151002.harbormaster.bparam.1.sql new file mode 100644 index 0000000000..0deb71c503 --- /dev/null +++ b/resources/sql/autopatches/20151002.harbormaster.bparam.1.sql @@ -0,0 +1,5 @@ +ALTER TABLE {$NAMESPACE}_harbormaster.harbormaster_build + ADD buildParameters LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL; + +UPDATE {$NAMESPACE}_harbormaster.harbormaster_build + SET buildParameters = '{}' WHERE buildParameters = ''; diff --git a/resources/sql/quickstart.sql b/resources/sql/quickstart.sql index cc07bb1e61..fdc1e673c7 100644 --- a/resources/sql/quickstart.sql +++ b/resources/sql/quickstart.sql @@ -64,13 +64,79 @@ CREATE TABLE `calendar_event` ( `userPHID` varbinary(64) NOT NULL, `dateFrom` int(10) unsigned NOT NULL, `dateTo` int(10) unsigned NOT NULL, - `status` int(10) unsigned NOT NULL, `dateCreated` int(10) unsigned NOT NULL, `dateModified` int(10) unsigned NOT NULL, `description` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `isCancelled` tinyint(1) NOT NULL, + `name` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `viewPolicy` varbinary(64) NOT NULL, + `editPolicy` varbinary(64) NOT NULL, + `mailKey` binary(20) NOT NULL, + `isAllDay` tinyint(1) NOT NULL, + `icon` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, + `isRecurring` tinyint(1) NOT NULL, + `recurrenceFrequency` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `recurrenceEndDate` int(10) unsigned DEFAULT NULL, + `instanceOfEventPHID` varbinary(64) DEFAULT NULL, + `sequenceIndex` int(10) unsigned DEFAULT NULL, + `spacePHID` varbinary(64) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `key_phid` (`phid`), - KEY `userPHID_dateFrom` (`userPHID`,`dateTo`) + UNIQUE KEY `key_instance` (`instanceOfEventPHID`,`sequenceIndex`), + KEY `userPHID_dateFrom` (`userPHID`,`dateTo`), + KEY `key_space` (`spacePHID`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `calendar_eventinvitee` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `eventPHID` varbinary(64) NOT NULL, + `inviteePHID` varbinary(64) NOT NULL, + `inviterPHID` varbinary(64) NOT NULL, + `status` varchar(64) COLLATE {$COLLATE_TEXT} NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_event` (`eventPHID`,`inviteePHID`), + KEY `key_invitee` (`inviteePHID`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `calendar_eventtransaction` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `phid` varbinary(64) NOT NULL, + `authorPHID` varbinary(64) NOT NULL, + `objectPHID` varbinary(64) NOT NULL, + `viewPolicy` varbinary(64) NOT NULL, + `editPolicy` varbinary(64) NOT NULL, + `commentPHID` varbinary(64) DEFAULT NULL, + `commentVersion` int(10) unsigned NOT NULL, + `transactionType` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, + `oldValue` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `newValue` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `contentSource` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `metadata` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_phid` (`phid`), + KEY `key_object` (`objectPHID`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `calendar_eventtransaction_comment` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `phid` varbinary(64) NOT NULL, + `transactionPHID` varbinary(64) DEFAULT NULL, + `authorPHID` varbinary(64) NOT NULL, + `viewPolicy` varbinary(64) NOT NULL, + `editPolicy` varbinary(64) NOT NULL, + `commentVersion` int(10) unsigned NOT NULL, + `content` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `contentSource` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `isDeleted` tinyint(1) NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_phid` (`phid`), + UNIQUE KEY `key_version` (`transactionPHID`,`commentVersion`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; CREATE TABLE `calendar_holiday` ( @@ -81,6 +147,24 @@ CREATE TABLE `calendar_holiday` ( UNIQUE KEY `day` (`day`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; +CREATE TABLE `edge` ( + `src` varbinary(64) NOT NULL, + `type` int(10) unsigned NOT NULL, + `dst` varbinary(64) NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `seq` int(10) unsigned NOT NULL, + `dataID` int(10) unsigned DEFAULT NULL, + PRIMARY KEY (`src`,`type`,`dst`), + UNIQUE KEY `key_dst` (`dst`,`type`,`src`), + KEY `src` (`src`,`type`,`dateCreated`,`seq`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `edgedata` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `data` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$NAMESPACE}_chatlog` /*!40100 DEFAULT CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} */; USE `{$NAMESPACE}_chatlog`; @@ -179,8 +263,70 @@ CREATE TABLE `countdown` ( `dateCreated` int(10) unsigned NOT NULL, `dateModified` int(10) unsigned NOT NULL, `viewPolicy` varbinary(64) NOT NULL, + `spacePHID` varbinary(64) DEFAULT NULL, + `editPolicy` varbinary(64) NOT NULL, + `description` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `mailKey` binary(20) NOT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `key_phid` (`phid`) + UNIQUE KEY `key_phid` (`phid`), + KEY `key_space` (`spacePHID`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `countdown_transaction` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `phid` varbinary(64) NOT NULL, + `authorPHID` varbinary(64) NOT NULL, + `objectPHID` varbinary(64) NOT NULL, + `viewPolicy` varbinary(64) NOT NULL, + `editPolicy` varbinary(64) NOT NULL, + `commentPHID` varbinary(64) DEFAULT NULL, + `commentVersion` int(10) unsigned NOT NULL, + `transactionType` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, + `oldValue` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `newValue` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `contentSource` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `metadata` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_phid` (`phid`), + KEY `key_object` (`objectPHID`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `countdown_transaction_comment` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `phid` varbinary(64) NOT NULL, + `transactionPHID` varbinary(64) DEFAULT NULL, + `authorPHID` varbinary(64) NOT NULL, + `viewPolicy` varbinary(64) NOT NULL, + `editPolicy` varbinary(64) NOT NULL, + `commentVersion` int(10) unsigned NOT NULL, + `content` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `contentSource` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `isDeleted` tinyint(1) NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_phid` (`phid`), + UNIQUE KEY `key_version` (`transactionPHID`,`commentVersion`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `edge` ( + `src` varbinary(64) NOT NULL, + `type` int(10) unsigned NOT NULL, + `dst` varbinary(64) NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `seq` int(10) unsigned NOT NULL, + `dataID` int(10) unsigned DEFAULT NULL, + PRIMARY KEY (`src`,`type`,`dst`), + UNIQUE KEY `key_dst` (`dst`,`type`,`src`), + KEY `src` (`src`,`type`,`dateCreated`,`seq`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `edgedata` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `data` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$NAMESPACE}_daemon` /*!40100 DEFAULT CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} */; @@ -309,7 +455,6 @@ CREATE TABLE `differential_diff` ( `lineCount` int(10) unsigned NOT NULL, `branch` varchar(255) COLLATE {$COLLATE_TEXT} DEFAULT NULL, `bookmark` varchar(255) COLLATE {$COLLATE_TEXT} DEFAULT NULL, - `arcanistProjectPHID` varbinary(64) DEFAULT NULL, `creationMethod` varchar(255) COLLATE {$COLLATE_TEXT} DEFAULT NULL, `dateCreated` int(10) unsigned NOT NULL, `dateModified` int(10) unsigned NOT NULL, @@ -364,6 +509,15 @@ CREATE TABLE `differential_draft` ( UNIQUE KEY `key_unique` (`objectPHID`,`authorPHID`,`draftKey`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; +CREATE TABLE `differential_hiddencomment` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `userPHID` varbinary(64) NOT NULL, + `commentID` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_user` (`userPHID`,`commentID`), + KEY `key_comment` (`commentID`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + CREATE TABLE `differential_hunk` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `changesetID` int(10) unsigned NOT NULL, @@ -412,14 +566,14 @@ CREATE TABLE `differential_revision` ( `attached` longtext COLLATE {$COLLATE_TEXT} NOT NULL, `mailKey` binary(40) NOT NULL, `branchName` varchar(255) COLLATE {$COLLATE_TEXT} DEFAULT NULL, - `arcanistProjectPHID` varbinary(64) DEFAULT NULL, `viewPolicy` varbinary(64) NOT NULL, `editPolicy` varbinary(64) NOT NULL, `repositoryPHID` varbinary(64) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `phid` (`phid`), KEY `authorPHID` (`authorPHID`,`status`), - KEY `repositoryPHID` (`repositoryPHID`) + KEY `repositoryPHID` (`repositoryPHID`), + KEY `key_status` (`status`,`phid`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; CREATE TABLE `differential_revisionhash` ( @@ -522,12 +676,13 @@ CREATE TABLE `drydock_blueprint` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `phid` varbinary(64) NOT NULL, `className` varchar(255) COLLATE {$COLLATE_TEXT} NOT NULL, - `blueprintName` varchar(255) COLLATE {$COLLATE_TEXT} NOT NULL, + `blueprintName` varchar(255) CHARACTER SET {$CHARSET_SORT} COLLATE {$COLLATE_SORT} NOT NULL, `viewPolicy` varbinary(64) NOT NULL, `editPolicy` varbinary(64) NOT NULL, `details` longtext COLLATE {$COLLATE_TEXT} NOT NULL, `dateCreated` int(10) unsigned NOT NULL, `dateModified` int(10) unsigned NOT NULL, + `isDisabled` tinyint(1) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `key_phid` (`phid`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; @@ -553,20 +708,32 @@ CREATE TABLE `drydock_blueprinttransaction` ( KEY `key_object` (`objectPHID`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; +CREATE TABLE `drydock_command` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `authorPHID` varbinary(64) NOT NULL, + `targetPHID` varbinary(64) NOT NULL, + `command` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, + `isConsumed` tinyint(1) NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + KEY `key_target` (`targetPHID`,`isConsumed`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + CREATE TABLE `drydock_lease` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `phid` varbinary(64) NOT NULL, - `resourceID` int(10) unsigned DEFAULT NULL, - `status` int(10) unsigned NOT NULL, + `status` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, `until` int(10) unsigned DEFAULT NULL, `ownerPHID` varbinary(64) DEFAULT NULL, `attributes` longtext COLLATE {$COLLATE_TEXT} NOT NULL, `dateCreated` int(10) unsigned NOT NULL, `dateModified` int(10) unsigned NOT NULL, - `taskID` int(10) unsigned DEFAULT NULL, `resourceType` varchar(128) COLLATE {$COLLATE_TEXT} NOT NULL, + `resourcePHID` varbinary(64) DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `phid` (`phid`) + UNIQUE KEY `key_phid` (`phid`), + KEY `key_resource` (`resourcePHID`,`status`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; CREATE TABLE `drydock_log` ( @@ -586,15 +753,28 @@ CREATE TABLE `drydock_resource` ( `phid` varbinary(64) NOT NULL, `name` varchar(255) COLLATE {$COLLATE_TEXT} NOT NULL, `ownerPHID` varbinary(64) DEFAULT NULL, - `status` int(10) unsigned NOT NULL, + `status` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, `type` varchar(64) COLLATE {$COLLATE_TEXT} NOT NULL, `attributes` longtext COLLATE {$COLLATE_TEXT} NOT NULL, `capabilities` longtext COLLATE {$COLLATE_TEXT} NOT NULL, `dateCreated` int(10) unsigned NOT NULL, `dateModified` int(10) unsigned NOT NULL, `blueprintPHID` varbinary(64) NOT NULL, + `until` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `phid` (`phid`) + UNIQUE KEY `key_phid` (`phid`), + KEY `key_type` (`type`,`status`), + KEY `key_blueprint` (`blueprintPHID`,`status`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `drydock_slotlock` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `ownerPHID` varbinary(64) NOT NULL, + `lockIndex` binary(12) NOT NULL, + `lockKey` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_lock` (`lockIndex`), + KEY `key_owner` (`ownerPHID`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$NAMESPACE}_feed` /*!40100 DEFAULT CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} */; @@ -621,7 +801,9 @@ CREATE TABLE `feed_storynotification` ( `chronologicalKey` bigint(20) unsigned NOT NULL, `hasViewed` tinyint(1) NOT NULL, UNIQUE KEY `userPHID` (`userPHID`,`chronologicalKey`), - KEY `userPHID_2` (`userPHID`,`hasViewed`,`primaryObjectPHID`) + KEY `userPHID_2` (`userPHID`,`hasViewed`,`primaryObjectPHID`), + KEY `key_object` (`primaryObjectPHID`), + KEY `key_chronological` (`chronologicalKey`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; CREATE TABLE `feed_storyreference` ( @@ -672,12 +854,25 @@ CREATE TABLE `file` ( `isExplicitUpload` tinyint(1) DEFAULT '1', `mailKey` binary(20) NOT NULL, `viewPolicy` varbinary(64) NOT NULL, + `isPartial` tinyint(1) NOT NULL DEFAULT '0', PRIMARY KEY (`id`), UNIQUE KEY `phid` (`phid`), KEY `authorPHID` (`authorPHID`), KEY `contentHash` (`contentHash`), KEY `key_ttl` (`ttl`), - KEY `key_dateCreated` (`dateCreated`) + KEY `key_dateCreated` (`dateCreated`), + KEY `key_partial` (`authorPHID`,`isPartial`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `file_chunk` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `chunkHandle` binary(12) NOT NULL, + `byteStart` bigint(20) unsigned NOT NULL, + `byteEnd` bigint(20) unsigned NOT NULL, + `dataFilePHID` varbinary(64) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `key_file` (`chunkHandle`,`byteStart`,`byteEnd`), + KEY `key_data` (`dataFilePHID`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; CREATE TABLE `file_imagemacro` ( @@ -848,8 +1043,10 @@ CREATE TABLE `harbormaster_build` ( `dateCreated` int(10) unsigned NOT NULL, `dateModified` int(10) unsigned NOT NULL, `buildGeneration` int(10) unsigned NOT NULL DEFAULT '0', + `planAutoKey` varchar(32) COLLATE {$COLLATE_TEXT} DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `key_phid` (`phid`), + UNIQUE KEY `key_planautokey` (`buildablePHID`,`planAutoKey`), KEY `key_buildable` (`buildablePHID`), KEY `key_plan` (`buildPlanPHID`), KEY `key_status` (`buildStatus`) @@ -894,6 +1091,7 @@ CREATE TABLE `harbormaster_buildabletransaction` ( CREATE TABLE `harbormaster_buildartifact` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `phid` varbinary(64) NOT NULL, `artifactType` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, `artifactIndex` binary(12) NOT NULL, `artifactKey` varchar(255) COLLATE {$COLLATE_TEXT} NOT NULL, @@ -903,6 +1101,7 @@ CREATE TABLE `harbormaster_buildartifact` ( `buildTargetPHID` varbinary(64) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `key_artifact` (`artifactType`,`artifactIndex`), + UNIQUE KEY `key_phid` (`phid`), KEY `key_garbagecollect` (`artifactType`,`dateCreated`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; @@ -917,6 +1116,22 @@ CREATE TABLE `harbormaster_buildcommand` ( KEY `key_target` (`targetPHID`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; +CREATE TABLE `harbormaster_buildlintmessage` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `buildTargetPHID` varbinary(64) NOT NULL, + `path` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `line` int(10) unsigned DEFAULT NULL, + `characterOffset` int(10) unsigned DEFAULT NULL, + `code` varchar(128) COLLATE {$COLLATE_TEXT} NOT NULL, + `severity` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, + `name` varchar(255) COLLATE {$COLLATE_TEXT} NOT NULL, + `properties` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + KEY `key_target` (`buildTargetPHID`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + CREATE TABLE `harbormaster_buildlog` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `phid` varbinary(64) NOT NULL, @@ -957,13 +1172,16 @@ CREATE TABLE `harbormaster_buildmessage` ( CREATE TABLE `harbormaster_buildplan` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `phid` varbinary(64) NOT NULL, - `name` varchar(255) COLLATE {$COLLATE_TEXT} NOT NULL, + `name` varchar(128) CHARACTER SET {$CHARSET_SORT} COLLATE {$COLLATE_SORT} NOT NULL, `planStatus` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, `dateCreated` int(10) unsigned NOT NULL, `dateModified` int(10) unsigned NOT NULL, + `planAutoKey` varchar(32) COLLATE {$COLLATE_TEXT} DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `key_phid` (`phid`), - KEY `key_status` (`planStatus`) + UNIQUE KEY `key_planautokey` (`planAutoKey`), + KEY `key_status` (`planStatus`), + KEY `key_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; CREATE TABLE `harbormaster_buildplantransaction` ( @@ -998,8 +1216,10 @@ CREATE TABLE `harbormaster_buildstep` ( `sequence` int(10) unsigned NOT NULL, `name` varchar(255) COLLATE {$COLLATE_TEXT} DEFAULT NULL, `description` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `stepAutoKey` varchar(32) COLLATE {$COLLATE_TEXT} DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `key_phid` (`phid`), + UNIQUE KEY `key_stepautokey` (`buildPlanPHID`,`stepAutoKey`), KEY `key_plan` (`buildPlanPHID`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; @@ -1065,6 +1285,21 @@ CREATE TABLE `harbormaster_buildtransaction` ( KEY `key_object` (`objectPHID`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; +CREATE TABLE `harbormaster_buildunitmessage` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `buildTargetPHID` varbinary(64) NOT NULL, + `engine` varchar(255) COLLATE {$COLLATE_TEXT} NOT NULL, + `namespace` varchar(255) COLLATE {$COLLATE_TEXT} NOT NULL, + `name` varchar(255) COLLATE {$COLLATE_TEXT} NOT NULL, + `result` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, + `duration` double DEFAULT NULL, + `properties` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + KEY `key_target` (`buildTargetPHID`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + CREATE TABLE `harbormaster_object` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `phid` varbinary(64) NOT NULL, @@ -1143,18 +1378,6 @@ CREATE TABLE `herald_ruleapplied` ( KEY `phid` (`phid`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; -CREATE TABLE `herald_ruleedit` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `ruleID` int(10) unsigned NOT NULL, - `editorPHID` varbinary(64) NOT NULL, - `dateCreated` int(10) unsigned NOT NULL, - `dateModified` int(10) unsigned NOT NULL, - `ruleName` varchar(255) COLLATE {$COLLATE_TEXT} NOT NULL, - `action` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, - PRIMARY KEY (`id`), - KEY `ruleID` (`ruleID`,`dateCreated`) -) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; - CREATE TABLE `herald_ruletransaction` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `phid` varbinary(64) NOT NULL, @@ -1299,6 +1522,7 @@ CREATE TABLE `maniphest_task` ( `subpriority` double NOT NULL, `viewPolicy` varbinary(64) NOT NULL, `editPolicy` varbinary(64) NOT NULL, + `spacePHID` varbinary(64) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `phid` (`phid`), KEY `priority` (`priority`,`status`), @@ -1309,7 +1533,8 @@ CREATE TABLE `maniphest_task` ( KEY `priority_2` (`priority`,`subpriority`), KEY `key_dateCreated` (`dateCreated`), KEY `key_dateModified` (`dateModified`), - KEY `key_title` (`title`(64)) + KEY `key_title` (`title`(64)), + KEY `key_space` (`spacePHID`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; CREATE TABLE `maniphest_transaction` ( @@ -1361,7 +1586,7 @@ CREATE TABLE `patch_status` ( PRIMARY KEY (`patch`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; -INSERT INTO `patch_status` VALUES ('phabricator:000.project.sql',1425312505),('phabricator:0000.legacy.sql',1425312505),('phabricator:001.maniphest_projects.sql',1425312505),('phabricator:002.oauth.sql',1425312505),('phabricator:003.more_oauth.sql',1425312505),('phabricator:004.daemonrepos.sql',1425312505),('phabricator:005.workers.sql',1425312505),('phabricator:006.repository.sql',1425312505),('phabricator:007.daemonlog.sql',1425312505),('phabricator:008.repoopt.sql',1425312505),('phabricator:009.repo_summary.sql',1425312505),('phabricator:010.herald.sql',1425312505),('phabricator:011.badcommit.sql',1425312505),('phabricator:012.dropphidtype.sql',1425312505),('phabricator:013.commitdetail.sql',1425312505),('phabricator:014.shortcuts.sql',1425312505),('phabricator:015.preferences.sql',1425312505),('phabricator:016.userrealnameindex.sql',1425312505),('phabricator:017.sessionkeys.sql',1425312505),('phabricator:018.owners.sql',1425312505),('phabricator:019.arcprojects.sql',1425312505),('phabricator:020.pathcapital.sql',1425312505),('phabricator:021.xhpastview.sql',1425312505),('phabricator:022.differentialcommit.sql',1425312505),('phabricator:023.dxkeys.sql',1425312505),('phabricator:024.mlistkeys.sql',1425312505),('phabricator:025.commentopt.sql',1425312505),('phabricator:026.diffpropkey.sql',1425312505),('phabricator:027.metamtakeys.sql',1425312505),('phabricator:028.systemagent.sql',1425312505),('phabricator:029.cursors.sql',1425312505),('phabricator:030.imagemacro.sql',1425312505),('phabricator:031.workerrace.sql',1425312505),('phabricator:032.viewtime.sql',1425312505),('phabricator:033.privtest.sql',1425312505),('phabricator:034.savedheader.sql',1425312505),('phabricator:035.proxyimage.sql',1425312505),('phabricator:036.mailkey.sql',1425312505),('phabricator:037.setuptest.sql',1425312505),('phabricator:038.admin.sql',1425312505),('phabricator:039.userlog.sql',1425312505),('phabricator:040.transform.sql',1425312505),('phabricator:041.heraldrepetition.sql',1425312506),('phabricator:042.commentmetadata.sql',1425312506),('phabricator:043.pastebin.sql',1425312506),('phabricator:044.countdown.sql',1425312506),('phabricator:045.timezone.sql',1425312506),('phabricator:046.conduittoken.sql',1425312506),('phabricator:047.projectstatus.sql',1425312506),('phabricator:048.relationshipkeys.sql',1425312506),('phabricator:049.projectowner.sql',1425312506),('phabricator:050.taskdenormal.sql',1425312506),('phabricator:051.projectfilter.sql',1425312506),('phabricator:052.pastelanguage.sql',1425312506),('phabricator:053.feed.sql',1425312506),('phabricator:054.subscribers.sql',1425312506),('phabricator:055.add_author_to_files.sql',1425312506),('phabricator:056.slowvote.sql',1425312506),('phabricator:057.parsecache.sql',1425312506),('phabricator:058.missingkeys.sql',1425312506),('phabricator:059.engines.php',1425312506),('phabricator:060.phriction.sql',1425312506),('phabricator:061.phrictioncontent.sql',1425312506),('phabricator:062.phrictionmenu.sql',1425312506),('phabricator:063.pasteforks.sql',1425312506),('phabricator:064.subprojects.sql',1425312506),('phabricator:065.sshkeys.sql',1425312506),('phabricator:066.phrictioncontent.sql',1425312506),('phabricator:067.preferences.sql',1425312506),('phabricator:068.maniphestauxiliarystorage.sql',1425312506),('phabricator:069.heraldxscript.sql',1425312506),('phabricator:070.differentialaux.sql',1425312506),('phabricator:071.contentsource.sql',1425312506),('phabricator:072.blamerevert.sql',1425312506),('phabricator:073.reposymbols.sql',1425312506),('phabricator:074.affectedpath.sql',1425312506),('phabricator:075.revisionhash.sql',1425312506),('phabricator:076.indexedlanguages.sql',1425312506),('phabricator:077.originalemail.sql',1425312506),('phabricator:078.nametoken.sql',1425312506),('phabricator:079.nametokenindex.php',1425312506),('phabricator:080.filekeys.sql',1425312506),('phabricator:081.filekeys.php',1425312506),('phabricator:082.xactionkey.sql',1425312506),('phabricator:083.dxviewtime.sql',1425312506),('phabricator:084.pasteauthorkey.sql',1425312506),('phabricator:085.packagecommitrelationship.sql',1425312506),('phabricator:086.formeraffil.sql',1425312506),('phabricator:087.phrictiondelete.sql',1425312506),('phabricator:088.audit.sql',1425312506),('phabricator:089.projectwiki.sql',1425312506),('phabricator:090.forceuniqueprojectnames.php',1425312506),('phabricator:091.uniqueslugkey.sql',1425312506),('phabricator:092.dropgithubnotification.sql',1425312506),('phabricator:093.gitremotes.php',1425312506),('phabricator:094.phrictioncolumn.sql',1425312506),('phabricator:095.directory.sql',1425312506),('phabricator:096.filename.sql',1425312506),('phabricator:097.heraldruletypes.sql',1425312506),('phabricator:098.heraldruletypemigration.php',1425312506),('phabricator:099.drydock.sql',1425312506),('phabricator:100.projectxaction.sql',1425312506),('phabricator:101.heraldruleapplied.sql',1425312506),('phabricator:102.heraldcleanup.php',1425312506),('phabricator:103.heraldedithistory.sql',1425312507),('phabricator:104.searchkey.sql',1425312507),('phabricator:105.mimetype.sql',1425312507),('phabricator:106.chatlog.sql',1425312507),('phabricator:107.oauthserver.sql',1425312507),('phabricator:108.oauthscope.sql',1425312507),('phabricator:109.oauthclientphidkey.sql',1425312507),('phabricator:110.commitaudit.sql',1425312507),('phabricator:111.commitauditmigration.php',1425312507),('phabricator:112.oauthaccesscoderedirecturi.sql',1425312507),('phabricator:113.lastreviewer.sql',1425312507),('phabricator:114.auditrequest.sql',1425312507),('phabricator:115.prepareutf8.sql',1425312507),('phabricator:116.utf8-backup-first-expect-wait.sql',1425312508),('phabricator:117.repositorydescription.php',1425312508),('phabricator:118.auditinline.sql',1425312508),('phabricator:119.filehash.sql',1425312508),('phabricator:120.noop.sql',1425312508),('phabricator:121.drydocklog.sql',1425312508),('phabricator:122.flag.sql',1425312508),('phabricator:123.heraldrulelog.sql',1425312508),('phabricator:124.subpriority.sql',1425312508),('phabricator:125.ipv6.sql',1425312508),('phabricator:126.edges.sql',1425312509),('phabricator:127.userkeybody.sql',1425312509),('phabricator:128.phabricatorcom.sql',1425312509),('phabricator:129.savedquery.sql',1425312509),('phabricator:130.denormalrevisionquery.sql',1425312509),('phabricator:131.migraterevisionquery.php',1425312509),('phabricator:132.phame.sql',1425312509),('phabricator:133.imagemacro.sql',1425312509),('phabricator:134.emptysearch.sql',1425312509),('phabricator:135.datecommitted.sql',1425312509),('phabricator:136.sex.sql',1425312509),('phabricator:137.auditmetadata.sql',1425312509),('phabricator:138.notification.sql',1425312509),('phabricator:20121209.pholioxactions.sql',1425312509),('phabricator:20121209.xmacroadd.sql',1425312510),('phabricator:20121209.xmacromigrate.php',1425312510),('phabricator:20121209.xmacromigratekey.sql',1425312510),('phabricator:20121220.generalcache.sql',1425312510),('phabricator:20121226.config.sql',1425312510),('phabricator:20130101.confxaction.sql',1425312510),('phabricator:20130102.metamtareceivedmailmessageidhash.sql',1425312510),('phabricator:20130103.filemetadata.sql',1425312510),('phabricator:20130111.conpherence.sql',1425312510),('phabricator:20130127.altheraldtranscript.sql',1425312510),('phabricator:20130131.conpherencepics.sql',1425312510),('phabricator:20130201.revisionunsubscribed.php',1425312510),('phabricator:20130201.revisionunsubscribed.sql',1425312510),('phabricator:20130214.chatlogchannel.sql',1425312510),('phabricator:20130214.chatlogchannelid.sql',1425312510),('phabricator:20130214.token.sql',1425312510),('phabricator:20130215.phabricatorfileaddttl.sql',1425312510),('phabricator:20130217.cachettl.sql',1425312510),('phabricator:20130218.longdaemon.sql',1425312510),('phabricator:20130218.updatechannelid.php',1425312510),('phabricator:20130219.commitsummary.sql',1425312510),('phabricator:20130219.commitsummarymig.php',1425312510),('phabricator:20130222.dropchannel.sql',1425312510),('phabricator:20130226.commitkey.sql',1425312510),('phabricator:20130304.lintauthor.sql',1425312510),('phabricator:20130310.xactionmeta.sql',1425312510),('phabricator:20130317.phrictionedge.sql',1425312510),('phabricator:20130319.conpherence.sql',1425312510),('phabricator:20130319.phabricatorfileexplicitupload.sql',1425312510),('phabricator:20130320.phlux.sql',1425312510),('phabricator:20130321.token.sql',1425312510),('phabricator:20130322.phortune.sql',1425312510),('phabricator:20130323.phortunepayment.sql',1425312510),('phabricator:20130324.phortuneproduct.sql',1425312510),('phabricator:20130330.phrequent.sql',1425312510),('phabricator:20130403.conpherencecache.sql',1425312510),('phabricator:20130403.conpherencecachemig.php',1425312510),('phabricator:20130409.commitdrev.php',1425312510),('phabricator:20130417.externalaccount.sql',1425312510),('phabricator:20130423.conpherenceindices.sql',1425312510),('phabricator:20130423.phortunepaymentrevised.sql',1425312510),('phabricator:20130423.updateexternalaccount.sql',1425312510),('phabricator:20130426.search_savedquery.sql',1425312510),('phabricator:20130502.countdownrevamp1.sql',1425312510),('phabricator:20130502.countdownrevamp2.php',1425312510),('phabricator:20130502.countdownrevamp3.sql',1425312510),('phabricator:20130507.releephrqmailkey.sql',1425312510),('phabricator:20130507.releephrqmailkeypop.php',1425312510),('phabricator:20130507.releephrqsimplifycols.sql',1425312510),('phabricator:20130508.releephtransactions.sql',1425312510),('phabricator:20130508.releephtransactionsmig.php',1425312510),('phabricator:20130508.search_namedquery.sql',1425312510),('phabricator:20130513.receviedmailstatus.sql',1425312510),('phabricator:20130519.diviner.sql',1425312510),('phabricator:20130521.dropconphimages.sql',1425312510),('phabricator:20130523.maniphest_owners.sql',1425312510),('phabricator:20130524.repoxactions.sql',1425312510),('phabricator:20130529.macroauthor.sql',1425312510),('phabricator:20130529.macroauthormig.php',1425312510),('phabricator:20130530.macrodatekey.sql',1425312510),('phabricator:20130530.pastekeys.sql',1425312510),('phabricator:20130530.sessionhash.php',1425312510),('phabricator:20130531.filekeys.sql',1425312511),('phabricator:20130602.morediviner.sql',1425312511),('phabricator:20130602.namedqueries.sql',1425312511),('phabricator:20130606.userxactions.sql',1425312511),('phabricator:20130607.xaccount.sql',1425312511),('phabricator:20130611.migrateoauth.php',1425312511),('phabricator:20130611.nukeldap.php',1425312511),('phabricator:20130613.authdb.sql',1425312511),('phabricator:20130619.authconf.php',1425312511),('phabricator:20130620.diffxactions.sql',1425312511),('phabricator:20130621.diffcommentphid.sql',1425312511),('phabricator:20130621.diffcommentphidmig.php',1425312511),('phabricator:20130621.diffcommentunphid.sql',1425312511),('phabricator:20130622.doorkeeper.sql',1425312511),('phabricator:20130628.legalpadv0.sql',1425312511),('phabricator:20130701.conduitlog.sql',1425312511),('phabricator:20130703.legalpaddocdenorm.php',1425312511),('phabricator:20130703.legalpaddocdenorm.sql',1425312511),('phabricator:20130709.droptimeline.sql',1425312511),('phabricator:20130709.legalpadsignature.sql',1425312511),('phabricator:20130711.pholioimageobsolete.php',1425312511),('phabricator:20130711.pholioimageobsolete.sql',1425312511),('phabricator:20130711.pholioimageobsolete2.sql',1425312511),('phabricator:20130711.trimrealnames.php',1425312511),('phabricator:20130714.votexactions.sql',1425312511),('phabricator:20130715.votecomments.php',1425312511),('phabricator:20130715.voteedges.sql',1425312511),('phabricator:20130716.archivememberlessprojects.php',1425312511),('phabricator:20130722.pholioreplace.sql',1425312511),('phabricator:20130723.taskstarttime.sql',1425312511),('phabricator:20130726.ponderxactions.sql',1425312511),('phabricator:20130727.ponderquestionstatus.sql',1425312511),('phabricator:20130728.ponderunique.php',1425312511),('phabricator:20130728.ponderuniquekey.sql',1425312511),('phabricator:20130728.ponderxcomment.php',1425312511),('phabricator:20130731.releephcutpointidentifier.sql',1425312511),('phabricator:20130731.releephproject.sql',1425312511),('phabricator:20130731.releephrepoid.sql',1425312511),('phabricator:20130801.pastexactions.php',1425312511),('phabricator:20130801.pastexactions.sql',1425312511),('phabricator:20130802.heraldphid.sql',1425312511),('phabricator:20130802.heraldphids.php',1425312511),('phabricator:20130802.heraldphidukey.sql',1425312511),('phabricator:20130802.heraldxactions.sql',1425312511),('phabricator:20130805.pasteedges.sql',1425312511),('phabricator:20130805.pastemailkey.sql',1425312511),('phabricator:20130805.pastemailkeypop.php',1425312511),('phabricator:20130814.usercustom.sql',1425312512),('phabricator:20130820.file-mailkey-populate.php',1425312512),('phabricator:20130820.filemailkey.sql',1425312512),('phabricator:20130820.filexactions.sql',1425312512),('phabricator:20130820.releephxactions.sql',1425312512),('phabricator:20130826.divinernode.sql',1425312512),('phabricator:20130912.maniphest.1.touch.sql',1425312512),('phabricator:20130912.maniphest.2.created.sql',1425312512),('phabricator:20130912.maniphest.3.nameindex.sql',1425312512),('phabricator:20130912.maniphest.4.fillindex.php',1425312512),('phabricator:20130913.maniphest.1.migratesearch.php',1425312512),('phabricator:20130914.usercustom.sql',1425312512),('phabricator:20130915.maniphestcustom.sql',1425312512),('phabricator:20130915.maniphestmigrate.php',1425312512),('phabricator:20130915.maniphestqdrop.sql',1425312512),('phabricator:20130919.mfieldconf.php',1425312512),('phabricator:20130920.repokeyspolicy.sql',1425312512),('phabricator:20130921.mtransactions.sql',1425312512),('phabricator:20130921.xmigratemaniphest.php',1425312512),('phabricator:20130923.mrename.sql',1425312512),('phabricator:20130924.mdraftkey.sql',1425312512),('phabricator:20130925.mpolicy.sql',1425312512),('phabricator:20130925.xpolicy.sql',1425312512),('phabricator:20130926.dcustom.sql',1425312512),('phabricator:20130926.dinkeys.sql',1425312512),('phabricator:20130926.dinline.php',1425312512),('phabricator:20130927.audiomacro.sql',1425312512),('phabricator:20130929.filepolicy.sql',1425312512),('phabricator:20131004.dxedgekey.sql',1425312512),('phabricator:20131004.dxreviewers.php',1425312512),('phabricator:20131006.hdisable.sql',1425312512),('phabricator:20131010.pstorage.sql',1425312512),('phabricator:20131015.cpolicy.sql',1425312512),('phabricator:20131020.col1.sql',1425312512),('phabricator:20131020.harbormaster.sql',1425312512),('phabricator:20131020.pcustom.sql',1425312512),('phabricator:20131020.pxaction.sql',1425312512),('phabricator:20131020.pxactionmig.php',1425312512),('phabricator:20131025.repopush.sql',1425312512),('phabricator:20131026.commitstatus.sql',1425312512),('phabricator:20131030.repostatusmessage.sql',1425312512),('phabricator:20131031.vcspassword.sql',1425312512),('phabricator:20131105.buildstep.sql',1425312512),('phabricator:20131106.diffphid.1.col.sql',1425312512),('phabricator:20131106.diffphid.2.mig.php',1425312512),('phabricator:20131106.diffphid.3.key.sql',1425312512),('phabricator:20131106.nuance-v0.sql',1425312512),('phabricator:20131107.buildlog.sql',1425312512),('phabricator:20131112.userverified.1.col.sql',1425312512),('phabricator:20131112.userverified.2.mig.php',1425312512),('phabricator:20131118.ownerorder.php',1425312512),('phabricator:20131119.passphrase.sql',1425312512),('phabricator:20131120.nuancesourcetype.sql',1425312513),('phabricator:20131121.passphraseedge.sql',1425312513),('phabricator:20131121.repocredentials.1.col.sql',1425312513),('phabricator:20131121.repocredentials.2.mig.php',1425312513),('phabricator:20131122.repomirror.sql',1425312513),('phabricator:20131123.drydockblueprintpolicy.sql',1425312513),('phabricator:20131129.drydockresourceblueprint.sql',1425312513),('phabricator:20131204.pushlog.sql',1425312513),('phabricator:20131205.buildsteporder.sql',1425312513),('phabricator:20131205.buildstepordermig.php',1425312513),('phabricator:20131205.buildtargets.sql',1425312513),('phabricator:20131206.phragment.sql',1425312513),('phabricator:20131206.phragmentnull.sql',1425312513),('phabricator:20131208.phragmentsnapshot.sql',1425312513),('phabricator:20131211.phragmentedges.sql',1425312513),('phabricator:20131217.pushlogphid.1.col.sql',1425312513),('phabricator:20131217.pushlogphid.2.mig.php',1425312513),('phabricator:20131217.pushlogphid.3.key.sql',1425312513),('phabricator:20131219.pxdrop.sql',1425312513),('phabricator:20131224.harbormanual.sql',1425312513),('phabricator:20131227.heraldobject.sql',1425312513),('phabricator:20131231.dropshortcut.sql',1425312513),('phabricator:20131302.maniphestvalue.sql',1425312510),('phabricator:20140104.harbormastercmd.sql',1425312513),('phabricator:20140106.macromailkey.1.sql',1425312513),('phabricator:20140106.macromailkey.2.php',1425312513),('phabricator:20140108.ddbpname.1.sql',1425312513),('phabricator:20140108.ddbpname.2.php',1425312513),('phabricator:20140109.ddxactions.sql',1425312513),('phabricator:20140109.projectcolumnsdates.sql',1425312513),('phabricator:20140113.legalpadsig.1.sql',1425312513),('phabricator:20140113.legalpadsig.2.php',1425312513),('phabricator:20140115.auth.1.id.sql',1425312513),('phabricator:20140115.auth.2.expires.sql',1425312513),('phabricator:20140115.auth.3.unlimit.php',1425312513),('phabricator:20140115.legalpadsigkey.sql',1425312513),('phabricator:20140116.reporefcursor.sql',1425312513),('phabricator:20140126.diff.1.parentrevisionid.sql',1425312513),('phabricator:20140126.diff.2.repositoryphid.sql',1425312513),('phabricator:20140130.dash.1.board.sql',1425312513),('phabricator:20140130.dash.2.panel.sql',1425312513),('phabricator:20140130.dash.3.boardxaction.sql',1425312513),('phabricator:20140130.dash.4.panelxaction.sql',1425312513),('phabricator:20140130.mail.1.retry.sql',1425312513),('phabricator:20140130.mail.2.next.sql',1425312513),('phabricator:20140201.gc.1.mailsent.sql',1425312513),('phabricator:20140201.gc.2.mailreceived.sql',1425312513),('phabricator:20140205.cal.1.rename.sql',1425312513),('phabricator:20140205.cal.2.phid-col.sql',1425312513),('phabricator:20140205.cal.3.phid-mig.php',1425312513),('phabricator:20140205.cal.4.phid-key.sql',1425312513),('phabricator:20140210.herald.rule-condition-mig.php',1425312513),('phabricator:20140210.projcfield.1.blurb.php',1425312513),('phabricator:20140210.projcfield.2.piccol.sql',1425312513),('phabricator:20140210.projcfield.3.picmig.sql',1425312513),('phabricator:20140210.projcfield.4.memmig.sql',1425312513),('phabricator:20140210.projcfield.5.dropprofile.sql',1425312513),('phabricator:20140211.dx.1.nullablechangesetid.sql',1425312513),('phabricator:20140211.dx.2.migcommenttext.php',1425312513),('phabricator:20140211.dx.3.migsubscriptions.sql',1425312513),('phabricator:20140211.dx.999.drop.relationships.sql',1425312513),('phabricator:20140212.dx.1.armageddon.php',1425312513),('phabricator:20140214.clean.1.legacycommentid.sql',1425312513),('phabricator:20140214.clean.2.dropcomment.sql',1425312513),('phabricator:20140214.clean.3.dropinline.sql',1425312513),('phabricator:20140218.differentialdraft.sql',1425312513),('phabricator:20140218.passwords.1.extend.sql',1425312513),('phabricator:20140218.passwords.2.prefix.sql',1425312513),('phabricator:20140218.passwords.3.vcsextend.sql',1425312513),('phabricator:20140218.passwords.4.vcs.php',1425312513),('phabricator:20140223.bigutf8scratch.sql',1425312513),('phabricator:20140224.dxclean.1.datecommitted.sql',1425312513),('phabricator:20140226.dxcustom.1.fielddata.php',1425312513),('phabricator:20140226.dxcustom.99.drop.sql',1425312513),('phabricator:20140228.dxcomment.1.sql',1425312513),('phabricator:20140305.diviner.1.slugcol.sql',1425312513),('phabricator:20140305.diviner.2.slugkey.sql',1425312514),('phabricator:20140311.mdroplegacy.sql',1425312514),('phabricator:20140314.projectcolumn.1.statuscol.sql',1425312514),('phabricator:20140314.projectcolumn.2.statuskey.sql',1425312514),('phabricator:20140317.mupdatedkey.sql',1425312514),('phabricator:20140321.harbor.1.bxaction.sql',1425312514),('phabricator:20140321.mstatus.1.col.sql',1425312514),('phabricator:20140321.mstatus.2.mig.php',1425312514),('phabricator:20140323.harbor.1.renames.php',1425312514),('phabricator:20140323.harbor.2.message.sql',1425312514),('phabricator:20140325.push.1.event.sql',1425312514),('phabricator:20140325.push.2.eventphid.sql',1425312514),('phabricator:20140325.push.3.groups.php',1425312514),('phabricator:20140325.push.4.prune.sql',1425312514),('phabricator:20140326.project.1.colxaction.sql',1425312514),('phabricator:20140328.releeph.1.productxaction.sql',1425312514),('phabricator:20140330.flagtext.sql',1425312514),('phabricator:20140402.actionlog.sql',1425312514),('phabricator:20140410.accountsecret.1.sql',1425312514),('phabricator:20140410.accountsecret.2.php',1425312514),('phabricator:20140416.harbor.1.sql',1425312514),('phabricator:20140420.rel.1.objectphid.sql',1425312514),('phabricator:20140420.rel.2.objectmig.php',1425312514),('phabricator:20140421.slowvotecolumnsisclosed.sql',1425312514),('phabricator:20140423.session.1.hisec.sql',1425312514),('phabricator:20140427.mfactor.1.sql',1425312514),('phabricator:20140430.auth.1.partial.sql',1425312514),('phabricator:20140430.dash.1.paneltype.sql',1425312514),('phabricator:20140430.dash.2.edge.sql',1425312514),('phabricator:20140501.passphraselockcredential.sql',1425312514),('phabricator:20140501.remove.1.dlog.sql',1425312514),('phabricator:20140507.smstable.sql',1425312514),('phabricator:20140509.coverage.1.sql',1425312514),('phabricator:20140509.dashboardlayoutconfig.sql',1425312514),('phabricator:20140512.dparents.1.sql',1425312514),('phabricator:20140514.harbormasterbuildabletransaction.sql',1425312514),('phabricator:20140514.pholiomockclose.sql',1425312514),('phabricator:20140515.trust-emails.sql',1425312514),('phabricator:20140517.dxbinarycache.sql',1425312514),('phabricator:20140518.dxmorebinarycache.sql',1425312514),('phabricator:20140519.dashboardinstall.sql',1425312514),('phabricator:20140520.authtemptoken.sql',1425312514),('phabricator:20140521.projectslug.1.create.sql',1425312514),('phabricator:20140521.projectslug.2.mig.php',1425312514),('phabricator:20140522.projecticon.sql',1425312514),('phabricator:20140524.auth.mfa.cache.sql',1425312514),('phabricator:20140525.hunkmodern.sql',1425312514),('phabricator:20140615.pholioedit.1.sql',1425312514),('phabricator:20140615.pholioedit.2.sql',1425312514),('phabricator:20140617.daemon.explicit-argv.sql',1425312514),('phabricator:20140617.daemonlog.sql',1425312514),('phabricator:20140624.projcolor.1.sql',1425312514),('phabricator:20140624.projcolor.2.sql',1425312514),('phabricator:20140629.dasharchive.1.sql',1425312514),('phabricator:20140629.legalsig.1.sql',1425312514),('phabricator:20140629.legalsig.2.php',1425312514),('phabricator:20140701.legalexemption.1.sql',1425312514),('phabricator:20140701.legalexemption.2.sql',1425312514),('phabricator:20140703.legalcorp.1.sql',1425312514),('phabricator:20140703.legalcorp.2.sql',1425312514),('phabricator:20140703.legalcorp.3.sql',1425312514),('phabricator:20140703.legalcorp.4.sql',1425312514),('phabricator:20140703.legalcorp.5.sql',1425312514),('phabricator:20140704.harbormasterstep.1.sql',1425312514),('phabricator:20140704.harbormasterstep.2.sql',1425312514),('phabricator:20140704.legalpreamble.1.sql',1425312514),('phabricator:20140706.harbormasterdepend.1.php',1425312514),('phabricator:20140706.pedge.1.sql',1425312514),('phabricator:20140711.pnames.1.sql',1425312514),('phabricator:20140711.pnames.2.php',1425312514),('phabricator:20140711.workerpriority.sql',1425312515),('phabricator:20140712.projcoluniq.sql',1425312515),('phabricator:20140721.phortune.1.cart.sql',1425312515),('phabricator:20140721.phortune.2.purchase.sql',1425312515),('phabricator:20140721.phortune.3.charge.sql',1425312515),('phabricator:20140721.phortune.4.cartstatus.sql',1425312515),('phabricator:20140721.phortune.5.cstatusdefault.sql',1425312515),('phabricator:20140721.phortune.6.onetimecharge.sql',1425312515),('phabricator:20140721.phortune.7.nullmethod.sql',1425312515),('phabricator:20140722.appname.php',1425312515),('phabricator:20140722.audit.1.xactions.sql',1425312515),('phabricator:20140722.audit.2.comments.sql',1425312515),('phabricator:20140722.audit.3.miginlines.php',1425312515),('phabricator:20140722.audit.4.migtext.php',1425312515),('phabricator:20140722.renameauth.php',1425312515),('phabricator:20140723.apprenamexaction.sql',1425312515),('phabricator:20140725.audit.1.migxactions.php',1425312515),('phabricator:20140731.audit.1.subscribers.php',1425312515),('phabricator:20140731.cancdn.php',1425312515),('phabricator:20140731.harbormasterstepdesc.sql',1425312515),('phabricator:20140805.boardcol.1.sql',1425312515),('phabricator:20140805.boardcol.2.php',1425312515),('phabricator:20140807.harbormastertargettime.sql',1425312515),('phabricator:20140808.boardprop.1.sql',1425312515),('phabricator:20140808.boardprop.2.sql',1425312515),('phabricator:20140808.boardprop.3.php',1425312515),('phabricator:20140811.blob.1.sql',1425312515),('phabricator:20140811.blob.2.sql',1425312515),('phabricator:20140812.projkey.1.sql',1425312515),('phabricator:20140812.projkey.2.sql',1425312515),('phabricator:20140814.passphrasecredentialconduit.sql',1425312515),('phabricator:20140815.cancdncase.php',1425312515),('phabricator:20140818.harbormasterindex.1.sql',1425312515),('phabricator:20140821.harbormasterbuildgen.1.sql',1425312515),('phabricator:20140822.daemonenvhash.sql',1425312515),('phabricator:20140902.almanacdevice.1.sql',1425312515),('phabricator:20140904.macroattach.php',1425312515),('phabricator:20140911.fund.1.initiative.sql',1425312515),('phabricator:20140911.fund.2.xaction.sql',1425312515),('phabricator:20140911.fund.3.edge.sql',1425312515),('phabricator:20140911.fund.4.backer.sql',1425312515),('phabricator:20140911.fund.5.backxaction.sql',1425312515),('phabricator:20140914.betaproto.php',1425312515),('phabricator:20140917.project.canlock.sql',1425312515),('phabricator:20140918.schema.1.dropaudit.sql',1425312515),('phabricator:20140918.schema.2.dropauditinline.sql',1425312515),('phabricator:20140918.schema.3.wipecache.sql',1425312515),('phabricator:20140918.schema.4.cachetype.sql',1425312515),('phabricator:20140918.schema.5.slowvote.sql',1425312515),('phabricator:20140919.schema.01.calstatus.sql',1425312515),('phabricator:20140919.schema.02.calname.sql',1425312515),('phabricator:20140919.schema.03.dropaux.sql',1425312515),('phabricator:20140919.schema.04.droptaskproj.sql',1425312515),('phabricator:20140926.schema.01.droprelev.sql',1425312515),('phabricator:20140926.schema.02.droprelreqev.sql',1425312515),('phabricator:20140926.schema.03.dropldapinfo.sql',1425312515),('phabricator:20140926.schema.04.dropoauthinfo.sql',1425312515),('phabricator:20140926.schema.05.dropprojaffil.sql',1425312515),('phabricator:20140926.schema.06.dropsubproject.sql',1425312515),('phabricator:20140926.schema.07.droppondcom.sql',1425312515),('phabricator:20140927.schema.01.dropsearchq.sql',1425312515),('phabricator:20140927.schema.02.pholio1.sql',1425312515),('phabricator:20140927.schema.03.pholio2.sql',1425312515),('phabricator:20140927.schema.04.pholio3.sql',1425312515),('phabricator:20140927.schema.05.phragment1.sql',1425312515),('phabricator:20140927.schema.06.releeph1.sql',1425312515),('phabricator:20141001.schema.01.version.sql',1425312515),('phabricator:20141001.schema.02.taskmail.sql',1425312515),('phabricator:20141002.schema.01.liskcounter.sql',1425312515),('phabricator:20141002.schema.02.draftnull.sql',1425312515),('phabricator:20141004.currency.01.sql',1425312515),('phabricator:20141004.currency.02.sql',1425312515),('phabricator:20141004.currency.03.sql',1425312515),('phabricator:20141004.currency.04.sql',1425312515),('phabricator:20141004.currency.05.sql',1425312515),('phabricator:20141004.currency.06.sql',1425312515),('phabricator:20141004.harborliskcounter.sql',1425312515),('phabricator:20141005.phortuneproduct.sql',1425312515),('phabricator:20141006.phortunecart.sql',1425312515),('phabricator:20141006.phortunemerchant.sql',1425312515),('phabricator:20141006.phortunemerchantx.sql',1425312515),('phabricator:20141007.fundmerchant.sql',1425312515),('phabricator:20141007.fundrisks.sql',1425312515),('phabricator:20141007.fundtotal.sql',1425312515),('phabricator:20141007.phortunecartmerchant.sql',1425312515),('phabricator:20141007.phortunecharge.sql',1425312516),('phabricator:20141007.phortunepayment.sql',1425312516),('phabricator:20141007.phortuneprovider.sql',1425312516),('phabricator:20141007.phortuneproviderx.sql',1425312516),('phabricator:20141008.phortunemerchdesc.sql',1425312516),('phabricator:20141008.phortuneprovdis.sql',1425312516),('phabricator:20141008.phortunerefund.sql',1425312516),('phabricator:20141010.fundmailkey.sql',1425312516),('phabricator:20141011.phortunemerchedit.sql',1425312516),('phabricator:20141012.phortunecartxaction.sql',1425312516),('phabricator:20141013.phortunecartkey.sql',1425312516),('phabricator:20141016.almanac.device.sql',1425312516),('phabricator:20141016.almanac.dxaction.sql',1425312516),('phabricator:20141016.almanac.interface.sql',1425312516),('phabricator:20141016.almanac.network.sql',1425312516),('phabricator:20141016.almanac.nxaction.sql',1425312516),('phabricator:20141016.almanac.service.sql',1425312516),('phabricator:20141016.almanac.sxaction.sql',1425312516),('phabricator:20141017.almanac.binding.sql',1425312516),('phabricator:20141017.almanac.bxaction.sql',1425312516),('phabricator:20141025.phriction.1.xaction.sql',1425312516),('phabricator:20141025.phriction.2.xaction.sql',1425312516),('phabricator:20141025.phriction.mailkey.sql',1425312516),('phabricator:20141103.almanac.1.delprop.sql',1425312516),('phabricator:20141103.almanac.2.addprop.sql',1425312516),('phabricator:20141104.almanac.3.edge.sql',1425312516),('phabricator:20141105.ssh.1.rename.sql',1425312516),('phabricator:20141106.dropold.sql',1425312516),('phabricator:20141106.uniqdrafts.php',1425312516),('phabricator:20141107.phriction.policy.1.sql',1425312516),('phabricator:20141107.phriction.policy.2.php',1425312516),('phabricator:20141107.phriction.popkeys.php',1425312516),('phabricator:20141107.ssh.1.colname.sql',1425312516),('phabricator:20141107.ssh.2.keyhash.sql',1425312516),('phabricator:20141107.ssh.3.keyindex.sql',1425312516),('phabricator:20141107.ssh.4.keymig.php',1425312516),('phabricator:20141107.ssh.5.indexnull.sql',1425312516),('phabricator:20141107.ssh.6.indexkey.sql',1425312516),('phabricator:20141107.ssh.7.colnull.sql',1425312516),('phabricator:20141113.auditdupes.php',1425312516),('phabricator:20141118.diffxaction.sql',1425312516),('phabricator:20141119.commitpedge.sql',1425312516),('phabricator:20141119.differential.diff.policy.sql',1425312516),('phabricator:20141119.sshtrust.sql',1425312516),('phabricator:20141123.taskpriority.1.sql',1425312516),('phabricator:20141123.taskpriority.2.sql',1425312516),('phabricator:20141210.maniphestsubscribersmig.1.sql',1425312516),('phabricator:20141210.maniphestsubscribersmig.2.sql',1425312516),('phabricator:20141210.reposervice.sql',1425312516),('phabricator:20141212.conduittoken.sql',1425312516),('phabricator:20141215.almanacservicetype.sql',1425312516),('phabricator:20141217.almanacdevicelock.sql',1425312516),('phabricator:20141217.almanaclock.sql',1425312516),('phabricator:20141218.maniphestcctxn.php',1425312516),('phabricator:20141222.maniphestprojtxn.php',1425312516),('phabricator:20141223.daemonloguser.sql',1425312516),('phabricator:20141223.daemonobjectphid.sql',1425312516),('phabricator:20141230.pasteeditpolicycolumn.sql',1425312516),('phabricator:20141230.pasteeditpolicyexisting.sql',1425312516),('phabricator:20150102.policyname.php',1425312516),('phabricator:20150102.tasksubscriber.sql',1425312516),('phabricator:20150105.conpsearch.sql',1425312516),('phabricator:20150114.oauthserver.client.policy.sql',1425312517),('phabricator:20150115.applicationemails.sql',1425312517),('phabricator:20150115.trigger.1.sql',1425312517),('phabricator:20150115.trigger.2.sql',1425312517),('phabricator:20150116.maniphestapplicationemails.php',1425312517),('phabricator:20150120.maniphestdefaultauthor.php',1425312517),('phabricator:20150124.subs.1.sql',1425312517),('phabricator:20150129.pastefileapplicationemails.php',1425312517),('phabricator:20150130.phortune.1.subphid.sql',1425312517),('phabricator:20150130.phortune.2.subkey.sql',1425312517),('phabricator:20150131.phortune.1.defaultpayment.sql',1425312517),('phabricator:20150205.authprovider.autologin.sql',1425312517),('phabricator:20150205.daemonenv.sql',1425312517),('phabricator:20150209.invite.sql',1425312517),('phabricator:20150209.oauthclient.trust.sql',1425312517),('phabricator:20150210.invitephid.sql',1425312517),('phabricator:20150212.legalpad.session.1.sql',1425312517),('phabricator:20150212.legalpad.session.2.sql',1425312517),('phabricator:20150219.scratch.nonmutable.sql',1425312517),('phabricator:20150223.daemon.1.id.sql',1425312517),('phabricator:20150223.daemon.2.idlegacy.sql',1425312517),('phabricator:20150223.daemon.3.idkey.sql',1425312517),('phabricator:daemonstatus.sql',1425312509),('phabricator:daemonstatuskey.sql',1425312509),('phabricator:daemontaskarchive.sql',1425312509),('phabricator:db.almanac',1425312504),('phabricator:db.audit',1425312504),('phabricator:db.auth',1425312504),('phabricator:db.cache',1425312504),('phabricator:db.calendar',1425312504),('phabricator:db.chatlog',1425312504),('phabricator:db.conduit',1425312504),('phabricator:db.config',1425312504),('phabricator:db.conpherence',1425312504),('phabricator:db.countdown',1425312504),('phabricator:db.daemon',1425312504),('phabricator:db.dashboard',1425312504),('phabricator:db.differential',1425312504),('phabricator:db.diviner',1425312504),('phabricator:db.doorkeeper',1425312504),('phabricator:db.draft',1425312504),('phabricator:db.drydock',1425312504),('phabricator:db.fact',1425312504),('phabricator:db.feed',1425312504),('phabricator:db.file',1425312504),('phabricator:db.flag',1425312504),('phabricator:db.fund',1425312504),('phabricator:db.harbormaster',1425312504),('phabricator:db.herald',1425312504),('phabricator:db.legalpad',1425312504),('phabricator:db.maniphest',1425312504),('phabricator:db.meta_data',1425312504),('phabricator:db.metamta',1425312504),('phabricator:db.nuance',1425312504),('phabricator:db.oauth_server',1425312504),('phabricator:db.owners',1425312504),('phabricator:db.passphrase',1425312504),('phabricator:db.pastebin',1425312504),('phabricator:db.phame',1425312504),('phabricator:db.phlux',1425312504),('phabricator:db.pholio',1425312504),('phabricator:db.phortune',1425312504),('phabricator:db.phragment',1425312504),('phabricator:db.phrequent',1425312504),('phabricator:db.phriction',1425312504),('phabricator:db.policy',1425312504),('phabricator:db.ponder',1425312504),('phabricator:db.project',1425312504),('phabricator:db.releeph',1425312504),('phabricator:db.repository',1425312504),('phabricator:db.search',1425312504),('phabricator:db.slowvote',1425312504),('phabricator:db.system',1425312504),('phabricator:db.timeline',1425312504),('phabricator:db.token',1425312504),('phabricator:db.user',1425312504),('phabricator:db.worker',1425312504),('phabricator:db.xhpastview',1425312504),('phabricator:db.xhprof',1425312504),('phabricator:differentialbookmarks.sql',1425312509),('phabricator:draft-metadata.sql',1425312509),('phabricator:dropfileproxyimage.sql',1425312509),('phabricator:drydockresoucetype.sql',1425312509),('phabricator:drydocktaskid.sql',1425312509),('phabricator:edgetype.sql',1425312509),('phabricator:emailtable.sql',1425312509),('phabricator:emailtableport.sql',1425312509),('phabricator:emailtableremove.sql',1425312509),('phabricator:fact-raw.sql',1425312509),('phabricator:harbormasterobject.sql',1425312509),('phabricator:holidays.sql',1425312509),('phabricator:ldapinfo.sql',1425312509),('phabricator:legalpad-mailkey-populate.php',1425312511),('phabricator:legalpad-mailkey.sql',1425312511),('phabricator:liskcounters-task.sql',1425312509),('phabricator:liskcounters.php',1425312509),('phabricator:liskcounters.sql',1425312509),('phabricator:maniphestxcache.sql',1425312509),('phabricator:markupcache.sql',1425312509),('phabricator:migrate-differential-dependencies.php',1425312509),('phabricator:migrate-maniphest-dependencies.php',1425312509),('phabricator:migrate-maniphest-revisions.php',1425312509),('phabricator:migrate-project-edges.php',1425312509),('phabricator:owners-exclude.sql',1425312509),('phabricator:pastepolicy.sql',1425312509),('phabricator:phameblog.sql',1425312509),('phabricator:phamedomain.sql',1425312509),('phabricator:phameoneblog.sql',1425312509),('phabricator:phamepolicy.sql',1425312509),('phabricator:phiddrop.sql',1425312509),('phabricator:pholio.sql',1425312509),('phabricator:policy-project.sql',1425312509),('phabricator:ponder-comments.sql',1425312509),('phabricator:ponder-mailkey-populate.php',1425312509),('phabricator:ponder-mailkey.sql',1425312509),('phabricator:ponder.sql',1425312509),('phabricator:releeph.sql',1425312510),('phabricator:repository-lint.sql',1425312509),('phabricator:statustxt.sql',1425312509),('phabricator:symbolcontexts.sql',1425312509),('phabricator:testdatabase.sql',1425312509),('phabricator:threadtopic.sql',1425312509),('phabricator:userstatus.sql',1425312509),('phabricator:usertranslation.sql',1425312509),('phabricator:xhprof.sql',1425312509); +INSERT INTO `patch_status` VALUES ('phabricator:000.project.sql',1443545049),('phabricator:0000.legacy.sql',1443545049),('phabricator:001.maniphest_projects.sql',1443545049),('phabricator:002.oauth.sql',1443545049),('phabricator:003.more_oauth.sql',1443545049),('phabricator:004.daemonrepos.sql',1443545049),('phabricator:005.workers.sql',1443545049),('phabricator:006.repository.sql',1443545049),('phabricator:007.daemonlog.sql',1443545049),('phabricator:008.repoopt.sql',1443545049),('phabricator:009.repo_summary.sql',1443545049),('phabricator:010.herald.sql',1443545049),('phabricator:011.badcommit.sql',1443545049),('phabricator:012.dropphidtype.sql',1443545049),('phabricator:013.commitdetail.sql',1443545049),('phabricator:014.shortcuts.sql',1443545049),('phabricator:015.preferences.sql',1443545049),('phabricator:016.userrealnameindex.sql',1443545049),('phabricator:017.sessionkeys.sql',1443545049),('phabricator:018.owners.sql',1443545049),('phabricator:019.arcprojects.sql',1443545049),('phabricator:020.pathcapital.sql',1443545049),('phabricator:021.xhpastview.sql',1443545049),('phabricator:022.differentialcommit.sql',1443545049),('phabricator:023.dxkeys.sql',1443545049),('phabricator:024.mlistkeys.sql',1443545049),('phabricator:025.commentopt.sql',1443545049),('phabricator:026.diffpropkey.sql',1443545049),('phabricator:027.metamtakeys.sql',1443545049),('phabricator:028.systemagent.sql',1443545049),('phabricator:029.cursors.sql',1443545049),('phabricator:030.imagemacro.sql',1443545049),('phabricator:031.workerrace.sql',1443545049),('phabricator:032.viewtime.sql',1443545049),('phabricator:033.privtest.sql',1443545049),('phabricator:034.savedheader.sql',1443545049),('phabricator:035.proxyimage.sql',1443545049),('phabricator:036.mailkey.sql',1443545049),('phabricator:037.setuptest.sql',1443545049),('phabricator:038.admin.sql',1443545049),('phabricator:039.userlog.sql',1443545050),('phabricator:040.transform.sql',1443545050),('phabricator:041.heraldrepetition.sql',1443545050),('phabricator:042.commentmetadata.sql',1443545050),('phabricator:043.pastebin.sql',1443545050),('phabricator:044.countdown.sql',1443545050),('phabricator:045.timezone.sql',1443545050),('phabricator:046.conduittoken.sql',1443545050),('phabricator:047.projectstatus.sql',1443545050),('phabricator:048.relationshipkeys.sql',1443545050),('phabricator:049.projectowner.sql',1443545050),('phabricator:050.taskdenormal.sql',1443545050),('phabricator:051.projectfilter.sql',1443545050),('phabricator:052.pastelanguage.sql',1443545050),('phabricator:053.feed.sql',1443545050),('phabricator:054.subscribers.sql',1443545050),('phabricator:055.add_author_to_files.sql',1443545050),('phabricator:056.slowvote.sql',1443545050),('phabricator:057.parsecache.sql',1443545050),('phabricator:058.missingkeys.sql',1443545050),('phabricator:059.engines.php',1443545050),('phabricator:060.phriction.sql',1443545050),('phabricator:061.phrictioncontent.sql',1443545050),('phabricator:062.phrictionmenu.sql',1443545050),('phabricator:063.pasteforks.sql',1443545050),('phabricator:064.subprojects.sql',1443545050),('phabricator:065.sshkeys.sql',1443545050),('phabricator:066.phrictioncontent.sql',1443545050),('phabricator:067.preferences.sql',1443545050),('phabricator:068.maniphestauxiliarystorage.sql',1443545050),('phabricator:069.heraldxscript.sql',1443545050),('phabricator:070.differentialaux.sql',1443545050),('phabricator:071.contentsource.sql',1443545050),('phabricator:072.blamerevert.sql',1443545050),('phabricator:073.reposymbols.sql',1443545050),('phabricator:074.affectedpath.sql',1443545050),('phabricator:075.revisionhash.sql',1443545050),('phabricator:076.indexedlanguages.sql',1443545050),('phabricator:077.originalemail.sql',1443545050),('phabricator:078.nametoken.sql',1443545050),('phabricator:079.nametokenindex.php',1443545050),('phabricator:080.filekeys.sql',1443545050),('phabricator:081.filekeys.php',1443545050),('phabricator:082.xactionkey.sql',1443545050),('phabricator:083.dxviewtime.sql',1443545051),('phabricator:084.pasteauthorkey.sql',1443545051),('phabricator:085.packagecommitrelationship.sql',1443545051),('phabricator:086.formeraffil.sql',1443545051),('phabricator:087.phrictiondelete.sql',1443545051),('phabricator:088.audit.sql',1443545051),('phabricator:089.projectwiki.sql',1443545051),('phabricator:090.forceuniqueprojectnames.php',1443545051),('phabricator:091.uniqueslugkey.sql',1443545051),('phabricator:092.dropgithubnotification.sql',1443545051),('phabricator:093.gitremotes.php',1443545051),('phabricator:094.phrictioncolumn.sql',1443545051),('phabricator:095.directory.sql',1443545051),('phabricator:096.filename.sql',1443545051),('phabricator:097.heraldruletypes.sql',1443545051),('phabricator:098.heraldruletypemigration.php',1443545051),('phabricator:099.drydock.sql',1443545051),('phabricator:100.projectxaction.sql',1443545051),('phabricator:101.heraldruleapplied.sql',1443545051),('phabricator:102.heraldcleanup.php',1443545051),('phabricator:103.heraldedithistory.sql',1443545051),('phabricator:104.searchkey.sql',1443545051),('phabricator:105.mimetype.sql',1443545051),('phabricator:106.chatlog.sql',1443545051),('phabricator:107.oauthserver.sql',1443545051),('phabricator:108.oauthscope.sql',1443545051),('phabricator:109.oauthclientphidkey.sql',1443545051),('phabricator:110.commitaudit.sql',1443545051),('phabricator:111.commitauditmigration.php',1443545051),('phabricator:112.oauthaccesscoderedirecturi.sql',1443545051),('phabricator:113.lastreviewer.sql',1443545051),('phabricator:114.auditrequest.sql',1443545051),('phabricator:115.prepareutf8.sql',1443545051),('phabricator:116.utf8-backup-first-expect-wait.sql',1443545053),('phabricator:117.repositorydescription.php',1443545053),('phabricator:118.auditinline.sql',1443545053),('phabricator:119.filehash.sql',1443545053),('phabricator:120.noop.sql',1443545053),('phabricator:121.drydocklog.sql',1443545053),('phabricator:122.flag.sql',1443545053),('phabricator:123.heraldrulelog.sql',1443545053),('phabricator:124.subpriority.sql',1443545053),('phabricator:125.ipv6.sql',1443545053),('phabricator:126.edges.sql',1443545053),('phabricator:127.userkeybody.sql',1443545053),('phabricator:128.phabricatorcom.sql',1443545053),('phabricator:129.savedquery.sql',1443545053),('phabricator:130.denormalrevisionquery.sql',1443545053),('phabricator:131.migraterevisionquery.php',1443545053),('phabricator:132.phame.sql',1443545053),('phabricator:133.imagemacro.sql',1443545053),('phabricator:134.emptysearch.sql',1443545053),('phabricator:135.datecommitted.sql',1443545053),('phabricator:136.sex.sql',1443545053),('phabricator:137.auditmetadata.sql',1443545053),('phabricator:138.notification.sql',1443545053),('phabricator:20121209.pholioxactions.sql',1443545054),('phabricator:20121209.xmacroadd.sql',1443545054),('phabricator:20121209.xmacromigrate.php',1443545054),('phabricator:20121209.xmacromigratekey.sql',1443545054),('phabricator:20121220.generalcache.sql',1443545054),('phabricator:20121226.config.sql',1443545054),('phabricator:20130101.confxaction.sql',1443545054),('phabricator:20130102.metamtareceivedmailmessageidhash.sql',1443545054),('phabricator:20130103.filemetadata.sql',1443545054),('phabricator:20130111.conpherence.sql',1443545054),('phabricator:20130127.altheraldtranscript.sql',1443545054),('phabricator:20130131.conpherencepics.sql',1443545054),('phabricator:20130201.revisionunsubscribed.php',1443545054),('phabricator:20130201.revisionunsubscribed.sql',1443545054),('phabricator:20130214.chatlogchannel.sql',1443545054),('phabricator:20130214.chatlogchannelid.sql',1443545054),('phabricator:20130214.token.sql',1443545054),('phabricator:20130215.phabricatorfileaddttl.sql',1443545054),('phabricator:20130217.cachettl.sql',1443545054),('phabricator:20130218.longdaemon.sql',1443545054),('phabricator:20130218.updatechannelid.php',1443545054),('phabricator:20130219.commitsummary.sql',1443545054),('phabricator:20130219.commitsummarymig.php',1443545054),('phabricator:20130222.dropchannel.sql',1443545054),('phabricator:20130226.commitkey.sql',1443545054),('phabricator:20130304.lintauthor.sql',1443545054),('phabricator:20130310.xactionmeta.sql',1443545055),('phabricator:20130317.phrictionedge.sql',1443545055),('phabricator:20130319.conpherence.sql',1443545054),('phabricator:20130319.phabricatorfileexplicitupload.sql',1443545054),('phabricator:20130320.phlux.sql',1443545055),('phabricator:20130321.token.sql',1443545055),('phabricator:20130322.phortune.sql',1443545055),('phabricator:20130323.phortunepayment.sql',1443545055),('phabricator:20130324.phortuneproduct.sql',1443545055),('phabricator:20130330.phrequent.sql',1443545055),('phabricator:20130403.conpherencecache.sql',1443545055),('phabricator:20130403.conpherencecachemig.php',1443545055),('phabricator:20130409.commitdrev.php',1443545055),('phabricator:20130417.externalaccount.sql',1443545055),('phabricator:20130423.conpherenceindices.sql',1443545055),('phabricator:20130423.phortunepaymentrevised.sql',1443545055),('phabricator:20130423.updateexternalaccount.sql',1443545055),('phabricator:20130426.search_savedquery.sql',1443545055),('phabricator:20130502.countdownrevamp1.sql',1443545055),('phabricator:20130502.countdownrevamp2.php',1443545055),('phabricator:20130502.countdownrevamp3.sql',1443545055),('phabricator:20130507.releephrqmailkey.sql',1443545055),('phabricator:20130507.releephrqmailkeypop.php',1443545055),('phabricator:20130507.releephrqsimplifycols.sql',1443545055),('phabricator:20130508.releephtransactions.sql',1443545055),('phabricator:20130508.releephtransactionsmig.php',1443545055),('phabricator:20130508.search_namedquery.sql',1443545055),('phabricator:20130513.receviedmailstatus.sql',1443545055),('phabricator:20130519.diviner.sql',1443545055),('phabricator:20130521.dropconphimages.sql',1443545055),('phabricator:20130523.maniphest_owners.sql',1443545055),('phabricator:20130524.repoxactions.sql',1443545055),('phabricator:20130529.macroauthor.sql',1443545055),('phabricator:20130529.macroauthormig.php',1443545055),('phabricator:20130530.macrodatekey.sql',1443545055),('phabricator:20130530.pastekeys.sql',1443545055),('phabricator:20130530.sessionhash.php',1443545055),('phabricator:20130531.filekeys.sql',1443545055),('phabricator:20130602.morediviner.sql',1443545055),('phabricator:20130602.namedqueries.sql',1443545055),('phabricator:20130606.userxactions.sql',1443545055),('phabricator:20130607.xaccount.sql',1443545055),('phabricator:20130611.migrateoauth.php',1443545055),('phabricator:20130611.nukeldap.php',1443545055),('phabricator:20130613.authdb.sql',1443545055),('phabricator:20130619.authconf.php',1443545055),('phabricator:20130620.diffxactions.sql',1443545055),('phabricator:20130621.diffcommentphid.sql',1443545055),('phabricator:20130621.diffcommentphidmig.php',1443545055),('phabricator:20130621.diffcommentunphid.sql',1443545056),('phabricator:20130622.doorkeeper.sql',1443545056),('phabricator:20130628.legalpadv0.sql',1443545056),('phabricator:20130701.conduitlog.sql',1443545056),('phabricator:20130703.legalpaddocdenorm.php',1443545056),('phabricator:20130703.legalpaddocdenorm.sql',1443545056),('phabricator:20130709.droptimeline.sql',1443545056),('phabricator:20130709.legalpadsignature.sql',1443545056),('phabricator:20130711.pholioimageobsolete.php',1443545056),('phabricator:20130711.pholioimageobsolete.sql',1443545056),('phabricator:20130711.pholioimageobsolete2.sql',1443545056),('phabricator:20130711.trimrealnames.php',1443545056),('phabricator:20130714.votexactions.sql',1443545056),('phabricator:20130715.votecomments.php',1443545056),('phabricator:20130715.voteedges.sql',1443545056),('phabricator:20130716.archivememberlessprojects.php',1443545056),('phabricator:20130722.pholioreplace.sql',1443545056),('phabricator:20130723.taskstarttime.sql',1443545056),('phabricator:20130726.ponderxactions.sql',1443545056),('phabricator:20130727.ponderquestionstatus.sql',1443545056),('phabricator:20130728.ponderunique.php',1443545056),('phabricator:20130728.ponderuniquekey.sql',1443545056),('phabricator:20130728.ponderxcomment.php',1443545056),('phabricator:20130731.releephcutpointidentifier.sql',1443545056),('phabricator:20130731.releephproject.sql',1443545056),('phabricator:20130731.releephrepoid.sql',1443545056),('phabricator:20130801.pastexactions.php',1443545056),('phabricator:20130801.pastexactions.sql',1443545056),('phabricator:20130802.heraldphid.sql',1443545056),('phabricator:20130802.heraldphids.php',1443545056),('phabricator:20130802.heraldphidukey.sql',1443545056),('phabricator:20130802.heraldxactions.sql',1443545056),('phabricator:20130805.pasteedges.sql',1443545056),('phabricator:20130805.pastemailkey.sql',1443545056),('phabricator:20130805.pastemailkeypop.php',1443545056),('phabricator:20130814.usercustom.sql',1443545056),('phabricator:20130820.file-mailkey-populate.php',1443545056),('phabricator:20130820.filemailkey.sql',1443545056),('phabricator:20130820.filexactions.sql',1443545056),('phabricator:20130820.releephxactions.sql',1443545056),('phabricator:20130826.divinernode.sql',1443545056),('phabricator:20130912.maniphest.1.touch.sql',1443545056),('phabricator:20130912.maniphest.2.created.sql',1443545056),('phabricator:20130912.maniphest.3.nameindex.sql',1443545056),('phabricator:20130912.maniphest.4.fillindex.php',1443545056),('phabricator:20130913.maniphest.1.migratesearch.php',1443545056),('phabricator:20130914.usercustom.sql',1443545056),('phabricator:20130915.maniphestcustom.sql',1443545056),('phabricator:20130915.maniphestmigrate.php',1443545056),('phabricator:20130915.maniphestqdrop.sql',1443545057),('phabricator:20130919.mfieldconf.php',1443545056),('phabricator:20130920.repokeyspolicy.sql',1443545056),('phabricator:20130921.mtransactions.sql',1443545056),('phabricator:20130921.xmigratemaniphest.php',1443545056),('phabricator:20130923.mrename.sql',1443545056),('phabricator:20130924.mdraftkey.sql',1443545056),('phabricator:20130925.mpolicy.sql',1443545056),('phabricator:20130925.xpolicy.sql',1443545057),('phabricator:20130926.dcustom.sql',1443545057),('phabricator:20130926.dinkeys.sql',1443545057),('phabricator:20130926.dinline.php',1443545057),('phabricator:20130927.audiomacro.sql',1443545057),('phabricator:20130929.filepolicy.sql',1443545057),('phabricator:20131004.dxedgekey.sql',1443545057),('phabricator:20131004.dxreviewers.php',1443545057),('phabricator:20131006.hdisable.sql',1443545057),('phabricator:20131010.pstorage.sql',1443545057),('phabricator:20131015.cpolicy.sql',1443545057),('phabricator:20131020.col1.sql',1443545057),('phabricator:20131020.harbormaster.sql',1443545057),('phabricator:20131020.pcustom.sql',1443545057),('phabricator:20131020.pxaction.sql',1443545057),('phabricator:20131020.pxactionmig.php',1443545057),('phabricator:20131025.repopush.sql',1443545057),('phabricator:20131026.commitstatus.sql',1443545057),('phabricator:20131030.repostatusmessage.sql',1443545057),('phabricator:20131031.vcspassword.sql',1443545057),('phabricator:20131105.buildstep.sql',1443545057),('phabricator:20131106.diffphid.1.col.sql',1443545057),('phabricator:20131106.diffphid.2.mig.php',1443545057),('phabricator:20131106.diffphid.3.key.sql',1443545057),('phabricator:20131106.nuance-v0.sql',1443545057),('phabricator:20131107.buildlog.sql',1443545057),('phabricator:20131112.userverified.1.col.sql',1443545057),('phabricator:20131112.userverified.2.mig.php',1443545057),('phabricator:20131118.ownerorder.php',1443545057),('phabricator:20131119.passphrase.sql',1443545057),('phabricator:20131120.nuancesourcetype.sql',1443545057),('phabricator:20131121.passphraseedge.sql',1443545057),('phabricator:20131121.repocredentials.1.col.sql',1443545057),('phabricator:20131121.repocredentials.2.mig.php',1443545057),('phabricator:20131122.repomirror.sql',1443545057),('phabricator:20131123.drydockblueprintpolicy.sql',1443545057),('phabricator:20131129.drydockresourceblueprint.sql',1443545057),('phabricator:20131204.pushlog.sql',1443545057),('phabricator:20131205.buildsteporder.sql',1443545058),('phabricator:20131205.buildstepordermig.php',1443545058),('phabricator:20131205.buildtargets.sql',1443545057),('phabricator:20131206.phragment.sql',1443545058),('phabricator:20131206.phragmentnull.sql',1443545058),('phabricator:20131208.phragmentsnapshot.sql',1443545058),('phabricator:20131211.phragmentedges.sql',1443545058),('phabricator:20131217.pushlogphid.1.col.sql',1443545058),('phabricator:20131217.pushlogphid.2.mig.php',1443545058),('phabricator:20131217.pushlogphid.3.key.sql',1443545058),('phabricator:20131219.pxdrop.sql',1443545058),('phabricator:20131224.harbormanual.sql',1443545058),('phabricator:20131227.heraldobject.sql',1443545058),('phabricator:20131231.dropshortcut.sql',1443545058),('phabricator:20131302.maniphestvalue.sql',1443545054),('phabricator:20140104.harbormastercmd.sql',1443545058),('phabricator:20140106.macromailkey.1.sql',1443545058),('phabricator:20140106.macromailkey.2.php',1443545058),('phabricator:20140108.ddbpname.1.sql',1443545058),('phabricator:20140108.ddbpname.2.php',1443545058),('phabricator:20140109.ddxactions.sql',1443545058),('phabricator:20140109.projectcolumnsdates.sql',1443545058),('phabricator:20140113.legalpadsig.1.sql',1443545058),('phabricator:20140113.legalpadsig.2.php',1443545058),('phabricator:20140115.auth.1.id.sql',1443545058),('phabricator:20140115.auth.2.expires.sql',1443545058),('phabricator:20140115.auth.3.unlimit.php',1443545058),('phabricator:20140115.legalpadsigkey.sql',1443545058),('phabricator:20140116.reporefcursor.sql',1443545058),('phabricator:20140126.diff.1.parentrevisionid.sql',1443545058),('phabricator:20140126.diff.2.repositoryphid.sql',1443545058),('phabricator:20140130.dash.1.board.sql',1443545058),('phabricator:20140130.dash.2.panel.sql',1443545058),('phabricator:20140130.dash.3.boardxaction.sql',1443545058),('phabricator:20140130.dash.4.panelxaction.sql',1443545058),('phabricator:20140130.mail.1.retry.sql',1443545058),('phabricator:20140130.mail.2.next.sql',1443545058),('phabricator:20140201.gc.1.mailsent.sql',1443545058),('phabricator:20140201.gc.2.mailreceived.sql',1443545058),('phabricator:20140205.cal.1.rename.sql',1443545058),('phabricator:20140205.cal.2.phid-col.sql',1443545058),('phabricator:20140205.cal.3.phid-mig.php',1443545058),('phabricator:20140205.cal.4.phid-key.sql',1443545058),('phabricator:20140210.herald.rule-condition-mig.php',1443545058),('phabricator:20140210.projcfield.1.blurb.php',1443545058),('phabricator:20140210.projcfield.2.piccol.sql',1443545058),('phabricator:20140210.projcfield.3.picmig.sql',1443545058),('phabricator:20140210.projcfield.4.memmig.sql',1443545058),('phabricator:20140210.projcfield.5.dropprofile.sql',1443545058),('phabricator:20140211.dx.1.nullablechangesetid.sql',1443545058),('phabricator:20140211.dx.2.migcommenttext.php',1443545058),('phabricator:20140211.dx.3.migsubscriptions.sql',1443545058),('phabricator:20140211.dx.999.drop.relationships.sql',1443545058),('phabricator:20140212.dx.1.armageddon.php',1443545058),('phabricator:20140214.clean.1.legacycommentid.sql',1443545058),('phabricator:20140214.clean.2.dropcomment.sql',1443545058),('phabricator:20140214.clean.3.dropinline.sql',1443545058),('phabricator:20140218.differentialdraft.sql',1443545058),('phabricator:20140218.passwords.1.extend.sql',1443545058),('phabricator:20140218.passwords.2.prefix.sql',1443545058),('phabricator:20140218.passwords.3.vcsextend.sql',1443545058),('phabricator:20140218.passwords.4.vcs.php',1443545058),('phabricator:20140223.bigutf8scratch.sql',1443545058),('phabricator:20140224.dxclean.1.datecommitted.sql',1443545058),('phabricator:20140226.dxcustom.1.fielddata.php',1443545058),('phabricator:20140226.dxcustom.99.drop.sql',1443545058),('phabricator:20140228.dxcomment.1.sql',1443545058),('phabricator:20140305.diviner.1.slugcol.sql',1443545058),('phabricator:20140305.diviner.2.slugkey.sql',1443545058),('phabricator:20140311.mdroplegacy.sql',1443545058),('phabricator:20140314.projectcolumn.1.statuscol.sql',1443545058),('phabricator:20140314.projectcolumn.2.statuskey.sql',1443545058),('phabricator:20140317.mupdatedkey.sql',1443545058),('phabricator:20140321.harbor.1.bxaction.sql',1443545058),('phabricator:20140321.mstatus.1.col.sql',1443545058),('phabricator:20140321.mstatus.2.mig.php',1443545058),('phabricator:20140323.harbor.1.renames.php',1443545058),('phabricator:20140323.harbor.2.message.sql',1443545058),('phabricator:20140325.push.1.event.sql',1443545058),('phabricator:20140325.push.2.eventphid.sql',1443545059),('phabricator:20140325.push.3.groups.php',1443545059),('phabricator:20140325.push.4.prune.sql',1443545059),('phabricator:20140326.project.1.colxaction.sql',1443545059),('phabricator:20140328.releeph.1.productxaction.sql',1443545059),('phabricator:20140330.flagtext.sql',1443545059),('phabricator:20140402.actionlog.sql',1443545059),('phabricator:20140410.accountsecret.1.sql',1443545059),('phabricator:20140410.accountsecret.2.php',1443545059),('phabricator:20140416.harbor.1.sql',1443545059),('phabricator:20140420.rel.1.objectphid.sql',1443545059),('phabricator:20140420.rel.2.objectmig.php',1443545059),('phabricator:20140421.slowvotecolumnsisclosed.sql',1443545059),('phabricator:20140423.session.1.hisec.sql',1443545059),('phabricator:20140427.mfactor.1.sql',1443545059),('phabricator:20140430.auth.1.partial.sql',1443545059),('phabricator:20140430.dash.1.paneltype.sql',1443545059),('phabricator:20140430.dash.2.edge.sql',1443545059),('phabricator:20140501.passphraselockcredential.sql',1443545059),('phabricator:20140501.remove.1.dlog.sql',1443545059),('phabricator:20140507.smstable.sql',1443545059),('phabricator:20140509.coverage.1.sql',1443545059),('phabricator:20140509.dashboardlayoutconfig.sql',1443545059),('phabricator:20140512.dparents.1.sql',1443545059),('phabricator:20140514.harbormasterbuildabletransaction.sql',1443545059),('phabricator:20140514.pholiomockclose.sql',1443545059),('phabricator:20140515.trust-emails.sql',1443545059),('phabricator:20140517.dxbinarycache.sql',1443545059),('phabricator:20140518.dxmorebinarycache.sql',1443545059),('phabricator:20140519.dashboardinstall.sql',1443545059),('phabricator:20140520.authtemptoken.sql',1443545059),('phabricator:20140521.projectslug.1.create.sql',1443545059),('phabricator:20140521.projectslug.2.mig.php',1443545059),('phabricator:20140522.projecticon.sql',1443545059),('phabricator:20140524.auth.mfa.cache.sql',1443545059),('phabricator:20140525.hunkmodern.sql',1443545059),('phabricator:20140615.pholioedit.1.sql',1443545059),('phabricator:20140615.pholioedit.2.sql',1443545059),('phabricator:20140617.daemon.explicit-argv.sql',1443545059),('phabricator:20140617.daemonlog.sql',1443545059),('phabricator:20140624.projcolor.1.sql',1443545059),('phabricator:20140624.projcolor.2.sql',1443545059),('phabricator:20140629.dasharchive.1.sql',1443545059),('phabricator:20140629.legalsig.1.sql',1443545059),('phabricator:20140629.legalsig.2.php',1443545059),('phabricator:20140701.legalexemption.1.sql',1443545059),('phabricator:20140701.legalexemption.2.sql',1443545059),('phabricator:20140703.legalcorp.1.sql',1443545059),('phabricator:20140703.legalcorp.2.sql',1443545059),('phabricator:20140703.legalcorp.3.sql',1443545059),('phabricator:20140703.legalcorp.4.sql',1443545059),('phabricator:20140703.legalcorp.5.sql',1443545059),('phabricator:20140704.harbormasterstep.1.sql',1443545059),('phabricator:20140704.harbormasterstep.2.sql',1443545059),('phabricator:20140704.legalpreamble.1.sql',1443545059),('phabricator:20140706.harbormasterdepend.1.php',1443545059),('phabricator:20140706.pedge.1.sql',1443545059),('phabricator:20140711.pnames.1.sql',1443545059),('phabricator:20140711.pnames.2.php',1443545059),('phabricator:20140711.workerpriority.sql',1443545059),('phabricator:20140712.projcoluniq.sql',1443545059),('phabricator:20140721.phortune.1.cart.sql',1443545059),('phabricator:20140721.phortune.2.purchase.sql',1443545059),('phabricator:20140721.phortune.3.charge.sql',1443545059),('phabricator:20140721.phortune.4.cartstatus.sql',1443545059),('phabricator:20140721.phortune.5.cstatusdefault.sql',1443545059),('phabricator:20140721.phortune.6.onetimecharge.sql',1443545060),('phabricator:20140721.phortune.7.nullmethod.sql',1443545060),('phabricator:20140722.appname.php',1443545060),('phabricator:20140722.audit.1.xactions.sql',1443545060),('phabricator:20140722.audit.2.comments.sql',1443545060),('phabricator:20140722.audit.3.miginlines.php',1443545060),('phabricator:20140722.audit.4.migtext.php',1443545060),('phabricator:20140722.renameauth.php',1443545060),('phabricator:20140723.apprenamexaction.sql',1443545060),('phabricator:20140725.audit.1.migxactions.php',1443545060),('phabricator:20140731.audit.1.subscribers.php',1443545060),('phabricator:20140731.cancdn.php',1443545060),('phabricator:20140731.harbormasterstepdesc.sql',1443545060),('phabricator:20140805.boardcol.1.sql',1443545060),('phabricator:20140805.boardcol.2.php',1443545060),('phabricator:20140807.harbormastertargettime.sql',1443545060),('phabricator:20140808.boardprop.1.sql',1443545060),('phabricator:20140808.boardprop.2.sql',1443545060),('phabricator:20140808.boardprop.3.php',1443545060),('phabricator:20140811.blob.1.sql',1443545060),('phabricator:20140811.blob.2.sql',1443545060),('phabricator:20140812.projkey.1.sql',1443545060),('phabricator:20140812.projkey.2.sql',1443545060),('phabricator:20140814.passphrasecredentialconduit.sql',1443545060),('phabricator:20140815.cancdncase.php',1443545060),('phabricator:20140818.harbormasterindex.1.sql',1443545060),('phabricator:20140821.harbormasterbuildgen.1.sql',1443545060),('phabricator:20140822.daemonenvhash.sql',1443545060),('phabricator:20140902.almanacdevice.1.sql',1443545060),('phabricator:20140904.macroattach.php',1443545060),('phabricator:20140911.fund.1.initiative.sql',1443545060),('phabricator:20140911.fund.2.xaction.sql',1443545060),('phabricator:20140911.fund.3.edge.sql',1443545060),('phabricator:20140911.fund.4.backer.sql',1443545060),('phabricator:20140911.fund.5.backxaction.sql',1443545060),('phabricator:20140914.betaproto.php',1443545060),('phabricator:20140917.project.canlock.sql',1443545060),('phabricator:20140918.schema.1.dropaudit.sql',1443545060),('phabricator:20140918.schema.2.dropauditinline.sql',1443545060),('phabricator:20140918.schema.3.wipecache.sql',1443545060),('phabricator:20140918.schema.4.cachetype.sql',1443545060),('phabricator:20140918.schema.5.slowvote.sql',1443545060),('phabricator:20140919.schema.01.calstatus.sql',1443545060),('phabricator:20140919.schema.02.calname.sql',1443545060),('phabricator:20140919.schema.03.dropaux.sql',1443545060),('phabricator:20140919.schema.04.droptaskproj.sql',1443545060),('phabricator:20140926.schema.01.droprelev.sql',1443545060),('phabricator:20140926.schema.02.droprelreqev.sql',1443545060),('phabricator:20140926.schema.03.dropldapinfo.sql',1443545060),('phabricator:20140926.schema.04.dropoauthinfo.sql',1443545060),('phabricator:20140926.schema.05.dropprojaffil.sql',1443545060),('phabricator:20140926.schema.06.dropsubproject.sql',1443545060),('phabricator:20140926.schema.07.droppondcom.sql',1443545060),('phabricator:20140927.schema.01.dropsearchq.sql',1443545060),('phabricator:20140927.schema.02.pholio1.sql',1443545060),('phabricator:20140927.schema.03.pholio2.sql',1443545060),('phabricator:20140927.schema.04.pholio3.sql',1443545060),('phabricator:20140927.schema.05.phragment1.sql',1443545060),('phabricator:20140927.schema.06.releeph1.sql',1443545060),('phabricator:20141001.schema.01.version.sql',1443545060),('phabricator:20141001.schema.02.taskmail.sql',1443545060),('phabricator:20141002.schema.01.liskcounter.sql',1443545060),('phabricator:20141002.schema.02.draftnull.sql',1443545060),('phabricator:20141004.currency.01.sql',1443545060),('phabricator:20141004.currency.02.sql',1443545060),('phabricator:20141004.currency.03.sql',1443545060),('phabricator:20141004.currency.04.sql',1443545060),('phabricator:20141004.currency.05.sql',1443545060),('phabricator:20141004.currency.06.sql',1443545060),('phabricator:20141004.harborliskcounter.sql',1443545060),('phabricator:20141005.phortuneproduct.sql',1443545060),('phabricator:20141006.phortunecart.sql',1443545060),('phabricator:20141006.phortunemerchant.sql',1443545060),('phabricator:20141006.phortunemerchantx.sql',1443545060),('phabricator:20141007.fundmerchant.sql',1443545060),('phabricator:20141007.fundrisks.sql',1443545060),('phabricator:20141007.fundtotal.sql',1443545060),('phabricator:20141007.phortunecartmerchant.sql',1443545060),('phabricator:20141007.phortunecharge.sql',1443545061),('phabricator:20141007.phortunepayment.sql',1443545061),('phabricator:20141007.phortuneprovider.sql',1443545061),('phabricator:20141007.phortuneproviderx.sql',1443545061),('phabricator:20141008.phortunemerchdesc.sql',1443545061),('phabricator:20141008.phortuneprovdis.sql',1443545061),('phabricator:20141008.phortunerefund.sql',1443545061),('phabricator:20141010.fundmailkey.sql',1443545061),('phabricator:20141011.phortunemerchedit.sql',1443545061),('phabricator:20141012.phortunecartxaction.sql',1443545061),('phabricator:20141013.phortunecartkey.sql',1443545061),('phabricator:20141016.almanac.device.sql',1443545061),('phabricator:20141016.almanac.dxaction.sql',1443545061),('phabricator:20141016.almanac.interface.sql',1443545061),('phabricator:20141016.almanac.network.sql',1443545061),('phabricator:20141016.almanac.nxaction.sql',1443545061),('phabricator:20141016.almanac.service.sql',1443545061),('phabricator:20141016.almanac.sxaction.sql',1443545061),('phabricator:20141017.almanac.binding.sql',1443545061),('phabricator:20141017.almanac.bxaction.sql',1443545061),('phabricator:20141025.phriction.1.xaction.sql',1443545061),('phabricator:20141025.phriction.2.xaction.sql',1443545061),('phabricator:20141025.phriction.mailkey.sql',1443545061),('phabricator:20141103.almanac.1.delprop.sql',1443545061),('phabricator:20141103.almanac.2.addprop.sql',1443545061),('phabricator:20141104.almanac.3.edge.sql',1443545061),('phabricator:20141105.ssh.1.rename.sql',1443545061),('phabricator:20141106.dropold.sql',1443545061),('phabricator:20141106.uniqdrafts.php',1443545061),('phabricator:20141107.phriction.policy.1.sql',1443545061),('phabricator:20141107.phriction.policy.2.php',1443545061),('phabricator:20141107.phriction.popkeys.php',1443545061),('phabricator:20141107.ssh.1.colname.sql',1443545061),('phabricator:20141107.ssh.2.keyhash.sql',1443545061),('phabricator:20141107.ssh.3.keyindex.sql',1443545061),('phabricator:20141107.ssh.4.keymig.php',1443545061),('phabricator:20141107.ssh.5.indexnull.sql',1443545061),('phabricator:20141107.ssh.6.indexkey.sql',1443545061),('phabricator:20141107.ssh.7.colnull.sql',1443545061),('phabricator:20141113.auditdupes.php',1443545061),('phabricator:20141118.diffxaction.sql',1443545061),('phabricator:20141119.commitpedge.sql',1443545061),('phabricator:20141119.differential.diff.policy.sql',1443545061),('phabricator:20141119.sshtrust.sql',1443545061),('phabricator:20141123.taskpriority.1.sql',1443545061),('phabricator:20141123.taskpriority.2.sql',1443545061),('phabricator:20141210.maniphestsubscribersmig.1.sql',1443545061),('phabricator:20141210.maniphestsubscribersmig.2.sql',1443545061),('phabricator:20141210.reposervice.sql',1443545061),('phabricator:20141212.conduittoken.sql',1443545061),('phabricator:20141215.almanacservicetype.sql',1443545061),('phabricator:20141217.almanacdevicelock.sql',1443545061),('phabricator:20141217.almanaclock.sql',1443545061),('phabricator:20141218.maniphestcctxn.php',1443545061),('phabricator:20141222.maniphestprojtxn.php',1443545061),('phabricator:20141223.daemonloguser.sql',1443545061),('phabricator:20141223.daemonobjectphid.sql',1443545061),('phabricator:20141230.pasteeditpolicycolumn.sql',1443545061),('phabricator:20141230.pasteeditpolicyexisting.sql',1443545061),('phabricator:20150102.policyname.php',1443545061),('phabricator:20150102.tasksubscriber.sql',1443545061),('phabricator:20150105.conpsearch.sql',1443545061),('phabricator:20150114.oauthserver.client.policy.sql',1443545062),('phabricator:20150115.applicationemails.sql',1443545062),('phabricator:20150115.trigger.1.sql',1443545062),('phabricator:20150115.trigger.2.sql',1443545062),('phabricator:20150116.maniphestapplicationemails.php',1443545062),('phabricator:20150120.maniphestdefaultauthor.php',1443545062),('phabricator:20150124.subs.1.sql',1443545062),('phabricator:20150129.pastefileapplicationemails.php',1443545062),('phabricator:20150130.phortune.1.subphid.sql',1443545062),('phabricator:20150130.phortune.2.subkey.sql',1443545062),('phabricator:20150131.phortune.1.defaultpayment.sql',1443545062),('phabricator:20150205.authprovider.autologin.sql',1443545062),('phabricator:20150205.daemonenv.sql',1443545062),('phabricator:20150209.invite.sql',1443545062),('phabricator:20150209.oauthclient.trust.sql',1443545062),('phabricator:20150210.invitephid.sql',1443545062),('phabricator:20150212.legalpad.session.1.sql',1443545062),('phabricator:20150212.legalpad.session.2.sql',1443545062),('phabricator:20150219.scratch.nonmutable.sql',1443545062),('phabricator:20150223.daemon.1.id.sql',1443545062),('phabricator:20150223.daemon.2.idlegacy.sql',1443545062),('phabricator:20150223.daemon.3.idkey.sql',1443545062),('phabricator:20150312.filechunk.1.sql',1443545062),('phabricator:20150312.filechunk.2.sql',1443545062),('phabricator:20150312.filechunk.3.sql',1443545062),('phabricator:20150317.conpherence.isroom.1.sql',1443545062),('phabricator:20150317.conpherence.isroom.2.sql',1443545062),('phabricator:20150317.conpherence.policy.sql',1443545062),('phabricator:20150410.nukeruleedit.sql',1443545062),('phabricator:20150420.invoice.1.sql',1443545062),('phabricator:20150420.invoice.2.sql',1443545062),('phabricator:20150425.isclosed.sql',1443545062),('phabricator:20150427.calendar.1.edge.sql',1443545062),('phabricator:20150427.calendar.1.xaction.sql',1443545062),('phabricator:20150427.calendar.2.xaction.sql',1443545062),('phabricator:20150428.calendar.1.iscancelled.sql',1443545062),('phabricator:20150428.calendar.1.name.sql',1443545062),('phabricator:20150429.calendar.1.invitee.sql',1443545062),('phabricator:20150430.calendar.1.policies.sql',1443545062),('phabricator:20150430.multimeter.1.sql',1443545062),('phabricator:20150430.multimeter.2.host.sql',1443545062),('phabricator:20150430.multimeter.3.viewer.sql',1443545062),('phabricator:20150430.multimeter.4.context.sql',1443545062),('phabricator:20150430.multimeter.5.label.sql',1443545062),('phabricator:20150501.calendar.1.reply.sql',1443545062),('phabricator:20150501.calendar.2.reply.php',1443545062),('phabricator:20150501.conpherencepics.sql',1443545062),('phabricator:20150503.repositorysymbols.1.sql',1443545062),('phabricator:20150503.repositorysymbols.2.php',1443545062),('phabricator:20150503.repositorysymbols.3.sql',1443545062),('phabricator:20150504.symbolsproject.1.php',1443545062),('phabricator:20150504.symbolsproject.2.sql',1443545062),('phabricator:20150506.calendarunnamedevents.1.php',1443545062),('phabricator:20150507.calendar.1.isallday.sql',1443545062),('phabricator:20150513.user.cache.1.sql',1443545062),('phabricator:20150514.calendar.status.sql',1443545062),('phabricator:20150514.phame.blog.xaction.sql',1443545062),('phabricator:20150514.user.cache.2.sql',1443545062),('phabricator:20150515.phame.post.xaction.sql',1443545062),('phabricator:20150515.project.mailkey.1.sql',1443545062),('phabricator:20150515.project.mailkey.2.php',1443545062),('phabricator:20150519.calendar.calendaricon.sql',1443545062),('phabricator:20150521.releephrepository.sql',1443545062),('phabricator:20150525.diff.hidden.1.sql',1443545062),('phabricator:20150526.owners.mailkey.1.sql',1443545062),('phabricator:20150526.owners.mailkey.2.php',1443545062),('phabricator:20150526.owners.xaction.sql',1443545062),('phabricator:20150527.calendar.recurringevents.sql',1443545062),('phabricator:20150601.spaces.1.namespace.sql',1443545063),('phabricator:20150601.spaces.2.xaction.sql',1443545063),('phabricator:20150602.mlist.1.sql',1443545063),('phabricator:20150602.mlist.2.php',1443545063),('phabricator:20150604.spaces.1.sql',1443545063),('phabricator:20150605.diviner.edges.sql',1443545063),('phabricator:20150605.diviner.editPolicy.sql',1443545063),('phabricator:20150605.diviner.xaction.sql',1443545063),('phabricator:20150606.mlist.1.php',1443545063),('phabricator:20150609.inline.sql',1443545063),('phabricator:20150609.spaces.1.pholio.sql',1443545063),('phabricator:20150609.spaces.2.maniphest.sql',1443545063),('phabricator:20150610.spaces.1.desc.sql',1443545063),('phabricator:20150610.spaces.2.edge.sql',1443545063),('phabricator:20150610.spaces.3.archive.sql',1443545063),('phabricator:20150611.spaces.1.mailxaction.sql',1443545063),('phabricator:20150611.spaces.2.appmail.sql',1443545063),('phabricator:20150616.divinerrepository.sql',1443545063),('phabricator:20150617.harbor.1.lint.sql',1443545063),('phabricator:20150617.harbor.2.unit.sql',1443545063),('phabricator:20150618.harbor.1.planauto.sql',1443545063),('phabricator:20150618.harbor.2.stepauto.sql',1443545063),('phabricator:20150618.harbor.3.buildauto.sql',1443545063),('phabricator:20150619.conpherencerooms.1.sql',1443545063),('phabricator:20150619.conpherencerooms.2.sql',1443545063),('phabricator:20150619.conpherencerooms.3.sql',1443545063),('phabricator:20150621.phrase.1.sql',1443545063),('phabricator:20150621.phrase.2.sql',1443545063),('phabricator:20150622.bulk.1.job.sql',1443545063),('phabricator:20150622.bulk.2.task.sql',1443545063),('phabricator:20150622.bulk.3.xaction.sql',1443545063),('phabricator:20150622.bulk.4.edge.sql',1443545063),('phabricator:20150622.metamta.1.phid-col.sql',1443545063),('phabricator:20150622.metamta.2.phid-mig.php',1443545063),('phabricator:20150622.metamta.3.phid-key.sql',1443545063),('phabricator:20150622.metamta.4.actor-phid-col.sql',1443545063),('phabricator:20150622.metamta.5.actor-phid-mig.php',1443545063),('phabricator:20150622.metamta.6.actor-phid-key.sql',1443545063),('phabricator:20150624.spaces.1.repo.sql',1443545063),('phabricator:20150626.spaces.1.calendar.sql',1443545063),('phabricator:20150630.herald.1.sql',1443545063),('phabricator:20150630.herald.2.sql',1443545063),('phabricator:20150701.herald.1.sql',1443545063),('phabricator:20150701.herald.2.sql',1443545063),('phabricator:20150702.spaces.1.slowvote.sql',1443545063),('phabricator:20150706.herald.1.sql',1443545063),('phabricator:20150707.herald.1.sql',1443545063),('phabricator:20150708.arcanistproject.sql',1443545063),('phabricator:20150708.herald.1.sql',1443545063),('phabricator:20150708.herald.2.sql',1443545063),('phabricator:20150708.herald.3.sql',1443545063),('phabricator:20150712.badges.1.sql',1443545063),('phabricator:20150714.spaces.countdown.1.sql',1443545063),('phabricator:20150717.herald.1.sql',1443545063),('phabricator:20150719.countdown.1.sql',1443545063),('phabricator:20150719.countdown.2.sql',1443545063),('phabricator:20150719.countdown.3.sql',1443545063),('phabricator:20150721.phurl.1.url.sql',1443545063),('phabricator:20150721.phurl.2.xaction.sql',1443545063),('phabricator:20150721.phurl.3.xactioncomment.sql',1443545063),('phabricator:20150721.phurl.4.url.sql',1443545063),('phabricator:20150721.phurl.5.edge.sql',1443545063),('phabricator:20150721.phurl.6.alias.sql',1443545063),('phabricator:20150721.phurl.7.authorphid.sql',1443545063),('phabricator:20150722.dashboard.1.sql',1443545063),('phabricator:20150722.dashboard.2.sql',1443545063),('phabricator:20150723.countdown.1.sql',1443545063),('phabricator:20150724.badges.comments.1.sql',1443545063),('phabricator:20150724.countdown.comments.1.sql',1443545063),('phabricator:20150725.badges.mailkey.1.sql',1443545063),('phabricator:20150725.badges.mailkey.2.php',1443545063),('phabricator:20150725.badges.viewpolicy.3.sql',1443545063),('phabricator:20150725.countdown.mailkey.1.sql',1443545063),('phabricator:20150725.countdown.mailkey.2.php',1443545063),('phabricator:20150725.slowvote.mailkey.1.sql',1443545063),('phabricator:20150725.slowvote.mailkey.2.php',1443545063),('phabricator:20150727.heraldaction.1.sql',1443545063),('phabricator:20150730.herald.1.sql',1443545063),('phabricator:20150730.herald.2.sql',1443545063),('phabricator:20150730.herald.3.sql',1443545063),('phabricator:20150730.herald.4.sql',1443545063),('phabricator:20150730.herald.5.sql',1443545063),('phabricator:20150730.herald.6.sql',1443545063),('phabricator:20150730.herald.7.sql',1443545063),('phabricator:20150803.herald.1.sql',1443545063),('phabricator:20150803.herald.2.sql',1443545063),('phabricator:20150804.ponder.answer.mailkey.1.sql',1443545063),('phabricator:20150804.ponder.answer.mailkey.2.php',1443545063),('phabricator:20150804.ponder.question.1.sql',1443545064),('phabricator:20150804.ponder.question.2.sql',1443545064),('phabricator:20150804.ponder.question.3.sql',1443545064),('phabricator:20150804.ponder.spaces.4.sql',1443545064),('phabricator:20150805.paste.status.1.sql',1443545064),('phabricator:20150805.paste.status.2.sql',1443545064),('phabricator:20150806.ponder.answer.1.sql',1443545064),('phabricator:20150806.ponder.editpolicy.2.sql',1443545064),('phabricator:20150806.ponder.status.1.sql',1443545064),('phabricator:20150806.ponder.status.2.sql',1443545064),('phabricator:20150806.ponder.status.3.sql',1443545064),('phabricator:20150808.ponder.vote.1.sql',1443545064),('phabricator:20150808.ponder.vote.2.sql',1443545064),('phabricator:20150812.ponder.answer.1.sql',1443545064),('phabricator:20150812.ponder.answer.2.sql',1443545064),('phabricator:20150814.harbormater.artifact.phid.sql',1443545064),('phabricator:20150815.owners.status.1.sql',1443545064),('phabricator:20150815.owners.status.2.sql',1443545064),('phabricator:20150823.nuance.queue.1.sql',1443545064),('phabricator:20150823.nuance.queue.2.sql',1443545064),('phabricator:20150823.nuance.queue.3.sql',1443545064),('phabricator:20150823.nuance.queue.4.sql',1443545064),('phabricator:20150828.ponder.wiki.1.sql',1443545064),('phabricator:20150829.ponder.dupe.1.sql',1443545064),('phabricator:20150904.herald.1.sql',1443545064),('phabricator:20150910.owners.custom.1.sql',1443545064),('phabricator:20150916.drydock.slotlocks.1.sql',1443545064),('phabricator:20150922.drydock.commands.1.sql',1443545064),('phabricator:20150923.drydock.resourceid.1.sql',1443545064),('phabricator:20150923.drydock.resourceid.2.sql',1443545064),('phabricator:20150923.drydock.resourceid.3.sql',1443545064),('phabricator:20150923.drydock.taskid.1.sql',1443545064),('phabricator:20150924.drydock.disable.1.sql',1443545064),('phabricator:20150924.drydock.status.1.sql',1443545064),('phabricator:20150928.drydock.rexpire.1.sql',1443545064),('phabricator:daemonstatus.sql',1443545053),('phabricator:daemonstatuskey.sql',1443545054),('phabricator:daemontaskarchive.sql',1443545054),('phabricator:db.almanac',1443545048),('phabricator:db.audit',1443545048),('phabricator:db.auth',1443545048),('phabricator:db.badges',1443545048),('phabricator:db.cache',1443545048),('phabricator:db.calendar',1443545048),('phabricator:db.chatlog',1443545048),('phabricator:db.conduit',1443545048),('phabricator:db.config',1443545048),('phabricator:db.conpherence',1443545048),('phabricator:db.countdown',1443545048),('phabricator:db.daemon',1443545048),('phabricator:db.dashboard',1443545048),('phabricator:db.differential',1443545048),('phabricator:db.diviner',1443545048),('phabricator:db.doorkeeper',1443545048),('phabricator:db.draft',1443545048),('phabricator:db.drydock',1443545048),('phabricator:db.fact',1443545048),('phabricator:db.feed',1443545048),('phabricator:db.file',1443545048),('phabricator:db.flag',1443545048),('phabricator:db.fund',1443545048),('phabricator:db.harbormaster',1443545048),('phabricator:db.herald',1443545048),('phabricator:db.legalpad',1443545048),('phabricator:db.maniphest',1443545048),('phabricator:db.meta_data',1443545048),('phabricator:db.metamta',1443545048),('phabricator:db.multimeter',1443545048),('phabricator:db.nuance',1443545048),('phabricator:db.oauth_server',1443545048),('phabricator:db.owners',1443545048),('phabricator:db.passphrase',1443545048),('phabricator:db.pastebin',1443545048),('phabricator:db.phame',1443545048),('phabricator:db.phlux',1443545048),('phabricator:db.pholio',1443545048),('phabricator:db.phortune',1443545048),('phabricator:db.phragment',1443545048),('phabricator:db.phrequent',1443545048),('phabricator:db.phriction',1443545048),('phabricator:db.phurl',1443545048),('phabricator:db.policy',1443545048),('phabricator:db.ponder',1443545048),('phabricator:db.project',1443545048),('phabricator:db.releeph',1443545048),('phabricator:db.repository',1443545048),('phabricator:db.search',1443545048),('phabricator:db.slowvote',1443545048),('phabricator:db.spaces',1443545048),('phabricator:db.system',1443545048),('phabricator:db.timeline',1443545048),('phabricator:db.token',1443545048),('phabricator:db.user',1443545048),('phabricator:db.worker',1443545048),('phabricator:db.xhpastview',1443545048),('phabricator:db.xhprof',1443545048),('phabricator:differentialbookmarks.sql',1443545053),('phabricator:draft-metadata.sql',1443545054),('phabricator:dropfileproxyimage.sql',1443545054),('phabricator:drydockresoucetype.sql',1443545054),('phabricator:drydocktaskid.sql',1443545054),('phabricator:edgetype.sql',1443545054),('phabricator:emailtable.sql',1443545053),('phabricator:emailtableport.sql',1443545053),('phabricator:emailtableremove.sql',1443545053),('phabricator:fact-raw.sql',1443545053),('phabricator:harbormasterobject.sql',1443545053),('phabricator:holidays.sql',1443545053),('phabricator:ldapinfo.sql',1443545053),('phabricator:legalpad-mailkey-populate.php',1443545056),('phabricator:legalpad-mailkey.sql',1443545056),('phabricator:liskcounters-task.sql',1443545054),('phabricator:liskcounters.php',1443545054),('phabricator:liskcounters.sql',1443545054),('phabricator:maniphestxcache.sql',1443545053),('phabricator:markupcache.sql',1443545053),('phabricator:migrate-differential-dependencies.php',1443545053),('phabricator:migrate-maniphest-dependencies.php',1443545053),('phabricator:migrate-maniphest-revisions.php',1443545053),('phabricator:migrate-project-edges.php',1443545053),('phabricator:owners-exclude.sql',1443545054),('phabricator:pastepolicy.sql',1443545054),('phabricator:phameblog.sql',1443545053),('phabricator:phamedomain.sql',1443545054),('phabricator:phameoneblog.sql',1443545054),('phabricator:phamepolicy.sql',1443545054),('phabricator:phiddrop.sql',1443545053),('phabricator:pholio.sql',1443545054),('phabricator:policy-project.sql',1443545054),('phabricator:ponder-comments.sql',1443545054),('phabricator:ponder-mailkey-populate.php',1443545054),('phabricator:ponder-mailkey.sql',1443545054),('phabricator:ponder.sql',1443545054),('phabricator:releeph.sql',1443545054),('phabricator:repository-lint.sql',1443545054),('phabricator:statustxt.sql',1443545054),('phabricator:symbolcontexts.sql',1443545053),('phabricator:testdatabase.sql',1443545053),('phabricator:threadtopic.sql',1443545053),('phabricator:userstatus.sql',1443545053),('phabricator:usertranslation.sql',1443545053),('phabricator:xhprof.sql',1443545054); CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$NAMESPACE}_metamta` /*!40100 DEFAULT CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} */; @@ -1393,14 +1618,39 @@ CREATE TABLE `metamta_applicationemail` ( `configData` longtext COLLATE {$COLLATE_TEXT} NOT NULL, `dateCreated` int(10) unsigned NOT NULL, `dateModified` int(10) unsigned NOT NULL, + `spacePHID` varbinary(64) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `key_address` (`address`), UNIQUE KEY `key_phid` (`phid`), - KEY `key_application` (`applicationPHID`) + KEY `key_application` (`applicationPHID`), + KEY `key_space` (`spacePHID`) ) ENGINE=MyISAM DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; +CREATE TABLE `metamta_applicationemailtransaction` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `phid` varbinary(64) NOT NULL, + `authorPHID` varbinary(64) NOT NULL, + `objectPHID` varbinary(64) NOT NULL, + `viewPolicy` varbinary(64) NOT NULL, + `editPolicy` varbinary(64) NOT NULL, + `commentPHID` varbinary(64) DEFAULT NULL, + `commentVersion` int(10) unsigned NOT NULL, + `transactionType` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, + `oldValue` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `newValue` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `contentSource` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `metadata` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_phid` (`phid`), + KEY `key_object` (`objectPHID`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + CREATE TABLE `metamta_mail` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `phid` varbinary(64) NOT NULL, + `actorPHID` varbinary(64) DEFAULT NULL, `parameters` longtext COLLATE {$COLLATE_TEXT} NOT NULL, `status` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, `message` longtext COLLATE {$COLLATE_TEXT}, @@ -1408,8 +1658,10 @@ CREATE TABLE `metamta_mail` ( `dateCreated` int(10) unsigned NOT NULL, `dateModified` int(10) unsigned NOT NULL, PRIMARY KEY (`id`), + UNIQUE KEY `key_phid` (`phid`), KEY `relatedPHID` (`relatedPHID`), KEY `key_created` (`dateCreated`), + KEY `key_actorPHID` (`actorPHID`), KEY `status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; @@ -1522,6 +1774,35 @@ CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$NAMESPACE}_owners` /*!40100 DEFAULT USE `{$NAMESPACE}_owners`; +CREATE TABLE `owners_customfieldnumericindex` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `objectPHID` varbinary(64) NOT NULL, + `indexKey` binary(12) NOT NULL, + `indexValue` bigint(20) NOT NULL, + PRIMARY KEY (`id`), + KEY `key_join` (`objectPHID`,`indexKey`,`indexValue`), + KEY `key_find` (`indexKey`,`indexValue`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `owners_customfieldstorage` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `objectPHID` varbinary(64) NOT NULL, + `fieldIndex` binary(12) NOT NULL, + `fieldValue` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `objectPHID` (`objectPHID`,`fieldIndex`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `owners_customfieldstringindex` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `objectPHID` varbinary(64) NOT NULL, + `indexKey` binary(12) NOT NULL, + `indexValue` longtext CHARACTER SET {$CHARSET_SORT} COLLATE {$COLLATE_SORT} NOT NULL, + PRIMARY KEY (`id`), + KEY `key_join` (`objectPHID`,`indexKey`,`indexValue`(64)), + KEY `key_find` (`indexKey`,`indexValue`(64)) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + CREATE TABLE `owners_owner` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `packageID` int(10) unsigned NOT NULL, @@ -1539,11 +1820,34 @@ CREATE TABLE `owners_package` ( `description` longtext COLLATE {$COLLATE_TEXT} NOT NULL, `primaryOwnerPHID` varbinary(64) DEFAULT NULL, `auditingEnabled` tinyint(1) NOT NULL DEFAULT '0', + `mailKey` binary(20) NOT NULL, + `status` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `phid` (`phid`), UNIQUE KEY `name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; +CREATE TABLE `owners_packagetransaction` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `phid` varbinary(64) NOT NULL, + `authorPHID` varbinary(64) NOT NULL, + `objectPHID` varbinary(64) NOT NULL, + `viewPolicy` varbinary(64) NOT NULL, + `editPolicy` varbinary(64) NOT NULL, + `commentPHID` varbinary(64) DEFAULT NULL, + `commentVersion` int(10) unsigned NOT NULL, + `transactionType` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, + `oldValue` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `newValue` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `contentSource` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `metadata` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_phid` (`phid`), + KEY `key_object` (`objectPHID`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + CREATE TABLE `owners_path` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `packageID` int(10) unsigned NOT NULL, @@ -1589,12 +1893,15 @@ CREATE TABLE `pastebin_paste` ( `viewPolicy` varbinary(64) DEFAULT NULL, `editPolicy` varbinary(64) NOT NULL, `mailKey` binary(20) NOT NULL, + `spacePHID` varbinary(64) DEFAULT NULL, + `status` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `key_phid` (`phid`), KEY `parentPHID` (`parentPHID`), KEY `authorPHID` (`authorPHID`), KEY `key_dateCreated` (`dateCreated`), - KEY `key_language` (`language`) + KEY `key_language` (`language`), + KEY `key_space` (`spacePHID`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; CREATE TABLE `pastebin_pastetransaction` ( @@ -1678,6 +1985,27 @@ CREATE TABLE `phame_blog` ( UNIQUE KEY `domain` (`domain`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; +CREATE TABLE `phame_blogtransaction` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `phid` varbinary(64) NOT NULL, + `authorPHID` varbinary(64) NOT NULL, + `objectPHID` varbinary(64) NOT NULL, + `viewPolicy` varbinary(64) NOT NULL, + `editPolicy` varbinary(64) NOT NULL, + `commentPHID` varbinary(64) DEFAULT NULL, + `commentVersion` int(10) unsigned NOT NULL, + `transactionType` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, + `oldValue` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `newValue` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `contentSource` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `metadata` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_phid` (`phid`), + KEY `key_object` (`objectPHID`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + CREATE TABLE `phame_post` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `phid` varbinary(64) NOT NULL, @@ -1697,6 +2025,27 @@ CREATE TABLE `phame_post` ( KEY `bloggerPosts` (`bloggerPHID`,`visibility`,`datePublished`,`id`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; +CREATE TABLE `phame_posttransaction` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `phid` varbinary(64) NOT NULL, + `authorPHID` varbinary(64) NOT NULL, + `objectPHID` varbinary(64) NOT NULL, + `viewPolicy` varbinary(64) NOT NULL, + `editPolicy` varbinary(64) NOT NULL, + `commentPHID` varbinary(64) DEFAULT NULL, + `commentVersion` int(10) unsigned NOT NULL, + `transactionType` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, + `oldValue` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `newValue` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `contentSource` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `metadata` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_phid` (`phid`), + KEY `key_object` (`objectPHID`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$NAMESPACE}_phriction` /*!40100 DEFAULT CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} */; USE `{$NAMESPACE}_phriction`; @@ -1832,6 +2181,7 @@ CREATE TABLE `project` ( `profileImagePHID` varbinary(64) DEFAULT NULL, `icon` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, `color` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, + `mailKey` binary(20) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `phid` (`phid`), UNIQUE KEY `name` (`name`), @@ -1996,23 +2346,13 @@ CREATE TABLE `repository` ( `pushPolicy` varbinary(64) NOT NULL, `credentialPHID` varbinary(64) DEFAULT NULL, `almanacServicePHID` varbinary(64) DEFAULT NULL, + `spacePHID` varbinary(64) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `callsign` (`callsign`), UNIQUE KEY `phid` (`phid`), KEY `key_vcs` (`versionControlSystem`), - KEY `key_name` (`name`(128)) -) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; - -CREATE TABLE `repository_arcanistproject` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `phid` varbinary(64) NOT NULL, - `name` varchar(128) COLLATE {$COLLATE_TEXT} NOT NULL, - `repositoryID` int(10) unsigned DEFAULT NULL, - `symbolIndexLanguages` longtext COLLATE {$COLLATE_TEXT} NOT NULL, - `symbolIndexProjects` longtext COLLATE {$COLLATE_TEXT} NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `phid` (`phid`), - UNIQUE KEY `name` (`name`) + KEY `key_name` (`name`(128)), + KEY `key_space` (`spacePHID`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; CREATE TABLE `repository_auditrequest` ( @@ -2060,7 +2400,9 @@ CREATE TABLE `repository_commit` ( UNIQUE KEY `key_commit_identity` (`commitIdentifier`,`repositoryID`), KEY `repositoryID_2` (`repositoryID`,`epoch`), KEY `authorPHID` (`authorPHID`,`auditStatus`,`epoch`), - KEY `repositoryID` (`repositoryID`,`importStatus`) + KEY `repositoryID` (`repositoryID`,`importStatus`), + KEY `key_epoch` (`epoch`), + KEY `key_author` (`authorPHID`,`epoch`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; CREATE TABLE `repository_commitdata` ( @@ -2201,6 +2543,7 @@ CREATE TABLE `repository_refcursor` ( `refNameRaw` longblob NOT NULL, `refNameEncoding` varchar(16) COLLATE {$COLLATE_TEXT} DEFAULT NULL, `commitIdentifier` varchar(40) COLLATE {$COLLATE_TEXT} NOT NULL, + `isClosed` tinyint(1) NOT NULL, PRIMARY KEY (`id`), KEY `key_cursor` (`repositoryPHID`,`refType`,`refNameHash`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; @@ -2226,7 +2569,7 @@ CREATE TABLE `repository_summary` ( ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; CREATE TABLE `repository_symbol` ( - `arcanistProjectID` int(10) unsigned NOT NULL, + `repositoryPHID` varbinary(64) NOT NULL, `symbolContext` varchar(128) COLLATE {$COLLATE_TEXT} NOT NULL, `symbolName` varchar(128) COLLATE {$COLLATE_TEXT} NOT NULL, `symbolType` varchar(12) COLLATE {$COLLATE_TEXT} NOT NULL, @@ -2385,8 +2728,11 @@ CREATE TABLE `slowvote_poll` ( `description` longtext COLLATE {$COLLATE_TEXT} NOT NULL, `viewPolicy` varbinary(64) NOT NULL, `isClosed` tinyint(1) NOT NULL, + `spacePHID` varbinary(64) DEFAULT NULL, + `mailKey` binary(20) NOT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `phid` (`phid`) + UNIQUE KEY `phid` (`phid`), + KEY `key_space` (`spacePHID`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; CREATE TABLE `slowvote_transaction` ( @@ -2490,6 +2836,10 @@ CREATE TABLE `user` ( `isApproved` int(10) unsigned NOT NULL, `accountSecret` binary(64) NOT NULL, `isEnrolledInMultiFactor` tinyint(1) NOT NULL DEFAULT '0', + `profileImageCache` varchar(255) COLLATE {$COLLATE_TEXT} DEFAULT NULL, + `availabilityCache` varchar(255) COLLATE {$COLLATE_TEXT} DEFAULT NULL, + `availabilityCacheTTL` int(10) unsigned DEFAULT NULL, + `isMailingList` tinyint(1) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `userName` (`userName`), UNIQUE KEY `phid` (`phid`), @@ -2651,6 +3001,24 @@ CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$NAMESPACE}_worker` /*!40100 DEFAULT USE `{$NAMESPACE}_worker`; +CREATE TABLE `edge` ( + `src` varbinary(64) NOT NULL, + `type` int(10) unsigned NOT NULL, + `dst` varbinary(64) NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `seq` int(10) unsigned NOT NULL, + `dataID` int(10) unsigned DEFAULT NULL, + PRIMARY KEY (`src`,`type`,`dst`), + UNIQUE KEY `key_dst` (`dst`,`type`,`src`), + KEY `src` (`src`,`type`,`dateCreated`,`seq`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `edgedata` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `data` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + CREATE TABLE `lisk_counter` ( `counterName` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, `counterValue` bigint(20) unsigned NOT NULL, @@ -2698,6 +3066,55 @@ CREATE TABLE `worker_archivetask` ( KEY `key_object` (`objectPHID`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; +CREATE TABLE `worker_bulkjob` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `phid` varbinary(64) NOT NULL, + `authorPHID` varbinary(64) NOT NULL, + `jobTypeKey` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, + `status` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, + `parameters` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `size` int(10) unsigned NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_phid` (`phid`), + KEY `key_type` (`jobTypeKey`), + KEY `key_author` (`authorPHID`), + KEY `key_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `worker_bulkjobtransaction` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `phid` varbinary(64) NOT NULL, + `authorPHID` varbinary(64) NOT NULL, + `objectPHID` varbinary(64) NOT NULL, + `viewPolicy` varbinary(64) NOT NULL, + `editPolicy` varbinary(64) NOT NULL, + `commentPHID` varbinary(64) DEFAULT NULL, + `commentVersion` int(10) unsigned NOT NULL, + `transactionType` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, + `oldValue` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `newValue` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `contentSource` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `metadata` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_phid` (`phid`), + KEY `key_object` (`objectPHID`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `worker_bulktask` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `bulkJobPHID` varbinary(64) NOT NULL, + `objectPHID` varbinary(64) NOT NULL, + `status` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, + `data` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + PRIMARY KEY (`id`), + KEY `key_job` (`bulkJobPHID`,`status`), + KEY `key_object` (`objectPHID`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + CREATE TABLE `worker_taskdata` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `data` longtext COLLATE {$COLLATE_TEXT} NOT NULL, @@ -2837,12 +3254,14 @@ CREATE TABLE `ponder_answer` ( `content` longtext COLLATE {$COLLATE_TEXT} NOT NULL, `dateCreated` int(10) unsigned NOT NULL, `dateModified` int(10) unsigned NOT NULL, - `contentSource` longtext COLLATE {$COLLATE_TEXT}, + `mailKey` binary(20) NOT NULL, + `status` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `phid` (`phid`), UNIQUE KEY `key_oneanswerperquestion` (`questionID`,`authorPHID`), KEY `questionID` (`questionID`), - KEY `authorPHID` (`authorPHID`) + KEY `authorPHID` (`authorPHID`), + KEY `status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; CREATE TABLE `ponder_answertransaction` ( @@ -2888,21 +3307,22 @@ CREATE TABLE `ponder_question` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) COLLATE {$COLLATE_TEXT} NOT NULL, `phid` varbinary(64) NOT NULL, - `voteCount` int(10) NOT NULL, `authorPHID` varbinary(64) NOT NULL, - `status` int(10) unsigned NOT NULL, + `status` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, `content` longtext COLLATE {$COLLATE_TEXT} NOT NULL, `dateCreated` int(10) unsigned NOT NULL, `dateModified` int(10) unsigned NOT NULL, `contentSource` longtext COLLATE {$COLLATE_TEXT}, - `heat` double NOT NULL, `answerCount` int(10) unsigned NOT NULL, `mailKey` binary(20) NOT NULL, + `viewPolicy` varbinary(64) NOT NULL, + `spacePHID` varbinary(64) DEFAULT NULL, + `answerWiki` longtext COLLATE {$COLLATE_TEXT} NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `phid` (`phid`), KEY `authorPHID` (`authorPHID`), - KEY `heat` (`heat`), - KEY `status` (`status`) + KEY `status` (`status`), + KEY `key_space` (`spacePHID`) ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; CREATE TABLE `ponder_questiontransaction` ( @@ -3016,9 +3436,11 @@ CREATE TABLE `pholio_mock` ( `dateModified` int(10) unsigned NOT NULL, `status` varchar(12) COLLATE {$COLLATE_TEXT} NOT NULL, `editPolicy` varbinary(64) NOT NULL, + `spacePHID` varbinary(64) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `phid` (`phid`), - KEY `authorPHID` (`authorPHID`) + KEY `authorPHID` (`authorPHID`), + KEY `key_space` (`spacePHID`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; CREATE TABLE `pholio_transaction` ( @@ -3104,8 +3526,12 @@ CREATE TABLE `conpherence_thread` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `phid` varbinary(64) NOT NULL, `title` varchar(255) COLLATE {$COLLATE_TEXT} DEFAULT NULL, + `imagePHIDs` longtext COLLATE {$COLLATE_TEXT} NOT NULL, `messageCount` bigint(20) unsigned NOT NULL, `recentParticipantPHIDs` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `viewPolicy` varbinary(64) NOT NULL, + `editPolicy` varbinary(64) NOT NULL, + `joinPolicy` varbinary(64) NOT NULL, `mailKey` varchar(20) COLLATE {$COLLATE_TEXT} NOT NULL, `dateCreated` int(10) unsigned NOT NULL, `dateModified` int(10) unsigned NOT NULL, @@ -3311,7 +3737,6 @@ CREATE TABLE `releeph_project` ( `name` varchar(128) COLLATE {$COLLATE_TEXT} NOT NULL, `trunkBranch` varchar(255) COLLATE {$COLLATE_TEXT} NOT NULL, `repositoryPHID` varbinary(64) NOT NULL, - `arcanistProjectID` int(10) unsigned NOT NULL, `createdByUserPHID` varbinary(64) NOT NULL, `isActive` tinyint(1) NOT NULL DEFAULT '1', `details` longtext COLLATE {$COLLATE_TEXT} NOT NULL, @@ -3487,6 +3912,7 @@ CREATE TABLE `phortune_cart` ( `merchantPHID` varbinary(64) NOT NULL, `mailKey` binary(20) NOT NULL, `subscriptionPHID` varbinary(64) DEFAULT NULL, + `isInvoice` tinyint(1) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `key_phid` (`phid`), KEY `key_account` (`accountPHID`), @@ -3719,7 +4145,9 @@ CREATE TABLE `diviner_livebook` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `phid` varbinary(64) NOT NULL, `name` varchar(64) COLLATE {$COLLATE_TEXT} NOT NULL, + `repositoryPHID` varbinary(64) DEFAULT NULL, `viewPolicy` varbinary(64) NOT NULL, + `editPolicy` varbinary(64) NOT NULL, `dateCreated` int(10) unsigned NOT NULL, `dateModified` int(10) unsigned NOT NULL, `configurationData` longtext COLLATE {$COLLATE_TEXT} NOT NULL, @@ -3728,10 +4156,32 @@ CREATE TABLE `diviner_livebook` ( UNIQUE KEY `phid` (`phid`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; +CREATE TABLE `diviner_livebooktransaction` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `phid` varbinary(64) NOT NULL, + `authorPHID` varbinary(64) NOT NULL, + `objectPHID` varbinary(64) NOT NULL, + `viewPolicy` varbinary(64) NOT NULL, + `editPolicy` varbinary(64) NOT NULL, + `commentPHID` varbinary(64) DEFAULT NULL, + `commentVersion` int(10) unsigned NOT NULL, + `transactionType` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, + `oldValue` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `newValue` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `contentSource` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `metadata` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_phid` (`phid`), + KEY `key_object` (`objectPHID`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + CREATE TABLE `diviner_livesymbol` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `phid` varbinary(64) NOT NULL, `bookPHID` varbinary(64) NOT NULL, + `repositoryPHID` varbinary(64) DEFAULT NULL, `context` varchar(255) COLLATE {$COLLATE_TEXT} DEFAULT NULL, `type` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, `name` varchar(255) COLLATE {$COLLATE_TEXT} NOT NULL, @@ -3754,6 +4204,24 @@ CREATE TABLE `diviner_livesymbol` ( KEY `name` (`name`(64)) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; +CREATE TABLE `edge` ( + `src` varbinary(64) NOT NULL, + `type` int(10) unsigned NOT NULL, + `dst` varbinary(64) NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `seq` int(10) unsigned NOT NULL, + `dataID` int(10) unsigned DEFAULT NULL, + PRIMARY KEY (`src`,`type`,`dst`), + UNIQUE KEY `key_dst` (`dst`,`type`,`src`), + KEY `src` (`src`,`type`,`dateCreated`,`seq`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `edgedata` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `data` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$NAMESPACE}_auth` /*!40100 DEFAULT CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} */; USE `{$NAMESPACE}_auth`; @@ -4061,12 +4529,13 @@ CREATE TABLE `nuance_item` ( `mailKey` binary(20) NOT NULL, `dateCreated` int(10) unsigned NOT NULL, `dateModified` int(10) unsigned NOT NULL, - `dateNuanced` int(10) unsigned NOT NULL, + `queuePHID` varbinary(64) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `key_phid` (`phid`), - KEY `key_source` (`sourcePHID`,`status`,`dateNuanced`,`id`), - KEY `key_owner` (`ownerPHID`,`status`,`dateNuanced`,`id`), - KEY `key_contacter` (`requestorPHID`,`status`,`dateNuanced`,`id`) + KEY `key_source` (`sourcePHID`,`status`), + KEY `key_owner` (`ownerPHID`,`status`), + KEY `key_requestor` (`requestorPHID`,`status`), + KEY `key_queue` (`queuePHID`,`status`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; CREATE TABLE `nuance_itemtransaction` ( @@ -4121,19 +4590,6 @@ CREATE TABLE `nuance_queue` ( UNIQUE KEY `key_phid` (`phid`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; -CREATE TABLE `nuance_queueitem` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `queuePHID` varbinary(64) NOT NULL, - `itemPHID` varbinary(64) NOT NULL, - `itemStatus` int(10) unsigned NOT NULL, - `itemDateNuanced` int(10) unsigned NOT NULL, - `dateCreated` int(10) unsigned NOT NULL, - `dateModified` int(10) unsigned NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `key_one_per_queue` (`itemPHID`,`queuePHID`), - KEY `key_queue` (`queuePHID`,`itemStatus`,`itemDateNuanced`,`id`) -) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; - CREATE TABLE `nuance_queuetransaction` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `phid` varbinary(64) NOT NULL, @@ -4247,6 +4703,7 @@ CREATE TABLE `nuance_source` ( `editPolicy` varbinary(64) NOT NULL, `dateCreated` int(10) unsigned NOT NULL, `dateModified` int(10) unsigned NOT NULL, + `defaultQueuePHID` varbinary(64) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `key_phid` (`phid`), KEY `key_type` (`type`,`dateModified`) @@ -4329,11 +4786,14 @@ CREATE TABLE `passphrase_credential` ( `dateModified` int(10) unsigned NOT NULL, `isLocked` tinyint(1) NOT NULL, `allowConduit` tinyint(1) NOT NULL DEFAULT '0', + `authorPHID` varbinary(64) NOT NULL, + `spacePHID` varbinary(64) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `key_phid` (`phid`), UNIQUE KEY `key_secret` (`secretID`), KEY `key_type` (`credentialType`), - KEY `key_provides` (`providesType`) + KEY `key_provides` (`providesType`), + KEY `key_space` (`spacePHID`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; CREATE TABLE `passphrase_credentialtransaction` ( @@ -4449,6 +4909,7 @@ CREATE TABLE `dashboard` ( `editPolicy` varbinary(64) NOT NULL, `dateCreated` int(10) unsigned NOT NULL, `dateModified` int(10) unsigned NOT NULL, + `status` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `key_phid` (`phid`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; @@ -4863,3 +5324,275 @@ CREATE TABLE `edgedata` ( `data` longtext COLLATE {$COLLATE_TEXT} NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$NAMESPACE}_multimeter` /*!40100 DEFAULT CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} */; + +USE `{$NAMESPACE}_multimeter`; + +CREATE TABLE `multimeter_context` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `nameHash` binary(12) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_hash` (`nameHash`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `multimeter_event` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `eventType` int(10) unsigned NOT NULL, + `eventLabelID` int(10) unsigned NOT NULL, + `resourceCost` bigint(20) NOT NULL, + `sampleRate` int(10) unsigned NOT NULL, + `eventContextID` int(10) unsigned NOT NULL, + `eventHostID` int(10) unsigned NOT NULL, + `eventViewerID` int(10) unsigned NOT NULL, + `epoch` int(10) unsigned NOT NULL, + `requestKey` binary(12) NOT NULL, + PRIMARY KEY (`id`), + KEY `key_request` (`requestKey`), + KEY `key_type` (`eventType`,`epoch`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `multimeter_host` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `nameHash` binary(12) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_hash` (`nameHash`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `multimeter_label` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `nameHash` binary(12) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_hash` (`nameHash`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `multimeter_viewer` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `nameHash` binary(12) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_hash` (`nameHash`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$NAMESPACE}_spaces` /*!40100 DEFAULT CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} */; + +USE `{$NAMESPACE}_spaces`; + +CREATE TABLE `edge` ( + `src` varbinary(64) NOT NULL, + `type` int(10) unsigned NOT NULL, + `dst` varbinary(64) NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `seq` int(10) unsigned NOT NULL, + `dataID` int(10) unsigned DEFAULT NULL, + PRIMARY KEY (`src`,`type`,`dst`), + UNIQUE KEY `key_dst` (`dst`,`type`,`src`), + KEY `src` (`src`,`type`,`dateCreated`,`seq`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `edgedata` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `data` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `spaces_namespace` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `phid` varbinary(64) NOT NULL, + `namespaceName` varchar(255) COLLATE {$COLLATE_TEXT} NOT NULL, + `viewPolicy` varbinary(64) NOT NULL, + `editPolicy` varbinary(64) NOT NULL, + `isDefaultNamespace` tinyint(1) DEFAULT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + `description` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `isArchived` tinyint(1) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_phid` (`phid`), + UNIQUE KEY `key_default` (`isDefaultNamespace`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `spaces_namespacetransaction` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `phid` varbinary(64) NOT NULL, + `authorPHID` varbinary(64) NOT NULL, + `objectPHID` varbinary(64) NOT NULL, + `viewPolicy` varbinary(64) NOT NULL, + `editPolicy` varbinary(64) NOT NULL, + `commentPHID` varbinary(64) DEFAULT NULL, + `commentVersion` int(10) unsigned NOT NULL, + `transactionType` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, + `oldValue` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `newValue` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `contentSource` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `metadata` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_phid` (`phid`), + KEY `key_object` (`objectPHID`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$NAMESPACE}_phurl` /*!40100 DEFAULT CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} */; + +USE `{$NAMESPACE}_phurl`; + +CREATE TABLE `edge` ( + `src` varbinary(64) NOT NULL, + `type` int(10) unsigned NOT NULL, + `dst` varbinary(64) NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `seq` int(10) unsigned NOT NULL, + `dataID` int(10) unsigned DEFAULT NULL, + PRIMARY KEY (`src`,`type`,`dst`), + UNIQUE KEY `key_dst` (`dst`,`type`,`src`), + KEY `src` (`src`,`type`,`dateCreated`,`seq`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `edgedata` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `data` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `phurl_url` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `phid` varbinary(64) NOT NULL, + `name` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `longURL` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `description` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `viewPolicy` varbinary(64) NOT NULL, + `editPolicy` varbinary(64) NOT NULL, + `spacePHID` varbinary(64) DEFAULT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + `alias` varchar(64) CHARACTER SET {$CHARSET_SORT} COLLATE {$COLLATE_SORT} DEFAULT NULL, + `authorPHID` varbinary(64) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_phid` (`phid`), + UNIQUE KEY `key_instance` (`alias`), + KEY `key_author` (`authorPHID`), + KEY `key_space` (`spacePHID`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `phurl_urltransaction` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `phid` varbinary(64) NOT NULL, + `authorPHID` varbinary(64) NOT NULL, + `objectPHID` varbinary(64) NOT NULL, + `viewPolicy` varbinary(64) NOT NULL, + `editPolicy` varbinary(64) NOT NULL, + `commentPHID` varbinary(64) DEFAULT NULL, + `commentVersion` int(10) unsigned NOT NULL, + `transactionType` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, + `oldValue` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `newValue` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `contentSource` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `metadata` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_phid` (`phid`), + KEY `key_object` (`objectPHID`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `phurl_urltransaction_comment` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `phid` varbinary(64) NOT NULL, + `transactionPHID` varbinary(64) DEFAULT NULL, + `authorPHID` varbinary(64) NOT NULL, + `viewPolicy` varbinary(64) NOT NULL, + `editPolicy` varbinary(64) NOT NULL, + `commentVersion` int(10) unsigned NOT NULL, + `content` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `contentSource` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `isDeleted` tinyint(1) NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_phid` (`phid`), + UNIQUE KEY `key_version` (`transactionPHID`,`commentVersion`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$NAMESPACE}_badges` /*!40100 DEFAULT CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} */; + +USE `{$NAMESPACE}_badges`; + +CREATE TABLE `badges_badge` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `phid` varbinary(64) NOT NULL, + `name` varchar(255) COLLATE {$COLLATE_TEXT} NOT NULL, + `flavor` varchar(255) COLLATE {$COLLATE_TEXT} NOT NULL, + `description` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `icon` varchar(255) COLLATE {$COLLATE_TEXT} NOT NULL, + `quality` varchar(255) COLLATE {$COLLATE_TEXT} NOT NULL, + `status` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + `editPolicy` varbinary(64) NOT NULL, + `creatorPHID` varbinary(64) NOT NULL, + `mailKey` binary(20) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_phid` (`phid`), + KEY `key_creator` (`creatorPHID`,`dateModified`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `badges_transaction` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `phid` varbinary(64) NOT NULL, + `authorPHID` varbinary(64) NOT NULL, + `objectPHID` varbinary(64) NOT NULL, + `viewPolicy` varbinary(64) NOT NULL, + `editPolicy` varbinary(64) NOT NULL, + `commentPHID` varbinary(64) DEFAULT NULL, + `commentVersion` int(10) unsigned NOT NULL, + `transactionType` varchar(32) COLLATE {$COLLATE_TEXT} NOT NULL, + `oldValue` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `newValue` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `contentSource` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `metadata` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_phid` (`phid`), + KEY `key_object` (`objectPHID`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `badges_transaction_comment` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `phid` varbinary(64) NOT NULL, + `transactionPHID` varbinary(64) DEFAULT NULL, + `authorPHID` varbinary(64) NOT NULL, + `viewPolicy` varbinary(64) NOT NULL, + `editPolicy` varbinary(64) NOT NULL, + `commentVersion` int(10) unsigned NOT NULL, + `content` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `contentSource` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + `isDeleted` tinyint(1) NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_phid` (`phid`), + UNIQUE KEY `key_version` (`transactionPHID`,`commentVersion`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `edge` ( + `src` varbinary(64) NOT NULL, + `type` int(10) unsigned NOT NULL, + `dst` varbinary(64) NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `seq` int(10) unsigned NOT NULL, + `dataID` int(10) unsigned DEFAULT NULL, + PRIMARY KEY (`src`,`type`,`dst`), + UNIQUE KEY `key_dst` (`dst`,`type`,`src`), + KEY `src` (`src`,`type`,`dateCreated`,`seq`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; + +CREATE TABLE `edgedata` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `data` longtext COLLATE {$COLLATE_TEXT} NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; diff --git a/scripts/setup/manage_garbage.php b/scripts/setup/manage_garbage.php new file mode 100755 index 0000000000..ba727eab60 --- /dev/null +++ b/scripts/setup/manage_garbage.php @@ -0,0 +1,21 @@ +#!/usr/bin/env php +setTagline(pht('manage garbage colletors')); +$args->setSynopsis(<<parseStandardArguments(); + +$workflows = id(new PhutilClassMapQuery()) + ->setAncestorClass('PhabricatorGarbageCollectorManagementWorkflow') + ->execute(); +$workflows[] = new PhutilHelpArgumentWorkflow(); +$args->parseWorkflows($workflows); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 064d80740a..61b4d1f390 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -796,7 +796,6 @@ phutil_register_library_map(array( 'DoorkeeperSchemaSpec' => 'applications/doorkeeper/storage/DoorkeeperSchemaSpec.php', 'DoorkeeperTagView' => 'applications/doorkeeper/view/DoorkeeperTagView.php', 'DoorkeeperTagsController' => 'applications/doorkeeper/controller/DoorkeeperTagsController.php', - 'DrydockAllocatorWorker' => 'applications/drydock/worker/DrydockAllocatorWorker.php', 'DrydockAlmanacServiceHostBlueprintImplementation' => 'applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php', 'DrydockApacheWebrootInterface' => 'applications/drydock/interface/webroot/DrydockApacheWebrootInterface.php', 'DrydockBlueprint' => 'applications/drydock/storage/DrydockBlueprint.php', @@ -830,25 +829,33 @@ phutil_register_library_map(array( 'DrydockFilesystemInterface' => 'applications/drydock/interface/filesystem/DrydockFilesystemInterface.php', 'DrydockInterface' => 'applications/drydock/interface/DrydockInterface.php', 'DrydockLease' => 'applications/drydock/storage/DrydockLease.php', + 'DrydockLeaseAcquiredLogType' => 'applications/drydock/logtype/DrydockLeaseAcquiredLogType.php', + 'DrydockLeaseActivatedLogType' => 'applications/drydock/logtype/DrydockLeaseActivatedLogType.php', + 'DrydockLeaseActivationFailureLogType' => 'applications/drydock/logtype/DrydockLeaseActivationFailureLogType.php', + 'DrydockLeaseActivationYieldLogType' => 'applications/drydock/logtype/DrydockLeaseActivationYieldLogType.php', 'DrydockLeaseController' => 'applications/drydock/controller/DrydockLeaseController.php', 'DrydockLeaseDatasource' => 'applications/drydock/typeahead/DrydockLeaseDatasource.php', - 'DrydockLeaseDestroyWorker' => 'applications/drydock/worker/DrydockLeaseDestroyWorker.php', + 'DrydockLeaseDestroyedLogType' => 'applications/drydock/logtype/DrydockLeaseDestroyedLogType.php', 'DrydockLeaseListController' => 'applications/drydock/controller/DrydockLeaseListController.php', 'DrydockLeaseListView' => 'applications/drydock/view/DrydockLeaseListView.php', 'DrydockLeasePHIDType' => 'applications/drydock/phid/DrydockLeasePHIDType.php', 'DrydockLeaseQuery' => 'applications/drydock/query/DrydockLeaseQuery.php', + 'DrydockLeaseQueuedLogType' => 'applications/drydock/logtype/DrydockLeaseQueuedLogType.php', 'DrydockLeaseReleaseController' => 'applications/drydock/controller/DrydockLeaseReleaseController.php', + 'DrydockLeaseReleasedLogType' => 'applications/drydock/logtype/DrydockLeaseReleasedLogType.php', 'DrydockLeaseSearchEngine' => 'applications/drydock/query/DrydockLeaseSearchEngine.php', 'DrydockLeaseStatus' => 'applications/drydock/constants/DrydockLeaseStatus.php', 'DrydockLeaseUpdateWorker' => 'applications/drydock/worker/DrydockLeaseUpdateWorker.php', 'DrydockLeaseViewController' => 'applications/drydock/controller/DrydockLeaseViewController.php', - 'DrydockLeaseWorker' => 'applications/drydock/worker/DrydockLeaseWorker.php', + 'DrydockLeaseWaitingForResourcesLogType' => 'applications/drydock/logtype/DrydockLeaseWaitingForResourcesLogType.php', 'DrydockLog' => 'applications/drydock/storage/DrydockLog.php', 'DrydockLogController' => 'applications/drydock/controller/DrydockLogController.php', + 'DrydockLogGarbageCollector' => 'applications/drydock/garbagecollector/DrydockLogGarbageCollector.php', 'DrydockLogListController' => 'applications/drydock/controller/DrydockLogListController.php', 'DrydockLogListView' => 'applications/drydock/view/DrydockLogListView.php', 'DrydockLogQuery' => 'applications/drydock/query/DrydockLogQuery.php', 'DrydockLogSearchEngine' => 'applications/drydock/query/DrydockLogSearchEngine.php', + 'DrydockLogType' => 'applications/drydock/logtype/DrydockLogType.php', 'DrydockManagementCommandWorkflow' => 'applications/drydock/management/DrydockManagementCommandWorkflow.php', 'DrydockManagementLeaseWorkflow' => 'applications/drydock/management/DrydockManagementLeaseWorkflow.php', 'DrydockManagementReleaseLeaseWorkflow' => 'applications/drydock/management/DrydockManagementReleaseLeaseWorkflow.php', @@ -858,9 +865,10 @@ phutil_register_library_map(array( 'DrydockManagementWorkflow' => 'applications/drydock/management/DrydockManagementWorkflow.php', 'DrydockQuery' => 'applications/drydock/query/DrydockQuery.php', 'DrydockResource' => 'applications/drydock/storage/DrydockResource.php', + 'DrydockResourceActivationFailureLogType' => 'applications/drydock/logtype/DrydockResourceActivationFailureLogType.php', + 'DrydockResourceActivationYieldLogType' => 'applications/drydock/logtype/DrydockResourceActivationYieldLogType.php', 'DrydockResourceController' => 'applications/drydock/controller/DrydockResourceController.php', 'DrydockResourceDatasource' => 'applications/drydock/typeahead/DrydockResourceDatasource.php', - 'DrydockResourceDestroyWorker' => 'applications/drydock/worker/DrydockResourceDestroyWorker.php', 'DrydockResourceListController' => 'applications/drydock/controller/DrydockResourceListController.php', 'DrydockResourceListView' => 'applications/drydock/view/DrydockResourceListView.php', 'DrydockResourcePHIDType' => 'applications/drydock/phid/DrydockResourcePHIDType.php', @@ -870,11 +878,11 @@ phutil_register_library_map(array( 'DrydockResourceStatus' => 'applications/drydock/constants/DrydockResourceStatus.php', 'DrydockResourceUpdateWorker' => 'applications/drydock/worker/DrydockResourceUpdateWorker.php', 'DrydockResourceViewController' => 'applications/drydock/controller/DrydockResourceViewController.php', - 'DrydockResourceWorker' => 'applications/drydock/worker/DrydockResourceWorker.php', 'DrydockSFTPFilesystemInterface' => 'applications/drydock/interface/filesystem/DrydockSFTPFilesystemInterface.php', 'DrydockSSHCommandInterface' => 'applications/drydock/interface/command/DrydockSSHCommandInterface.php', 'DrydockSlotLock' => 'applications/drydock/storage/DrydockSlotLock.php', 'DrydockSlotLockException' => 'applications/drydock/exception/DrydockSlotLockException.php', + 'DrydockSlotLockFailureLogType' => 'applications/drydock/logtype/DrydockSlotLockFailureLogType.php', 'DrydockWebrootInterface' => 'applications/drydock/interface/webroot/DrydockWebrootInterface.php', 'DrydockWorker' => 'applications/drydock/worker/DrydockWorker.php', 'DrydockWorkingCopyBlueprintImplementation' => 'applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php', @@ -964,6 +972,7 @@ phutil_register_library_map(array( 'HarbormasterBuildPlanTransaction' => 'applications/harbormaster/storage/configuration/HarbormasterBuildPlanTransaction.php', 'HarbormasterBuildPlanTransactionQuery' => 'applications/harbormaster/query/HarbormasterBuildPlanTransactionQuery.php', 'HarbormasterBuildQuery' => 'applications/harbormaster/query/HarbormasterBuildQuery.php', + 'HarbormasterBuildRequest' => 'applications/harbormaster/engine/HarbormasterBuildRequest.php', 'HarbormasterBuildStep' => 'applications/harbormaster/storage/configuration/HarbormasterBuildStep.php', 'HarbormasterBuildStepCoreCustomField' => 'applications/harbormaster/customfield/HarbormasterBuildStepCoreCustomField.php', 'HarbormasterBuildStepCustomField' => 'applications/harbormaster/customfield/HarbormasterBuildStepCustomField.php', @@ -1821,6 +1830,7 @@ phutil_register_library_map(array( 'PhabricatorConfigAllController' => 'applications/config/controller/PhabricatorConfigAllController.php', 'PhabricatorConfigApplication' => 'applications/config/application/PhabricatorConfigApplication.php', 'PhabricatorConfigCacheController' => 'applications/config/controller/PhabricatorConfigCacheController.php', + 'PhabricatorConfigCollectorsModule' => 'applications/config/module/PhabricatorConfigCollectorsModule.php', 'PhabricatorConfigColumnSchema' => 'applications/config/schema/PhabricatorConfigColumnSchema.php', 'PhabricatorConfigConfigPHIDType' => 'applications/config/phid/PhabricatorConfigConfigPHIDType.php', 'PhabricatorConfigController' => 'applications/config/controller/PhabricatorConfigController.php', @@ -2187,7 +2197,9 @@ phutil_register_library_map(array( 'PhabricatorFundApplication' => 'applications/fund/application/PhabricatorFundApplication.php', 'PhabricatorGDSetupCheck' => 'applications/config/check/PhabricatorGDSetupCheck.php', 'PhabricatorGarbageCollector' => 'infrastructure/daemon/garbagecollector/PhabricatorGarbageCollector.php', - 'PhabricatorGarbageCollectorConfigOptions' => 'applications/config/option/PhabricatorGarbageCollectorConfigOptions.php', + 'PhabricatorGarbageCollectorManagementCollectWorkflow' => 'infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementCollectWorkflow.php', + 'PhabricatorGarbageCollectorManagementSetPolicyWorkflow' => 'infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementSetPolicyWorkflow.php', + 'PhabricatorGarbageCollectorManagementWorkflow' => 'infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementWorkflow.php', 'PhabricatorGestureUIExample' => 'applications/uiexample/examples/PhabricatorGestureUIExample.php', 'PhabricatorGitGraphStream' => 'applications/repository/daemon/PhabricatorGitGraphStream.php', 'PhabricatorGitHubAuthProvider' => 'applications/auth/provider/PhabricatorGitHubAuthProvider.php', @@ -4520,7 +4532,6 @@ phutil_register_library_map(array( 'DoorkeeperSchemaSpec' => 'PhabricatorConfigSchemaSpec', 'DoorkeeperTagView' => 'AphrontView', 'DoorkeeperTagsController' => 'PhabricatorController', - 'DrydockAllocatorWorker' => 'DrydockWorker', 'DrydockAlmanacServiceHostBlueprintImplementation' => 'DrydockBlueprintImplementation', 'DrydockApacheWebrootInterface' => 'DrydockWebrootInterface', 'DrydockBlueprint' => array( @@ -4568,28 +4579,36 @@ phutil_register_library_map(array( 'DrydockDAO', 'PhabricatorPolicyInterface', ), + 'DrydockLeaseAcquiredLogType' => 'DrydockLogType', + 'DrydockLeaseActivatedLogType' => 'DrydockLogType', + 'DrydockLeaseActivationFailureLogType' => 'DrydockLogType', + 'DrydockLeaseActivationYieldLogType' => 'DrydockLogType', 'DrydockLeaseController' => 'DrydockController', 'DrydockLeaseDatasource' => 'PhabricatorTypeaheadDatasource', - 'DrydockLeaseDestroyWorker' => 'DrydockWorker', + 'DrydockLeaseDestroyedLogType' => 'DrydockLogType', 'DrydockLeaseListController' => 'DrydockLeaseController', 'DrydockLeaseListView' => 'AphrontView', 'DrydockLeasePHIDType' => 'PhabricatorPHIDType', 'DrydockLeaseQuery' => 'DrydockQuery', + 'DrydockLeaseQueuedLogType' => 'DrydockLogType', 'DrydockLeaseReleaseController' => 'DrydockLeaseController', + 'DrydockLeaseReleasedLogType' => 'DrydockLogType', 'DrydockLeaseSearchEngine' => 'PhabricatorApplicationSearchEngine', 'DrydockLeaseStatus' => 'DrydockConstants', 'DrydockLeaseUpdateWorker' => 'DrydockWorker', 'DrydockLeaseViewController' => 'DrydockLeaseController', - 'DrydockLeaseWorker' => 'DrydockWorker', + 'DrydockLeaseWaitingForResourcesLogType' => 'DrydockLogType', 'DrydockLog' => array( 'DrydockDAO', 'PhabricatorPolicyInterface', ), 'DrydockLogController' => 'DrydockController', + 'DrydockLogGarbageCollector' => 'PhabricatorGarbageCollector', 'DrydockLogListController' => 'DrydockLogController', 'DrydockLogListView' => 'AphrontView', 'DrydockLogQuery' => 'DrydockQuery', 'DrydockLogSearchEngine' => 'PhabricatorApplicationSearchEngine', + 'DrydockLogType' => 'Phobject', 'DrydockManagementCommandWorkflow' => 'DrydockManagementWorkflow', 'DrydockManagementLeaseWorkflow' => 'DrydockManagementWorkflow', 'DrydockManagementReleaseLeaseWorkflow' => 'DrydockManagementWorkflow', @@ -4602,9 +4621,10 @@ phutil_register_library_map(array( 'DrydockDAO', 'PhabricatorPolicyInterface', ), + 'DrydockResourceActivationFailureLogType' => 'DrydockLogType', + 'DrydockResourceActivationYieldLogType' => 'DrydockLogType', 'DrydockResourceController' => 'DrydockController', 'DrydockResourceDatasource' => 'PhabricatorTypeaheadDatasource', - 'DrydockResourceDestroyWorker' => 'DrydockWorker', 'DrydockResourceListController' => 'DrydockResourceController', 'DrydockResourceListView' => 'AphrontView', 'DrydockResourcePHIDType' => 'PhabricatorPHIDType', @@ -4614,11 +4634,11 @@ phutil_register_library_map(array( 'DrydockResourceStatus' => 'DrydockConstants', 'DrydockResourceUpdateWorker' => 'DrydockWorker', 'DrydockResourceViewController' => 'DrydockResourceController', - 'DrydockResourceWorker' => 'DrydockWorker', 'DrydockSFTPFilesystemInterface' => 'DrydockFilesystemInterface', 'DrydockSSHCommandInterface' => 'DrydockCommandInterface', 'DrydockSlotLock' => 'DrydockDAO', 'DrydockSlotLockException' => 'Exception', + 'DrydockSlotLockFailureLogType' => 'DrydockLogType', 'DrydockWebrootInterface' => 'DrydockInterface', 'DrydockWorker' => 'PhabricatorWorker', 'DrydockWorkingCopyBlueprintImplementation' => 'DrydockBlueprintImplementation', @@ -4740,6 +4760,7 @@ phutil_register_library_map(array( 'HarbormasterBuildPlanTransaction' => 'PhabricatorApplicationTransaction', 'HarbormasterBuildPlanTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'HarbormasterBuildQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'HarbormasterBuildRequest' => 'Phobject', 'HarbormasterBuildStep' => array( 'HarbormasterDAO', 'PhabricatorApplicationTransactionInterface', @@ -5745,6 +5766,7 @@ phutil_register_library_map(array( 'PhabricatorConfigAllController' => 'PhabricatorConfigController', 'PhabricatorConfigApplication' => 'PhabricatorApplication', 'PhabricatorConfigCacheController' => 'PhabricatorConfigController', + 'PhabricatorConfigCollectorsModule' => 'PhabricatorConfigModule', 'PhabricatorConfigColumnSchema' => 'PhabricatorConfigStorageSchema', 'PhabricatorConfigConfigPHIDType' => 'PhabricatorPHIDType', 'PhabricatorConfigController' => 'PhabricatorController', @@ -6177,7 +6199,9 @@ phutil_register_library_map(array( 'PhabricatorFundApplication' => 'PhabricatorApplication', 'PhabricatorGDSetupCheck' => 'PhabricatorSetupCheck', 'PhabricatorGarbageCollector' => 'Phobject', - 'PhabricatorGarbageCollectorConfigOptions' => 'PhabricatorApplicationConfigOptions', + 'PhabricatorGarbageCollectorManagementCollectWorkflow' => 'PhabricatorGarbageCollectorManagementWorkflow', + 'PhabricatorGarbageCollectorManagementSetPolicyWorkflow' => 'PhabricatorGarbageCollectorManagementWorkflow', + 'PhabricatorGarbageCollectorManagementWorkflow' => 'PhabricatorManagementWorkflow', 'PhabricatorGestureUIExample' => 'PhabricatorUIExample', 'PhabricatorGitGraphStream' => 'PhabricatorRepositoryGraphStream', 'PhabricatorGitHubAuthProvider' => 'PhabricatorOAuth2AuthProvider', diff --git a/src/applications/auth/garbagecollector/PhabricatorAuthSessionGarbageCollector.php b/src/applications/auth/garbagecollector/PhabricatorAuthSessionGarbageCollector.php index f9994c2fd6..de9b704d7a 100644 --- a/src/applications/auth/garbagecollector/PhabricatorAuthSessionGarbageCollector.php +++ b/src/applications/auth/garbagecollector/PhabricatorAuthSessionGarbageCollector.php @@ -3,7 +3,17 @@ final class PhabricatorAuthSessionGarbageCollector extends PhabricatorGarbageCollector { - public function collectGarbage() { + const COLLECTORCONST = 'auth.sessions'; + + public function getCollectorName() { + return pht('Authentication Sessions'); + } + + public function hasAutomaticPolicy() { + return true; + } + + protected function collectGarbage() { $session_table = new PhabricatorAuthSession(); $conn_w = $session_table->establishConnection('w'); diff --git a/src/applications/auth/garbagecollector/PhabricatorAuthTemporaryTokenGarbageCollector.php b/src/applications/auth/garbagecollector/PhabricatorAuthTemporaryTokenGarbageCollector.php index 49d1ffb05b..e163421351 100644 --- a/src/applications/auth/garbagecollector/PhabricatorAuthTemporaryTokenGarbageCollector.php +++ b/src/applications/auth/garbagecollector/PhabricatorAuthTemporaryTokenGarbageCollector.php @@ -3,7 +3,17 @@ final class PhabricatorAuthTemporaryTokenGarbageCollector extends PhabricatorGarbageCollector { - public function collectGarbage() { + const COLLECTORCONST = 'auth.tokens'; + + public function getCollectorName() { + return pht('Authentication Tokens'); + } + + public function hasAutomaticPolicy() { + return true; + } + + protected function collectGarbage() { $session_table = new PhabricatorAuthTemporaryToken(); $conn_w = $session_table->establishConnection('w'); diff --git a/src/applications/cache/garbagecollector/PhabricatorCacheGeneralGarbageCollector.php b/src/applications/cache/garbagecollector/PhabricatorCacheGeneralGarbageCollector.php index 29ad8ffb18..9f03fb459f 100644 --- a/src/applications/cache/garbagecollector/PhabricatorCacheGeneralGarbageCollector.php +++ b/src/applications/cache/garbagecollector/PhabricatorCacheGeneralGarbageCollector.php @@ -3,13 +3,17 @@ final class PhabricatorCacheGeneralGarbageCollector extends PhabricatorGarbageCollector { - public function collectGarbage() { - $key = 'gcdaemon.ttl.general-cache'; - $ttl = PhabricatorEnv::getEnvConfig($key); - if ($ttl <= 0) { - return false; - } + const COLLECTORCONST = 'cache.general'; + public function getCollectorName() { + return pht('General Cache'); + } + + public function getDefaultRetentionPolicy() { + return phutil_units('30 days in seconds'); + } + + protected function collectGarbage() { $cache = new PhabricatorKeyValueDatabaseCache(); $conn_w = $cache->establishConnection('w'); @@ -18,7 +22,7 @@ final class PhabricatorCacheGeneralGarbageCollector 'DELETE FROM %T WHERE cacheCreated < %d ORDER BY cacheCreated ASC LIMIT 100', $cache->getTableName(), - time() - $ttl); + $this->getGarbageEpoch()); return ($conn_w->getAffectedRows() == 100); } diff --git a/src/applications/cache/garbagecollector/PhabricatorCacheMarkupGarbageCollector.php b/src/applications/cache/garbagecollector/PhabricatorCacheMarkupGarbageCollector.php index 75b25e2530..5f79ba171c 100644 --- a/src/applications/cache/garbagecollector/PhabricatorCacheMarkupGarbageCollector.php +++ b/src/applications/cache/garbagecollector/PhabricatorCacheMarkupGarbageCollector.php @@ -3,13 +3,17 @@ final class PhabricatorCacheMarkupGarbageCollector extends PhabricatorGarbageCollector { - public function collectGarbage() { - $key = 'gcdaemon.ttl.markup-cache'; - $ttl = PhabricatorEnv::getEnvConfig($key); - if ($ttl <= 0) { - return false; - } + const COLLECTORCONST = 'cache.markup'; + public function getCollectorName() { + return pht('Markup Cache'); + } + + public function getDefaultRetentionPolicy() { + return phutil_units('30 days in seconds'); + } + + protected function collectGarbage() { $table = new PhabricatorMarkupCache(); $conn_w = $table->establishConnection('w'); @@ -17,7 +21,7 @@ final class PhabricatorCacheMarkupGarbageCollector $conn_w, 'DELETE FROM %T WHERE dateCreated < %d LIMIT 100', $table->getTableName(), - time() - $ttl); + $this->getGarbageEpoch()); return ($conn_w->getAffectedRows() == 100); } diff --git a/src/applications/cache/garbagecollector/PhabricatorCacheTTLGarbageCollector.php b/src/applications/cache/garbagecollector/PhabricatorCacheTTLGarbageCollector.php index c459553396..0f6153f0ad 100644 --- a/src/applications/cache/garbagecollector/PhabricatorCacheTTLGarbageCollector.php +++ b/src/applications/cache/garbagecollector/PhabricatorCacheTTLGarbageCollector.php @@ -3,7 +3,17 @@ final class PhabricatorCacheTTLGarbageCollector extends PhabricatorGarbageCollector { - public function collectGarbage() { + const COLLECTORCONST = 'cache.general.ttl'; + + public function getCollectorName() { + return pht('General Cache (TTL)'); + } + + public function hasAutomaticPolicy() { + return true; + } + + protected function collectGarbage() { $cache = new PhabricatorKeyValueDatabaseCache(); $conn_w = $cache->establishConnection('w'); @@ -12,7 +22,7 @@ final class PhabricatorCacheTTLGarbageCollector 'DELETE FROM %T WHERE cacheExpires < %d ORDER BY cacheExpires ASC LIMIT 100', $cache->getTableName(), - time()); + PhabricatorTime::getNow()); return ($conn_w->getAffectedRows() == 100); } diff --git a/src/applications/conduit/garbagecollector/ConduitConnectionGarbageCollector.php b/src/applications/conduit/garbagecollector/ConduitConnectionGarbageCollector.php index 14467af2bf..f87fc57572 100644 --- a/src/applications/conduit/garbagecollector/ConduitConnectionGarbageCollector.php +++ b/src/applications/conduit/garbagecollector/ConduitConnectionGarbageCollector.php @@ -3,21 +3,26 @@ final class ConduitConnectionGarbageCollector extends PhabricatorGarbageCollector { - public function collectGarbage() { - $key = 'gcdaemon.ttl.conduit-logs'; - $ttl = PhabricatorEnv::getEnvConfig($key); - if ($ttl <= 0) { - return false; - } + const COLLECTORCONST = 'conduit.connections'; + public function getCollectorName() { + return pht('Conduit Connections'); + } + + public function getDefaultRetentionPolicy() { + return phutil_units('180 days in seconds'); + } + + protected function collectGarbage() { $table = new PhabricatorConduitConnectionLog(); $conn_w = $table->establishConnection('w'); + queryfx( $conn_w, 'DELETE FROM %T WHERE dateCreated < %d ORDER BY dateCreated ASC LIMIT 100', $table->getTableName(), - time() - $ttl); + $this->getGarbageEpoch()); return ($conn_w->getAffectedRows() == 100); } diff --git a/src/applications/conduit/garbagecollector/ConduitLogGarbageCollector.php b/src/applications/conduit/garbagecollector/ConduitLogGarbageCollector.php index 9ed39d5832..318cb43ad1 100644 --- a/src/applications/conduit/garbagecollector/ConduitLogGarbageCollector.php +++ b/src/applications/conduit/garbagecollector/ConduitLogGarbageCollector.php @@ -3,21 +3,26 @@ final class ConduitLogGarbageCollector extends PhabricatorGarbageCollector { - public function collectGarbage() { - $key = 'gcdaemon.ttl.conduit-logs'; - $ttl = PhabricatorEnv::getEnvConfig($key); - if ($ttl <= 0) { - return false; - } + const COLLECTORCONST = 'conduit.logs'; + public function getCollectorName() { + return pht('Conduit Logs'); + } + + public function getDefaultRetentionPolicy() { + return phutil_units('180 days in seconds'); + } + + protected function collectGarbage() { $table = new PhabricatorConduitMethodCallLog(); $conn_w = $table->establishConnection('w'); + queryfx( $conn_w, 'DELETE FROM %T WHERE dateCreated < %d ORDER BY dateCreated ASC LIMIT 100', $table->getTableName(), - time() - $ttl); + $this->getGarbageEpoch()); return ($conn_w->getAffectedRows() == 100); } diff --git a/src/applications/conduit/garbagecollector/ConduitTokenGarbageCollector.php b/src/applications/conduit/garbagecollector/ConduitTokenGarbageCollector.php index f57a23e3bb..fab142b8e8 100644 --- a/src/applications/conduit/garbagecollector/ConduitTokenGarbageCollector.php +++ b/src/applications/conduit/garbagecollector/ConduitTokenGarbageCollector.php @@ -3,9 +3,20 @@ final class ConduitTokenGarbageCollector extends PhabricatorGarbageCollector { - public function collectGarbage() { + const COLLECTORCONST = 'conduit.tokens'; + + public function getCollectorName() { + return pht('Conduit Tokens'); + } + + public function hasAutomaticPolicy() { + return true; + } + + protected function collectGarbage() { $table = new PhabricatorConduitToken(); $conn_w = $table->establishConnection('w'); + queryfx( $conn_w, 'DELETE FROM %T WHERE expires <= %d diff --git a/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php b/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php index b73f145033..92cafb9449 100644 --- a/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php +++ b/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php @@ -176,6 +176,10 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck { 'Inbound mail addresses are now configured for each application '. 'in the Applications tool.'); + $gc_reason = pht( + 'Garbage collectors are now configured with "%s".', + 'bin/garbage set-policy'); + $ancient_config += array( 'phid.external-loaders' => pht( @@ -280,6 +284,14 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck { 'auth.login-message' => pht( 'This configuration option has been replaced with a modular '. 'handler. See T9346.'), + + 'gcdaemon.ttl.herald-transcripts' => $gc_reason, + 'gcdaemon.ttl.daemon-logs' => $gc_reason, + 'gcdaemon.ttl.differential-parse-cache' => $gc_reason, + 'gcdaemon.ttl.markup-cache' => $gc_reason, + 'gcdaemon.ttl.task-archive' => $gc_reason, + 'gcdaemon.ttl.general-cache' => $gc_reason, + 'gcdaemon.ttl.conduit-logs' => $gc_reason, ); return $ancient_config; diff --git a/src/applications/config/module/PhabricatorConfigCollectorsModule.php b/src/applications/config/module/PhabricatorConfigCollectorsModule.php new file mode 100644 index 0000000000..f00e9d3489 --- /dev/null +++ b/src/applications/config/module/PhabricatorConfigCollectorsModule.php @@ -0,0 +1,79 @@ +getViewer(); + + $collectors = PhabricatorGarbageCollector::getAllCollectors(); + $collectors = msort($collectors, 'getCollectorConstant'); + + $rows = array(); + $rowc = array(); + foreach ($collectors as $key => $collector) { + $class = null; + if ($collector->hasAutomaticPolicy()) { + $policy_view = phutil_tag('em', array(), pht('Automatic')); + } else { + $policy = $collector->getRetentionPolicy(); + if ($policy === null) { + $policy_view = pht('Indefinite'); + } else { + $days = ceil($policy / phutil_units('1 day in seconds')); + $policy_view = pht( + '%s Day(s)', + new PhutilNumber($days)); + } + + $default = $collector->getDefaultRetentionPolicy(); + if ($policy !== $default) { + $class = 'highlighted'; + $policy_view = phutil_tag('strong', array(), $policy_view); + } + } + + $rowc[] = $class; + $rows[] = array( + $collector->getCollectorConstant(), + $collector->getCollectorName(), + $policy_view, + ); + } + + $table = id(new AphrontTableView($rows)) + ->setRowClasses($rowc) + ->setHeaders( + array( + pht('Constant'), + pht('Name'), + pht('Retention Policy'), + )) + ->setColumnClasses( + array( + null, + 'pri wide', + null, + )); + + $header = id(new PHUIHeaderView()) + ->setHeader(pht('Garbage Collectors')) + ->setSubheader( + pht( + 'Collectors with custom policies are highlighted. Use '. + '%s to change retention policies.', + phutil_tag('tt', array(), 'bin/garbage set-policy'))); + + return id(new PHUIObjectBoxView()) + ->setHeader($header) + ->setTable($table); + } + +} diff --git a/src/applications/config/option/PhabricatorGarbageCollectorConfigOptions.php b/src/applications/config/option/PhabricatorGarbageCollectorConfigOptions.php deleted file mode 100644 index c1095d1520..0000000000 --- a/src/applications/config/option/PhabricatorGarbageCollectorConfigOptions.php +++ /dev/null @@ -1,70 +0,0 @@ - array( - 30, - pht('Number of seconds to retain Herald transcripts for.'), - ), - 'gcdaemon.ttl.daemon-logs' => array( - 7, - pht('Number of seconds to retain Daemon logs for.'), - ), - 'gcdaemon.ttl.differential-parse-cache' => array( - 14, - pht('Number of seconds to retain Differential parse caches for.'), - ), - 'gcdaemon.ttl.markup-cache' => array( - 30, - pht('Number of seconds to retain Markup cache entries for.'), - ), - 'gcdaemon.ttl.task-archive' => array( - 14, - pht('Number of seconds to retain archived background tasks for.'), - ), - 'gcdaemon.ttl.general-cache' => array( - 30, - pht('Number of seconds to retain general cache entries for.'), - ), - 'gcdaemon.ttl.conduit-logs' => array( - 180, - pht('Number of seconds to retain Conduit call logs for.'), - ), - ); - - $result = array(); - foreach ($options as $key => $spec) { - list($default_days, $description) = $spec; - $result[] = $this - ->newOption($key, 'int', $default_days * (24 * 60 * 60)) - ->setDescription($description) - ->addExample((7 * 24 * 60 * 60), pht('Retain for 1 week')) - ->addExample((14 * 24 * 60 * 60), pht('Retain for 2 weeks')) - ->addExample((30 * 24 * 60 * 60), pht('Retain for 30 days')) - ->addExample((60 * 24 * 60 * 60), pht('Retain for 60 days')) - ->addExample(0, pht('Retain indefinitely')); - } - return $result; - } - -} diff --git a/src/applications/config/option/PhabricatorPHDConfigOptions.php b/src/applications/config/option/PhabricatorPHDConfigOptions.php index 59cb8ea728..587194cd4d 100644 --- a/src/applications/config/option/PhabricatorPHDConfigOptions.php +++ b/src/applications/config/option/PhabricatorPHDConfigOptions.php @@ -80,6 +80,17 @@ final class PhabricatorPHDConfigOptions 'and the daemons. Primarily, this is a way to suppress the '. '"Daemons and Web Have Different Config" setup issue on a per '. 'config key basis.')), + $this->newOption('phd.garbage-collection', 'wild', array()) + ->setLocked(true) + ->setLockedMessage( + pht( + 'This option can not be edited from the web UI. Use %s to adjust '. + 'garbage collector policies.', + phutil_tag('tt', array(), 'bin/garbage set-policy'))) + ->setSummary(pht('Retention policies for garbage collection.')) + ->setDescription( + pht( + 'Customizes retention policies for garbage collectors.')), ); } diff --git a/src/applications/daemon/garbagecollector/PhabricatorDaemonLogEventGarbageCollector.php b/src/applications/daemon/garbagecollector/PhabricatorDaemonLogEventGarbageCollector.php index c5a502c599..3f6d25bb5b 100644 --- a/src/applications/daemon/garbagecollector/PhabricatorDaemonLogEventGarbageCollector.php +++ b/src/applications/daemon/garbagecollector/PhabricatorDaemonLogEventGarbageCollector.php @@ -3,12 +3,17 @@ final class PhabricatorDaemonLogEventGarbageCollector extends PhabricatorGarbageCollector { - public function collectGarbage() { - $ttl = PhabricatorEnv::getEnvConfig('gcdaemon.ttl.daemon-logs'); - if ($ttl <= 0) { - return false; - } + const COLLECTORCONST = 'daemon.processes'; + public function getCollectorName() { + return pht('Daemon Processes'); + } + + public function getDefaultRetentionPolicy() { + return phutil_units('7 days in seconds'); + } + + protected function collectGarbage() { $table = new PhabricatorDaemonLogEvent(); $conn_w = $table->establishConnection('w'); @@ -16,7 +21,7 @@ final class PhabricatorDaemonLogEventGarbageCollector $conn_w, 'DELETE FROM %T WHERE epoch < %d LIMIT 100', $table->getTableName(), - time() - $ttl); + $this->getGarbageEpoch()); return ($conn_w->getAffectedRows() == 100); } diff --git a/src/applications/daemon/garbagecollector/PhabricatorDaemonLogGarbageCollector.php b/src/applications/daemon/garbagecollector/PhabricatorDaemonLogGarbageCollector.php index bd8b92bf92..3ff26e3db7 100644 --- a/src/applications/daemon/garbagecollector/PhabricatorDaemonLogGarbageCollector.php +++ b/src/applications/daemon/garbagecollector/PhabricatorDaemonLogGarbageCollector.php @@ -3,12 +3,17 @@ final class PhabricatorDaemonLogGarbageCollector extends PhabricatorGarbageCollector { - public function collectGarbage() { - $ttl = PhabricatorEnv::getEnvConfig('gcdaemon.ttl.daemon-logs'); - if ($ttl <= 0) { - return false; - } + const COLLECTORCONST = 'daemon.logs'; + public function getCollectorName() { + return pht('Daemon Logs'); + } + + public function getDefaultRetentionPolicy() { + return phutil_units('7 days in seconds'); + } + + protected function collectGarbage() { $table = new PhabricatorDaemonLog(); $conn_w = $table->establishConnection('w'); @@ -16,7 +21,7 @@ final class PhabricatorDaemonLogGarbageCollector $conn_w, 'DELETE FROM %T WHERE dateCreated < %d AND status != %s LIMIT 100', $table->getTableName(), - time() - $ttl, + $this->getGarbageEpoch(), PhabricatorDaemonLog::STATUS_RUNNING); return ($conn_w->getAffectedRows() == 100); diff --git a/src/applications/daemon/garbagecollector/PhabricatorDaemonTaskGarbageCollector.php b/src/applications/daemon/garbagecollector/PhabricatorDaemonTaskGarbageCollector.php index 9f320289ab..e0b2bda79b 100644 --- a/src/applications/daemon/garbagecollector/PhabricatorDaemonTaskGarbageCollector.php +++ b/src/applications/daemon/garbagecollector/PhabricatorDaemonTaskGarbageCollector.php @@ -3,21 +3,25 @@ final class PhabricatorDaemonTaskGarbageCollector extends PhabricatorGarbageCollector { - public function collectGarbage() { - $key = 'gcdaemon.ttl.task-archive'; - $ttl = PhabricatorEnv::getEnvConfig($key); - if ($ttl <= 0) { - return false; - } + const COLLECTORCONST = 'worker.tasks'; + public function getCollectorName() { + return pht('Archived Tasks'); + } + + public function getDefaultRetentionPolicy() { + return phutil_units('14 days in seconds'); + } + + protected function collectGarbage() { $table = new PhabricatorWorkerArchiveTask(); $data_table = new PhabricatorWorkerTaskData(); $conn_w = $table->establishConnection('w'); $tasks = id(new PhabricatorWorkerArchiveTaskQuery()) - ->withDateCreatedBefore(time() - $ttl) + ->withDateCreatedBefore($this->getGarbageEpoch()) + ->setLimit(100) ->execute(); - if (!$tasks) { return false; } diff --git a/src/applications/differential/config/PhabricatorDifferentialConfigOptions.php b/src/applications/differential/config/PhabricatorDifferentialConfigOptions.php index 7d0eae99c2..a2bb63a2a2 100644 --- a/src/applications/differential/config/PhabricatorDifferentialConfigOptions.php +++ b/src/applications/differential/config/PhabricatorDifferentialConfigOptions.php @@ -133,7 +133,7 @@ final class PhabricatorDifferentialConfigOptions 'to affect existing revisions. For instructions, see '. '**[[ %s | Managing Caches ]]** in the documentation.', $caches_href)) - ->addExample("/config\.h$/\n#/autobuilt/#", pht('Valid Setting')), + ->addExample("/config\.h$/\n#(^|/)autobuilt/#", pht('Valid Setting')), $this->newOption('differential.sticky-accept', 'bool', true) ->setBoolOptions( array( diff --git a/src/applications/differential/garbagecollector/DifferentialParseCacheGarbageCollector.php b/src/applications/differential/garbagecollector/DifferentialParseCacheGarbageCollector.php index 08cdfe97db..b740060821 100644 --- a/src/applications/differential/garbagecollector/DifferentialParseCacheGarbageCollector.php +++ b/src/applications/differential/garbagecollector/DifferentialParseCacheGarbageCollector.php @@ -3,13 +3,17 @@ final class DifferentialParseCacheGarbageCollector extends PhabricatorGarbageCollector { - public function collectGarbage() { - $key = 'gcdaemon.ttl.differential-parse-cache'; - $ttl = PhabricatorEnv::getEnvConfig($key); - if ($ttl <= 0) { - return false; - } + const COLLECTORCONST = 'differential.parse'; + public function getCollectorName() { + return pht('Differential Parse Cache'); + } + + public function getDefaultRetentionPolicy() { + return phutil_units('14 days in seconds'); + } + + protected function collectGarbage() { $table = new DifferentialChangeset(); $conn_w = $table->establishConnection('w'); @@ -17,7 +21,7 @@ final class DifferentialParseCacheGarbageCollector $conn_w, 'DELETE FROM %T WHERE dateCreated < %d LIMIT 100', DifferentialChangeset::TABLE_CACHE, - time() - $ttl); + $this->getGarbageEpoch()); return ($conn_w->getAffectedRows() == 100); } diff --git a/src/applications/differential/herald/HeraldDifferentialRevisionAdapter.php b/src/applications/differential/herald/HeraldDifferentialRevisionAdapter.php index a47d433357..d7b8c5dd33 100644 --- a/src/applications/differential/herald/HeraldDifferentialRevisionAdapter.php +++ b/src/applications/differential/herald/HeraldDifferentialRevisionAdapter.php @@ -10,7 +10,7 @@ final class HeraldDifferentialRevisionAdapter protected $changesets; private $haveHunks; - private $buildPlanPHIDs = array(); + private $buildRequests = array(); public function getAdapterApplicationClass() { return 'PhabricatorDifferentialApplication'; @@ -139,12 +139,13 @@ final class HeraldDifferentialRevisionAdapter return $this->getObject()->getPHID(); } - public function getQueuedHarbormasterBuildPlanPHIDs() { - return $this->buildPlanPHIDs; + public function getQueuedHarbormasterBuildRequests() { + return $this->buildRequests; } - public function queueHarbormasterBuildPlanPHID($phid) { - $this->buildPlanPHIDs[] = $phid; + public function queueHarbormasterBuildRequest( + HarbormasterBuildRequest $request) { + $this->buildRequests[] = $request; } } diff --git a/src/applications/differential/storage/DifferentialDiff.php b/src/applications/differential/storage/DifferentialDiff.php index 25421bafe6..7b3a31635a 100644 --- a/src/applications/differential/storage/DifferentialDiff.php +++ b/src/applications/differential/storage/DifferentialDiff.php @@ -446,6 +446,13 @@ final class DifferentialDiff $results['repository.phid'] = $repo->getPHID(); $results['repository.vcs'] = $repo->getVersionControlSystem(); $results['repository.uri'] = $repo->getPublicCloneURI(); + + // TODO: We're just hoping to get lucky. Instead, `arc` should store + // where it sent changes and we should only provide staging details + // if we reasonably believe they are accurate. + $staging_ref = 'refs/tags/phabricator/diff/'.$this->getID(); + $results['repository.staging.uri'] = $repo->getStagingURI(); + $results['repository.staging.ref'] = $staging_ref; } } @@ -466,6 +473,10 @@ final class DifferentialDiff pht('The version control system, either "svn", "hg" or "git".'), 'repository.uri' => pht('The URI to clone or checkout the repository from.'), + 'repository.staging.uri' => + pht('The URI of the staging repository.'), + 'repository.staging.ref' => + pht('The ref name for this change in the staging repository.'), ); } diff --git a/src/applications/diffusion/controller/DiffusionCommitController.php b/src/applications/diffusion/controller/DiffusionCommitController.php index 8a7d7d7d7c..fe2f539f09 100644 --- a/src/applications/diffusion/controller/DiffusionCommitController.php +++ b/src/applications/diffusion/controller/DiffusionCommitController.php @@ -7,6 +7,12 @@ final class DiffusionCommitController extends DiffusionController { private $auditAuthorityPHIDs; private $highlightedAudits; + private $commitParents; + private $commitRefs; + private $commitMerges; + private $commitErrors; + private $commitExists; + public function shouldAllowPublic() { return true; } @@ -17,6 +23,7 @@ final class DiffusionCommitController extends DiffusionController { protected function processDiffusionRequest(AphrontRequest $request) { $user = $request->getUser(); + // This controller doesn't use blob/path stuff, just pass the dictionary // in directly instead of using the AphrontRequest parsing mechanism. $data = $request->getURIMap(); @@ -45,10 +52,7 @@ final class DiffusionCommitController extends DiffusionController { )); if (!$commit) { - $exists = $this->callConduitWithDiffusionRequest( - 'diffusion.existsquery', - array('commit' => $drequest->getCommit())); - if (!$exists) { + if (!$this->getCommitExists()) { return new Aphront404Response(); } @@ -95,18 +99,6 @@ final class DiffusionCommitController extends DiffusionController { require_celerity_resource('phabricator-remarkup-css'); - $parents = $this->callConduitWithDiffusionRequest( - 'diffusion.commitparentsquery', - array('commit' => $drequest->getCommit())); - - if ($parents) { - $parents = id(new DiffusionCommitQuery()) - ->setViewer($user) - ->withRepository($repository) - ->withIdentifiers($parents) - ->execute(); - } - $headsup_view = id(new PHUIHeaderView()) ->setHeader(nonempty($commit->getSummary(), pht('Commit Detail'))); @@ -115,7 +107,6 @@ final class DiffusionCommitController extends DiffusionController { $commit_properties = $this->loadCommitProperties( $commit, $commit_data, - $parents, $audit_requests); $property_list = id(new PHUIPropertyListView()) ->setHasKeyboardShortcuts(true) @@ -148,8 +139,10 @@ final class DiffusionCommitController extends DiffusionController { $message)); $headsup_view->setTall(true); + $object_box = id(new PHUIObjectBoxView()) ->setHeader($headsup_view) + ->setFormErrors($this->getCommitErrors()) ->addPropertyList($property_list) ->addPropertyList($detail_list); @@ -216,6 +209,10 @@ final class DiffusionCommitController extends DiffusionController { 'This commit is enormous, and affects more than %d files. '. 'Changes are not shown.', $hard_limit)); + } else if (!$this->getCommitExists()) { + $content[] = $this->renderStatusMessage( + pht('Commit No Longer Exists'), + pht('This commit no longer exists in the repository.')); } else { $show_changesets = true; @@ -382,10 +379,8 @@ final class DiffusionCommitController extends DiffusionController { private function loadCommitProperties( PhabricatorRepositoryCommit $commit, PhabricatorRepositoryCommitData $data, - array $parents, array $audit_requests) { - assert_instances_of($parents, 'PhabricatorRepositoryCommit'); $viewer = $this->getRequest()->getUser(); $commit_phid = $commit->getPHID(); $drequest = $this->getDiffusionRequest(); @@ -423,11 +418,6 @@ final class DiffusionCommitController extends DiffusionController { if ($data->getCommitDetail('committerPHID')) { $phids[] = $data->getCommitDetail('committerPHID'); } - if ($parents) { - foreach ($parents as $parent) { - $phids[] = $parent->getPHID(); - } - } // NOTE: We should never normally have more than a single push log, but // it can occur naturally if a commit is pushed, then the branch it was @@ -564,39 +554,47 @@ final class DiffusionCommitController extends DiffusionController { $props['Differential Revision'] = $handles[$revision_phid]->renderLink(); } + $parents = $this->getCommitParents(); if ($parents) { - $parent_links = array(); - foreach ($parents as $parent) { - $parent_links[] = $handles[$parent->getPHID()]->renderLink(); - } - $props['Parents'] = phutil_implode_html(" \xC2\xB7 ", $parent_links); + $props['Parents'] = $viewer->renderHandleList(mpull($parents, 'getPHID')); } - $props['Branches'] = phutil_tag( - 'span', - array( - 'id' => 'commit-branches', - ), - pht('Unknown')); - $props['Tags'] = phutil_tag( - 'span', - array( - 'id' => 'commit-tags', - ), - pht('Unknown')); + if ($this->getCommitExists()) { + $props['Branches'] = phutil_tag( + 'span', + array( + 'id' => 'commit-branches', + ), + pht('Unknown')); + $props['Tags'] = phutil_tag( + 'span', + array( + 'id' => 'commit-tags', + ), + pht('Unknown')); - $callsign = $repository->getCallsign(); - $root = '/diffusion/'.$callsign.'/commit/'.$commit->getCommitIdentifier(); - Javelin::initBehavior( - 'diffusion-commit-branches', - array( - $root.'/branches/' => 'commit-branches', - $root.'/tags/' => 'commit-tags', - )); + $callsign = $repository->getCallsign(); + $root = '/diffusion/'.$callsign.'/commit/'.$commit->getCommitIdentifier(); + Javelin::initBehavior( + 'diffusion-commit-branches', + array( + $root.'/branches/' => 'commit-branches', + $root.'/tags/' => 'commit-tags', + )); + } - $refs = $this->buildRefs($drequest); + $refs = $this->getCommitRefs(); if ($refs) { - $props['References'] = $refs; + $ref_links = array(); + foreach ($refs as $ref_data) { + $ref_links[] = phutil_tag( + 'a', + array( + 'href' => $ref_data['href'], + ), + $ref_data['ref']); + } + $props['References'] = phutil_implode_html(', ', $ref_links); } if ($reverts_phids) { @@ -860,26 +858,12 @@ final class DiffusionCommitController extends DiffusionController { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); - $vcs = $repository->getVersionControlSystem(); - switch ($vcs) { - case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: - // These aren't supported under SVN. - return null; - } - - $limit = 50; - - $merges = $this->callConduitWithDiffusionRequest( - 'diffusion.mergedcommitsquery', - array( - 'commit' => $drequest->getCommit(), - 'limit' => $limit + 1, - )); - + $merges = $this->getCommitMerges(); if (!$merges) { return null; } - $merges = DiffusionPathChange::newFromConduit($merges); + + $limit = $this->getMergeDisplayLimit(); $caption = null; if (count($merges) > $limit) { @@ -961,27 +945,6 @@ final class DiffusionCommitController extends DiffusionController { return $actions; } - private function buildRefs(DiffusionRequest $request) { - // this is git-only, so save a conduit round trip and just get out of - // here if the repository isn't git - $type_git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT; - $repository = $request->getRepository(); - if ($repository->getVersionControlSystem() != $type_git) { - return null; - } - - $results = $this->callConduitWithDiffusionRequest( - 'diffusion.refsquery', - array('commit' => $request->getCommit())); - $ref_links = array(); - foreach ($results as $ref_data) { - $ref_links[] = phutil_tag('a', - array('href' => $ref_data['href']), - $ref_data['ref']); - } - return phutil_implode_html(', ', $ref_links); - } - private function buildRawDiffResponse(DiffusionRequest $drequest) { $raw_diff = $this->callConduitWithDiffusionRequest( 'diffusion.rawdiffquery', @@ -1137,4 +1100,140 @@ final class DiffusionCommitController extends DiffusionController { return $toc_view; } + private function loadCommitState() { + $viewer = $this->getViewer(); + $drequest = $this->getDiffusionRequest(); + $repository = $drequest->getRepository(); + $commit = $drequest->getCommit(); + + // TODO: We could use futures here and resolve these calls in parallel. + + $exceptions = array(); + + try { + $parent_refs = $this->callConduitWithDiffusionRequest( + 'diffusion.commitparentsquery', + array( + 'commit' => $commit, + )); + + if ($parent_refs) { + $parents = id(new DiffusionCommitQuery()) + ->setViewer($viewer) + ->withRepository($repository) + ->withIdentifiers($parent_refs) + ->execute(); + } else { + $parents = array(); + } + + $this->commitParents = $parents; + } catch (Exception $ex) { + $this->commitParents = false; + $exceptions[] = $ex; + } + + $merge_limit = $this->getMergeDisplayLimit(); + + try { + $merges = $this->callConduitWithDiffusionRequest( + 'diffusion.mergedcommitsquery', + array( + 'commit' => $commit, + 'limit' => $merge_limit + 1, + )); + + $this->commitMerges = DiffusionPathChange::newFromConduit($merges); + } catch (Exception $ex) { + $this->commitMerges = false; + $exceptions[] = $ex; + } + + + try { + if ($repository->isGit()) { + $refs = $this->callConduitWithDiffusionRequest( + 'diffusion.refsquery', + array( + 'commit' => $commit, + )); + } else { + $refs = array(); + } + + $this->commitRefs = $refs; + } catch (Exception $ex) { + $this->commitRefs = false; + $exceptions[] = $ex; + } + + if ($exceptions) { + $exists = $this->callConduitWithDiffusionRequest( + 'diffusion.existsquery', + array( + 'commit' => $commit, + )); + + if ($exists) { + $this->commitExists = true; + foreach ($exceptions as $exception) { + $this->commitErrors[] = $exception->getMessage(); + } + } else { + $this->commitExists = false; + $this->commitErrors[] = pht( + 'This commit no longer exists in the repository. It may have '. + 'been part of a branch which was deleted.'); + } + } else { + $this->commitExists = true; + $this->commitErrors = array(); + } + } + + private function getMergeDisplayLimit() { + return 50; + } + + private function getCommitExists() { + if ($this->commitExists === null) { + $this->loadCommitState(); + } + + return $this->commitExists; + } + + private function getCommitParents() { + if ($this->commitParents === null) { + $this->loadCommitState(); + } + + return $this->commitParents; + } + + private function getCommitRefs() { + if ($this->commitRefs === null) { + $this->loadCommitState(); + } + + return $this->commitRefs; + } + + private function getCommitMerges() { + if ($this->commitMerges === null) { + $this->loadCommitState(); + } + + return $this->commitMerges; + } + + private function getCommitErrors() { + if ($this->commitErrors === null) { + $this->loadCommitState(); + } + + return $this->commitErrors; + } + + } diff --git a/src/applications/diffusion/herald/HeraldCommitAdapter.php b/src/applications/diffusion/herald/HeraldCommitAdapter.php index ed82e90820..6530b72e6f 100644 --- a/src/applications/diffusion/herald/HeraldCommitAdapter.php +++ b/src/applications/diffusion/herald/HeraldCommitAdapter.php @@ -17,7 +17,7 @@ final class HeraldCommitAdapter protected $affectedPackages; protected $auditNeededPackages; - private $buildPlanPHIDs = array(); + private $buildRequests = array(); public function getAdapterApplicationClass() { return 'PhabricatorDiffusionApplication'; @@ -308,12 +308,13 @@ final class HeraldCommitAdapter return $this->getObject()->getRepository()->getPHID(); } - public function getQueuedHarbormasterBuildPlanPHIDs() { - return $this->buildPlanPHIDs; + public function getQueuedHarbormasterBuildRequests() { + return $this->buildRequests; } - public function queueHarbormasterBuildPlanPHID($phid) { - $this->buildPlanPHIDs[] = $phid; + public function queueHarbormasterBuildRequest( + HarbormasterBuildRequest $request) { + $this->buildRequests[] = $request; } } diff --git a/src/applications/diffusion/remarkup/__tests__/DiffusionCommitRemarkupRuleTestCase.php b/src/applications/diffusion/remarkup/__tests__/DiffusionCommitRemarkupRuleTestCase.php index 450727bbe1..d2dcda0a23 100644 --- a/src/applications/diffusion/remarkup/__tests__/DiffusionCommitRemarkupRuleTestCase.php +++ b/src/applications/diffusion/remarkup/__tests__/DiffusionCommitRemarkupRuleTestCase.php @@ -121,6 +121,25 @@ final class DiffusionCommitRemarkupRuleTestCase extends PhabricatorTestCase { ), ), ), + + // After an "@", we should not be recognizing references because these + // are username mentions. + 'deadbeef' => array( + 'embed' => array( + ), + 'ref' => array( + array( + 'offset' => 0, + 'id' => 'deadbeef', + ), + ), + ), + '@deadbeef' => array( + 'embed' => array( + ), + 'ref' => array( + ), + ), ); foreach ($cases as $input => $expect) { diff --git a/src/applications/diffusion/view/DiffusionHistoryTableView.php b/src/applications/diffusion/view/DiffusionHistoryTableView.php index fd5f463b34..80989efdf2 100644 --- a/src/applications/diffusion/view/DiffusionHistoryTableView.php +++ b/src/applications/diffusion/view/DiffusionHistoryTableView.php @@ -88,6 +88,11 @@ final class DiffusionHistoryTableView extends DiffusionView { $drequest = $this->getDiffusionRequest(); $viewer = $this->getUser(); + + $show_revisions = PhabricatorApplication::isClassInstalledForViewer( + 'PhabricatorDifferentialApplication', + $viewer); + $handles = $viewer->loadHandles($this->getRequiredHandlePHIDs()); $graph = null; @@ -242,6 +247,10 @@ final class DiffusionHistoryTableView extends DiffusionView { $view->setColumnVisibility( array( $graph ? true : false, + true, + true, + true, + $show_revisions, )); $view->setDeviceVisibility( array( diff --git a/src/applications/drydock/application/PhabricatorDrydockApplication.php b/src/applications/drydock/application/PhabricatorDrydockApplication.php index 5df54593ee..e662fea9e6 100644 --- a/src/applications/drydock/application/PhabricatorDrydockApplication.php +++ b/src/applications/drydock/application/PhabricatorDrydockApplication.php @@ -47,7 +47,7 @@ final class PhabricatorDrydockApplication extends PhabricatorApplication { return array( '/drydock/' => array( '' => 'DrydockConsoleController', - 'blueprint/' => array( + '(?Pblueprint)/' => array( '(?:query/(?P[^/]+)/)?' => 'DrydockBlueprintListController', '(?P[1-9]\d*)/' => array( '' => 'DrydockBlueprintViewController', @@ -55,29 +55,32 @@ final class PhabricatorDrydockApplication extends PhabricatorApplication { 'DrydockBlueprintDisableController', 'resources/(?:query/(?P[^/]+)/)?' => 'DrydockResourceListController', + 'logs/(?:query/(?P[^/]+)/)?' => + 'DrydockLogListController', ), 'create/' => 'DrydockBlueprintCreateController', 'edit/(?:(?P[1-9]\d*)/)?' => 'DrydockBlueprintEditController', ), - 'resource/' => array( + '(?Presource)/' => array( '(?:query/(?P[^/]+)/)?' => 'DrydockResourceListController', '(?P[1-9]\d*)/' => array( '' => 'DrydockResourceViewController', 'release/' => 'DrydockResourceReleaseController', 'leases/(?:query/(?P[^/]+)/)?' => 'DrydockLeaseListController', + 'logs/(?:query/(?P[^/]+)/)?' => + 'DrydockLogListController', ), ), - 'lease/' => array( + '(?Please)/' => array( '(?:query/(?P[^/]+)/)?' => 'DrydockLeaseListController', '(?P[1-9]\d*)/' => array( '' => 'DrydockLeaseViewController', 'release/' => 'DrydockLeaseReleaseController', + 'logs/(?:query/(?P[^/]+)/)?' => + 'DrydockLogListController', ), ), - 'log/' => array( - '(?:query/(?P[^/]+)/)?' => 'DrydockLogListController', - ), ), ); } diff --git a/src/applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php b/src/applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php index 4ae64b39be..e458020600 100644 --- a/src/applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php +++ b/src/applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php @@ -69,8 +69,9 @@ final class DrydockAlmanacServiceHostBlueprintImplementation $binding_phid = $binding->getPHID(); - $resource = $this->newResourceTemplate($blueprint, $device_name) + $resource = $this->newResourceTemplate($blueprint) ->setActivateWhenAllocated(true) + ->setAttribute('almanacDeviceName', $device_name) ->setAttribute('almanacServicePHID', $binding->getServicePHID()) ->setAttribute('almanacBindingPHID', $binding_phid) ->needSlotLock("almanac.host.binding({$binding_phid})"); @@ -95,6 +96,15 @@ final class DrydockAlmanacServiceHostBlueprintImplementation return; } + public function getResourceName( + DrydockBlueprint $blueprint, + DrydockResource $resource) { + $device_name = $resource->getAttribute( + 'almanacDeviceName', + pht('')); + return pht('Host (%s)', $device_name); + } + public function canAcquireLeaseOnResource( DrydockBlueprint $blueprint, DrydockResource $resource, @@ -163,7 +173,6 @@ final class DrydockAlmanacServiceHostBlueprintImplementation ->withPHIDs(array($binding_phid)) ->executeOne(); if (!$binding) { - // TODO: This is probably a permanent failure, destroy this resource? throw new Exception( pht( 'Unable to load binding "%s" to create command interface.', diff --git a/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php b/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php index f58767c4fc..513d7b37f9 100644 --- a/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php +++ b/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php @@ -237,6 +237,19 @@ abstract class DrydockBlueprintImplementation extends Phobject { DrydockResource $resource); + /** + * Get a human readable name for a resource. + * + * @param DrydockBlueprint Blueprint which built the resource. + * @param DrydockResource Resource to get the name of. + * @return string Human-readable resource name. + * @task resource + */ + abstract public function getResourceName( + DrydockBlueprint $blueprint, + DrydockResource $resource); + + /* -( Resource Interfaces )------------------------------------------------ */ @@ -250,46 +263,6 @@ abstract class DrydockBlueprintImplementation extends Phobject { /* -( Logging )------------------------------------------------------------ */ - /** - * @task log - */ - protected function logException(Exception $ex) { - $this->log($ex->getMessage()); - } - - - /** - * @task log - */ - protected function log($message) { - self::writeLog(null, null, $message); - } - - - /** - * @task log - */ - public static function writeLog( - DrydockResource $resource = null, - DrydockLease $lease = null, - $message = null) { - - $log = id(new DrydockLog()) - ->setEpoch(time()) - ->setMessage($message); - - if ($resource) { - $log->setResourceID($resource->getID()); - } - - if ($lease) { - $log->setLeaseID($lease->getID()); - } - - $log->save(); - } - - public static function getAllBlueprintImplementations() { return id(new PhutilClassMapQuery()) ->setAncestorClass(__CLASS__) @@ -300,16 +273,13 @@ abstract class DrydockBlueprintImplementation extends Phobject { return idx(self::getAllBlueprintImplementations(), $class); } - protected function newResourceTemplate( - DrydockBlueprint $blueprint, - $name) { + protected function newResourceTemplate(DrydockBlueprint $blueprint) { $resource = id(new DrydockResource()) ->setBlueprintPHID($blueprint->getPHID()) ->attachBlueprint($blueprint) ->setType($this->getType()) - ->setStatus(DrydockResourceStatus::STATUS_PENDING) - ->setName($name); + ->setStatus(DrydockResourceStatus::STATUS_PENDING); // Pre-allocate the resource PHID. $resource->setPHID($resource->generatePHID()); @@ -325,14 +295,18 @@ abstract class DrydockBlueprintImplementation extends Phobject { $lease_status = $lease->getStatus(); switch ($lease_status) { + case DrydockLeaseStatus::STATUS_PENDING: case DrydockLeaseStatus::STATUS_ACQUIRED: - // TODO: Temporary failure. - throw new Exception(pht('Lease still activating.')); + throw new PhabricatorWorkerYieldException(15); case DrydockLeaseStatus::STATUS_ACTIVE: return; default: - // TODO: Permanent failure. - throw new Exception(pht('Lease in bad state.')); + throw new Exception( + pht( + 'Lease ("%s") is in bad state ("%s"), expected "%s".', + $lease->getPHID(), + $lease_status, + DrydockLeaseStatus::STATUS_ACTIVE)); } } diff --git a/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php b/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php index 6ffe90469d..7a34e2d3bc 100644 --- a/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php +++ b/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php @@ -37,10 +37,37 @@ final class DrydockWorkingCopyBlueprintImplementation DrydockResource $resource, DrydockLease $lease) { - $have_phid = $resource->getAttribute('repositoryPHID'); - $need_phid = $lease->getAttribute('repositoryPHID'); + $need_map = $lease->getAttribute('repositories.map'); + if (!is_array($need_map)) { + return false; + } - if ($need_phid !== $have_phid) { + $have_map = $resource->getAttribute('repositories.map'); + if (!is_array($have_map)) { + return false; + } + + $have_as = ipull($have_map, 'phid'); + $need_as = ipull($need_map, 'phid'); + + foreach ($need_as as $need_directory => $need_phid) { + if (empty($have_as[$need_directory])) { + // This resource is missing a required working copy. + return false; + } + + if ($have_as[$need_directory] != $need_phid) { + // This resource has a required working copy, but it contains + // the wrong repository. + return false; + } + + unset($have_as[$need_directory]); + } + + if ($have_as && $lease->getAttribute('repositories.strict')) { + // This resource has extra repositories, but the lease is strict about + // which repositories are allowed to exist. return false; } @@ -70,14 +97,7 @@ final class DrydockWorkingCopyBlueprintImplementation DrydockBlueprint $blueprint, DrydockLease $lease) { - $repository_phid = $lease->getAttribute('repositoryPHID'); - $repository = $this->loadRepository($repository_phid); - - $resource = $this->newResourceTemplate( - $blueprint, - pht( - 'Working Copy (%s)', - $repository->getCallsign())); + $resource = $this->newResourceTemplate($blueprint); $resource_phid = $resource->getPHID(); @@ -90,8 +110,17 @@ final class DrydockWorkingCopyBlueprintImplementation // TODO: Add some limits to the number of working copies we can have at // once? + $map = $lease->getAttribute('repositories.map'); + foreach ($map as $key => $value) { + $map[$key] = array_select_keys( + $value, + array( + 'phid', + )); + } + return $resource - ->setAttribute('repositoryPHID', $repository->getPHID()) + ->setAttribute('repositories.map', $map) ->setAttribute('host.leasePHID', $host_lease->getPHID()) ->allocateResource(); } @@ -103,26 +132,32 @@ final class DrydockWorkingCopyBlueprintImplementation $lease = $this->loadHostLease($resource); $this->requireActiveLease($lease); - $repository_phid = $resource->getAttribute('repositoryPHID'); - $repository = $this->loadRepository($repository_phid); - $repository_id = $repository->getID(); - $command_type = DrydockCommandInterface::INTERFACE_TYPE; $interface = $lease->getInterface($command_type); // TODO: Make this configurable. $resource_id = $resource->getID(); $root = "/var/drydock/workingcopy-{$resource_id}"; - $path = "{$root}/repo/{$repository_id}/"; - $interface->execx( - 'git clone -- %s %s', - (string)$repository->getCloneURIObject(), - $path); + $map = $resource->getAttribute('repositories.map'); + + $repositories = $this->loadRepositories(ipull($map, 'phid')); + foreach ($map as $directory => $spec) { + // TODO: Validate directory isn't goofy like "/etc" or "../../lol" + // somewhere? + + $repository = $repositories[$spec['phid']]; + $path = "{$root}/repo/{$directory}/"; + + // TODO: Run these in parallel? + $interface->execx( + 'git clone -- %s %s', + (string)$repository->getCloneURIObject(), + $path); + } $resource ->setAttribute('workingcopy.root', $root) - ->setAttribute('workingcopy.path', $path) ->activateResource(); } @@ -135,49 +170,99 @@ final class DrydockWorkingCopyBlueprintImplementation // Destroy the lease on the host. $lease->releaseOnDestruction(); - // Destroy the working copy on disk. - $command_type = DrydockCommandInterface::INTERFACE_TYPE; - $interface = $lease->getInterface($command_type); + if ($lease->isActive()) { + // Destroy the working copy on disk. + $command_type = DrydockCommandInterface::INTERFACE_TYPE; + $interface = $lease->getInterface($command_type); - $root_key = 'workingcopy.root'; - $root = $resource->getAttribute($root_key); - if (strlen($root)) { - $interface->execx('rm -rf -- %s', $root); + $root_key = 'workingcopy.root'; + $root = $resource->getAttribute($root_key); + if (strlen($root)) { + $interface->execx('rm -rf -- %s', $root); + } } } + public function getResourceName( + DrydockBlueprint $blueprint, + DrydockResource $resource) { + return pht('Working Copy'); + } + + public function activateLease( DrydockBlueprint $blueprint, DrydockResource $resource, DrydockLease $lease) { + $host_lease = $this->loadHostLease($resource); $command_type = DrydockCommandInterface::INTERFACE_TYPE; - $interface = $lease->getInterface($command_type); + $interface = $host_lease->getInterface($command_type); - $cmd = array(); - $arg = array(); + $map = $lease->getAttribute('repositories.map'); + $root = $resource->getAttribute('workingcopy.root'); - $cmd[] = 'git clean -d --force'; - $cmd[] = 'git reset --hard HEAD'; - $cmd[] = 'git fetch'; + $default = null; + foreach ($map as $directory => $spec) { + $cmd = array(); + $arg = array(); - $commit = $lease->getAttribute('commit'); - $branch = $lease->getAttribute('branch'); + $cmd[] = 'cd %s'; + $arg[] = "{$root}/repo/{$directory}/"; - if ($commit !== null) { - $cmd[] = 'git reset --hard %s'; - $arg[] = $commit; - } else if ($branch !== null) { - $cmd[] = 'git reset --hard %s'; - $arg[] = $branch; + $cmd[] = 'git clean -d --force'; + $cmd[] = 'git fetch'; + + $commit = idx($spec, 'commit'); + $branch = idx($spec, 'branch'); + + $ref = idx($spec, 'ref'); + + if ($commit !== null) { + $cmd[] = 'git reset --hard %s'; + $arg[] = $commit; + } else if ($branch !== null) { + $cmd[] = 'git checkout %s'; + $arg[] = $branch; + + $cmd[] = 'git reset --hard origin/%s'; + $arg[] = $branch; + } else if ($ref) { + $ref_uri = $ref['uri']; + $ref_ref = $ref['ref']; + + $cmd[] = 'git fetch --no-tags -- %s +%s:%s'; + $arg[] = $ref_uri; + $arg[] = $ref_ref; + $arg[] = $ref_ref; + + $cmd[] = 'git checkout %s'; + $arg[] = $ref_ref; + + $cmd[] = 'git reset --hard %s'; + $arg[] = $ref_ref; + } else { + $cmd[] = 'git reset --hard HEAD'; + } + + $cmd = implode(' && ', $cmd); + $argv = array_merge(array($cmd), $arg); + + $result = call_user_func_array( + array($interface, 'execx'), + $argv); + + if (idx($spec, 'default')) { + $default = $directory; + } } - $cmd = implode(' && ', $cmd); - $argv = array_merge(array($cmd), $arg); + if ($default === null) { + $default = head_key($map); + } - $result = call_user_func_array( - array($interface, 'execx'), - $argv); + // TODO: Use working storage? + $lease->setAttribute('workingcopy.default', "{$root}/repo/{$default}/"); $lease->activateOnResource($resource); } @@ -217,35 +302,44 @@ final class DrydockWorkingCopyBlueprintImplementation $host_lease = $this->loadHostLease($resource); $command_interface = $host_lease->getInterface($type); - $path = $resource->getAttribute('workingcopy.path'); + $path = $lease->getAttribute('workingcopy.default'); $command_interface->setWorkingDirectory($path); return $command_interface; } } - private function loadRepository($repository_phid) { - $repository = id(new PhabricatorRepositoryQuery()) + private function loadRepositories(array $phids) { + $repositories = id(new PhabricatorRepositoryQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withPHIDs(array($repository_phid)) - ->executeOne(); - if (!$repository) { - // TODO: Permanent failure. - throw new Exception( - pht( - 'Repository PHID "%s" does not exist.', - $repository_phid)); + ->withPHIDs($phids) + ->execute(); + $repositories = mpull($repositories, null, 'getPHID'); + + foreach ($phids as $phid) { + if (empty($repositories[$phid])) { + throw new Exception( + pht( + 'Repository PHID "%s" does not exist.', + $phid)); + } } - switch ($repository->getVersionControlSystem()) { - case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: - break; - default: - // TODO: Permanent failure. - throw new Exception(pht('Unsupported VCS!')); + foreach ($repositories as $repository) { + $repository_vcs = $repository->getVersionControlSystem(); + switch ($repository_vcs) { + case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: + break; + default: + throw new Exception( + pht( + 'Repository ("%s") has unsupported VCS ("%s").', + $repository->getPHID(), + $repository_vcs)); + } } - return $repository; + return $repositories; } private function loadHostLease(DrydockResource $resource) { @@ -258,8 +352,10 @@ final class DrydockWorkingCopyBlueprintImplementation ->withPHIDs(array($lease_phid)) ->executeOne(); if (!$lease) { - // TODO: Permanent failure. - throw new Exception(pht('Unable to load lease "%s".', $lease_phid)); + throw new Exception( + pht( + 'Unable to load lease ("%s").', + $lease_phid)); } return $lease; diff --git a/src/applications/drydock/capability/DrydockDefaultViewCapability.php b/src/applications/drydock/capability/DrydockDefaultViewCapability.php index af51edc38d..f43471766c 100644 --- a/src/applications/drydock/capability/DrydockDefaultViewCapability.php +++ b/src/applications/drydock/capability/DrydockDefaultViewCapability.php @@ -8,4 +8,8 @@ final class DrydockDefaultViewCapability extends PhabricatorPolicyCapability { return pht('Default Blueprint View Policy'); } + public function shouldAllowPublicPolicySetting() { + return true; + } + } diff --git a/src/applications/drydock/controller/DrydockBlueprintViewController.php b/src/applications/drydock/controller/DrydockBlueprintViewController.php index 6991e18fa2..7102962600 100644 --- a/src/applications/drydock/controller/DrydockBlueprintViewController.php +++ b/src/applications/drydock/controller/DrydockBlueprintViewController.php @@ -56,11 +56,19 @@ final class DrydockBlueprintViewController extends DrydockBlueprintController { new DrydockBlueprintTransactionQuery()); $timeline->setShouldTerminate(true); + $log_query = id(new DrydockLogQuery()) + ->withBlueprintPHIDs(array($blueprint->getPHID())); + + $log_box = $this->buildLogBox( + $log_query, + $this->getApplicationURI("blueprint/{$id}/logs/query/all/")); + return $this->buildApplicationPage( array( $crumbs, $object_box, $resource_box, + $log_box, $timeline, ), array( diff --git a/src/applications/drydock/controller/DrydockConsoleController.php b/src/applications/drydock/controller/DrydockConsoleController.php index 118d49ea59..1964f508d0 100644 --- a/src/applications/drydock/controller/DrydockConsoleController.php +++ b/src/applications/drydock/controller/DrydockConsoleController.php @@ -15,7 +15,6 @@ final class DrydockConsoleController extends DrydockController { $nav->addFilter('blueprint', pht('Blueprints')); $nav->addFilter('resource', pht('Resources')); $nav->addFilter('lease', pht('Leases')); - $nav->addFilter('log', pht('Logs')); $nav->selectFilter(null); @@ -31,6 +30,7 @@ final class DrydockConsoleController extends DrydockController { $menu->addItem( id(new PHUIObjectItemView()) ->setHeader(pht('Blueprints')) + ->setFontIcon('fa-map-o') ->setHref($this->getApplicationURI('blueprint/')) ->addAttribute( pht( @@ -40,6 +40,7 @@ final class DrydockConsoleController extends DrydockController { $menu->addItem( id(new PHUIObjectItemView()) ->setHeader(pht('Resources')) + ->setFontIcon('fa-map') ->setHref($this->getApplicationURI('resource/')) ->addAttribute( pht('View and manage resources Drydock has built, like hosts.'))); @@ -47,16 +48,10 @@ final class DrydockConsoleController extends DrydockController { $menu->addItem( id(new PHUIObjectItemView()) ->setHeader(pht('Leases')) + ->setFontIcon('fa-link') ->setHref($this->getApplicationURI('lease/')) ->addAttribute(pht('Manage leases on resources.'))); - $menu->addItem( - id(new PHUIObjectItemView()) - ->setHeader(pht('Logs')) - ->setHref($this->getApplicationURI('log/')) - ->addAttribute(pht('View logs.'))); - - $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Console')); diff --git a/src/applications/drydock/controller/DrydockController.php b/src/applications/drydock/controller/DrydockController.php index e0130bdf56..760334cbdf 100644 --- a/src/applications/drydock/controller/DrydockController.php +++ b/src/applications/drydock/controller/DrydockController.php @@ -85,4 +85,31 @@ abstract class DrydockController extends PhabricatorController { ->addRawContent($table); } + protected function buildLogBox(DrydockLogQuery $query, $all_uri) { + $viewer = $this->getViewer(); + + $logs = $query + ->setViewer($viewer) + ->setLimit(100) + ->execute(); + + $log_table = id(new DrydockLogListView()) + ->setUser($viewer) + ->setLogs($logs) + ->render(); + + $log_header = id(new PHUIHeaderView()) + ->setHeader(pht('Logs')) + ->addActionLink( + id(new PHUIButtonView()) + ->setTag('a') + ->setHref($all_uri) + ->setIconFont('fa-search') + ->setText(pht('View All Logs'))); + + return id(new PHUIObjectBoxView()) + ->setHeader($log_header) + ->setTable($log_table); + } + } diff --git a/src/applications/drydock/controller/DrydockLeaseController.php b/src/applications/drydock/controller/DrydockLeaseController.php index d5a6335454..48082b8728 100644 --- a/src/applications/drydock/controller/DrydockLeaseController.php +++ b/src/applications/drydock/controller/DrydockLeaseController.php @@ -44,7 +44,7 @@ abstract class DrydockLeaseController $this->getApplicationURI('resource/')); $crumbs->addTextCrumb( - $resource->getName(), + $resource->getResourceName(), $this->getApplicationURI("resource/{$id}/")); $crumbs->addTextCrumb( diff --git a/src/applications/drydock/controller/DrydockLeaseViewController.php b/src/applications/drydock/controller/DrydockLeaseViewController.php index bb748cceee..b9cf592313 100644 --- a/src/applications/drydock/controller/DrydockLeaseViewController.php +++ b/src/applications/drydock/controller/DrydockLeaseViewController.php @@ -9,35 +9,33 @@ final class DrydockLeaseViewController extends DrydockLeaseController { $lease = id(new DrydockLeaseQuery()) ->setViewer($viewer) ->withIDs(array($id)) + ->needUnconsumedCommands(true) ->executeOne(); if (!$lease) { return new Aphront404Response(); } - $lease_uri = $this->getApplicationURI('lease/'.$lease->getID().'/'); + $id = $lease->getID(); + $lease_uri = $this->getApplicationURI("lease/{$id}/"); $title = pht('Lease %d', $lease->getID()); $header = id(new PHUIHeaderView()) ->setHeader($title); + if ($lease->isReleasing()) { + $header->setStatus('fa-exclamation-triangle', 'red', pht('Releasing')); + } + $actions = $this->buildActionListView($lease); $properties = $this->buildPropertyListView($lease, $actions); - $pager = new PHUIPagerView(); - $pager->setURI(new PhutilURI($lease_uri), 'offset'); - $pager->setOffset($request->getInt('offset')); + $log_query = id(new DrydockLogQuery()) + ->withLeasePHIDs(array($lease->getPHID())); - $logs = id(new DrydockLogQuery()) - ->setViewer($viewer) - ->withLeaseIDs(array($lease->getID())) - ->executeWithOffsetPager($pager); - - $log_table = id(new DrydockLogListView()) - ->setUser($viewer) - ->setLogs($logs) - ->render(); - $log_table->appendChild($pager); + $log_box = $this->buildLogBox( + $log_query, + $this->getApplicationURI("lease/{$id}/logs/query/all/")); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb($title, $lease_uri); @@ -51,10 +49,6 @@ final class DrydockLeaseViewController extends DrydockLeaseController { ->addPropertyList($locks, pht('Slot Locks')) ->addPropertyList($commands, pht('Commands')); - $log_box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Lease Logs')) - ->setTable($log_table); - return $this->buildApplicationPage( array( $crumbs, @@ -78,6 +72,10 @@ final class DrydockLeaseViewController extends DrydockLeaseController { $id = $lease->getID(); $can_release = $lease->canRelease(); + if ($lease->isReleasing()) { + $can_release = false; + } + $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $lease, @@ -110,6 +108,14 @@ final class DrydockLeaseViewController extends DrydockLeaseController { pht('Resource Type'), $lease->getResourceType()); + $owner_phid = $lease->getOwnerPHID(); + if ($owner_phid) { + $owner_display = $viewer->renderHandle($owner_phid); + } else { + $owner_display = phutil_tag('em', array(), pht('No Owner')); + } + $view->addProperty(pht('Owner'), $owner_display); + $resource_phid = $lease->getResourcePHID(); if ($resource_phid) { $resource_display = $viewer->renderHandle($resource_phid); diff --git a/src/applications/drydock/controller/DrydockLogController.php b/src/applications/drydock/controller/DrydockLogController.php index 0e28c62cb4..704cbb53b9 100644 --- a/src/applications/drydock/controller/DrydockLogController.php +++ b/src/applications/drydock/controller/DrydockLogController.php @@ -3,13 +3,60 @@ abstract class DrydockLogController extends DrydockController { + private $blueprint; + private $resource; + private $lease; + + public function setBlueprint(DrydockBlueprint $blueprint) { + $this->blueprint = $blueprint; + return $this; + } + + public function getBlueprint() { + return $this->blueprint; + } + + public function setResource(DrydockResource $resource) { + $this->resource = $resource; + return $this; + } + + public function getResource() { + return $this->resource; + } + + public function setLease(DrydockLease $lease) { + $this->lease = $lease; + return $this; + } + + public function getLease() { + return $this->lease; + } + public function buildSideNavView() { $nav = new AphrontSideNavFilterView(); $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); - id(new DrydockLogSearchEngine()) - ->setViewer($this->getRequest()->getUser()) - ->addNavigationItems($nav->getMenu()); + $engine = id(new DrydockLogSearchEngine()) + ->setViewer($this->getRequest()->getUser()); + + $blueprint = $this->getBlueprint(); + if ($blueprint) { + $engine->setBlueprint($blueprint); + } + + $resource = $this->getResource(); + if ($resource) { + $engine->setResource($resource); + } + + $lease = $this->getLease(); + if ($lease) { + $engine->setLease($lease); + } + + $engine->addNavigationItems($nav->getMenu()); $nav->selectFilter(null); @@ -18,9 +65,54 @@ abstract class DrydockLogController protected function buildApplicationCrumbs() { $crumbs = parent::buildApplicationCrumbs(); - $crumbs->addTextCrumb( - pht('Logs'), - $this->getApplicationURI('log/')); + + $blueprint = $this->getBlueprint(); + $resource = $this->getResource(); + $lease = $this->getLease(); + if ($blueprint) { + $id = $blueprint->getID(); + + $crumbs->addTextCrumb( + pht('Blueprints'), + $this->getApplicationURI('blueprint/')); + + $crumbs->addTextCrumb( + $blueprint->getBlueprintName(), + $this->getApplicationURI("blueprint/{$id}/")); + + $crumbs->addTextCrumb( + pht('Logs'), + $this->getApplicationURI("blueprint/{$id}/logs/")); + } else if ($resource) { + $id = $resource->getID(); + + $crumbs->addTextCrumb( + pht('Resources'), + $this->getApplicationURI('resource/')); + + $crumbs->addTextCrumb( + $resource->getResourceName(), + $this->getApplicationURI("resource/{$id}/")); + + $crumbs->addTextCrumb( + pht('Logs'), + $this->getApplicationURI("resource/{$id}/logs/")); + } else if ($lease) { + $id = $lease->getID(); + + $crumbs->addTextCrumb( + pht('Leases'), + $this->getApplicationURI('lease/')); + + $crumbs->addTextCrumb( + $lease->getLeaseName(), + $this->getApplicationURI("lease/{$id}/")); + + $crumbs->addTextCrumb( + pht('Logs'), + $this->getApplicationURI("lease/{$id}/logs/")); + } + return $crumbs; } diff --git a/src/applications/drydock/controller/DrydockLogListController.php b/src/applications/drydock/controller/DrydockLogListController.php index aecf77dc77..b5e4d4ff0c 100644 --- a/src/applications/drydock/controller/DrydockLogListController.php +++ b/src/applications/drydock/controller/DrydockLogListController.php @@ -8,11 +8,53 @@ final class DrydockLogListController extends DrydockLogController { public function handleRequest(AphrontRequest $request) { $viewer = $request->getViewer(); - $querykey = $request->getURIData('queryKey'); + $engine = new DrydockLogSearchEngine(); + + $id = $request->getURIData('id'); + $type = $request->getURIData('type'); + switch ($type) { + case 'blueprint': + $blueprint = id(new DrydockBlueprintQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->executeOne(); + if (!$blueprint) { + return new Aphront404Response(); + } + $engine->setBlueprint($blueprint); + $this->setBlueprint($blueprint); + break; + case 'resource': + $resource = id(new DrydockResourceQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->executeOne(); + if (!$resource) { + return new Aphront404Response(); + } + $engine->setResource($resource); + $this->setResource($resource); + break; + case 'lease': + $lease = id(new DrydockLeaseQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->executeOne(); + if (!$lease) { + return new Aphront404Response(); + } + $engine->setLease($lease); + $this->setLease($lease); + break; + default: + return new Aphront404Response(); + } + + $query_key = $request->getURIData('queryKey'); $controller = id(new PhabricatorApplicationSearchController()) - ->setQueryKey($querykey) - ->setSearchEngine(new DrydockLogSearchEngine()) + ->setQueryKey($query_key) + ->setSearchEngine($engine) ->setNavigation($this->buildSideNavView()); return $this->delegateToController($controller); diff --git a/src/applications/drydock/controller/DrydockResourceViewController.php b/src/applications/drydock/controller/DrydockResourceViewController.php index 0641ced96d..4809cf970c 100644 --- a/src/applications/drydock/controller/DrydockResourceViewController.php +++ b/src/applications/drydock/controller/DrydockResourceViewController.php @@ -9,38 +9,38 @@ final class DrydockResourceViewController extends DrydockResourceController { $resource = id(new DrydockResourceQuery()) ->setViewer($viewer) ->withIDs(array($id)) + ->needUnconsumedCommands(true) ->executeOne(); if (!$resource) { return new Aphront404Response(); } - $title = pht('Resource %s %s', $resource->getID(), $resource->getName()); + $title = pht( + 'Resource %s %s', + $resource->getID(), + $resource->getResourceName()); $header = id(new PHUIHeaderView()) ->setUser($viewer) ->setPolicyObject($resource) ->setHeader($title); + if ($resource->isReleasing()) { + $header->setStatus('fa-exclamation-triangle', 'red', pht('Releasing')); + } + $actions = $this->buildActionListView($resource); $properties = $this->buildPropertyListView($resource, $actions); - $resource_uri = 'resource/'.$resource->getID().'/'; - $resource_uri = $this->getApplicationURI($resource_uri); + $id = $resource->getID(); + $resource_uri = $this->getApplicationURI("resource/{$id}/"); - $pager = new PHUIPagerView(); - $pager->setURI(new PhutilURI($resource_uri), 'offset'); - $pager->setOffset($request->getInt('offset')); + $log_query = id(new DrydockLogQuery()) + ->withResourcePHIDs(array($resource->getPHID())); - $logs = id(new DrydockLogQuery()) - ->setViewer($viewer) - ->withResourceIDs(array($resource->getID())) - ->executeWithOffsetPager($pager); - - $log_table = id(new DrydockLogListView()) - ->setUser($viewer) - ->setLogs($logs) - ->render(); - $log_table->appendChild($pager); + $log_box = $this->buildLogBox( + $log_query, + $this->getApplicationURI("resource/{$id}/logs/query/all/")); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Resource %d', $resource->getID())); @@ -56,10 +56,6 @@ final class DrydockResourceViewController extends DrydockResourceController { $lease_box = $this->buildLeaseBox($resource); - $log_box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Resource Logs')) - ->setTable($log_table); - return $this->buildApplicationPage( array( $crumbs, @@ -82,6 +78,10 @@ final class DrydockResourceViewController extends DrydockResourceController { ->setObject($resource); $can_release = $resource->canRelease(); + if ($resource->isReleasing()) { + $can_release = false; + } + $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $resource, @@ -116,6 +116,14 @@ final class DrydockResourceViewController extends DrydockResourceController { pht('Status'), $status); + $until = $resource->getUntil(); + if ($until) { + $until_display = phabricator_datetime($until, $viewer); + } else { + $until_display = phutil_tag('em', array(), pht('Never')); + } + $view->addProperty(pht('Expires'), $until_display); + $view->addProperty( pht('Resource Type'), $resource->getType()); diff --git a/src/applications/drydock/exception/DrydockSlotLockException.php b/src/applications/drydock/exception/DrydockSlotLockException.php index 2bcf5f8cd7..3ee40f1b32 100644 --- a/src/applications/drydock/exception/DrydockSlotLockException.php +++ b/src/applications/drydock/exception/DrydockSlotLockException.php @@ -22,4 +22,8 @@ final class DrydockSlotLockException extends Exception { parent::__construct($message); } + public function getLockMap() { + return $this->lockMap; + } + } diff --git a/src/applications/drydock/garbagecollector/DrydockLogGarbageCollector.php b/src/applications/drydock/garbagecollector/DrydockLogGarbageCollector.php new file mode 100644 index 0000000000..0c9ccb7021 --- /dev/null +++ b/src/applications/drydock/garbagecollector/DrydockLogGarbageCollector.php @@ -0,0 +1,29 @@ +establishConnection('w'); + + queryfx( + $conn_w, + 'DELETE FROM %T WHERE epoch <= %d LIMIT 100', + $log_table->getTableName(), + $this->getGarbageEpoch()); + + return ($conn_w->getAffectedRows() == 100); + } + +} diff --git a/src/applications/drydock/logtype/DrydockLeaseAcquiredLogType.php b/src/applications/drydock/logtype/DrydockLeaseAcquiredLogType.php new file mode 100644 index 0000000000..c9eef61922 --- /dev/null +++ b/src/applications/drydock/logtype/DrydockLeaseAcquiredLogType.php @@ -0,0 +1,19 @@ +getViewer(); + + $blueprint_phids = idx($data, 'blueprintPHIDs', array()); + + return pht( + 'Waiting for available resources from: %s.', + $viewer->renderHandleList($blueprint_phids)); + } + +} diff --git a/src/applications/drydock/logtype/DrydockLogType.php b/src/applications/drydock/logtype/DrydockLogType.php new file mode 100644 index 0000000000..7faab42dfe --- /dev/null +++ b/src/applications/drydock/logtype/DrydockLogType.php @@ -0,0 +1,41 @@ +viewer = $viewer; + return $this; + } + + public function getViewer() { + return $this->viewer; + } + + final public function setLog(DrydockLog $log) { + $this->log = $log; + return $this; + } + + final public function getLog() { + return $this->log; + } + + final public function getLogTypeConstant() { + return $this->getPhobjectClassConstant('LOGCONST', 64); + } + + final public static function getAllLogTypes() { + return id(new PhutilClassMapQuery()) + ->setAncestorClass(__CLASS__) + ->setUniqueMethod('getLogTypeConstant') + ->execute(); + } + +} diff --git a/src/applications/drydock/logtype/DrydockResourceActivationFailureLogType.php b/src/applications/drydock/logtype/DrydockResourceActivationFailureLogType.php new file mode 100644 index 0000000000..d5fac38da4 --- /dev/null +++ b/src/applications/drydock/logtype/DrydockResourceActivationFailureLogType.php @@ -0,0 +1,22 @@ +getName())); + $resource->getResourceName())); $handle->setURI("/drydock/resource/{$id}/"); } diff --git a/src/applications/drydock/query/DrydockLeaseQuery.php b/src/applications/drydock/query/DrydockLeaseQuery.php index c7c770bbea..c5fb41ff56 100644 --- a/src/applications/drydock/query/DrydockLeaseQuery.php +++ b/src/applications/drydock/query/DrydockLeaseQuery.php @@ -7,7 +7,7 @@ final class DrydockLeaseQuery extends DrydockQuery { private $resourcePHIDs; private $statuses; private $datasourceQuery; - private $needCommands; + private $needUnconsumedCommands; public function withIDs(array $ids) { $this->ids = $ids; @@ -34,6 +34,11 @@ final class DrydockLeaseQuery extends DrydockQuery { return $this; } + public function needUnconsumedCommands($need) { + $this->needUnconsumedCommands = $need; + return $this; + } + public function newResultObject() { return new DrydockLease(); } @@ -71,6 +76,25 @@ final class DrydockLeaseQuery extends DrydockQuery { return $leases; } + protected function didFilterPage(array $leases) { + if ($this->needUnconsumedCommands) { + $commands = id(new DrydockCommandQuery()) + ->setViewer($this->getViewer()) + ->setParentQuery($this) + ->withTargetPHIDs(mpull($leases, 'getPHID')) + ->withConsumed(false) + ->execute(); + $commands = mgroup($commands, 'getTargetPHID'); + + foreach ($leases as $lease) { + $list = idx($commands, $lease->getPHID(), array()); + $lease->attachUnconsumedCommands($list); + } + } + + return $leases; + } + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/drydock/query/DrydockLogQuery.php b/src/applications/drydock/query/DrydockLogQuery.php index 47a6795463..00980edb4d 100644 --- a/src/applications/drydock/query/DrydockLogQuery.php +++ b/src/applications/drydock/query/DrydockLogQuery.php @@ -2,112 +2,125 @@ final class DrydockLogQuery extends DrydockQuery { - private $resourceIDs; - private $leaseIDs; + private $blueprintPHIDs; + private $resourcePHIDs; + private $leasePHIDs; - public function withResourceIDs(array $ids) { - $this->resourceIDs = $ids; + public function withBlueprintPHIDs(array $phids) { + $this->blueprintPHIDs = $phids; return $this; } - public function withLeaseIDs(array $ids) { - $this->leaseIDs = $ids; + public function withResourcePHIDs(array $phids) { + $this->resourcePHIDs = $phids; return $this; } + public function withLeasePHIDs(array $phids) { + $this->leasePHIDs = $phids; + return $this; + } + + public function newResultObject() { + return new DrydockLog(); + } + protected function loadPage() { - $table = new DrydockLog(); - $conn_r = $table->establishConnection('r'); - - $data = queryfx_all( - $conn_r, - 'SELECT log.* FROM %T log %Q %Q %Q', - $table->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); - - return $table->loadAllFromArray($data); + return $this->loadStandardPage($this->newResultObject()); } - protected function willFilterPage(array $logs) { - $resource_ids = array_filter(mpull($logs, 'getResourceID')); - if ($resource_ids) { + protected function didFilterPage(array $logs) { + $blueprint_phids = array_filter(mpull($logs, 'getBlueprintPHID')); + if ($blueprint_phids) { + $blueprints = id(new DrydockBlueprintQuery()) + ->setParentQuery($this) + ->setViewer($this->getViewer()) + ->withPHIDs($blueprint_phids) + ->execute(); + $blueprints = mpull($blueprints, null, 'getPHID'); + } else { + $blueprints = array(); + } + + foreach ($logs as $key => $log) { + $blueprint = null; + $blueprint_phid = $log->getBlueprintPHID(); + if ($blueprint_phid) { + $blueprint = idx($blueprints, $blueprint_phid); + } + $log->attachBlueprint($blueprint); + } + + $resource_phids = array_filter(mpull($logs, 'getResourcePHID')); + if ($resource_phids) { $resources = id(new DrydockResourceQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) - ->withIDs(array_unique($resource_ids)) + ->withPHIDs($resource_phids) ->execute(); + $resources = mpull($resources, null, 'getPHID'); } else { $resources = array(); } foreach ($logs as $key => $log) { $resource = null; - if ($log->getResourceID()) { - $resource = idx($resources, $log->getResourceID()); - if (!$resource) { - unset($logs[$key]); - continue; - } + $resource_phid = $log->getResourcePHID(); + if ($resource_phid) { + $resource = idx($resources, $resource_phid); } $log->attachResource($resource); } - $lease_ids = array_filter(mpull($logs, 'getLeaseID')); - if ($lease_ids) { + $lease_phids = array_filter(mpull($logs, 'getLeasePHID')); + if ($lease_phids) { $leases = id(new DrydockLeaseQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) - ->withIDs(array_unique($lease_ids)) + ->withPHIDs($lease_phids) ->execute(); + $leases = mpull($leases, null, 'getPHID'); } else { $leases = array(); } foreach ($logs as $key => $log) { $lease = null; - if ($log->getLeaseID()) { - $lease = idx($leases, $log->getLeaseID()); - if (!$lease) { - unset($logs[$key]); - continue; - } + $lease_phid = $log->getLeasePHID(); + if ($lease_phid) { + $lease = idx($leases, $lease_phid); } $log->attachLease($lease); } - // These logs are meaningless and their policies aren't computable. They - // shouldn't exist, but throw them away if they do. - foreach ($logs as $key => $log) { - if (!$log->getResource() && !$log->getLease()) { - unset($logs[$key]); - } - } - return $logs; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { - $where = array(); + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); - if ($this->resourceIDs !== null) { + if ($this->blueprintPHIDs !== null) { $where[] = qsprintf( - $conn_r, - 'resourceID IN (%Ld)', - $this->resourceIDs); + $conn, + 'blueprintPHID IN (%Ls)', + $this->blueprintPHIDs); } - if ($this->leaseIDs !== null) { + if ($this->resourcePHIDs !== null) { $where[] = qsprintf( - $conn_r, - 'leaseID IN (%Ld)', - $this->leaseIDs); + $conn, + 'resourcePHID IN (%Ls)', + $this->resourcePHIDs); } - $where[] = $this->buildPagingClause($conn_r); + if ($this->leasePHIDs !== null) { + $where[] = qsprintf( + $conn, + 'leasePHID IN (%Ls)', + $this->leasePHIDs); + } - return $this->formatWhereClause($where); + return $where; } } diff --git a/src/applications/drydock/query/DrydockLogSearchEngine.php b/src/applications/drydock/query/DrydockLogSearchEngine.php index 13777031d6..43b1511c01 100644 --- a/src/applications/drydock/query/DrydockLogSearchEngine.php +++ b/src/applications/drydock/query/DrydockLogSearchEngine.php @@ -2,6 +2,43 @@ final class DrydockLogSearchEngine extends PhabricatorApplicationSearchEngine { + private $blueprint; + private $resource; + private $lease; + + public function setBlueprint(DrydockBlueprint $blueprint) { + $this->blueprint = $blueprint; + return $this; + } + + public function getBlueprint() { + return $this->blueprint; + } + + public function setResource(DrydockResource $resource) { + $this->resource = $resource; + return $this; + } + + public function getResource() { + return $this->resource; + } + + public function setLease(DrydockLease $lease) { + $this->lease = $lease; + return $this; + } + + public function getLease() { + return $this->lease; + } + + public function canUseInPanelContext() { + // Prevent use on Dashboard panels since all log queries currently need a + // parent object and these don't seem particularly useful in any case. + return false; + } + public function getResultTypeDescription() { return pht('Drydock Logs'); } @@ -10,75 +47,59 @@ final class DrydockLogSearchEngine extends PhabricatorApplicationSearchEngine { return 'PhabricatorDrydockApplication'; } - public function buildSavedQueryFromRequest(AphrontRequest $request) { - $query = new PhabricatorSavedQuery(); - - $query->setParameter( - 'resourcePHIDs', - $this->readListFromRequest($request, 'resources')); - $query->setParameter( - 'leasePHIDs', - $this->readListFromRequest($request, 'leases')); - - return $query; - } - - public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { - $resource_phids = $saved->getParameter('resourcePHIDs', array()); - $lease_phids = $saved->getParameter('leasePHIDs', array()); - - // TODO: Change logs to use PHIDs instead of IDs. - $resource_ids = array(); - $lease_ids = array(); - - if ($resource_phids) { - $resource_ids = id(new DrydockResourceQuery()) - ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withPHIDs($resource_phids) - ->execute(); - $resource_ids = mpull($resource_ids, 'getID'); - } - - if ($lease_phids) { - $lease_ids = id(new DrydockLeaseQuery()) - ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withPHIDs($lease_phids) - ->execute(); - $lease_ids = mpull($lease_ids, 'getID'); - } - + public function newQuery() { $query = new DrydockLogQuery(); - if ($resource_ids) { - $query->withResourceIDs($resource_ids); + + $blueprint = $this->getBlueprint(); + if ($blueprint) { + $query->withBlueprintPHIDs(array($blueprint->getPHID())); } - if ($lease_ids) { - $query->withLeaseIDs($lease_ids); + + $resource = $this->getResource(); + if ($resource) { + $query->withResourcePHIDs(array($resource->getPHID())); + } + + $lease = $this->getLease(); + if ($lease) { + $query->withLeasePHIDs(array($lease->getPHID())); } return $query; } - public function buildSearchForm( - AphrontFormView $form, - PhabricatorSavedQuery $saved) { + protected function buildQueryFromParameters(array $map) { + $query = $this->newQuery(); - $form - ->appendControl( - id(new AphrontFormTokenizerControl()) - ->setDatasource(new DrydockResourceDatasource()) - ->setName('resources') - ->setLabel(pht('Resources')) - ->setValue($saved->getParameter('resourcePHIDs', array()))) - ->appendControl( - id(new AphrontFormTokenizerControl()) - ->setDatasource(new DrydockLeaseDatasource()) - ->setName('leases') - ->setLabel(pht('Leases')) - ->setValue($saved->getParameter('leasePHIDs', array()))); + return $query; + } + + protected function buildCustomSearchFields() { + return array(); } protected function getURI($path) { - return '/drydock/log/'.$path; + $blueprint = $this->getBlueprint(); + if ($blueprint) { + $id = $blueprint->getID(); + return "/drydock/blueprint/{$id}/logs/{$path}"; + } + + $resource = $this->getResource(); + if ($resource) { + $id = $resource->getID(); + return "/drydock/resource/{$id}/logs/{$path}"; + } + + $lease = $this->getLease(); + if ($lease) { + $id = $lease->getID(); + return "/drydock/lease/{$id}/logs/{$path}"; + } + + throw new Exception( + pht( + 'Search engine has no blueprint, resource, or lease.')); } protected function getBuiltinQueryNames() { diff --git a/src/applications/drydock/query/DrydockResourceQuery.php b/src/applications/drydock/query/DrydockResourceQuery.php index d15b737141..c477da20b5 100644 --- a/src/applications/drydock/query/DrydockResourceQuery.php +++ b/src/applications/drydock/query/DrydockResourceQuery.php @@ -8,6 +8,7 @@ final class DrydockResourceQuery extends DrydockQuery { private $types; private $blueprintPHIDs; private $datasourceQuery; + private $needUnconsumedCommands; public function withIDs(array $ids) { $this->ids = $ids; @@ -39,6 +40,11 @@ final class DrydockResourceQuery extends DrydockQuery { return $this; } + public function needUnconsumedCommands($need) { + $this->needUnconsumedCommands = $need; + return $this; + } + public function newResultObject() { return new DrydockResource(); } @@ -69,6 +75,25 @@ final class DrydockResourceQuery extends DrydockQuery { return $resources; } + protected function didFilterPage(array $resources) { + if ($this->needUnconsumedCommands) { + $commands = id(new DrydockCommandQuery()) + ->setViewer($this->getViewer()) + ->setParentQuery($this) + ->withTargetPHIDs(mpull($resources, 'getPHID')) + ->withConsumed(false) + ->execute(); + $commands = mgroup($commands, 'getTargetPHID'); + + foreach ($resources as $resource) { + $list = idx($commands, $resource->getPHID(), array()); + $resource->attachUnconsumedCommands($list); + } + } + + return $resources; + } + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/drydock/storage/DrydockBlueprint.php b/src/applications/drydock/storage/DrydockBlueprint.php index 429e5c2971..4bc3dbe86d 100644 --- a/src/applications/drydock/storage/DrydockBlueprint.php +++ b/src/applications/drydock/storage/DrydockBlueprint.php @@ -107,6 +107,17 @@ final class DrydockBlueprint extends DrydockDAO return $this->fields; } + public function logEvent($type, array $data = array()) { + $log = id(new DrydockLog()) + ->setEpoch(PhabricatorTime::getNow()) + ->setType($type) + ->setData($data); + + $log->setBlueprintPHID($this->getPHID()); + + return $log->save(); + } + /* -( Allocating Resources )----------------------------------------------- */ @@ -162,6 +173,16 @@ final class DrydockBlueprint extends DrydockDAO } + /** + * @task resource + */ + public function getResourceName(DrydockResource $resource) { + return $this->getImplementation()->getResourceName( + $this, + $resource); + } + + /* -( Acquiring Leases )--------------------------------------------------- */ diff --git a/src/applications/drydock/storage/DrydockLease.php b/src/applications/drydock/storage/DrydockLease.php index fe38f54fe0..aa2ea753f0 100644 --- a/src/applications/drydock/storage/DrydockLease.php +++ b/src/applications/drydock/storage/DrydockLease.php @@ -11,6 +11,8 @@ final class DrydockLease extends DrydockDAO protected $status = DrydockLeaseStatus::STATUS_PENDING; private $resource = self::ATTACHABLE; + private $unconsumedCommands = self::ATTACHABLE; + private $releaseOnDestruction; private $isAcquired = false; private $isActivated = false; @@ -104,6 +106,25 @@ final class DrydockLease extends DrydockDAO return ($this->resource !== null); } + public function getUnconsumedCommands() { + return $this->assertAttached($this->unconsumedCommands); + } + + public function attachUnconsumedCommands(array $commands) { + $this->unconsumedCommands = $commands; + return $this; + } + + public function isReleasing() { + foreach ($this->getUnconsumedCommands() as $command) { + if ($command->getCommand() == DrydockCommand::COMMAND_RELEASE) { + return true; + } + } + + return false; + } + public function queueForActivation() { if ($this->getID()) { throw new Exception( @@ -114,14 +135,9 @@ final class DrydockLease extends DrydockDAO ->setStatus(DrydockLeaseStatus::STATUS_PENDING) ->save(); - $task = PhabricatorWorker::scheduleTask( - 'DrydockAllocatorWorker', - array( - 'leasePHID' => $this->getPHID(), - ), - array( - 'objectPHID' => $this->getPHID(), - )); + $this->scheduleUpdate(); + + $this->logEvent(DrydockLeaseQueuedLogType::LOGCONST); return $this; } @@ -216,19 +232,37 @@ final class DrydockLease extends DrydockDAO } $this->openTransaction(); - - $this - ->setResourcePHID($resource->getPHID()) - ->setStatus($new_status) - ->save(); - + try { DrydockSlotLock::acquireLocks($this->getPHID(), $this->slotLocks); $this->slotLocks = array(); + } catch (DrydockSlotLockException $ex) { + $this->killTransaction(); + $this->logEvent( + DrydockSlotLockFailureLogType::LOGCONST, + array( + 'locks' => $ex->getLockMap(), + )); + + throw $ex; + } + + try { + $this + ->setResourcePHID($resource->getPHID()) + ->attachResource($resource) + ->setStatus($new_status) + ->save(); + } catch (Exception $ex) { + $this->killTransaction(); + throw $ex; + } $this->saveTransaction(); $this->isAcquired = true; + $this->logEvent(DrydockLeaseAcquiredLogType::LOGCONST); + if ($new_status == DrydockLeaseStatus::STATUS_ACTIVE) { $this->didActivate(); } @@ -295,6 +329,16 @@ final class DrydockLease extends DrydockDAO } } + public function canReceiveCommands() { + switch ($this->getStatus()) { + case DrydockLeaseStatus::STATUS_RELEASED: + case DrydockLeaseStatus::STATUS_DESTROYED: + return false; + default: + return true; + } + } + public function scheduleUpdate($epoch = null) { PhabricatorWorker::scheduleTask( 'DrydockLeaseUpdateWorker', @@ -304,14 +348,21 @@ final class DrydockLease extends DrydockDAO ), array( 'objectPHID' => $this->getPHID(), - 'delayUntil' => $epoch, + 'delayUntil' => ($epoch ? (int)$epoch : null), )); } + public function setAwakenTaskIDs(array $ids) { + $this->setAttribute('internal.awakenTaskIDs', $ids); + return $this; + } + private function didActivate() { $viewer = PhabricatorUser::getOmnipotentUser(); $need_update = false; + $this->logEvent(DrydockLeaseActivatedLogType::LOGCONST); + $commands = id(new DrydockCommandQuery()) ->setViewer($viewer) ->withTargetPHIDs(array($this->getPHID())) @@ -329,8 +380,33 @@ final class DrydockLease extends DrydockDAO if ($expires) { $this->scheduleUpdate($expires); } + + $awaken_ids = $this->getAttribute('internal.awakenTaskIDs'); + if (is_array($awaken_ids) && $awaken_ids) { + PhabricatorWorker::awakenTaskIDs($awaken_ids); + } } + public function logEvent($type, array $data = array()) { + $log = id(new DrydockLog()) + ->setEpoch(PhabricatorTime::getNow()) + ->setType($type) + ->setData($data); + + $log->setLeasePHID($this->getPHID()); + + $resource_phid = $this->getResourcePHID(); + if ($resource_phid) { + $resource = $this->getResource(); + + $log->setResourcePHID($resource->getPHID()); + $log->setBlueprintPHID($resource->getBlueprintPHID()); + } + + return $log->save(); + } + + /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/applications/drydock/storage/DrydockLog.php b/src/applications/drydock/storage/DrydockLog.php index 36d310c510..5d75d82c65 100644 --- a/src/applications/drydock/storage/DrydockLog.php +++ b/src/applications/drydock/storage/DrydockLog.php @@ -3,28 +3,38 @@ final class DrydockLog extends DrydockDAO implements PhabricatorPolicyInterface { - protected $resourceID; - protected $leaseID; + protected $blueprintPHID; + protected $resourcePHID; + protected $leasePHID; protected $epoch; - protected $message; + protected $type; + protected $data = array(); + private $blueprint = self::ATTACHABLE; private $resource = self::ATTACHABLE; private $lease = self::ATTACHABLE; protected function getConfiguration() { return array( self::CONFIG_TIMESTAMPS => false, + self::CONFIG_SERIALIZATION => array( + 'data' => self::SERIALIZATION_JSON, + ), self::CONFIG_COLUMN_SCHEMA => array( - 'resourceID' => 'id?', - 'leaseID' => 'id?', - 'message' => 'text', + 'blueprintPHID' => 'phid?', + 'resourcePHID' => 'phid?', + 'leasePHID' => 'phid?', + 'type' => 'text64', ), self::CONFIG_KEY_SCHEMA => array( - 'resourceID' => array( - 'columns' => array('resourceID', 'epoch'), + 'key_blueprint' => array( + 'columns' => array('blueprintPHID', 'type'), ), - 'leaseID' => array( - 'columns' => array('leaseID', 'epoch'), + 'key_resource' => array( + 'columns' => array('resourcePHID', 'type'), + ), + 'key_lease' => array( + 'columns' => array('leasePHID', 'type'), ), 'epoch' => array( 'columns' => array('epoch'), @@ -33,6 +43,15 @@ final class DrydockLog extends DrydockDAO ) + parent::getConfiguration(); } + public function attachBlueprint(DrydockBlueprint $blueprint = null) { + $this->blueprint = $blueprint; + return $this; + } + + public function getBlueprint() { + return $this->assertAttached($this->blueprint); + } + public function attachResource(DrydockResource $resource = null) { $this->resource = $resource; return $this; @@ -51,6 +70,22 @@ final class DrydockLog extends DrydockDAO return $this->assertAttached($this->lease); } + public function isComplete() { + if ($this->getBlueprintPHID() && !$this->getBlueprint()) { + return false; + } + + if ($this->getResourcePHID() && !$this->getResource()) { + return false; + } + + if ($this->getLeasePHID() && !$this->getLease()) { + return false; + } + + return true; + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ @@ -62,21 +97,19 @@ final class DrydockLog extends DrydockDAO } public function getPolicy($capability) { - if ($this->getResource()) { - return $this->getResource()->getPolicy($capability); - } - return $this->getLease()->getPolicy($capability); + // NOTE: We let you see that logs exist no matter what, but don't actually + // show you log content unless you can see all of the associated objects. + return PhabricatorPolicies::getMostOpenPolicy(); } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { - if ($this->getResource()) { - return $this->getResource()->hasAutomaticCapability($capability, $viewer); - } - return $this->getLease()->hasAutomaticCapability($capability, $viewer); + return false; } public function describeAutomaticCapability($capability) { - return pht('Logs inherit the policy of their resources.'); + return pht( + 'To view log details, you must be able to view the associated '. + 'blueprint, resource and lease.'); } } diff --git a/src/applications/drydock/storage/DrydockResource.php b/src/applications/drydock/storage/DrydockResource.php index f2be89a6f2..da97a089d9 100644 --- a/src/applications/drydock/storage/DrydockResource.php +++ b/src/applications/drydock/storage/DrydockResource.php @@ -7,14 +7,15 @@ final class DrydockResource extends DrydockDAO protected $phid; protected $blueprintPHID; protected $status; - + protected $until; protected $type; - protected $name; protected $attributes = array(); protected $capabilities = array(); protected $ownerPHID; private $blueprint = self::ATTACHABLE; + private $unconsumedCommands = self::ATTACHABLE; + private $isAllocated = false; private $isActivated = false; private $activateWhenAllocated = false; @@ -28,10 +29,10 @@ final class DrydockResource extends DrydockDAO 'capabilities' => self::SERIALIZATION_JSON, ), self::CONFIG_COLUMN_SCHEMA => array( - 'name' => 'text255', 'ownerPHID' => 'phid?', 'status' => 'text32', 'type' => 'text64', + 'until' => 'epoch?', ), self::CONFIG_KEY_SCHEMA => array( 'key_type' => array( @@ -48,6 +49,10 @@ final class DrydockResource extends DrydockDAO return PhabricatorPHID::generateNewPHID(DrydockResourcePHIDType::TYPECONST); } + public function getResourceName() { + return $this->getBlueprint()->getResourceName($this); + } + public function getAttribute($key, $default = null) { return idx($this->attributes, $key, $default); } @@ -78,6 +83,25 @@ final class DrydockResource extends DrydockDAO return $this; } + public function getUnconsumedCommands() { + return $this->assertAttached($this->unconsumedCommands); + } + + public function attachUnconsumedCommands(array $commands) { + $this->unconsumedCommands = $commands; + return $this; + } + + public function isReleasing() { + foreach ($this->getUnconsumedCommands() as $command) { + if ($command->getCommand() == DrydockCommand::COMMAND_RELEASE) { + return true; + } + } + + return false; + } + public function setActivateWhenAllocated($activate) { $this->activateWhenAllocated = $activate; return $this; @@ -96,6 +120,16 @@ final class DrydockResource extends DrydockDAO 'Only new resources may be allocated.')); } + // We expect resources to have a pregenerated PHID, as they should have + // been created by a call to DrydockBlueprint->newResourceTemplate(). + if (!$this->getPHID()) { + throw new Exception( + pht( + 'Trying to allocate a resource with no generated PHID. Use "%s" to '. + 'create new resource templates.', + 'newResourceTemplate()')); + } + $expect_status = DrydockResourceStatus::STATUS_PENDING; $actual_status = $this->getStatus(); if ($actual_status != $expect_status) { @@ -115,17 +149,40 @@ final class DrydockResource extends DrydockDAO $this->openTransaction(); + try { + DrydockSlotLock::acquireLocks($this->getPHID(), $this->slotLocks); + $this->slotLocks = array(); + } catch (DrydockSlotLockException $ex) { + $this->killTransaction(); + + // NOTE: We have to log this on the blueprint, as the resource is not + // going to be saved so the PHID will vanish. + $this->getBlueprint()->logEvent( + DrydockSlotLockFailureLogType::LOGCONST, + array( + 'locks' => $ex->getLockMap(), + )); + + throw $ex; + } + + try { $this ->setStatus($new_status) ->save(); - - DrydockSlotLock::acquireLocks($this->getPHID(), $this->slotLocks); - $this->slotLocks = array(); + } catch (Exception $ex) { + $this->killTransaction(); + throw $ex; + } $this->saveTransaction(); $this->isAllocated = true; + if ($new_status == DrydockResourceStatus::STATUS_ACTIVE) { + $this->didActivate(); + } + return $this; } @@ -164,6 +221,8 @@ final class DrydockResource extends DrydockDAO $this->isActivated = true; + $this->didActivate(); + return $this; } @@ -181,14 +240,16 @@ final class DrydockResource extends DrydockDAO } } - public function scheduleUpdate() { + public function scheduleUpdate($epoch = null) { PhabricatorWorker::scheduleTask( 'DrydockResourceUpdateWorker', array( 'resourcePHID' => $this->getPHID(), + 'isExpireTask' => ($epoch !== null), ), array( 'objectPHID' => $this->getPHID(), + 'delayUntil' => ($epoch ? (int)$epoch : null), )); } @@ -209,6 +270,34 @@ final class DrydockResource extends DrydockDAO if ($need_update) { $this->scheduleUpdate(); } + + $expires = $this->getUntil(); + if ($expires) { + $this->scheduleUpdate($expires); + } + } + + public function canReceiveCommands() { + switch ($this->getStatus()) { + case DrydockResourceStatus::STATUS_RELEASED: + case DrydockResourceStatus::STATUS_BROKEN: + case DrydockResourceStatus::STATUS_DESTROYED: + return false; + default: + return true; + } + } + + public function logEvent($type, array $data = array()) { + $log = id(new DrydockLog()) + ->setEpoch(PhabricatorTime::getNow()) + ->setType($type) + ->setData($data); + + $log->setResourcePHID($this->getPHID()); + $log->setBlueprintPHID($this->getBlueprintPHID()); + + return $log->save(); } diff --git a/src/applications/drydock/view/DrydockLeaseListView.php b/src/applications/drydock/view/DrydockLeaseListView.php index d3507546ad..8c161b00ce 100644 --- a/src/applications/drydock/view/DrydockLeaseListView.php +++ b/src/applications/drydock/view/DrydockLeaseListView.php @@ -22,19 +22,10 @@ final class DrydockLeaseListView extends AphrontView { ->setHeader($lease->getLeaseName()) ->setHref('/drydock/lease/'.$lease->getID().'/'); - if ($lease->hasAttachedResource()) { - $resource = $lease->getResource(); - - $resource_href = '/drydock/resource/'.$resource->getID().'/'; - $resource_name = $resource->getName(); - + $resource_phid = $lease->getResourcePHID(); + if ($resource_phid) { $item->addAttribute( - phutil_tag( - 'a', - array( - 'href' => $resource_href, - ), - $resource_name)); + $viewer->renderHandle($resource_phid)); } $status = DrydockLeaseStatus::getNameForStatus($lease->getStatus()); diff --git a/src/applications/drydock/view/DrydockLogListView.php b/src/applications/drydock/view/DrydockLogListView.php index 22e280939b..4e1fe664cd 100644 --- a/src/applications/drydock/view/DrydockLogListView.php +++ b/src/applications/drydock/view/DrydockLogListView.php @@ -16,57 +16,90 @@ final class DrydockLogListView extends AphrontView { $view = new PHUIObjectItemListView(); + $types = DrydockLogType::getAllLogTypes(); + $rows = array(); foreach ($logs as $log) { - $resource_uri = '/drydock/resource/'.$log->getResourceID().'/'; - $lease_uri = '/drydock/lease/'.$log->getLeaseID().'/'; + $blueprint_phid = $log->getBlueprintPHID(); + if ($blueprint_phid) { + $blueprint = $viewer->renderHandle($blueprint_phid); + } else { + $blueprint = null; + } - $resource_name = $log->getResourceID(); - if ($log->getResourceID() !== null) { - $resource_name = $log->getResource()->getName(); + $resource_phid = $log->getResourcePHID(); + if ($resource_phid) { + $resource = $viewer->renderHandle($resource_phid); + } else { + $resource = null; + } + + $lease_phid = $log->getLeasePHID(); + if ($lease_phid) { + $lease = $viewer->renderHandle($lease_phid); + } else { + $lease = null; + } + + if ($log->isComplete()) { + $type_key = $log->getType(); + if (isset($types[$type_key])) { + $type_object = id(clone $types[$type_key]) + ->setLog($log) + ->setViewer($viewer); + + $log_data = $log->getData(); + + $type = $type_object->getLogTypeName(); + $icon = $type_object->getLogTypeIcon($log_data); + $data = $type_object->renderLog($log_data); + } else { + $type = pht('', $type_key); + $data = null; + $icon = 'fa-question-circle red'; + } + } else { + $type = phutil_tag('em', array(), pht('Restricted')); + $data = phutil_tag( + 'em', + array(), + pht('You do not have permission to view this log event.')); + $icon = 'fa-lock grey'; } $rows[] = array( - phutil_tag( - 'a', - array( - 'href' => $resource_uri, - ), - $resource_name), - phutil_tag( - 'a', - array( - 'href' => $lease_uri, - ), - $log->getLeaseID()), - $log->getMessage(), + $blueprint, + $resource, + $lease, + id(new PHUIIconView())->setIconFont($icon), + $type, + $data, phabricator_datetime($log->getEpoch(), $viewer), ); } - $table = new AphrontTableView($rows); - $table->setDeviceReadyTable(true); - $table->setHeaders( - array( - pht('Resource'), - pht('Lease'), - pht('Message'), - pht('Date'), - )); - $table->setShortHeaders( - array( - pht('R'), - pht('L'), - pht('Message'), - '', - )); - $table->setColumnClasses( - array( - '', - '', - 'wide', - '', - )); + $table = id(new AphrontTableView($rows)) + ->setDeviceReadyTable(true) + ->setHeaders( + array( + pht('Blueprint'), + pht('Resource'), + pht('Lease'), + null, + pht('Type'), + pht('Data'), + pht('Date'), + )) + ->setColumnClasses( + array( + '', + '', + '', + 'icon', + '', + 'wide', + '', + )); return $table; } diff --git a/src/applications/drydock/view/DrydockResourceListView.php b/src/applications/drydock/view/DrydockResourceListView.php index 9b7706c38b..739b464bdb 100644 --- a/src/applications/drydock/view/DrydockResourceListView.php +++ b/src/applications/drydock/view/DrydockResourceListView.php @@ -16,11 +16,12 @@ final class DrydockResourceListView extends AphrontView { $view = new PHUIObjectItemListView(); foreach ($resources as $resource) { - $name = pht('Resource %d', $resource->getID()).': '.$resource->getName(); + $id = $resource->getID(); $item = id(new PHUIObjectItemView()) - ->setHref('/drydock/resource/'.$resource->getID().'/') - ->setHeader($name); + ->setHref("/drydock/resource/{$id}/") + ->setObjectName(pht('Resource %d', $id)) + ->setHeader($resource->getResourceName()); $status = DrydockResourceStatus::getNameForStatus($resource->getStatus()); $item->addAttribute($status); diff --git a/src/applications/drydock/worker/DrydockAllocatorWorker.php b/src/applications/drydock/worker/DrydockAllocatorWorker.php deleted file mode 100644 index a67f242c01..0000000000 --- a/src/applications/drydock/worker/DrydockAllocatorWorker.php +++ /dev/null @@ -1,479 +0,0 @@ -getTaskDataValue('leasePHID'); - $lease = $this->loadLease($lease_phid); - - $this->allocateAndAcquireLease($lease); - } - - -/* -( Allocator )---------------------------------------------------------- */ - - - /** - * Find or build a resource which can satisfy a given lease request, then - * acquire the lease. - * - * @param DrydockLease Requested lease. - * @return void - * @task allocator - */ - private function allocateAndAcquireLease(DrydockLease $lease) { - $blueprints = $this->loadBlueprintsForAllocatingLease($lease); - - // If we get nothing back, that means no blueprint is defined which can - // ever build the requested resource. This is a permanent failure, since - // we don't expect to succeed no matter how many times we try. - if (!$blueprints) { - $lease - ->setStatus(DrydockLeaseStatus::STATUS_BROKEN) - ->save(); - throw new PhabricatorWorkerPermanentFailureException( - pht( - 'No active Drydock blueprint exists which can ever allocate a '. - 'resource for lease "%s".', - $lease->getPHID())); - } - - // First, try to find a suitable open resource which we can acquire a new - // lease on. - $resources = $this->loadResourcesForAllocatingLease($blueprints, $lease); - - // If no resources exist yet, see if we can build one. - if (!$resources) { - $usable_blueprints = $this->removeOverallocatedBlueprints( - $blueprints, - $lease); - - // If we get nothing back here, some blueprint claims it can eventually - // satisfy the lease, just not right now. This is a temporary failure, - // and we expect allocation to succeed eventually. - if (!$blueprints) { - // TODO: More formal temporary failure here. We should retry this - // "soon" but not "immediately". - throw new Exception( - pht('No blueprints have space to allocate a resource right now.')); - } - - $usable_blueprints = $this->rankBlueprints($blueprints, $lease); - - $exceptions = array(); - foreach ($usable_blueprints as $blueprint) { - try { - $resources[] = $this->allocateResource($blueprint, $lease); - - // Bail after allocating one resource, we don't need any more than - // this. - break; - } catch (Exception $ex) { - $exceptions[] = $ex; - } - } - - if (!$resources) { - // TODO: We should distinguish between temporary and permament failures - // here. If any blueprint failed temporarily, retry "soon". If none - // of these failures were temporary, maybe this should be a permanent - // failure? - throw new PhutilAggregateException( - pht( - 'All blueprints failed to allocate a suitable new resource when '. - 'trying to allocate lease "%s".', - $lease->getPHID()), - $exceptions); - } - - // NOTE: We have not acquired the lease yet, so it is possible that the - // resource we just built will be snatched up by some other lease before - // we can. This is not problematic: we'll retry a little later and should - // suceed eventually. - } - - $resources = $this->rankResources($resources, $lease); - - $exceptions = array(); - $allocated = false; - foreach ($resources as $resource) { - try { - $this->acquireLease($resource, $lease); - $allocated = true; - break; - } catch (Exception $ex) { - $exceptions[] = $ex; - } - } - - if (!$allocated) { - // TODO: We should distinguish between temporary and permanent failures - // here. If any failures were temporary (specifically, failed to acquire - // locks) - - throw new PhutilAggregateException( - pht( - 'Unable to acquire lease "%s" on any resouce.', - $lease->getPHID()), - $exceptions); - } - } - - - /** - * Get all the @{class:DrydockBlueprintImplementation}s which can possibly - * build a resource to satisfy a lease. - * - * This method returns blueprints which might, at some time, be able to - * build a resource which can satisfy the lease. They may not be able to - * build that resource right now. - * - * @param DrydockLease Requested lease. - * @return list List of qualifying blueprint - * implementations. - * @task allocator - */ - private function loadBlueprintImplementationsForAllocatingLease( - DrydockLease $lease) { - - $impls = DrydockBlueprintImplementation::getAllBlueprintImplementations(); - - $keep = array(); - foreach ($impls as $key => $impl) { - // Don't use disabled blueprint types. - if (!$impl->isEnabled()) { - continue; - } - - // Don't use blueprint types which can't allocate the correct kind of - // resource. - if ($impl->getType() != $lease->getResourceType()) { - continue; - } - - if (!$impl->canAnyBlueprintEverAllocateResourceForLease($lease)) { - continue; - } - - $keep[$key] = $impl; - } - - return $keep; - } - - - /** - * Get all the concrete @{class:DrydockBlueprint}s which can possibly - * build a resource to satisfy a lease. - * - * @param DrydockLease Requested lease. - * @return list List of qualifying blueprints. - * @task allocator - */ - private function loadBlueprintsForAllocatingLease( - DrydockLease $lease) { - $viewer = $this->getViewer(); - - $impls = $this->loadBlueprintImplementationsForAllocatingLease($lease); - if (!$impls) { - return array(); - } - - $blueprints = id(new DrydockBlueprintQuery()) - ->setViewer($viewer) - ->withBlueprintClasses(array_keys($impls)) - ->withDisabled(false) - ->execute(); - - $keep = array(); - foreach ($blueprints as $key => $blueprint) { - if (!$blueprint->canEverAllocateResourceForLease($lease)) { - continue; - } - - $keep[$key] = $blueprint; - } - - return $keep; - } - - - /** - * Load a list of all resources which a given lease can possibly be - * allocated against. - * - * @param list Blueprints which may produce suitable - * resources. - * @param DrydockLease Requested lease. - * @return list Resources which may be able to allocate - * the lease. - * @task allocator - */ - private function loadResourcesForAllocatingLease( - array $blueprints, - DrydockLease $lease) { - assert_instances_of($blueprints, 'DrydockBlueprint'); - $viewer = $this->getViewer(); - - $resources = id(new DrydockResourceQuery()) - ->setViewer($viewer) - ->withBlueprintPHIDs(mpull($blueprints, 'getPHID')) - ->withTypes(array($lease->getResourceType())) - ->withStatuses( - array( - DrydockResourceStatus::STATUS_PENDING, - DrydockResourceStatus::STATUS_ACTIVE, - )) - ->execute(); - - $keep = array(); - foreach ($resources as $key => $resource) { - $blueprint = $resource->getBlueprint(); - - if (!$blueprint->canAcquireLeaseOnResource($resource, $lease)) { - continue; - } - - $keep[$key] = $resource; - } - - return $keep; - } - - - /** - * Remove blueprints which are too heavily allocated to build a resource for - * a lease from a list of blueprints. - * - * @param list List of blueprints. - * @return list List with blueprints that can not allocate - * a resource for the lease right now removed. - * @task allocator - */ - private function removeOverallocatedBlueprints( - array $blueprints, - DrydockLease $lease) { - assert_instances_of($blueprints, 'DrydockBlueprint'); - - $keep = array(); - foreach ($blueprints as $key => $blueprint) { - if (!$blueprint->canAllocateResourceForLease($lease)) { - continue; - } - - $keep[$key] = $blueprint; - } - - return $keep; - } - - - /** - * Rank blueprints by suitability for building a new resource for a - * particular lease. - * - * @param list List of blueprints. - * @param DrydockLease Requested lease. - * @return list Ranked list of blueprints. - * @task allocator - */ - private function rankBlueprints(array $blueprints, DrydockLease $lease) { - assert_instances_of($blueprints, 'DrydockBlueprint'); - - // TODO: Implement improvements to this ranking algorithm if they become - // available. - shuffle($blueprints); - - return $blueprints; - } - - - /** - * Rank resources by suitability for allocating a particular lease. - * - * @param list List of resources. - * @param DrydockLease Requested lease. - * @return list Ranked list of resources. - * @task allocator - */ - private function rankResources(array $resources, DrydockLease $lease) { - assert_instances_of($resources, 'DrydockResource'); - - // TODO: Implement improvements to this ranking algorithm if they become - // available. - shuffle($resources); - - return $resources; - } - - -/* -( Managing Resources )------------------------------------------------- */ - - - /** - * Perform an actual resource allocation with a particular blueprint. - * - * @param DrydockBlueprint The blueprint to allocate a resource from. - * @param DrydockLease Requested lease. - * @return DrydockResource Allocated resource. - * @task resource - */ - private function allocateResource( - DrydockBlueprint $blueprint, - DrydockLease $lease) { - $resource = $blueprint->allocateResource($lease); - $this->validateAllocatedResource($blueprint, $resource, $lease); - - // If this resource was allocated as a pending resource, queue a task to - // activate it. - if ($resource->getStatus() == DrydockResourceStatus::STATUS_PENDING) { - PhabricatorWorker::scheduleTask( - 'DrydockResourceWorker', - array( - 'resourcePHID' => $resource->getPHID(), - ), - array( - 'objectPHID' => $resource->getPHID(), - )); - } - - return $resource; - } - - - /** - * Check that the resource a blueprint allocated is roughly the sort of - * object we expect. - * - * @param DrydockBlueprint Blueprint which built the resource. - * @param wild Thing which the blueprint claims is a valid resource. - * @param DrydockLease Lease the resource was allocated for. - * @return void - * @task resource - */ - private function validateAllocatedResource( - DrydockBlueprint $blueprint, - $resource, - DrydockLease $lease) { - - if (!($resource instanceof DrydockResource)) { - throw new Exception( - pht( - 'Blueprint "%s" (of type "%s") is not properly implemented: %s must '. - 'return an object of type %s or throw, but returned something else.', - $blueprint->getBlueprintName(), - $blueprint->getClassName(), - 'allocateResource()', - 'DrydockResource')); - } - - if (!$resource->isAllocatedResource()) { - throw new Exception( - pht( - 'Blueprint "%s" (of type "%s") is not properly implemented: %s '. - 'must actually allocate the resource it returns.', - $blueprint->getBlueprintName(), - $blueprint->getClassName(), - 'allocateResource()')); - } - - $resource_type = $resource->getType(); - $lease_type = $lease->getResourceType(); - - if ($resource_type !== $lease_type) { - // TODO: Destroy the resource here? - - throw new Exception( - pht( - 'Blueprint "%s" (of type "%s") is not properly implemented: it '. - 'built a resource of type "%s" to satisfy a lease requesting a '. - 'resource of type "%s".', - $blueprint->getBlueprintName(), - $blueprint->getClassName(), - $resource_type, - $lease_type)); - } - } - - -/* -( Managing Leases )---------------------------------------------------- */ - - - /** - * Perform an actual lease acquisition on a particular resource. - * - * @param DrydockResource Resource to acquire a lease on. - * @param DrydockLease Lease to acquire. - * @return void - * @task lease - */ - private function acquireLease( - DrydockResource $resource, - DrydockLease $lease) { - - $blueprint = $resource->getBlueprint(); - $blueprint->acquireLease($resource, $lease); - - $this->validateAcquiredLease($blueprint, $resource, $lease); - - // If this lease has been acquired but not activated, queue a task to - // activate it. - if ($lease->getStatus() == DrydockLeaseStatus::STATUS_ACQUIRED) { - PhabricatorWorker::scheduleTask( - 'DrydockLeaseWorker', - array( - 'leasePHID' => $lease->getPHID(), - ), - array( - 'objectPHID' => $lease->getPHID(), - )); - } - } - - - /** - * Make sure that a lease was really acquired properly. - * - * @param DrydockBlueprint Blueprint which created the resource. - * @param DrydockResource Resource which was acquired. - * @param DrydockLease The lease which was supposedly acquired. - * @return void - * @task lease - */ - private function validateAcquiredLease( - DrydockBlueprint $blueprint, - DrydockResource $resource, - DrydockLease $lease) { - - if (!$lease->isAcquiredLease()) { - throw new Exception( - pht( - 'Blueprint "%s" (of type "%s") is not properly implemented: it '. - 'returned from "%s" without acquiring a lease.', - $blueprint->getBlueprintName(), - $blueprint->getClassName(), - 'acquireLease()')); - } - - $lease_phid = $lease->getResourcePHID(); - $resource_phid = $resource->getPHID(); - - if ($lease_phid !== $resource_phid) { - // TODO: Destroy the lease? - throw new Exception( - pht( - 'Blueprint "%s" (of type "%s") is not properly implemented: it '. - 'returned from "%s" with a lease acquired on the wrong resource.', - $blueprint->getBlueprintName(), - $blueprint->getClassName(), - 'acquireLease()')); - } - } - - -} diff --git a/src/applications/drydock/worker/DrydockLeaseDestroyWorker.php b/src/applications/drydock/worker/DrydockLeaseDestroyWorker.php deleted file mode 100644 index e0b15095c7..0000000000 --- a/src/applications/drydock/worker/DrydockLeaseDestroyWorker.php +++ /dev/null @@ -1,37 +0,0 @@ -getTaskDataValue('leasePHID'); - $lease = $this->loadLease($lease_phid); - $this->destroyLease($lease); - } - - private function destroyLease(DrydockLease $lease) { - $status = $lease->getStatus(); - - switch ($status) { - case DrydockLeaseStatus::STATUS_RELEASED: - case DrydockLeaseStatus::STATUS_BROKEN: - break; - default: - throw new PhabricatorWorkerPermanentFailureException( - pht( - 'Unable to destroy lease ("%s"), lease has the wrong '. - 'status ("%s").', - $lease->getPHID(), - $status)); - } - - $resource = $lease->getResource(); - $blueprint = $resource->getBlueprint(); - - $blueprint->destroyLease($resource, $lease); - - $lease - ->setStatus(DrydockLeaseStatus::STATUS_DESTROYED) - ->save(); - } - -} diff --git a/src/applications/drydock/worker/DrydockLeaseUpdateWorker.php b/src/applications/drydock/worker/DrydockLeaseUpdateWorker.php index 98ecf2b498..b6ac6cec52 100644 --- a/src/applications/drydock/worker/DrydockLeaseUpdateWorker.php +++ b/src/applications/drydock/worker/DrydockLeaseUpdateWorker.php @@ -1,5 +1,15 @@ loadLease($lease_phid); - $this->updateLease($lease); + $this->handleUpdate($lease); } catch (Exception $ex) { $lock->unlock(); throw $ex; @@ -22,54 +32,103 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker { $lock->unlock(); } + +/* -( Updating Leases )---------------------------------------------------- */ + + + /** + * @task update + */ + private function handleUpdate(DrydockLease $lease) { + try { + $this->updateLease($lease); + } catch (Exception $ex) { + if ($this->isTemporaryException($ex)) { + $this->yieldLease($lease, $ex); + } else { + $this->breakLease($lease, $ex); + } + } + } + + + /** + * @task update + */ private function updateLease(DrydockLease $lease) { - if ($lease->getStatus() != DrydockLeaseStatus::STATUS_ACTIVE) { + $this->processLeaseCommands($lease); + + $lease_status = $lease->getStatus(); + switch ($lease_status) { + case DrydockLeaseStatus::STATUS_PENDING: + $this->executeAllocator($lease); + break; + case DrydockLeaseStatus::STATUS_ACQUIRED: + $this->activateLease($lease); + break; + case DrydockLeaseStatus::STATUS_ACTIVE: + // Nothing to do. + break; + case DrydockLeaseStatus::STATUS_RELEASED: + case DrydockLeaseStatus::STATUS_BROKEN: + $this->destroyLease($lease); + break; + case DrydockLeaseStatus::STATUS_DESTROYED: + break; + } + + $this->yieldIfExpiringLease($lease); + } + + + /** + * @task update + */ + private function yieldLease(DrydockLease $lease, Exception $ex) { + $duration = $this->getYieldDurationFromException($ex); + + $lease->logEvent( + DrydockLeaseActivationYieldLogType::LOGCONST, + array( + 'duration' => $duration, + )); + + throw new PhabricatorWorkerYieldException($duration); + } + + +/* -( Processing Commands )------------------------------------------------ */ + + + /** + * @task command + */ + private function processLeaseCommands(DrydockLease $lease) { + if (!$lease->canReceiveCommands()) { return; } - $viewer = $this->getViewer(); - $drydock_phid = id(new PhabricatorDrydockApplication())->getPHID(); - - // Check if the lease has expired. If it is, we're going to send it a - // release command. This command will be handled immediately below, it - // just generates a command log and improves consistency. - $now = PhabricatorTime::getNow(); - $expires = $lease->getUntil(); - if ($expires && ($expires <= $now)) { - $command = DrydockCommand::initializeNewCommand($viewer) - ->setTargetPHID($lease->getPHID()) - ->setAuthorPHID($drydock_phid) - ->setCommand(DrydockCommand::COMMAND_RELEASE) - ->save(); - } + $this->checkLeaseExpiration($lease); $commands = $this->loadCommands($lease->getPHID()); foreach ($commands as $command) { - if ($lease->getStatus() != DrydockLeaseStatus::STATUS_ACTIVE) { - // Leases can't receive commands before they activate or after they - // release. + if (!$lease->canReceiveCommands()) { break; } - $this->processCommand($lease, $command); + $this->processLeaseCommand($lease, $command); $command ->setIsConsumed(true) ->save(); } - - // If this is the task which will eventually release the lease after it - // expires but it is still active, reschedule the task to run after the - // lease expires. This can happen if the lease's expiration was pushed - // forward. - if ($lease->getStatus() == DrydockLeaseStatus::STATUS_ACTIVE) { - if ($this->getTaskDataValue('isExpireTask') && $expires) { - throw new PhabricatorWorkerYieldException($expires - $now); - } - } } - private function processCommand( + + /** + * @task command + */ + private function processLeaseCommand( DrydockLease $lease, DrydockCommand $command) { switch ($command->getCommand()) { @@ -79,29 +138,597 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker { } } + +/* -( Drydock Allocator )-------------------------------------------------- */ + + + /** + * Find or build a resource which can satisfy a given lease request, then + * acquire the lease. + * + * @param DrydockLease Requested lease. + * @return void + * @task allocator + */ + private function executeAllocator(DrydockLease $lease) { + $blueprints = $this->loadBlueprintsForAllocatingLease($lease); + + // If we get nothing back, that means no blueprint is defined which can + // ever build the requested resource. This is a permanent failure, since + // we don't expect to succeed no matter how many times we try. + if (!$blueprints) { + throw new PhabricatorWorkerPermanentFailureException( + pht( + 'No active Drydock blueprint exists which can ever allocate a '. + 'resource for lease "%s".', + $lease->getPHID())); + } + + // First, try to find a suitable open resource which we can acquire a new + // lease on. + $resources = $this->loadResourcesForAllocatingLease($blueprints, $lease); + + // If no resources exist yet, see if we can build one. + if (!$resources) { + $usable_blueprints = $this->removeOverallocatedBlueprints( + $blueprints, + $lease); + + // If we get nothing back here, some blueprint claims it can eventually + // satisfy the lease, just not right now. This is a temporary failure, + // and we expect allocation to succeed eventually. + if (!$usable_blueprints) { + $lease->logEvent( + DrydockLeaseWaitingForResourcesLogType::LOGCONST, + array( + 'blueprintPHIDs' => mpull($blueprints, 'getPHID'), + )); + + throw new PhabricatorWorkerYieldException(15); + } + + $usable_blueprints = $this->rankBlueprints($usable_blueprints, $lease); + + $exceptions = array(); + foreach ($usable_blueprints as $blueprint) { + try { + $resources[] = $this->allocateResource($blueprint, $lease); + + // Bail after allocating one resource, we don't need any more than + // this. + break; + } catch (Exception $ex) { + $exceptions[] = $ex; + } + } + + if (!$resources) { + throw new PhutilAggregateException( + pht( + 'All blueprints failed to allocate a suitable new resource when '. + 'trying to allocate lease "%s".', + $lease->getPHID()), + $exceptions); + } + + // NOTE: We have not acquired the lease yet, so it is possible that the + // resource we just built will be snatched up by some other lease before + // we can. This is not problematic: we'll retry a little later and should + // suceed eventually. + } + + $resources = $this->rankResources($resources, $lease); + + $exceptions = array(); + $allocated = false; + foreach ($resources as $resource) { + try { + $this->acquireLease($resource, $lease); + $allocated = true; + break; + } catch (Exception $ex) { + $exceptions[] = $ex; + } + } + + if (!$allocated) { + throw new PhutilAggregateException( + pht( + 'Unable to acquire lease "%s" on any resouce.', + $lease->getPHID()), + $exceptions); + } + } + + + /** + * Get all the @{class:DrydockBlueprintImplementation}s which can possibly + * build a resource to satisfy a lease. + * + * This method returns blueprints which might, at some time, be able to + * build a resource which can satisfy the lease. They may not be able to + * build that resource right now. + * + * @param DrydockLease Requested lease. + * @return list List of qualifying blueprint + * implementations. + * @task allocator + */ + private function loadBlueprintImplementationsForAllocatingLease( + DrydockLease $lease) { + + $impls = DrydockBlueprintImplementation::getAllBlueprintImplementations(); + + $keep = array(); + foreach ($impls as $key => $impl) { + // Don't use disabled blueprint types. + if (!$impl->isEnabled()) { + continue; + } + + // Don't use blueprint types which can't allocate the correct kind of + // resource. + if ($impl->getType() != $lease->getResourceType()) { + continue; + } + + if (!$impl->canAnyBlueprintEverAllocateResourceForLease($lease)) { + continue; + } + + $keep[$key] = $impl; + } + + return $keep; + } + + + /** + * Get all the concrete @{class:DrydockBlueprint}s which can possibly + * build a resource to satisfy a lease. + * + * @param DrydockLease Requested lease. + * @return list List of qualifying blueprints. + * @task allocator + */ + private function loadBlueprintsForAllocatingLease( + DrydockLease $lease) { + $viewer = $this->getViewer(); + + $impls = $this->loadBlueprintImplementationsForAllocatingLease($lease); + if (!$impls) { + return array(); + } + + $blueprints = id(new DrydockBlueprintQuery()) + ->setViewer($viewer) + ->withBlueprintClasses(array_keys($impls)) + ->withDisabled(false) + ->execute(); + + $keep = array(); + foreach ($blueprints as $key => $blueprint) { + if (!$blueprint->canEverAllocateResourceForLease($lease)) { + continue; + } + + $keep[$key] = $blueprint; + } + + return $keep; + } + + + /** + * Load a list of all resources which a given lease can possibly be + * allocated against. + * + * @param list Blueprints which may produce suitable + * resources. + * @param DrydockLease Requested lease. + * @return list Resources which may be able to allocate + * the lease. + * @task allocator + */ + private function loadResourcesForAllocatingLease( + array $blueprints, + DrydockLease $lease) { + assert_instances_of($blueprints, 'DrydockBlueprint'); + $viewer = $this->getViewer(); + + $resources = id(new DrydockResourceQuery()) + ->setViewer($viewer) + ->withBlueprintPHIDs(mpull($blueprints, 'getPHID')) + ->withTypes(array($lease->getResourceType())) + ->withStatuses( + array( + DrydockResourceStatus::STATUS_PENDING, + DrydockResourceStatus::STATUS_ACTIVE, + )) + ->execute(); + + $keep = array(); + foreach ($resources as $key => $resource) { + $blueprint = $resource->getBlueprint(); + + if (!$blueprint->canAcquireLeaseOnResource($resource, $lease)) { + continue; + } + + $keep[$key] = $resource; + } + + return $keep; + } + + + /** + * Remove blueprints which are too heavily allocated to build a resource for + * a lease from a list of blueprints. + * + * @param list List of blueprints. + * @return list List with blueprints that can not allocate + * a resource for the lease right now removed. + * @task allocator + */ + private function removeOverallocatedBlueprints( + array $blueprints, + DrydockLease $lease) { + assert_instances_of($blueprints, 'DrydockBlueprint'); + + $keep = array(); + foreach ($blueprints as $key => $blueprint) { + if (!$blueprint->canAllocateResourceForLease($lease)) { + continue; + } + + $keep[$key] = $blueprint; + } + + return $keep; + } + + + /** + * Rank blueprints by suitability for building a new resource for a + * particular lease. + * + * @param list List of blueprints. + * @param DrydockLease Requested lease. + * @return list Ranked list of blueprints. + * @task allocator + */ + private function rankBlueprints(array $blueprints, DrydockLease $lease) { + assert_instances_of($blueprints, 'DrydockBlueprint'); + + // TODO: Implement improvements to this ranking algorithm if they become + // available. + shuffle($blueprints); + + return $blueprints; + } + + + /** + * Rank resources by suitability for allocating a particular lease. + * + * @param list List of resources. + * @param DrydockLease Requested lease. + * @return list Ranked list of resources. + * @task allocator + */ + private function rankResources(array $resources, DrydockLease $lease) { + assert_instances_of($resources, 'DrydockResource'); + + // TODO: Implement improvements to this ranking algorithm if they become + // available. + shuffle($resources); + + return $resources; + } + + + /** + * Perform an actual resource allocation with a particular blueprint. + * + * @param DrydockBlueprint The blueprint to allocate a resource from. + * @param DrydockLease Requested lease. + * @return DrydockResource Allocated resource. + * @task allocator + */ + private function allocateResource( + DrydockBlueprint $blueprint, + DrydockLease $lease) { + $resource = $blueprint->allocateResource($lease); + $this->validateAllocatedResource($blueprint, $resource, $lease); + + // If this resource was allocated as a pending resource, queue a task to + // activate it. + if ($resource->getStatus() == DrydockResourceStatus::STATUS_PENDING) { + PhabricatorWorker::scheduleTask( + 'DrydockResourceUpdateWorker', + array( + 'resourcePHID' => $resource->getPHID(), + ), + array( + 'objectPHID' => $resource->getPHID(), + )); + } + + return $resource; + } + + + /** + * Check that the resource a blueprint allocated is roughly the sort of + * object we expect. + * + * @param DrydockBlueprint Blueprint which built the resource. + * @param wild Thing which the blueprint claims is a valid resource. + * @param DrydockLease Lease the resource was allocated for. + * @return void + * @task allocator + */ + private function validateAllocatedResource( + DrydockBlueprint $blueprint, + $resource, + DrydockLease $lease) { + + if (!($resource instanceof DrydockResource)) { + throw new Exception( + pht( + 'Blueprint "%s" (of type "%s") is not properly implemented: %s must '. + 'return an object of type %s or throw, but returned something else.', + $blueprint->getBlueprintName(), + $blueprint->getClassName(), + 'allocateResource()', + 'DrydockResource')); + } + + if (!$resource->isAllocatedResource()) { + throw new Exception( + pht( + 'Blueprint "%s" (of type "%s") is not properly implemented: %s '. + 'must actually allocate the resource it returns.', + $blueprint->getBlueprintName(), + $blueprint->getClassName(), + 'allocateResource()')); + } + + $resource_type = $resource->getType(); + $lease_type = $lease->getResourceType(); + + if ($resource_type !== $lease_type) { + throw new Exception( + pht( + 'Blueprint "%s" (of type "%s") is not properly implemented: it '. + 'built a resource of type "%s" to satisfy a lease requesting a '. + 'resource of type "%s".', + $blueprint->getBlueprintName(), + $blueprint->getClassName(), + $resource_type, + $lease_type)); + } + } + + +/* -( Acquiring Leases )--------------------------------------------------- */ + + + /** + * Perform an actual lease acquisition on a particular resource. + * + * @param DrydockResource Resource to acquire a lease on. + * @param DrydockLease Lease to acquire. + * @return void + * @task acquire + */ + private function acquireLease( + DrydockResource $resource, + DrydockLease $lease) { + + $blueprint = $resource->getBlueprint(); + $blueprint->acquireLease($resource, $lease); + + $this->validateAcquiredLease($blueprint, $resource, $lease); + + // If this lease has been acquired but not activated, queue a task to + // activate it. + if ($lease->getStatus() == DrydockLeaseStatus::STATUS_ACQUIRED) { + PhabricatorWorker::scheduleTask( + __CLASS__, + array( + 'leasePHID' => $lease->getPHID(), + ), + array( + 'objectPHID' => $lease->getPHID(), + )); + } + } + + + /** + * Make sure that a lease was really acquired properly. + * + * @param DrydockBlueprint Blueprint which created the resource. + * @param DrydockResource Resource which was acquired. + * @param DrydockLease The lease which was supposedly acquired. + * @return void + * @task acquire + */ + private function validateAcquiredLease( + DrydockBlueprint $blueprint, + DrydockResource $resource, + DrydockLease $lease) { + + if (!$lease->isAcquiredLease()) { + throw new Exception( + pht( + 'Blueprint "%s" (of type "%s") is not properly implemented: it '. + 'returned from "%s" without acquiring a lease.', + $blueprint->getBlueprintName(), + $blueprint->getClassName(), + 'acquireLease()')); + } + + $lease_phid = $lease->getResourcePHID(); + $resource_phid = $resource->getPHID(); + + if ($lease_phid !== $resource_phid) { + throw new Exception( + pht( + 'Blueprint "%s" (of type "%s") is not properly implemented: it '. + 'returned from "%s" with a lease acquired on the wrong resource.', + $blueprint->getBlueprintName(), + $blueprint->getClassName(), + 'acquireLease()')); + } + } + + +/* -( Activating Leases )-------------------------------------------------- */ + + + /** + * @task activate + */ + private function activateLease(DrydockLease $lease) { + $resource = $lease->getResource(); + if (!$resource) { + throw new Exception( + pht('Trying to activate lease with no resource.')); + } + + $resource_status = $resource->getStatus(); + + if ($resource_status == DrydockResourceStatus::STATUS_PENDING) { + throw new PhabricatorWorkerYieldException(15); + } + + if ($resource_status != DrydockResourceStatus::STATUS_ACTIVE) { + throw new Exception( + pht( + 'Trying to activate lease on a dead resource (in status "%s").', + $resource_status)); + } + + // NOTE: We can race resource destruction here. Between the time we + // performed the read above and now, the resource might have closed, so + // we may activate leases on dead resources. At least for now, this seems + // fine: a resource dying right before we activate a lease on it should not + // be distinguisahble from a resource dying right after we activate a lease + // on it. We end up with an active lease on a dead resource either way, and + // can not prevent resources dying from lightning strikes. + + $blueprint = $resource->getBlueprint(); + $blueprint->activateLease($resource, $lease); + $this->validateActivatedLease($blueprint, $resource, $lease); + } + + /** + * @task activate + */ + private function validateActivatedLease( + DrydockBlueprint $blueprint, + DrydockResource $resource, + DrydockLease $lease) { + + if (!$lease->isActivatedLease()) { + throw new Exception( + pht( + 'Blueprint "%s" (of type "%s") is not properly implemented: it '. + 'returned from "%s" without activating a lease.', + $blueprint->getBlueprintName(), + $blueprint->getClassName(), + 'acquireLease()')); + } + + } + + +/* -( Releasing Leases )--------------------------------------------------- */ + + + /** + * @task release + */ private function releaseLease(DrydockLease $lease) { - $lease->openTransaction(); - $lease - ->setStatus(DrydockLeaseStatus::STATUS_RELEASED) - ->save(); + $lease + ->setStatus(DrydockLeaseStatus::STATUS_RELEASED) + ->save(); - // TODO: Hold slot locks until destruction? - DrydockSlotLock::releaseLocks($lease->getPHID()); - $lease->saveTransaction(); - - PhabricatorWorker::scheduleTask( - 'DrydockLeaseDestroyWorker', - array( - 'leasePHID' => $lease->getPHID(), - ), - array( - 'objectPHID' => $lease->getPHID(), - )); + $lease->logEvent(DrydockLeaseReleasedLogType::LOGCONST); $resource = $lease->getResource(); - $blueprint = $resource->getBlueprint(); + if ($resource) { + $blueprint = $resource->getBlueprint(); + $blueprint->didReleaseLease($resource, $lease); + } - $blueprint->didReleaseLease($resource, $lease); + $this->destroyLease($lease); + } + + +/* -( Breaking Leases )---------------------------------------------------- */ + + + /** + * @task break + */ + protected function breakLease(DrydockLease $lease, Exception $ex) { + switch ($lease->getStatus()) { + case DrydockLeaseStatus::STATUS_BROKEN: + case DrydockLeaseStatus::STATUS_RELEASED: + case DrydockLeaseStatus::STATUS_DESTROYED: + throw new PhutilProxyException( + pht( + 'Unexpected failure while destroying lease ("%s").', + $lease->getPHID()), + $ex); + } + + $lease + ->setStatus(DrydockLeaseStatus::STATUS_BROKEN) + ->save(); + + $lease->scheduleUpdate(); + + $lease->logEvent( + DrydockLeaseActivationFailureLogType::LOGCONST, + array( + 'class' => get_class($ex), + 'message' => $ex->getMessage(), + )); + + throw new PhabricatorWorkerPermanentFailureException( + pht( + 'Permanent failure while activating lease ("%s"): %s', + $lease->getPHID(), + $ex->getMessage())); + } + + +/* -( Destroying Leases )-------------------------------------------------- */ + + + /** + * @task destroy + */ + private function destroyLease(DrydockLease $lease) { + $resource = $lease->getResource(); + + if ($resource) { + $blueprint = $resource->getBlueprint(); + $blueprint->destroyLease($resource, $lease); + } + + DrydockSlotLock::releaseLocks($lease->getPHID()); + + $lease + ->setStatus(DrydockLeaseStatus::STATUS_DESTROYED) + ->save(); + + $lease->logEvent(DrydockLeaseDestroyedLogType::LOGCONST); } } diff --git a/src/applications/drydock/worker/DrydockLeaseWorker.php b/src/applications/drydock/worker/DrydockLeaseWorker.php deleted file mode 100644 index 5919c59613..0000000000 --- a/src/applications/drydock/worker/DrydockLeaseWorker.php +++ /dev/null @@ -1,74 +0,0 @@ -getTaskDataValue('leasePHID'); - $lease = $this->loadLease($lease_phid); - - $this->activateLease($lease); - } - - - private function activateLease(DrydockLease $lease) { - $actual_status = $lease->getStatus(); - - if ($actual_status != DrydockLeaseStatus::STATUS_ACQUIRED) { - throw new PhabricatorWorkerPermanentFailureException( - pht( - 'Trying to activate lease from wrong status ("%s").', - $actual_status)); - } - - $resource = $lease->getResource(); - if (!$resource) { - throw new PhabricatorWorkerPermanentFailureException( - pht('Trying to activate lease with no resource.')); - } - - $resource_status = $resource->getStatus(); - - if ($resource_status == DrydockResourceStatus::STATUS_PENDING) { - // TODO: This is explicitly a temporary failure -- we are waiting for - // the resource to come up. - throw new Exception(pht('Resource still activating.')); - } - - if ($resource_status != DrydockResourceStatus::STATUS_ACTIVE) { - throw new PhabricatorWorkerPermanentFailureException( - pht( - 'Trying to activate lease on a dead resource (in status "%s").', - $resource_status)); - } - - // NOTE: We can race resource destruction here. Between the time we - // performed the read above and now, the resource might have closed, so - // we may activate leases on dead resources. At least for now, this seems - // fine: a resource dying right before we activate a lease on it should not - // be distinguisahble from a resource dying right after we activate a lease - // on it. We end up with an active lease on a dead resource either way, and - // can not prevent resources dying from lightning strikes. - - $blueprint = $resource->getBlueprint(); - $blueprint->activateLease($resource, $lease); - $this->validateActivatedLease($blueprint, $resource, $lease); - } - - private function validateActivatedLease( - DrydockBlueprint $blueprint, - DrydockResource $resource, - DrydockLease $lease) { - - if (!$lease->isActivatedLease()) { - throw new Exception( - pht( - 'Blueprint "%s" (of type "%s") is not properly implemented: it '. - 'returned from "%s" without activating a lease.', - $blueprint->getBlueprintName(), - $blueprint->getClassName(), - 'acquireLease()')); - } - - } - -} diff --git a/src/applications/drydock/worker/DrydockResourceDestroyWorker.php b/src/applications/drydock/worker/DrydockResourceDestroyWorker.php deleted file mode 100644 index af00ebeb2a..0000000000 --- a/src/applications/drydock/worker/DrydockResourceDestroyWorker.php +++ /dev/null @@ -1,35 +0,0 @@ -getTaskDataValue('resourcePHID'); - $resource = $this->loadResource($resource_phid); - $this->destroyResource($resource); - } - - private function destroyResource(DrydockResource $resource) { - $status = $resource->getStatus(); - - switch ($status) { - case DrydockResourceStatus::STATUS_RELEASED: - case DrydockResourceStatus::STATUS_BROKEN: - break; - default: - throw new PhabricatorWorkerPermanentFailureException( - pht( - 'Unable to destroy resource ("%s"), resource has the wrong '. - 'status ("%s").', - $resource->getPHID(), - $status)); - } - - $blueprint = $resource->getBlueprint(); - $blueprint->destroyResource($resource); - - $resource - ->setStatus(DrydockResourceStatus::STATUS_DESTROYED) - ->save(); - } - -} diff --git a/src/applications/drydock/worker/DrydockResourceUpdateWorker.php b/src/applications/drydock/worker/DrydockResourceUpdateWorker.php index 4afc51bc23..e0deabdb1b 100644 --- a/src/applications/drydock/worker/DrydockResourceUpdateWorker.php +++ b/src/applications/drydock/worker/DrydockResourceUpdateWorker.php @@ -1,5 +1,13 @@ lock(1); - $resource = $this->loadResource($resource_phid); - $this->updateResource($resource); + try { + $resource = $this->loadResource($resource_phid); + $this->handleUpdate($resource); + } catch (Exception $ex) { + $lock->unlock(); + throw $ex; + } $lock->unlock(); } + +/* -( Updating Resources )------------------------------------------------- */ + + + /** + * Update a resource, handling exceptions thrown during the update. + * + * @param DrydockReosource Resource to update. + * @return void + * @task update + */ + private function handleUpdate(DrydockResource $resource) { + try { + $this->updateResource($resource); + } catch (Exception $ex) { + if ($this->isTemporaryException($ex)) { + $this->yieldResource($resource, $ex); + } else { + $this->breakResource($resource, $ex); + } + } + } + + + /** + * Update a resource. + * + * @param DrydockResource Resource to update. + * @return void + * @task update + */ private function updateResource(DrydockResource $resource) { + $this->processResourceCommands($resource); + + $resource_status = $resource->getStatus(); + switch ($resource_status) { + case DrydockResourceStatus::STATUS_PENDING: + $this->activateResource($resource); + break; + case DrydockResourceStatus::STATUS_ACTIVE: + // Nothing to do. + break; + case DrydockResourceStatus::STATUS_RELEASED: + case DrydockResourceStatus::STATUS_BROKEN: + $this->destroyResource($resource); + break; + case DrydockResourceStatus::STATUS_DESTROYED: + // Nothing to do. + break; + } + + $this->yieldIfExpiringResource($resource); + } + + + /** + * Convert a temporary exception into a yield. + * + * @param DrydockResource Resource to yield. + * @param Exception Temporary exception worker encountered. + * @task update + */ + private function yieldResource(DrydockResource $resource, Exception $ex) { + $duration = $this->getYieldDurationFromException($ex); + + $resource->logEvent( + DrydockResourceActivationYieldLogType::LOGCONST, + array( + 'duration' => $duration, + )); + + throw new PhabricatorWorkerYieldException($duration); + } + + +/* -( Processing Commands )------------------------------------------------ */ + + + /** + * @task command + */ + private function processResourceCommands(DrydockResource $resource) { + if (!$resource->canReceiveCommands()) { + return; + } + + $this->checkResourceExpiration($resource); + $commands = $this->loadCommands($resource->getPHID()); foreach ($commands as $command) { - if ($resource->getStatus() != DrydockResourceStatus::STATUS_ACTIVE) { - // Resources can't receive commands before they activate or after they - // release. + if (!$resource->canReceiveCommands()) { break; } - $this->processCommand($resource, $command); + $this->processResourceCommand($resource, $command); $command ->setIsConsumed(true) @@ -34,7 +132,11 @@ final class DrydockResourceUpdateWorker extends DrydockWorker { } } - private function processCommand( + + /** + * @task command + */ + private function processResourceCommand( DrydockResource $resource, DrydockCommand $command) { @@ -45,24 +147,53 @@ final class DrydockResourceUpdateWorker extends DrydockWorker { } } - private function releaseResource(DrydockResource $resource) { - if ($resource->getStatus() != DrydockResourceStatus::STATUS_ACTIVE) { - // If we had multiple release commands - // This command is only meaningful to resources in the "Open" state. - return; + +/* -( Activating Resources )----------------------------------------------- */ + + + /** + * @task activate + */ + private function activateResource(DrydockResource $resource) { + $blueprint = $resource->getBlueprint(); + $blueprint->activateResource($resource); + $this->validateActivatedResource($blueprint, $resource); + } + + + /** + * @task activate + */ + private function validateActivatedResource( + DrydockBlueprint $blueprint, + DrydockResource $resource) { + + if (!$resource->isActivatedResource()) { + throw new Exception( + pht( + 'Blueprint "%s" (of type "%s") is not properly implemented: %s '. + 'must actually allocate the resource it returns.', + $blueprint->getBlueprintName(), + $blueprint->getClassName(), + 'allocateResource()')); } + } + + +/* -( Releasing Resources )------------------------------------------------ */ + + + /** + * @task release + */ + private function releaseResource(DrydockResource $resource) { $viewer = $this->getViewer(); $drydock_phid = id(new PhabricatorDrydockApplication())->getPHID(); - $resource->openTransaction(); - $resource - ->setStatus(DrydockResourceStatus::STATUS_RELEASED) - ->save(); - - // TODO: Hold slot locks until destruction? - DrydockSlotLock::releaseLocks($resource->getPHID()); - $resource->saveTransaction(); + $resource + ->setStatus(DrydockResourceStatus::STATUS_RELEASED) + ->save(); $statuses = array( DrydockLeaseStatus::STATUS_PENDING, @@ -86,14 +217,65 @@ final class DrydockResourceUpdateWorker extends DrydockWorker { $lease->scheduleUpdate(); } - PhabricatorWorker::scheduleTask( - 'DrydockResourceDestroyWorker', - array( - 'resourcePHID' => $resource->getPHID(), - ), - array( - 'objectPHID' => $resource->getPHID(), - )); + $this->destroyResource($resource); } + +/* -( Breaking Resources )------------------------------------------------- */ + + + /** + * @task break + */ + private function breakResource(DrydockResource $resource, Exception $ex) { + switch ($resource->getStatus()) { + case DrydockResourceStatus::STATUS_BROKEN: + case DrydockResourceStatus::STATUS_RELEASED: + case DrydockResourceStatus::STATUS_DESTROYED: + // If the resource was already broken, just throw a normal exception. + // This will retry the task eventually. + throw new PhutilProxyException( + pht( + 'Unexpected failure while destroying resource ("%s").', + $resource->getPHID()), + $ex); + } + + $resource + ->setStatus(DrydockResourceStatus::STATUS_BROKEN) + ->save(); + + $resource->scheduleUpdate(); + + $resource->logEvent( + DrydockResourceActivationFailureLogType::LOGCONST, + array( + 'class' => get_class($ex), + 'message' => $ex->getMessage(), + )); + + throw new PhabricatorWorkerPermanentFailureException( + pht( + 'Permanent failure while activating resource ("%s"): %s', + $resource->getPHID(), + $ex->getMessage())); + } + + +/* -( Destroying Resources )----------------------------------------------- */ + + + /** + * @task destroy + */ + private function destroyResource(DrydockResource $resource) { + $blueprint = $resource->getBlueprint(); + $blueprint->destroyResource($resource); + + DrydockSlotLock::releaseLocks($resource->getPHID()); + + $resource + ->setStatus(DrydockResourceStatus::STATUS_DESTROYED) + ->save(); + } } diff --git a/src/applications/drydock/worker/DrydockResourceWorker.php b/src/applications/drydock/worker/DrydockResourceWorker.php deleted file mode 100644 index 45d63029f7..0000000000 --- a/src/applications/drydock/worker/DrydockResourceWorker.php +++ /dev/null @@ -1,45 +0,0 @@ -getTaskDataValue('resourcePHID'); - $resource = $this->loadResource($resource_phid); - - $this->activateResource($resource); - } - - - private function activateResource(DrydockResource $resource) { - $resource_status = $resource->getStatus(); - - if ($resource_status != DrydockResourceStatus::STATUS_PENDING) { - throw new PhabricatorWorkerPermanentFailureException( - pht( - 'Trying to activate resource from wrong status ("%s").', - $resource_status)); - } - - $blueprint = $resource->getBlueprint(); - $blueprint->activateResource($resource); - $this->validateActivatedResource($blueprint, $resource); - } - - - private function validateActivatedResource( - DrydockBlueprint $blueprint, - DrydockResource $resource) { - - if (!$resource->isActivatedResource()) { - throw new Exception( - pht( - 'Blueprint "%s" (of type "%s") is not properly implemented: %s '. - 'must actually allocate the resource it returns.', - $blueprint->getBlueprintName(), - $blueprint->getClassName(), - 'allocateResource()')); - } - - } - -} diff --git a/src/applications/drydock/worker/DrydockWorker.php b/src/applications/drydock/worker/DrydockWorker.php index d41643de47..d2dc1ca399 100644 --- a/src/applications/drydock/worker/DrydockWorker.php +++ b/src/applications/drydock/worker/DrydockWorker.php @@ -50,4 +50,109 @@ abstract class DrydockWorker extends PhabricatorWorker { return $commands; } + protected function checkLeaseExpiration(DrydockLease $lease) { + $this->checkObjectExpiration($lease); + } + + protected function checkResourceExpiration(DrydockResource $resource) { + $this->checkObjectExpiration($resource); + } + + private function checkObjectExpiration($object) { + // Check if the resource or lease has expired. If it has, we're going to + // send it a release command. + + // This command is sent from within the update worker so it is handled + // immediately, but doing this generates a log and improves consistency. + + $expires = $object->getUntil(); + if (!$expires) { + return; + } + + $now = PhabricatorTime::getNow(); + if ($expires > $now) { + return; + } + + $viewer = $this->getViewer(); + $drydock_phid = id(new PhabricatorDrydockApplication())->getPHID(); + + $command = DrydockCommand::initializeNewCommand($viewer) + ->setTargetPHID($object->getPHID()) + ->setAuthorPHID($drydock_phid) + ->setCommand(DrydockCommand::COMMAND_RELEASE) + ->save(); + } + + protected function yieldIfExpiringLease(DrydockLease $lease) { + if (!$lease->canReceiveCommands()) { + return; + } + + $this->yieldIfExpiring($lease->getUntil()); + } + + protected function yieldIfExpiringResource(DrydockResource $resource) { + if (!$resource->canReceiveCommands()) { + return; + } + + $this->yieldIfExpiring($resource->getUntil()); + } + + private function yieldIfExpiring($expires) { + if (!$expires) { + return; + } + + if (!$this->getTaskDataValue('isExpireTask')) { + return; + } + + $now = PhabricatorTime::getNow(); + throw new PhabricatorWorkerYieldException($expires - $now); + } + + protected function isTemporaryException(Exception $ex) { + if ($ex instanceof PhabricatorWorkerYieldException) { + return true; + } + + if ($ex instanceof DrydockSlotLockException) { + return true; + } + + if ($ex instanceof PhutilAggregateException) { + $any_temporary = false; + foreach ($ex->getExceptions() as $sub) { + if ($this->isTemporaryException($sub)) { + $any_temporary = true; + break; + } + } + if ($any_temporary) { + return true; + } + } + + if ($ex instanceof PhutilProxyException) { + return $this->isTemporaryException($ex->getPreviousException()); + } + + return false; + } + + protected function getYieldDurationFromException(Exception $ex) { + if ($ex instanceof PhabricatorWorkerYieldException) { + return $ex->getDuration(); + } + + if ($ex instanceof DrydockSlotLockException) { + return 5; + } + + return 15; + } + } diff --git a/src/applications/files/garbagecollector/PhabricatorFileTemporaryGarbageCollector.php b/src/applications/files/garbagecollector/PhabricatorFileTemporaryGarbageCollector.php index 624ec01097..c79bb9ba99 100644 --- a/src/applications/files/garbagecollector/PhabricatorFileTemporaryGarbageCollector.php +++ b/src/applications/files/garbagecollector/PhabricatorFileTemporaryGarbageCollector.php @@ -3,10 +3,20 @@ final class PhabricatorFileTemporaryGarbageCollector extends PhabricatorGarbageCollector { - public function collectGarbage() { + const COLLECTORCONST = 'files.ttl'; + + public function getCollectorName() { + return pht('Files (TTL)'); + } + + public function hasAutomaticPolicy() { + return true; + } + + protected function collectGarbage() { $files = id(new PhabricatorFile())->loadAllWhere( 'ttl < %d LIMIT 100', - time()); + PhabricatorTime::getNow()); foreach ($files as $file) { $file->delete(); diff --git a/src/applications/harbormaster/artifact/HarbormasterArtifact.php b/src/applications/harbormaster/artifact/HarbormasterArtifact.php index a0d3a86101..8d3d8dd169 100644 --- a/src/applications/harbormaster/artifact/HarbormasterArtifact.php +++ b/src/applications/harbormaster/artifact/HarbormasterArtifact.php @@ -43,35 +43,7 @@ abstract class HarbormasterArtifact extends Phobject { } final public function getArtifactConstant() { - $class = new ReflectionClass($this); - - $const = $class->getConstant('ARTIFACTCONST'); - if ($const === false) { - throw new Exception( - pht( - '"%s" class "%s" must define a "%s" property.', - __CLASS__, - get_class($this), - 'ARTIFACTCONST')); - } - - $limit = self::getArtifactConstantByteLimit(); - if (!is_string($const) || (strlen($const) > $limit)) { - throw new Exception( - pht( - '"%s" class "%s" has an invalid "%s" property. Action constants '. - 'must be strings and no more than %s bytes in length.', - __CLASS__, - get_class($this), - 'ARTIFACTCONST', - new PhutilNumber($limit))); - } - - return $const; - } - - final public static function getArtifactConstantByteLimit() { - return 32; + return $this->getPhobjectClassConstant('ARTIFACTCONST', 32); } final public static function getAllArtifactTypes() { diff --git a/src/applications/harbormaster/controller/HarbormasterPlanRunController.php b/src/applications/harbormaster/controller/HarbormasterPlanRunController.php index 980c7fad4b..be6d138cf7 100644 --- a/src/applications/harbormaster/controller/HarbormasterPlanRunController.php +++ b/src/applications/harbormaster/controller/HarbormasterPlanRunController.php @@ -62,7 +62,7 @@ final class HarbormasterPlanRunController extends HarbormasterController { if (!$errors) { $buildable->save(); - $buildable->applyPlan($plan); + $buildable->applyPlan($plan, array()); $buildable_uri = '/B'.$buildable->getID(); return id(new AphrontRedirectResponse())->setURI($buildable_uri); diff --git a/src/applications/harbormaster/controller/HarbormasterStepEditController.php b/src/applications/harbormaster/controller/HarbormasterStepEditController.php index 9d742740d1..089a801220 100644 --- a/src/applications/harbormaster/controller/HarbormasterStepEditController.php +++ b/src/applications/harbormaster/controller/HarbormasterStepEditController.php @@ -72,9 +72,9 @@ final class HarbormasterStepEditController extends HarbormasterController { $e_name = true; $v_name = $step->getName(); - $e_description = true; + $e_description = null; $v_description = $step->getDescription(); - $e_depends_on = true; + $e_depends_on = null; $v_depends_on = $step->getDetail('dependsOn', array()); $errors = array(); @@ -82,9 +82,7 @@ final class HarbormasterStepEditController extends HarbormasterController { if ($request->isFormPost()) { $e_name = null; $v_name = $request->getStr('name'); - $e_description = null; $v_description = $request->getStr('description'); - $e_depends_on = null; $v_depends_on = $request->getArr('dependsOn'); $xactions = $field_list->buildFieldTransactionsFromRequest( @@ -139,6 +137,12 @@ final class HarbormasterStepEditController extends HarbormasterController { ->setError($e_name) ->setValue($v_name)); + $form->appendChild(id(new AphrontFormDividerControl())); + + $field_list->appendFieldsToForm($form); + + $form->appendChild(id(new AphrontFormDividerControl())); + $form ->appendControl( id(new AphrontFormTokenizerControl()) @@ -152,8 +156,6 @@ final class HarbormasterStepEditController extends HarbormasterController { ->setError($e_depends_on) ->setValue($v_depends_on)); - $field_list->appendFieldsToForm($form); - $form ->appendChild( id(new PhabricatorRemarkupControl()) diff --git a/src/applications/harbormaster/customfield/HarbormasterBuildStepCoreCustomField.php b/src/applications/harbormaster/customfield/HarbormasterBuildStepCoreCustomField.php index 03703a48f4..0ad8f960cf 100644 --- a/src/applications/harbormaster/customfield/HarbormasterBuildStepCoreCustomField.php +++ b/src/applications/harbormaster/customfield/HarbormasterBuildStepCoreCustomField.php @@ -72,4 +72,8 @@ final class HarbormasterBuildStepCoreCustomField return; } + public function getBuildTargetFieldValue() { + return $this->getProxy()->getFieldValue(); + } + } diff --git a/src/applications/harbormaster/customfield/HarbormasterBuildStepCustomField.php b/src/applications/harbormaster/customfield/HarbormasterBuildStepCustomField.php index abc442cb20..4ff6ba799d 100644 --- a/src/applications/harbormaster/customfield/HarbormasterBuildStepCustomField.php +++ b/src/applications/harbormaster/customfield/HarbormasterBuildStepCustomField.php @@ -1,4 +1,8 @@ buildPlanPHID = $build_plan_phid; + return $this; + } + + public function getBuildPlanPHID() { + return $this->buildPlanPHID; + } + + public function setBuildParameters(array $build_parameters) { + $this->buildParameters = $build_parameters; + return $this; + } + + public function getBuildParameters() { + return $this->buildParameters; + } + +} diff --git a/src/applications/harbormaster/engine/HarbormasterTargetEngine.php b/src/applications/harbormaster/engine/HarbormasterTargetEngine.php index a8403385ac..b201891b6b 100644 --- a/src/applications/harbormaster/engine/HarbormasterTargetEngine.php +++ b/src/applications/harbormaster/engine/HarbormasterTargetEngine.php @@ -206,7 +206,7 @@ final class HarbormasterTargetEngine extends Phobject { // resource and "own" it, so we don't try to handle this, but may need // to be more careful here if use of autotargets expands. - $build = $buildable->applyPlan($plan); + $build = $buildable->applyPlan($plan, array()); PhabricatorWorker::setRunAllTasksInProcess(false); } catch (Exception $ex) { PhabricatorWorker::setRunAllTasksInProcess(false); diff --git a/src/applications/harbormaster/herald/HarbormasterBuildableAdapterInterface.php b/src/applications/harbormaster/herald/HarbormasterBuildableAdapterInterface.php index cf6cda8f95..6933306145 100644 --- a/src/applications/harbormaster/herald/HarbormasterBuildableAdapterInterface.php +++ b/src/applications/harbormaster/herald/HarbormasterBuildableAdapterInterface.php @@ -4,8 +4,9 @@ interface HarbormasterBuildableAdapterInterface { public function getHarbormasterBuildablePHID(); public function getHarbormasterContainerPHID(); - public function getQueuedHarbormasterBuildPlanPHIDs(); - public function queueHarbormasterBuildPlanPHID($phid); + public function getQueuedHarbormasterBuildRequests(); + public function queueHarbormasterBuildRequest( + HarbormasterBuildRequest $request); } diff --git a/src/applications/harbormaster/herald/HarbormasterRunBuildPlansHeraldAction.php b/src/applications/harbormaster/herald/HarbormasterRunBuildPlansHeraldAction.php index 830598e812..db75b36d2f 100644 --- a/src/applications/harbormaster/herald/HarbormasterRunBuildPlansHeraldAction.php +++ b/src/applications/harbormaster/herald/HarbormasterRunBuildPlansHeraldAction.php @@ -31,7 +31,9 @@ final class HarbormasterRunBuildPlansHeraldAction $phids = array_fuse(array_keys($targets)); foreach ($phids as $phid) { - $adapter->queueHarbormasterBuildPlanPHID($phid); + $request = id(new HarbormasterBuildRequest()) + ->setBuildPlanPHID($phid); + $adapter->queueHarbormasterBuildRequest($request); } $this->logEffect(self::DO_BUILD, $phids); diff --git a/src/applications/harbormaster/management/HarbormasterManagementBuildWorkflow.php b/src/applications/harbormaster/management/HarbormasterManagementBuildWorkflow.php index fc0f670633..bc6a52018c 100644 --- a/src/applications/harbormaster/management/HarbormasterManagementBuildWorkflow.php +++ b/src/applications/harbormaster/management/HarbormasterManagementBuildWorkflow.php @@ -89,7 +89,7 @@ final class HarbormasterManagementBuildWorkflow PhabricatorEnv::getProductionURI('/B'.$buildable->getID())); PhabricatorWorker::setRunAllTasksInProcess(true); - $buildable->applyPlan($plan); + $buildable->applyPlan($plan, array()); $console->writeOut("%s\n", pht('Done.')); diff --git a/src/applications/harbormaster/step/HarbormasterBuildStepImplementation.php b/src/applications/harbormaster/step/HarbormasterBuildStepImplementation.php index 744ad2474f..87148b0830 100644 --- a/src/applications/harbormaster/step/HarbormasterBuildStepImplementation.php +++ b/src/applications/harbormaster/step/HarbormasterBuildStepImplementation.php @@ -6,6 +6,16 @@ abstract class HarbormasterBuildStepImplementation extends Phobject { private $settings; + private $currentWorkerTaskID; + + public function setCurrentWorkerTaskID($id) { + $this->currentWorkerTaskID = $id; + return $this; + } + + public function getCurrentWorkerTaskID() { + return $this->currentWorkerTaskID; + } public static function getImplementations() { return id(new PhutilClassMapQuery()) @@ -184,7 +194,7 @@ abstract class HarbormasterBuildStepImplementation extends Phobject { * @return string String with variables replaced safely into it. */ protected function mergeVariables($function, $pattern, array $variables) { - $regexp = '/\\$\\{(?P[a-z\\.]+)\\}/'; + $regexp = '@\\$\\{(?P[a-z\\./-]+)\\}@'; $matches = null; preg_match_all($regexp, $pattern, $matches); diff --git a/src/applications/harbormaster/step/HarbormasterHTTPRequestBuildStepImplementation.php b/src/applications/harbormaster/step/HarbormasterHTTPRequestBuildStepImplementation.php index bfaa5c6a56..10d2cb08b2 100644 --- a/src/applications/harbormaster/step/HarbormasterHTTPRequestBuildStepImplementation.php +++ b/src/applications/harbormaster/step/HarbormasterHTTPRequestBuildStepImplementation.php @@ -75,6 +75,12 @@ final class HarbormasterHTTPRequestBuildStepImplementation list($status, $body, $headers) = $future->resolve(); $header_lines = array(); + + // TODO: We don't currently preserve the entire "HTTP" response header, but + // should. Once we do, reproduce it here faithfully. + $status_code = $status->getStatusCode(); + $header_lines[] = "HTTP {$status_code}"; + foreach ($headers as $header) { list($head, $tail) = $header; $header_lines[] = "{$head}: {$tail}"; @@ -89,7 +95,7 @@ final class HarbormasterHTTPRequestBuildStepImplementation ->newLog($uri, 'http.body') ->append($body); - if ($status->getStatusCode() != 200) { + if ($status->isError()) { throw new HarbormasterBuildFailureException(); } } diff --git a/src/applications/harbormaster/step/HarbormasterLeaseWorkingCopyBuildStepImplementation.php b/src/applications/harbormaster/step/HarbormasterLeaseWorkingCopyBuildStepImplementation.php index 1f5b139008..001a981a60 100644 --- a/src/applications/harbormaster/step/HarbormasterLeaseWorkingCopyBuildStepImplementation.php +++ b/src/applications/harbormaster/step/HarbormasterLeaseWorkingCopyBuildStepImplementation.php @@ -45,14 +45,14 @@ final class HarbormasterLeaseWorkingCopyBuildStepImplementation ->setResourceType($working_copy_type) ->setOwnerPHID($build_target->getPHID()); - $variables = $build_target->getVariables(); + $map = $this->buildRepositoryMap($build_target); - $repository_phid = idx($variables, 'repository.phid'); - $commit = idx($variables, 'repository.commit'); + $lease->setAttribute('repositories.map', $map); - $lease - ->setAttribute('repositoryPHID', $repository_phid) - ->setAttribute('commit', $commit); + $task_id = $this->getCurrentWorkerTaskID(); + if ($task_id) { + $lease->setAwakenTaskIDs(array($task_id)); + } $lease->queueForActivation(); @@ -100,7 +100,87 @@ final class HarbormasterLeaseWorkingCopyBuildStepImplementation 'type' => 'text', 'required' => true, ), + 'repositoryPHIDs' => array( + 'name' => pht('Also Clone'), + 'type' => 'datasource', + 'datasource.class' => 'DiffusionRepositoryDatasource', + ), ); } + private function buildRepositoryMap(HarbormasterBuildTarget $build_target) { + $viewer = PhabricatorUser::getOmnipotentUser(); + $variables = $build_target->getVariables(); + + $repository_phid = idx($variables, 'repository.phid'); + if (!$repository_phid) { + throw new Exception( + pht( + 'Unable to determine how to clone the repository for this '. + 'buildable: it is not associated with a tracked repository.')); + } + + $also_phids = $build_target->getFieldValue('repositoryPHIDs'); + + $all_phids = $also_phids; + $all_phids[] = $repository_phid; + + $repositories = id(new PhabricatorRepositoryQuery()) + ->setViewer($viewer) + ->withPHIDs($all_phids) + ->execute(); + $repositories = mpull($repositories, null, 'getPHID'); + + foreach ($all_phids as $phid) { + if (empty($repositories[$phid])) { + throw new PhabricatorWorkerPermanentFailureException( + pht( + 'Unable to load repository with PHID "%s".', + $phid)); + } + } + + $map = array(); + + foreach ($also_phids as $also_phid) { + $also_repo = $repositories[$also_phid]; + $map[$also_repo->getCloneName()] = array( + 'phid' => $also_repo->getPHID(), + 'branch' => 'master', + ); + } + + $repository = $repositories[$repository_phid]; + + $commit = idx($variables, 'buildable.commit'); + $ref_uri = idx($variables, 'repository.staging.uri'); + $ref_ref = idx($variables, 'repository.staging.ref'); + if ($commit) { + $spec = array( + 'commit' => $commit, + ); + } else if ($ref_uri && $ref_ref) { + $spec = array( + 'ref' => array( + 'uri' => $ref_uri, + 'ref' => $ref_ref, + ), + ); + } else { + throw new Exception( + pht( + 'Unable to determine how to fetch changes: this buildable does not '. + 'identify a commit or a staging ref. You may need to configure a '. + 'repository staging area.')); + } + + $directory = $repository->getCloneName(); + $map[$directory] = array( + 'phid' => $repository->getPHID(), + 'default' => true, + ) + $spec; + + return $map; + } + } diff --git a/src/applications/harbormaster/stepgroup/HarbormasterBuildStepGroup.php b/src/applications/harbormaster/stepgroup/HarbormasterBuildStepGroup.php index bb439b75b4..5806861836 100644 --- a/src/applications/harbormaster/stepgroup/HarbormasterBuildStepGroup.php +++ b/src/applications/harbormaster/stepgroup/HarbormasterBuildStepGroup.php @@ -14,19 +14,7 @@ abstract class HarbormasterBuildStepGroup extends Phobject { } final public function getGroupKey() { - $class = new ReflectionClass($this); - - $const = $class->getConstant('GROUPKEY'); - if ($const === false) { - throw new Exception( - pht( - '"%s" class "%s" must define a "%s" property.', - __CLASS__, - get_class($this), - 'GROUPKEY')); - } - - return $const; + return $this->getPhobjectClassConstant('GROUPKEY'); } final public static function getAllGroups() { diff --git a/src/applications/harbormaster/storage/HarbormasterBuildable.php b/src/applications/harbormaster/storage/HarbormasterBuildable.php index 0376ebc890..e74471e879 100644 --- a/src/applications/harbormaster/storage/HarbormasterBuildable.php +++ b/src/applications/harbormaster/storage/HarbormasterBuildable.php @@ -96,15 +96,21 @@ final class HarbormasterBuildable extends HarbormasterDAO } /** - * Looks up the plan PHIDs and applies the plans to the specified - * object identified by it's PHID. + * Start builds for a given buildable. + * + * @param phid PHID of the object to build. + * @param phid Container PHID for the buildable. + * @param list List of builds to perform. + * @return void */ public static function applyBuildPlans( $phid, $container_phid, - array $plan_phids) { + array $requests) { - if (!$plan_phids) { + assert_instances_of($requests, 'HarbormasterBuildRequest'); + + if (!$requests) { return; } @@ -116,31 +122,49 @@ final class HarbormasterBuildable extends HarbormasterDAO return; } + $viewer = PhabricatorUser::getOmnipotentUser(); + $buildable = self::createOrLoadExisting( - PhabricatorUser::getOmnipotentUser(), + $viewer, $phid, $container_phid); + $plan_phids = mpull($requests, 'getBuildPlanPHID'); $plans = id(new HarbormasterBuildPlanQuery()) - ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->setViewer($viewer) ->withPHIDs($plan_phids) ->execute(); - foreach ($plans as $plan) { + $plans = mpull($plans, null, 'getPHID'); + + foreach ($requests as $request) { + $plan_phid = $request->getBuildPlanPHID(); + $plan = idx($plans, $plan_phid); + + if (!$plan) { + throw new Exception( + pht( + 'Failed to load build plan ("%s").', + $plan_phid)); + } + if ($plan->isDisabled()) { // TODO: This should be communicated more clearly -- maybe we should // create the build but set the status to "disabled" or "derelict". continue; } - $buildable->applyPlan($plan); + $parameters = $request->getBuildParameters(); + $buildable->applyPlan($plan, $parameters); } } - public function applyPlan(HarbormasterBuildPlan $plan) { + public function applyPlan(HarbormasterBuildPlan $plan, array $parameters) { + $viewer = PhabricatorUser::getOmnipotentUser(); $build = HarbormasterBuild::initializeNewBuild($viewer) ->setBuildablePHID($this->getPHID()) ->setBuildPlanPHID($plan->getPHID()) + ->setBuildParameters($parameters) ->setBuildStatus(HarbormasterBuild::STATUS_PENDING); $auto_key = $plan->getPlanAutoKey(); diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuild.php b/src/applications/harbormaster/storage/build/HarbormasterBuild.php index 1c012b1842..154681d93f 100644 --- a/src/applications/harbormaster/storage/build/HarbormasterBuild.php +++ b/src/applications/harbormaster/storage/build/HarbormasterBuild.php @@ -9,6 +9,7 @@ final class HarbormasterBuild extends HarbormasterDAO protected $buildPlanPHID; protected $buildStatus; protected $buildGeneration; + protected $buildParameters = array(); protected $planAutoKey; private $buildable = self::ATTACHABLE; @@ -156,6 +157,9 @@ final class HarbormasterBuild extends HarbormasterDAO protected function getConfiguration() { return array( self::CONFIG_AUX_PHID => true, + self::CONFIG_SERIALIZATION => array( + 'buildParameters' => self::SERIALIZATION_JSON, + ), self::CONFIG_COLUMN_SCHEMA => array( 'buildStatus' => 'text32', 'buildGeneration' => 'uint32', @@ -258,6 +262,10 @@ final class HarbormasterBuild extends HarbormasterDAO 'build.id' => null, ); + foreach ($this->getBuildParameters() as $key => $value) { + $results['build/'.$key] = $value; + } + $buildable = $this->getBuildable(); $object = $buildable->getBuildableObject(); diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuildLog.php b/src/applications/harbormaster/storage/build/HarbormasterBuildLog.php index 2e964e8931..85e7ae2411 100644 --- a/src/applications/harbormaster/storage/build/HarbormasterBuildLog.php +++ b/src/applications/harbormaster/storage/build/HarbormasterBuildLog.php @@ -20,7 +20,7 @@ final class HarbormasterBuildLog extends HarbormasterDAO const ENCODING_TEXT = 'text'; public function __destruct() { - if ($this->live) { + if ($this->start) { $this->finalize($this->start); } } @@ -154,7 +154,8 @@ final class HarbormasterBuildLog extends HarbormasterDAO public function finalize($start = 0) { if (!$this->getLive()) { - throw new Exception(pht('Start logging before finalizing it.')); + // TODO: Clean up this API. + return; } // TODO: Encode the log contents in a gzipped format. diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php b/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php index fffa30a883..27655189e6 100644 --- a/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php +++ b/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php @@ -263,6 +263,28 @@ final class HarbormasterBuildTarget extends HarbormasterDAO return $log; } + public function getFieldValue($key) { + $field_list = PhabricatorCustomField::getObjectFields( + $this->getBuildStep(), + PhabricatorCustomField::ROLE_VIEW); + + $fields = $field_list->getFields(); + $full_key = "std:harbormaster:core:{$key}"; + + $field = idx($fields, $full_key); + if (!$field) { + throw new Exception( + pht( + 'Unknown build step field "%s"!', + $key)); + } + + $field = clone $field; + $field->setValueFromStorage($this->getDetail($key)); + return $field->getBuildTargetFieldValue(); + } + + /* -( Status )------------------------------------------------------------- */ diff --git a/src/applications/harbormaster/worker/HarbormasterTargetWorker.php b/src/applications/harbormaster/worker/HarbormasterTargetWorker.php index db355b145c..ac3014dc29 100644 --- a/src/applications/harbormaster/worker/HarbormasterTargetWorker.php +++ b/src/applications/harbormaster/worker/HarbormasterTargetWorker.php @@ -28,6 +28,7 @@ final class HarbormasterTargetWorker extends HarbormasterWorker { $target = id(new HarbormasterBuildTargetQuery()) ->withIDs(array($id)) ->setViewer($this->getViewer()) + ->needBuildSteps(true) ->executeOne(); if (!$target) { @@ -59,6 +60,7 @@ final class HarbormasterTargetWorker extends HarbormasterWorker { } $implementation = $target->getImplementation(); + $implementation->setCurrentWorkerTaskID($this->getCurrentWorkerTaskID()); $implementation->execute($build, $target); $next_status = HarbormasterBuildTarget::STATUS_PASSED; diff --git a/src/applications/herald/action/HeraldAction.php b/src/applications/herald/action/HeraldAction.php index 02a9dfa60a..f4217cd4db 100644 --- a/src/applications/herald/action/HeraldAction.php +++ b/src/applications/herald/action/HeraldAction.php @@ -122,35 +122,7 @@ abstract class HeraldAction extends Phobject { } final public function getActionConstant() { - $class = new ReflectionClass($this); - - $const = $class->getConstant('ACTIONCONST'); - if ($const === false) { - throw new Exception( - pht( - '"%s" class "%s" must define a "%s" property.', - __CLASS__, - get_class($this), - 'ACTIONCONST')); - } - - $limit = self::getActionConstantByteLimit(); - if (!is_string($const) || (strlen($const) > $limit)) { - throw new Exception( - pht( - '"%s" class "%s" has an invalid "%s" property. Action constants '. - 'must be strings and no more than %s bytes in length.', - __CLASS__, - get_class($this), - 'ACTIONCONST', - new PhutilNumber($limit))); - } - - return $const; - } - - final public static function getActionConstantByteLimit() { - return 64; + return $this->getPhobjectClassConstant('ACTIONCONST', 64); } final public static function getAllActions() { diff --git a/src/applications/herald/action/HeraldActionGroup.php b/src/applications/herald/action/HeraldActionGroup.php index a087909609..ad4fecd6b6 100644 --- a/src/applications/herald/action/HeraldActionGroup.php +++ b/src/applications/herald/action/HeraldActionGroup.php @@ -3,19 +3,7 @@ abstract class HeraldActionGroup extends HeraldGroup { final public function getGroupKey() { - $class = new ReflectionClass($this); - - $const = $class->getConstant('ACTIONGROUPKEY'); - if ($const === false) { - throw new Exception( - pht( - '"%s" class "%s" must define a "%s" property.', - __CLASS__, - get_class($this), - 'ACTIONGROUPKEY')); - } - - return $const; + return $this->getPhobjectClassConstant('ACTIONGROUPKEY'); } final public static function getAllActionGroups() { diff --git a/src/applications/herald/field/HeraldField.php b/src/applications/herald/field/HeraldField.php index a6d2e580c2..2aba443077 100644 --- a/src/applications/herald/field/HeraldField.php +++ b/src/applications/herald/field/HeraldField.php @@ -169,31 +169,9 @@ abstract class HeraldField extends Phobject { } final public function getFieldConstant() { - $class = new ReflectionClass($this); - - $const = $class->getConstant('FIELDCONST'); - if ($const === false) { - throw new Exception( - pht( - '"%s" class "%s" must define a "%s" property.', - __CLASS__, - get_class($this), - 'FIELDCONST')); - } - - $limit = self::getFieldConstantByteLimit(); - if (!is_string($const) || (strlen($const) > $limit)) { - throw new Exception( - pht( - '"%s" class "%s" has an invalid "%s" property. Field constants '. - 'must be strings and no more than %s bytes in length.', - __CLASS__, - get_class($this), - 'FIELDCONST', - new PhutilNumber($limit))); - } - - return $const; + return $this->getPhobjectClassConstant( + 'FIELDCONST', + self::getFieldConstantByteLimit()); } final public static function getFieldConstantByteLimit() { diff --git a/src/applications/herald/field/HeraldFieldGroup.php b/src/applications/herald/field/HeraldFieldGroup.php index adb7fbe372..9e13f17cb7 100644 --- a/src/applications/herald/field/HeraldFieldGroup.php +++ b/src/applications/herald/field/HeraldFieldGroup.php @@ -3,19 +3,7 @@ abstract class HeraldFieldGroup extends HeraldGroup { final public function getGroupKey() { - $class = new ReflectionClass($this); - - $const = $class->getConstant('FIELDGROUPKEY'); - if ($const === false) { - throw new Exception( - pht( - '"%s" class "%s" must define a "%s" property.', - __CLASS__, - get_class($this), - 'FIELDGROUPKEY')); - } - - return $const; + return $this->getPhobjectClassConstant('FIELDGROUPKEY'); } final public static function getAllFieldGroups() { diff --git a/src/applications/herald/garbagecollector/HeraldTranscriptGarbageCollector.php b/src/applications/herald/garbagecollector/HeraldTranscriptGarbageCollector.php index 583778e535..2567bd86b3 100644 --- a/src/applications/herald/garbagecollector/HeraldTranscriptGarbageCollector.php +++ b/src/applications/herald/garbagecollector/HeraldTranscriptGarbageCollector.php @@ -3,12 +3,17 @@ final class HeraldTranscriptGarbageCollector extends PhabricatorGarbageCollector { - public function collectGarbage() { - $ttl = PhabricatorEnv::getEnvConfig('gcdaemon.ttl.herald-transcripts'); - if ($ttl <= 0) { - return false; - } + const COLLECTORCONST = 'herald.transcripts'; + public function getCollectorName() { + return pht('Herald Transcripts'); + } + + public function getDefaultRetentionPolicy() { + return phutil_units('30 days in seconds'); + } + + protected function collectGarbage() { $table = new HeraldTranscript(); $conn_w = $table->establishConnection('w'); @@ -23,7 +28,7 @@ final class HeraldTranscriptGarbageCollector WHERE garbageCollected = 0 AND time < %d LIMIT 100', $table->getTableName(), - time() - $ttl); + $this->getGarbageEpoch()); return ($conn_w->getAffectedRows() == 100); } diff --git a/src/applications/metamta/garbagecollector/MetaMTAMailReceivedGarbageCollector.php b/src/applications/metamta/garbagecollector/MetaMTAMailReceivedGarbageCollector.php index 02911354b5..b23a006c65 100644 --- a/src/applications/metamta/garbagecollector/MetaMTAMailReceivedGarbageCollector.php +++ b/src/applications/metamta/garbagecollector/MetaMTAMailReceivedGarbageCollector.php @@ -3,9 +3,17 @@ final class MetaMTAMailReceivedGarbageCollector extends PhabricatorGarbageCollector { - public function collectGarbage() { - $ttl = phutil_units('90 days in seconds'); + const COLLECTORCONST = 'metamta.received'; + public function getCollectorName() { + return pht('Mail (Received)'); + } + + public function getDefaultRetentionPolicy() { + return phutil_units('90 days in seconds'); + } + + protected function collectGarbage() { $table = new PhabricatorMetaMTAReceivedMail(); $conn_w = $table->establishConnection('w'); @@ -13,7 +21,7 @@ final class MetaMTAMailReceivedGarbageCollector $conn_w, 'DELETE FROM %T WHERE dateCreated < %d LIMIT 100', $table->getTableName(), - time() - $ttl); + $this->getGarbageEpoch()); return ($conn_w->getAffectedRows() == 100); } diff --git a/src/applications/metamta/garbagecollector/MetaMTAMailSentGarbageCollector.php b/src/applications/metamta/garbagecollector/MetaMTAMailSentGarbageCollector.php index 99103a2f2e..c9ca274436 100644 --- a/src/applications/metamta/garbagecollector/MetaMTAMailSentGarbageCollector.php +++ b/src/applications/metamta/garbagecollector/MetaMTAMailSentGarbageCollector.php @@ -3,12 +3,20 @@ final class MetaMTAMailSentGarbageCollector extends PhabricatorGarbageCollector { - public function collectGarbage() { - $ttl = phutil_units('90 days in seconds'); + const COLLECTORCONST = 'metamta.sent'; + public function getCollectorName() { + return pht('Mail (Sent)'); + } + + public function getDefaultRetentionPolicy() { + return phutil_units('90 days in seconds'); + } + + protected function collectGarbage() { $mails = id(new PhabricatorMetaMTAMail())->loadAllWhere( 'dateCreated < %d LIMIT 100', - PhabricatorTime::getNow() - $ttl); + $this->getGarbageEpoch()); foreach ($mails as $mail) { $mail->delete(); diff --git a/src/applications/multimeter/garbagecollector/MultimeterEventGarbageCollector.php b/src/applications/multimeter/garbagecollector/MultimeterEventGarbageCollector.php index 3b8095ce05..7a5677341c 100644 --- a/src/applications/multimeter/garbagecollector/MultimeterEventGarbageCollector.php +++ b/src/applications/multimeter/garbagecollector/MultimeterEventGarbageCollector.php @@ -3,9 +3,17 @@ final class MultimeterEventGarbageCollector extends PhabricatorGarbageCollector { - public function collectGarbage() { - $ttl = phutil_units('90 days in seconds'); + const COLLECTORCONST = 'multimeter.events'; + public function getCollectorName() { + return pht('Multimeter Events'); + } + + public function getDefaultRetentionPolicy() { + return phutil_units('90 days in seconds'); + } + + protected function collectGarbage() { $table = new MultimeterEvent(); $conn_w = $table->establishConnection('w'); @@ -13,7 +21,7 @@ final class MultimeterEventGarbageCollector $conn_w, 'DELETE FROM %T WHERE epoch < %d LIMIT 100', $table->getTableName(), - PhabricatorTime::getNow() - $ttl); + $this->getGarbageEpoch()); return ($conn_w->getAffectedRows() == 100); } diff --git a/src/applications/notification/garbagecollector/FeedStoryNotificationGarbageCollector.php b/src/applications/notification/garbagecollector/FeedStoryNotificationGarbageCollector.php index 5b74237205..8b134d44b4 100644 --- a/src/applications/notification/garbagecollector/FeedStoryNotificationGarbageCollector.php +++ b/src/applications/notification/garbagecollector/FeedStoryNotificationGarbageCollector.php @@ -3,9 +3,17 @@ final class FeedStoryNotificationGarbageCollector extends PhabricatorGarbageCollector { - public function collectGarbage() { - $ttl = 90 * 24 * 60 * 60; + const COLLECTORCONST = 'feed.notifications'; + public function getCollectorName() { + return pht('Notifications'); + } + + public function getDefaultRetentionPolicy() { + return phutil_units('90 days in seconds'); + } + + protected function collectGarbage() { $table = new PhabricatorFeedStoryNotification(); $conn_w = $table->establishConnection('w'); @@ -14,7 +22,7 @@ final class FeedStoryNotificationGarbageCollector 'DELETE FROM %T WHERE chronologicalKey < (%d << 32) ORDER BY chronologicalKey ASC LIMIT 100', $table->getTableName(), - time() - $ttl); + $this->getGarbageEpoch()); return ($conn_w->getAffectedRows() == 100); } diff --git a/src/applications/owners/controller/PhabricatorOwnersDetailController.php b/src/applications/owners/controller/PhabricatorOwnersDetailController.php index bb178cfbcb..80fd67bcdb 100644 --- a/src/applications/owners/controller/PhabricatorOwnersDetailController.php +++ b/src/applications/owners/controller/PhabricatorOwnersDetailController.php @@ -76,27 +76,28 @@ final class PhabricatorOwnersDetailController 'auditorPHIDs' => $package->getPHID(), )); + $status_concern = DiffusionCommitQuery::AUDIT_STATUS_CONCERN; + $attention_commits = id(new DiffusionCommitQuery()) ->setViewer($request->getUser()) ->withAuditorPHIDs(array($package->getPHID())) - ->withAuditStatus(DiffusionCommitQuery::AUDIT_STATUS_CONCERN) + ->withAuditStatus($status_concern) ->needCommitData(true) ->setLimit(10) ->execute(); - if ($attention_commits) { - $view = id(new PhabricatorAuditListView()) - ->setUser($viewer) - ->setCommits($attention_commits); + $view = id(new PhabricatorAuditListView()) + ->setUser($viewer) + ->setNoDataString(pht('This package has no open problem commits.')) + ->setCommits($attention_commits); - $commit_views[] = array( - 'view' => $view, - 'header' => pht('Commits in this Package that Need Attention'), - 'button' => id(new PHUIButtonView()) - ->setTag('a') - ->setHref($commit_uri->alter('status', 'open')) - ->setText(pht('View All Problem Commits')), - ); - } + $commit_views[] = array( + 'view' => $view, + 'header' => pht('Commits in this Package that Need Attention'), + 'button' => id(new PHUIButtonView()) + ->setTag('a') + ->setHref($commit_uri->alter('status', $status_concern)) + ->setText(pht('View All Problem Commits')), + ); $all_commits = id(new DiffusionCommitQuery()) ->setViewer($request->getUser()) diff --git a/src/applications/people/garbagecollector/PeopleUserLogGarbageCollector.php b/src/applications/people/garbagecollector/PeopleUserLogGarbageCollector.php index 1d8d13a81e..daa669540c 100644 --- a/src/applications/people/garbagecollector/PeopleUserLogGarbageCollector.php +++ b/src/applications/people/garbagecollector/PeopleUserLogGarbageCollector.php @@ -3,9 +3,17 @@ final class PeopleUserLogGarbageCollector extends PhabricatorGarbageCollector { - public function collectGarbage() { - $ttl = phutil_units('180 days in seconds'); + const COLLECTORCONST = 'user.logs'; + public function getCollectorName() { + return pht('User Activity Logs'); + } + + public function getDefaultRetentionPolicy() { + return phutil_units('180 days in seconds'); + } + + protected function collectGarbage() { $table = new PhabricatorUserLog(); $conn_w = $table->establishConnection('w'); @@ -13,7 +21,7 @@ final class PeopleUserLogGarbageCollector $conn_w, 'DELETE FROM %T WHERE dateCreated < %d LIMIT 100', $table->getTableName(), - time() - $ttl); + $this->getGarbageEpoch()); return ($conn_w->getAffectedRows() == 100); } diff --git a/src/applications/phid/type/PhabricatorPHIDType.php b/src/applications/phid/type/PhabricatorPHIDType.php index c69075bdad..a8502b12f7 100644 --- a/src/applications/phid/type/PhabricatorPHIDType.php +++ b/src/applications/phid/type/PhabricatorPHIDType.php @@ -3,17 +3,7 @@ abstract class PhabricatorPHIDType extends Phobject { final public function getTypeConstant() { - $class = new ReflectionClass($this); - - $const = $class->getConstant('TYPECONST'); - if ($const === false) { - throw new Exception( - pht( - '%s class "%s" must define a %s property.', - __CLASS__, - get_class($this), - 'TYPECONST')); - } + $const = $this->getPhobjectClassConstant('TYPECONST'); if (!is_string($const) || !preg_match('/^[A-Z]{4}$/', $const)) { throw new Exception( diff --git a/src/applications/policy/capability/PhabricatorPolicyCapability.php b/src/applications/policy/capability/PhabricatorPolicyCapability.php index b7ff6a060b..36b8ea87c0 100644 --- a/src/applications/policy/capability/PhabricatorPolicyCapability.php +++ b/src/applications/policy/capability/PhabricatorPolicyCapability.php @@ -15,29 +15,7 @@ abstract class PhabricatorPolicyCapability extends Phobject { * @return string Globally unique capability key. */ final public function getCapabilityKey() { - $class = new ReflectionClass($this); - - $const = $class->getConstant('CAPABILITY'); - if ($const === false) { - throw new Exception( - pht( - '%s class "%s" must define a %s property.', - __CLASS__, - get_class($this), - 'CAPABILITY')); - } - - if (!is_string($const)) { - throw new Exception( - pht( - '%s class "%s" has an invalid %s property. '. - 'Capability constants must be a string.', - __CLASS__, - get_class($this), - 'CAPABILITY')); - } - - return $const; + return $this->getPhobjectClassConstant('CAPABILITY'); } diff --git a/src/applications/ponder/editor/PonderAnswerEditor.php b/src/applications/ponder/editor/PonderAnswerEditor.php index fdb5c48437..521c1948ed 100644 --- a/src/applications/ponder/editor/PonderAnswerEditor.php +++ b/src/applications/ponder/editor/PonderAnswerEditor.php @@ -85,6 +85,27 @@ final class PonderAnswerEditor extends PonderEditor { return true; } + protected function getMailTo(PhabricatorLiskDAO $object) { + $phids = array(); + $phids[] = $object->getAuthorPHID(); + $phids[] = $this->requireActor()->getPHID(); + + $question = id(new PonderQuestionQuery()) + ->setViewer($this->requireActor()) + ->withIDs(array($object->getQuestionID())) + ->executeOne(); + + $phids[] = $question->getAuthorPHID(); + + return $phids; + } + + protected function shouldPublishFeedStory( + PhabricatorLiskDAO $object, + array $xactions) { + return true; + } + protected function buildReplyHandler(PhabricatorLiskDAO $object) { return id(new PonderAnswerReplyHandler()) ->setMailReceiver($object); diff --git a/src/applications/ponder/editor/PonderEditor.php b/src/applications/ponder/editor/PonderEditor.php index 24c6f2d8d2..fcfa981f16 100644 --- a/src/applications/ponder/editor/PonderEditor.php +++ b/src/applications/ponder/editor/PonderEditor.php @@ -7,13 +7,6 @@ abstract class PonderEditor return 'PhabricatorPonderApplication'; } - protected function getMailTo(PhabricatorLiskDAO $object) { - return array( - $object->getAuthorPHID(), - $this->requireActor()->getPHID(), - ); - } - protected function getMailSubjectPrefix() { return '[Ponder]'; } diff --git a/src/applications/ponder/editor/PonderQuestionEditor.php b/src/applications/ponder/editor/PonderQuestionEditor.php index b2f143b176..73d9ed0ce0 100644 --- a/src/applications/ponder/editor/PonderQuestionEditor.php +++ b/src/applications/ponder/editor/PonderQuestionEditor.php @@ -209,6 +209,13 @@ final class PonderQuestionEditor return true; } + protected function getMailTo(PhabricatorLiskDAO $object) { + return array( + $object->getAuthorPHID(), + $this->requireActor()->getPHID(), + ); + } + protected function shouldPublishFeedStory( PhabricatorLiskDAO $object, array $xactions) { diff --git a/src/applications/search/index/PhabricatorSearchDocumentIndexer.php b/src/applications/search/index/PhabricatorSearchDocumentIndexer.php index 5e2140d4e0..a9ecc3e9c0 100644 --- a/src/applications/search/index/PhabricatorSearchDocumentIndexer.php +++ b/src/applications/search/index/PhabricatorSearchDocumentIndexer.php @@ -42,55 +42,37 @@ abstract class PhabricatorSearchDocumentIndexer extends Phobject { } public function indexDocumentByPHID($phid, $context) { - try { - $this->setContext($context); + $this->setContext($context); - $document = $this->buildAbstractDocumentByPHID($phid); - if ($document === null) { - // This indexer doesn't build a document index, so we're done. - return $this; - } - - $object = $this->loadDocumentByPHID($phid); - - // Automatically rebuild CustomField indexes if the object uses custom - // fields. - if ($object instanceof PhabricatorCustomFieldInterface) { - $this->indexCustomFields($document, $object); - } - - // Automatically rebuild subscriber indexes if the object is subscribable. - if ($object instanceof PhabricatorSubscribableInterface) { - $this->indexSubscribers($document); - } - - // Automatically build project relationships - if ($object instanceof PhabricatorProjectInterface) { - $this->indexProjects($document, $object); - } - - $engine = PhabricatorSearchEngine::loadEngine(); - try { - $engine->reindexAbstractDocument($document); - } catch (Exception $ex) { - phlog( - pht( - 'Unable to index document %s with engine %s.', - $document->getPHID(), - get_class($engine))); - phlog($ex); - } - - $this->dispatchDidUpdateIndexEvent($phid, $document); - } catch (Exception $ex) { - phlog( - pht( - 'Unable to build document %s with indexer %s.', - $phid, - get_class($this))); - phlog($ex); + $document = $this->buildAbstractDocumentByPHID($phid); + if ($document === null) { + // This indexer doesn't build a document index, so we're done. + return $this; } + $object = $this->loadDocumentByPHID($phid); + + // Automatically rebuild CustomField indexes if the object uses custom + // fields. + if ($object instanceof PhabricatorCustomFieldInterface) { + $this->indexCustomFields($document, $object); + } + + // Automatically rebuild subscriber indexes if the object is subscribable. + if ($object instanceof PhabricatorSubscribableInterface) { + $this->indexSubscribers($document); + } + + // Automatically build project relationships + if ($object instanceof PhabricatorProjectInterface) { + $this->indexProjects($document, $object); + } + + $engine = PhabricatorSearchEngine::loadEngine(); + $engine->reindexAbstractDocument($document); + + $this->dispatchDidUpdateIndexEvent($phid, $document); + return $this; } diff --git a/src/applications/search/management/PhabricatorSearchManagementIndexWorkflow.php b/src/applications/search/management/PhabricatorSearchManagementIndexWorkflow.php index 853f8c42c1..b1afc3619b 100644 --- a/src/applications/search/management/PhabricatorSearchManagementIndexWorkflow.php +++ b/src/applications/search/management/PhabricatorSearchManagementIndexWorkflow.php @@ -93,13 +93,26 @@ final class PhabricatorSearchManagementIndexWorkflow $bar = id(new PhutilConsoleProgressBar()) ->setTotal(count($phids)); + $any_success = false; $indexer = new PhabricatorSearchIndexer(); foreach ($phids as $phid) { - $indexer->queueDocumentForIndexing($phid); + try { + $indexer->queueDocumentForIndexing($phid); + $any_success = true; + } catch (Exception $ex) { + phlog($ex); + } + $bar->update(1); } $bar->done(); + + if (!$any_success) { + throw new Exception( + pht('Failed to rebuild search index for any documents.')); + } + } private function loadPHIDsByNames(array $names) { diff --git a/src/applications/search/worker/PhabricatorSearchWorker.php b/src/applications/search/worker/PhabricatorSearchWorker.php index 873d9ff2dc..689602ab06 100644 --- a/src/applications/search/worker/PhabricatorSearchWorker.php +++ b/src/applications/search/worker/PhabricatorSearchWorker.php @@ -8,8 +8,16 @@ final class PhabricatorSearchWorker extends PhabricatorWorker { $phid = idx($data, 'documentPHID'); $context = idx($data, 'context'); - id(new PhabricatorSearchIndexer()) - ->indexDocumentByPHID($phid, $context); + try { + id(new PhabricatorSearchIndexer()) + ->indexDocumentByPHID($phid, $context); + } catch (Exception $ex) { + throw new PhabricatorWorkerPermanentFailureException( + pht( + 'Failed to update search index for document "%s": %s', + $phid, + $ex->getMessage())); + } } } diff --git a/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php b/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php index 40980e5f41..97a6a1f84f 100644 --- a/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php +++ b/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php @@ -3,6 +3,10 @@ final class PhabricatorSlowvotePollController extends PhabricatorSlowvoteController { + public function shouldAllowPublic() { + return true; + } + public function handleRequest(AphrontRequest $request) { $viewer = $request->getViewer(); $id = $request->getURIData('id'); diff --git a/src/applications/system/garbagecollector/PhabricatorSystemActionGarbageCollector.php b/src/applications/system/garbagecollector/PhabricatorSystemActionGarbageCollector.php index 9b9728ac8c..d8810da274 100644 --- a/src/applications/system/garbagecollector/PhabricatorSystemActionGarbageCollector.php +++ b/src/applications/system/garbagecollector/PhabricatorSystemActionGarbageCollector.php @@ -3,9 +3,17 @@ final class PhabricatorSystemActionGarbageCollector extends PhabricatorGarbageCollector { - public function collectGarbage() { - $ttl = phutil_units('3 days in seconds'); + const COLLECTORCONST = 'system.actions'; + public function getCollectorName() { + return pht('Rate Limiting Actions'); + } + + public function getDefaultRetentionPolicy() { + return phutil_units('3 days in seconds'); + } + + protected function collectGarbage() { $table = new PhabricatorSystemActionLog(); $conn_w = $table->establishConnection('w'); @@ -13,7 +21,7 @@ final class PhabricatorSystemActionGarbageCollector $conn_w, 'DELETE FROM %T WHERE epoch < %d LIMIT 100', $table->getTableName(), - time() - $ttl); + $this->getGarbageEpoch()); return ($conn_w->getAffectedRows() == 100); } diff --git a/src/applications/system/garbagecollector/PhabricatorSystemDestructionGarbageCollector.php b/src/applications/system/garbagecollector/PhabricatorSystemDestructionGarbageCollector.php index ad068cd5c9..8c19f02874 100644 --- a/src/applications/system/garbagecollector/PhabricatorSystemDestructionGarbageCollector.php +++ b/src/applications/system/garbagecollector/PhabricatorSystemDestructionGarbageCollector.php @@ -3,9 +3,17 @@ final class PhabricatorSystemDestructionGarbageCollector extends PhabricatorGarbageCollector { - public function collectGarbage() { - $ttl = phutil_units('90 days in seconds'); + const COLLECTORCONST = 'system.destruction.logs'; + public function getCollectorName() { + return pht('Destruction Logs'); + } + + public function getDefaultRetentionPolicy() { + return phutil_units('90 days in seconds'); + } + + protected function collectGarbage() { $table = new PhabricatorSystemDestructionLog(); $conn_w = $table->establishConnection('w'); @@ -13,7 +21,7 @@ final class PhabricatorSystemDestructionGarbageCollector $conn_w, 'DELETE FROM %T WHERE epoch < %d LIMIT 100', $table->getTableName(), - time() - $ttl); + $this->getGarbageEpoch()); return ($conn_w->getAffectedRows() == 100); } diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php index 851cc24cc0..2efd19a000 100644 --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -2837,7 +2837,7 @@ abstract class PhabricatorApplicationTransactionEditor HarbormasterBuildable::applyBuildPlans( $adapter->getHarbormasterBuildablePHID(), $adapter->getHarbormasterContainerPHID(), - $adapter->getQueuedHarbormasterBuildPlanPHIDs()); + $adapter->getQueuedHarbormasterBuildRequests()); } return array_merge( diff --git a/src/docs/contributor/internationalization.diviner b/src/docs/contributor/internationalization.diviner index f937ec1ae2..f00d09a5db 100644 --- a/src/docs/contributor/internationalization.diviner +++ b/src/docs/contributor/internationalization.diviner @@ -22,7 +22,8 @@ introduce a new locale, like "German" or "Klingon". Once you've created a locale, applications can add translations for that locale. -For instructions on adding new classes, see @{article:Adding New Classes}. +For instructions on adding new classes, see +@{article@phabcontrib:Adding New Classes}. Adding Translations to Locale @@ -36,7 +37,8 @@ Translations are separated from locales so that third-party applications can provide translations into different locales without needing to define those locales themselves. -For instructions on adding new classes, see @{article:Adding New Classes}. +For instructions on adding new classes, see +@{article@phabcontrib:Adding New Classes}. Writing Translatable Code @@ -375,4 +377,5 @@ Next Steps Continue by: - - adding a new locale or translation file with @{article:Adding New Classes}. + - adding a new locale or translation file with + @{article@phabcontrib:Adding New Classes}. diff --git a/src/docs/user/configuration/configuring_file_domain.diviner b/src/docs/user/configuration/configuring_file_domain.diviner index 98576e5ae3..d6e81bb6b1 100644 --- a/src/docs/user/configuration/configuring_file_domain.diviner +++ b/src/docs/user/configuration/configuring_file_domain.diviner @@ -65,7 +65,7 @@ Continue to "Configuring Phabricator", below. Approach: CloudFlare ======== -[[ https://cloudflare.net | CloudFlare ]] is a general-purpose CDN service. +[[ https://cloudflare.com | CloudFlare ]] is a general-purpose CDN service. To set up CloudFlare, you'll need to register a second domain and go through their enrollment process to host the alternate domain on their servers. Use a diff --git a/src/docs/user/configuration/custom_fields.diviner b/src/docs/user/configuration/custom_fields.diviner index 9ada0efa06..7ecfa98723 100644 --- a/src/docs/user/configuration/custom_fields.diviner +++ b/src/docs/user/configuration/custom_fields.diviner @@ -213,5 +213,5 @@ integrations, see the base class for your application and Continue by: - learning more about extending Phabricator with custom code in - @{article@contributor:Adding New Classes}; + @{article@phabcontrib:Adding New Classes}; - or returning to the @{article: Configuration Guide}. diff --git a/src/docs/user/configuration/managing_daemons.diviner b/src/docs/user/configuration/managing_daemons.diviner index b8d102a13d..4382e12c8a 100644 --- a/src/docs/user/configuration/managing_daemons.diviner +++ b/src/docs/user/configuration/managing_daemons.diviner @@ -109,7 +109,7 @@ This daemon will daemonize and run normally. just those started with `phd start`. If you're writing a restart script, have it launch any custom daemons explicitly after `phd restart`. - You can write your own daemons and manage them with `phd` by extending - @{class:PhabricatorDaemon}. See @{article@contributor:Adding New Classes}. + @{class:PhabricatorDaemon}. See @{article@phabcontrib:Adding New Classes}. - See @{article:Diffusion User Guide} for details about tuning the repository daemon. @@ -137,4 +137,4 @@ Continue by: - learning about the repository daemon with @{article:Diffusion User Guide}; or - - writing your own daemons with @{article@contributor:Adding New Classes}. + - writing your own daemons with @{article@phabcontrib:Adding New Classes}. diff --git a/src/docs/user/configuration/managing_garbage.diviner b/src/docs/user/configuration/managing_garbage.diviner new file mode 100644 index 0000000000..1896a73904 --- /dev/null +++ b/src/docs/user/configuration/managing_garbage.diviner @@ -0,0 +1,68 @@ +@title Managing Garbage Collection +@group config + +Understanding and configuring garbage collection. + +Overview +======== + +Phabricator generates various logs and caches during normal operation. Some of +these logs and caches are usually of very little use after some time has +passed, so they are deleted automatically (often after a month or two) in a +process called "garbage collection". + +Garbage collection is performed automatically by the daemons. You can review +all of the installed garbage collectors by browsing to {nav Config > Garbage +Collectors}. + + +Configuring Retention Policies +============================== + +You can reconfigure the data retention policies for most collectors. + +The default retention polcies should be suitable for most installs. However, +you might want to **decrease** retention to reduce the amount of disk space +used by some high-volume log that you don't find particularly interesting, or +to adhere to an organizational data retention policy. + +Alternatively, you might want to **increase** retention if you want to retain +some logs for a longer period of time, perhaps for auditing or analytic +purposes. + +You can review the current retention policies in +{nav Config > Garbage Collectors}. To change a policy, use +`bin/garbage set-policy` to select a new policy: + +``` +phabricator/ $ ./bin/garbage set-policy --collector cache.markup --days 7 +``` + +You can use `--days` to select how long data is retained for. You can also use +`--indefinite` to set an indefinite retention policy. This will stop the +garbage collector from cleaning up any data. Finally, you can use `--default` +to restore the default policy. + +Your changes should be reflected in the web UI immediately, and will take +effect in the actual collector **the next time the daemons are restarted**. + + +Troubleshooting +=============== + +You can manually run a collector with `bin/garbage collect`. + +``` +phabricator/ $ ./bin/garbage collect --collector cache.general +``` + +By using the `--trace` flag, you can inspect the operation of the collector +in detail. + + +Next Steps +========== + +Continue by: + + - exploring other daemon topics with @{article:Managing Daemons with phd}. diff --git a/src/docs/user/field/exit_codes.diviner b/src/docs/user/field/exit_codes.diviner new file mode 100644 index 0000000000..7c69b3509b --- /dev/null +++ b/src/docs/user/field/exit_codes.diviner @@ -0,0 +1,243 @@ +@title Command Line Exit Codes +@group fieldmanual + +Explains the use of exit codes in Phabricator command line scripts. + +Overview +======== + +When you run a command from the command line, it exits with an //exit code//. +This code is normally not shown on the CLI, but you can examine the exit code +of the last command you ran by looking at `$?` in your shell: + + $ ls + ... + $ echo $? + 0 + +Programs which run commands can operate on exit codes, and shell constructs +like `cmdx && cmdy` operate on exit codes. + +The code `0` means success. Other codes signal some sort of error or status +condition, depending on the system and command. + +With rare exception, Phabricator uses //all other codes// to signal +**catastrophic failure**. + +This is an explicit architectural decision and one we are unlikely to deviate +from: generally, we will not accept patches which give a command a nonzero exit +code to indicate an expected state, an application status, or a minor abnormal +condition. + +Generally, this decision reflects a philosophical belief that attaching +application semantics to exit codes is a relic of a simpler time, and that +they are not appropriate for communicating application state in a modern +operational environment. This document explains the reasoning behind our use of +exit codes in more detail. + +In particular, this approach is informed by a focus on operating Phabricator +clusters at scale. This is not a common deployment scenario, but we consider it +the most important one. Our use of exit codes makes it easier to deploy and +operate a Phabricator cluster at larger scales. It makes it slightly harder to +deploy and operate a small cluster or single host by gluing together `bash` +scripts. We are willingly trading the small scale away for advantages at larger +scales. + + +Problems With Exit Codes +======================== + +We do not use exit codes to communicate application state because doing so +makes it harder to write correct scripts, and the primary benefit is that it +makes it easier to write incorrect ones. + +This is somewhat at odds with the philosophy of "worse is better", but a modern +operations environment faces different forces than the interactive shell did +in the 1970s, particularly at scale. + +We consider correctness to be very important to modern operations environments. +In particular, we manage a Phabricator cluster (Phacility) and believe that +having reliable, repeatable processes for provisioning, configuration and +deployment is critical to maintaining and scaling our operations. Our use of +exit codes makes it easier to implement processes that are correct and reliable +on top of Phabricator management scripts. + +Exit codes as signals for application state are problematic because they are +ambiguous: you can't use them to distinguish between dissimilar failure states +which should prompt very different operational responses. + +Exit codes primarily make writing things like `bash` scripts easier, but we +think you shouldn't be writing `bash` scripts in a modern operational +environment if you care very much about your software working. + +Software environments which are powerful enough to handle errors properly are +also powerful enough to parse command output to unambiguously read and react to +complex state. Communicating application state through exit codes almost +exclusively makes it easier to handle errors in a haphazard way which is often +incorrect. + + +Exit Codes are Ambiguous +======================== + +In many cases, exit codes carry very little information and many different +conditions can produce the same exit code, including conditions which should +prompt very different responses. + +The command line tool `grep` searches for text. For example, you might run +a command like this: + + $ grep zebra corpus.txt + +This searches for the text `zebra` in the file `corpus.txt`. If the text is +not found, `grep` exits with a nonzero exit code (specifically, `1`). + +Suppose you run `grep zebra corpus.txt` and observe a nonzero exit code. What +does that mean? These are //some// of the possible conditions which are +consistent with your observation: + + - The text `zebra` was not found in `corpus.txt`. + - `corpus.txt` does not exist. + - You do not have permission to read `corpus.txt`. + - `grep` is not installed. + - You do not have permission to run `grep`. + - There is a bug in `grep`. + - Your `grep` binary is corrupt. + - `grep` was killed by a signal. + +If you're running this command interactively on a single machine, it's probably +OK for all of these conditions to be conflated. You aren't going to examine the +exit code anyway (it isn't even visible to you by default), and `grep` likely +printed useful information to `stderr` if you hit one of the less common issues. + +If you're running this command from operational software (like deployment, +configuration or monitoring scripts) and you care about the correctness and +repeatability of your process, we believe conflating these conditions is not +OK. The operational response to text not being present in a file should almost +always differ substantially from the response to the file not being present or +`grep` being broken. + +In a particularly bad case, a broken `grep` might cause a careless deployment +script to continue down an inappropriate path and cascade into a more serious +failure. + +Even in a less severe case, unexpected conditions should be detected and raised +to operations staff. `grep` being broken or a file that is expected to exist +not existing are both detectable, unexpected, and likely severe conditions, but +they can not be differentiated and handled by examining the exit code of +`grep`. It is much better to detect and raise these problems immediately than +discover them after a lengthy root cause analysis. + +Some of these conditions can be differentiated by examining the specific exit +code of the command instead of acting on all nonzero exit codes. However, many +failure conditions produce the same exit codes (particularly code `1`) and +there is no way to guarantee that a particular code signals a particular +condition, especially across systems. + +Realistically, it is also relatively rare for scripts to even make an effort to +distinguish between exit codes, and all nonzero exit codes are often treated +the same way. + + +Bash Scripts are not Robust +============================ + +Exit codes that indicate application status make writing `bash` scripts (or +scripts in other tools which provide a thin layer on top of what is essentially +`bash`) a lot easier and more convenient. + +For example, it is pretty tricky to parse JSON in `bash` or with standard +command-line tools, and much easier to react to exit codes. This is sometimes +used as an argument for communicating application status in exit codes. + +We reject this because we don't think you should be writing `bash` scripts if +you're doing real operations. Fundamentally, `bash` shell scripts are not a +robust building block for creating correct, reliable operational processes. + +Here is one problem with using `bash` scripts to perform operational tasks. +Consider this command: + + $ mysqldump | gzip > backup.sql.gz + +Now, consider this command: + + $ mysqldermp | gzip > backup.sql.gz + +These commands represent a fairly standard way to accomplish a task (dumping +a compressed database backup to disk) in a `bash` script. + +Note that the second command contains a typo (`dermp` instead of `dump`) which +will cause the command to exit abruptly with a nonzero exit code. + +However, both these statements run successfully and exit with exit code `0` +(indicating success). Both will create a `backup.sql.gz` file. One backs up +your data; the other never backs up your data. This second command will never +work and never do what the author intended, but will appear successful under +casual inspection. + +These behaviors are the same under `set -e`. + +This fragile attitude toward error handling is endemic to `bash` scripts. The +default behavior is to continue on errors, and it isn't easy to change this +default. Options like `set -e` are unreliable and it is difficult to detect and +react to errors in fundamental constructs like pipes. The tools that `bash` +scripts employ (like `grep`) emit ambiguous error codes. Scripts can not help +but propagate this ambiguity no matter how careful they are with error handling. + +It is likely //possible// to implement these things safely and correctly in +`bash`, but it is not easy or straightforward. More importantly, it is not the +default: the default behavior of `bash` is to ignore errors and continue. + +Gluing commands together in `bash` or something that sits on top of `bash` +makes it easy and convenient to get a process that works fairly well most of +the time at small scales, but we are not satisfied that it represents a robust +foundation for operations at larger scales. + + +Reacting to State +================= + +Instead of communicating application state through exit codes, we generally +communicate application state through machine-parseable output with a success +(`0`) exit code. All nonzero exit codes indicate catastrophic failure which +requires operational intervention. + +Callers are expected to request machine-parseable output if necessary (for +example, by passing a `--json` flag or other similar flags), verify the command +exits with a `0` exit code, parse the output, then react to the state it +communicates as appropriate. + +In a sufficiently powerful scripting environment (e.g., one with data +structures and a JSON parser), this is straightforward and makes it easy to +react precisely and correctly. It also allows scripts to communicate +arbitrarily complex state. Provided your environment gives you an appropriate +toolset, it is much more powerful and not significantly more complex than using +error codes. + +Most importantly, it allows the calling environment to treat nonzero exit +statuses as catastrophic failure by default. + + +Moving Forward +============== + +Given these concerns, we are generally unwilling to bring changes which use +exit codes to communicate application state (other than catastrophic failure) +into the upstream. There are some exceptions, but these are rare. In +particular, ease of use in a `bash` environment is not a compelling motivation. + +We are broadly willing to make output machine parseable or provide an explicit +machine output mode (often a `--json` flag) if there is a reasonable use case +for it. However, we operate a large production cluster of Phabricator instances +with the tools available in the upstream, so the lack of machine parseable +output is not sufficient to motivate adding such output on its own: we also +need to understand the problem you're facing, and why it isn't a problem we +face. A simpler or cleaner approach to the problem may already exist. + +If you just want to write `bash` scripts on top of Phabricator scripts and you +are unswayed by these concerns, you can often just build a composite command to +get roughly the same effect that you'd get out of an exit code. + +For example, you can pipe things to `grep` to convert output into exit codes. +This should generally have failure rates that are comparable to the background +failure level of relying on `bash` as a scripting environment. diff --git a/src/docs/user/userguide/arcanist_lint_unit.diviner b/src/docs/user/userguide/arcanist_lint_unit.diviner index 00ad4e237b..6ecc9aaa3e 100644 --- a/src/docs/user/userguide/arcanist_lint_unit.diviner +++ b/src/docs/user/userguide/arcanist_lint_unit.diviner @@ -38,7 +38,7 @@ make this work, you need to do three things: If you haven't created a library for the class to live in yet, you need to do that first. Follow the instructions in -@{article@contributor:Adding New Classes}, then make the library loadable by +@{article@phabcontrib:Adding New Classes}, then make the library loadable by adding it to your `.arcconfig` like this: { diff --git a/src/docs/user/userguide/arcanist_new_project.diviner b/src/docs/user/userguide/arcanist_new_project.diviner index b414a1e997..a8e8e49202 100644 --- a/src/docs/user/userguide/arcanist_new_project.diviner +++ b/src/docs/user/userguide/arcanist_new_project.diviner @@ -47,7 +47,7 @@ Other options include: - **load**: list of additional Phutil libraries to load at startup. See below for details about path resolution, or see - @{article@contributor:Adding New Classes} for a general introduction to + @{article@phabcontrib:Adding New Classes} for a general introduction to libphutil libraries. - **https.cabundle**: specifies the path to an alternate certificate bundle for use when making HTTPS connections. diff --git a/src/docs/user/userguide/diffusion_symbols.diviner b/src/docs/user/userguide/diffusion_symbols.diviner index c58ca72e1d..f5da8aefe0 100644 --- a/src/docs/user/userguide/diffusion_symbols.diviner +++ b/src/docs/user/userguide/diffusion_symbols.diviner @@ -88,7 +88,7 @@ You can configure some more options by going to {nav Diffusion > (Select == External Symbols == -By @{article:Adding New Classes}, you can teach Phabricator +By @{article@phabcontrib:Adding New Classes}, you can teach Phabricator about symbols from the outside world. Extend @{class:DiffusionExternalSymbolsSource}; Once loaded, your new implementation will be used any time a symbol is queried. diff --git a/src/docs/user/userguide/drydock.diviner b/src/docs/user/userguide/drydock.diviner index 2ba4f241fd..a03346f1cb 100644 --- a/src/docs/user/userguide/drydock.diviner +++ b/src/docs/user/userguide/drydock.diviner @@ -1,8 +1,60 @@ @title Drydock User Guide @group userguide -Configuring Drydock for machine resource management. +Drydock, a software and hardware resource manager. -= Overview = +Overview +======== + +WARNING: Drydock is very new and has many sharp edges. Prepare yourself for +a challenging adventure in unmapped territory, not a streamlined experience +where things work properly or make sense. + +Drydock is an infrastructure application that primarily helps other +applications coordinate during complex build and deployment tasks. Typically, +you will configure Drydock to enable capabilities in other applications: + + - Harbormaster can use Drydock to host builds. + - In the future, Differential will be able to use Drydock to perform + server-side merges. + +Users will not normally interact with Drydock directly. + + +What Drydock Does +================= + +Drydock manages working copies, build hosts, and other software and hardware +resources that build and deployment processes may require in order to perform +useful work. + +Many useful processes need a working copy of a repository (or some similar sort +of resource) so they can read files, perform version control operations, or +execute code. + +For example, you might want to be able to automatically run unit tests, build a +binary, or generate documentation every time a new commit is pushed. Or you +might want to automatically merge a revision or cherry-pick a commit from a +development branch to a release branch. Any of these tasks need a working copy +of the repository before they can get underway. + +These processes could just clone a new working copy when they started and +delete it when they finished. This works reasonably well at a small scale, but +will eventually hit limitations if you want to do things like: expand the build +tier to multiple machines; or automatically scale the tier up and down based on +usage; or reuse working copies to improve performance; or make sure things get +cleaned up after a process fails; or have jobs wait if the tier is too busy. +Solving these problems effectively requires coordination between the processes +doing the actual work. + +Drydock solves these scaling problems by providing a central allocation +framework for //resources//, which are physical or virtual resources like a +build host or a working copy. Processes which need to share hardware or +software can use Drydock to coordinate creation, access, and destruction of +those resources. + +Applications ask Drydock for resources matching a description, and it allocates +a corresponding resource by either finding a suitable unused resource or +creating a new resource. When work completes, the resource is returned to the +resource pool or destroyed. -NOTE: Drydock is extremely new and not very useful yet. diff --git a/src/docs/user/userguide/events.diviner b/src/docs/user/userguide/events.diviner index 8959585f4a..8374a47101 100644 --- a/src/docs/user/userguide/events.diviner +++ b/src/docs/user/userguide/events.diviner @@ -21,7 +21,7 @@ To install event listeners in Phabricator, follow these steps: - Write a listener class which extends @{class@libphutil:PhutilEventListener}. - Add it to a libphutil library, or create a new library (for instructions, - see @{article@contributor:Adding New Classes}. + see @{article@phabcontrib:Adding New Classes}. - Configure Phabricator to load the library by adding it to `load-libraries` in the Phabricator config. - Configure Phabricator to install the event listener by adding the class @@ -38,7 +38,7 @@ To install event listeners in Arcanist, follow these steps: - Write a listener class which extends @{class@libphutil:PhutilEventListener}. - Add it to a libphutil library, or create a new library (for instructions, - see @{article@contributor:Adding New Classes}. + see @{article@phabcontrib:Adding New Classes}. - Configure Phabricator to load the library by adding it to `load` in the Arcanist config (e.g., `.arcconfig`, or user/global config). - Configure Arcanist to install the event listener by adding the class diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php index d900d41eb3..ecaf67caa9 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php @@ -93,15 +93,8 @@ abstract class PhabricatorStandardCustomFieldPHIDs public function getApplicationTransactionRequiredHandlePHIDs( PhabricatorApplicationTransaction $xaction) { - $old = json_decode($xaction->getOldValue()); - if (!is_array($old)) { - $old = array(); - } - - $new = json_decode($xaction->getNewValue()); - if (!is_array($new)) { - $new = array(); - } + $old = $this->decodeValue($xaction->getOldValue()); + $new = $this->decodeValue($xaction->getNewValue()); $add = array_diff($new, $old); $rem = array_diff($old, $new); @@ -113,15 +106,8 @@ abstract class PhabricatorStandardCustomFieldPHIDs PhabricatorApplicationTransaction $xaction) { $author_phid = $xaction->getAuthorPHID(); - $old = json_decode($xaction->getOldValue()); - if (!is_array($old)) { - $old = array(); - } - - $new = json_decode($xaction->getNewValue()); - if (!is_array($new)) { - $new = array(); - } + $old = $this->decodeValue($xaction->getOldValue()); + $new = $this->decodeValue($xaction->getNewValue()); $add = array_diff($new, $old); $rem = array_diff($old, $new); @@ -152,6 +138,61 @@ abstract class PhabricatorStandardCustomFieldPHIDs } } + public function validateApplicationTransactions( + PhabricatorApplicationTransactionEditor $editor, + $type, + array $xactions) { + + $errors = parent::validateApplicationTransactions( + $editor, + $type, + $xactions); + + // If the user is adding PHIDs, make sure the new PHIDs are valid and + // visible to the actor. It's OK for a user to edit a field which includes + // some invalid or restricted values, but they can't add new ones. + + foreach ($xactions as $xaction) { + $old = $this->decodeValue($xaction->getOldValue()); + $new = $this->decodeValue($xaction->getNewValue()); + + $add = array_diff($new, $old); + + if (!$add) { + continue; + } + + $objects = id(new PhabricatorObjectQuery()) + ->setViewer($editor->getActor()) + ->withPHIDs($add) + ->execute(); + $objects = mpull($objects, null, 'getPHID'); + + $invalid = array(); + foreach ($add as $phid) { + if (empty($objects[$phid])) { + $invalid[] = $phid; + } + } + + if ($invalid) { + $error = new PhabricatorApplicationTransactionValidationError( + $type, + pht('Invalid'), + pht( + 'Some of the selected PHIDs in field "%s" are invalid or '. + 'restricted: %s.', + $this->getFieldName(), + implode(', ', $invalid)), + $xaction); + $errors[] = $error; + $this->setFieldError(pht('Invalid')); + } + } + + return $errors; + } + public function shouldAppearInHerald() { return true; } @@ -176,4 +217,13 @@ abstract class PhabricatorStandardCustomFieldPHIDs return array(); } + private function decodeValue($value) { + $value = json_decode($value); + if (!is_array($value)) { + $value = array(); + } + + return $value; + } + } diff --git a/src/infrastructure/daemon/garbagecollector/PhabricatorGarbageCollector.php b/src/infrastructure/daemon/garbagecollector/PhabricatorGarbageCollector.php index 5aea82c845..3e5b70cc05 100644 --- a/src/infrastructure/daemon/garbagecollector/PhabricatorGarbageCollector.php +++ b/src/infrastructure/daemon/garbagecollector/PhabricatorGarbageCollector.php @@ -1,12 +1,155 @@ hasAutomaticPolicy()) { + throw new Exception( + pht( + 'Can not get retention policy of collector with automatic '. + 'policy.')); + } + + $config = PhabricatorEnv::getEnvConfig('phd.garbage-collection'); + $const = $this->getCollectorConstant(); + + return idx($config, $const, $this->getDefaultRetentionPolicy()); + } + + + + /** + * Get a unique string constant identifying this collector. + * + * @return string Collector constant. + * @task info + */ + final public function getCollectorConstant() { + return $this->getPhobjectClassConstant('COLLECTORCONST', 64); + } + + +/* -( Collecting Garbage )------------------------------------------------- */ + + + /** + * Run the collector. + * + * @return bool True if there is more garbage to collect. + * @task collect + */ + final public function runCollector() { + // Don't do anything if this collector is configured with an indefinite + // retention policy. + if (!$this->hasAutomaticPolicy()) { + $policy = $this->getRetentionPolicy(); + if (!$policy) { + return false; + } + } + + return $this->collectGarbage(); + } + + /** * Collect garbage from whatever source this GC handles. * * @return bool True if there is more garbage to collect. + * @task collect */ - abstract public function collectGarbage(); + abstract protected function collectGarbage(); + + + /** + * Get the most recent epoch timestamp that is considered garbage. + * + * Records older than this should be collected. + * + * @return int Most recent garbage timestamp. + * @task collect + */ + final protected function getGarbageEpoch() { + if ($this->hasAutomaticPolicy()) { + throw new Exception( + pht( + 'Can not get garbage epoch for a collector with an automatic '. + 'collection policy.')); + } + + $ttl = $this->getRetentionPolicy(); + if (!$ttl) { + throw new Exception( + pht( + 'Can not get garbage epoch for a collector with an indefinite '. + 'retention policy.')); + } + + return (PhabricatorTime::getNow() - $ttl); + } + + + /** + * Load all of the available garbage collectors. + * + * @return list Garbage collectors. + * @task collect + */ + final public static function getAllCollectors() { + return id(new PhutilClassMapQuery()) + ->setAncestorClass(__CLASS__) + ->setUniqueMethod('getCollectorConstant') + ->execute(); + } } diff --git a/src/infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementCollectWorkflow.php b/src/infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementCollectWorkflow.php new file mode 100644 index 0000000000..8af86dc2d5 --- /dev/null +++ b/src/infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementCollectWorkflow.php @@ -0,0 +1,50 @@ +setName('collect') + ->setExamples('**collect** --collector __collector__') + ->setSynopsis( + pht('Run a garbage collector in the foreground.')) + ->setArguments( + array( + array( + 'name' => 'collector', + 'param' => 'const', + 'help' => pht( + 'Constant identifying the garbage collector to run.'), + ), + )); + } + + public function execute(PhutilArgumentParser $args) { + $collector = $this->getCollector($args->getArg('collector')); + + echo tsprintf( + "%s\n", + pht('Collecting "%s" garbage...', $collector->getCollectorName())); + + $any = false; + while (true) { + $more = $collector->runCollector(); + if ($more) { + $any = true; + } else { + break; + } + } + + if ($any) { + $message = pht('Finished collecting all the garbage.'); + } else { + $message = pht('Could not find any garbage to collect.'); + } + echo tsprintf("\n%s\n", $message); + + return 0; + } + +} diff --git a/src/infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementSetPolicyWorkflow.php b/src/infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementSetPolicyWorkflow.php new file mode 100644 index 0000000000..83f74c6dde --- /dev/null +++ b/src/infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementSetPolicyWorkflow.php @@ -0,0 +1,141 @@ +setName('set-policy') + ->setExamples( + "**set-policy** --collector __collector__ --days 30\n". + "**set-policy** --collector __collector__ --indefinite\n". + "**set-policy** --collector __collector__ --default") + ->setSynopsis( + pht( + 'Change retention policies for a garbage collector.')) + ->setArguments( + array( + array( + 'name' => 'collector', + 'param' => 'const', + 'help' => pht( + 'Constant identifying the garbage collector.'), + ), + array( + 'name' => 'indefinite', + 'help' => pht( + 'Set an indefinite retention policy.'), + ), + array( + 'name' => 'default', + 'help' => pht( + 'Use the default retention policy.'), + ), + array( + 'name' => 'days', + 'param' => 'count', + 'help' => pht( + 'Retain data for the specified number of days.'), + ), + )); + } + + public function execute(PhutilArgumentParser $args) { + $config_key = 'phd.garbage-collection'; + + $collector = $this->getCollector($args->getArg('collector')); + + $days = $args->getArg('days'); + $indefinite = $args->getArg('indefinite'); + $default = $args->getArg('default'); + + $count = 0; + if ($days !== null) { + $count++; + } + if ($indefinite) { + $count++; + } + if ($default) { + $count++; + } + + if (!$count) { + throw new PhutilArgumentUsageException( + pht( + 'Choose a policy with "%s", "%s" or "%s".', + '--days', + '--indefinite', + '--default')); + } + + if ($count > 1) { + throw new PhutilArgumentUsageException( + pht( + 'Options "%s", "%s" and "%s" represent mutually exclusive ways '. + 'to choose a policy. Specify only one.', + '--days', + '--indefinite', + '--default')); + } + + if ($days !== null) { + $days = (int)$days; + if ($days < 1) { + throw new PhutilArgumentUsageException( + pht( + 'Specify a positive number of days to retain data for.')); + } + } + + $collector_const = $collector->getCollectorConstant(); + $value = PhabricatorEnv::getEnvConfig($config_key); + + if ($days !== null) { + echo tsprintf( + "%s\n", + pht( + 'Setting retention policy for "%s" to %s day(s).', + $collector->getCollectorName(), + new PhutilNumber($days))); + + $value[$collector_const] = phutil_units($days.' days in seconds'); + } else if ($indefinite) { + echo tsprintf( + "%s\n", + pht( + 'Setting "%s" to be retained indefinitely.', + $collector->getCollectorName())); + + $value[$collector_const] = null; + } else { + echo tsprintf( + "%s\n", + pht( + 'Restoring "%s" to the default retention policy.', + $collector->getCollectorName())); + + unset($value[$collector_const]); + } + + id(new PhabricatorConfigLocalSource()) + ->setKeys( + array( + $config_key => $value, + )); + + echo tsprintf( + "%s\n", + pht( + 'Wrote new policy to local configuration.')); + + echo tsprintf( + "%s\n", + pht( + 'This change will take effect the next time the daemons are '. + 'restarted.')); + + return 0; + } + +} diff --git a/src/infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementWorkflow.php b/src/infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementWorkflow.php new file mode 100644 index 0000000000..eabb2f5b0d --- /dev/null +++ b/src/infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementWorkflow.php @@ -0,0 +1,32 @@ +nextCollection; if ($next && (PhabricatorTime::getNow() >= $next)) { $this->nextCollection = null; - $this->garbageCollectors = $this->loadGarbageCollectors(); + + $all_collectors = PhabricatorGarbageCollector::getAllCollectors(); + $this->garbageCollectors = $all_collectors; } // If we're in a collection cycle, continue collection. if ($this->garbageCollectors) { foreach ($this->garbageCollectors as $key => $collector) { - $more_garbage = $collector->collectGarbage(); + $more_garbage = $collector->runCollector(); if (!$more_garbage) { unset($this->garbageCollectors[$key]); } @@ -377,18 +379,4 @@ final class PhabricatorTriggerDaemon return false; } - - /** - * Load all of the available garbage collectors. - * - * @return list Garbage collectors. - * @task garbage - */ - private function loadGarbageCollectors() { - return id(new PhutilClassMapQuery()) - ->setAncestorClass('PhabricatorGarbageCollector') - ->execute(); - } - - } diff --git a/src/infrastructure/daemon/workers/PhabricatorWorker.php b/src/infrastructure/daemon/workers/PhabricatorWorker.php index 4e71440604..c448e7221a 100644 --- a/src/infrastructure/daemon/workers/PhabricatorWorker.php +++ b/src/infrastructure/daemon/workers/PhabricatorWorker.php @@ -8,6 +8,7 @@ abstract class PhabricatorWorker extends Phobject { private $data; private static $runAllTasksInProcess = false; private $queuedTasks = array(); + private $currentWorkerTask; // NOTE: Lower priority numbers execute first. The priority numbers have to // have the same ordering that IDs do (lowest first) so MySQL can use a @@ -18,6 +19,10 @@ abstract class PhabricatorWorker extends Phobject { const PRIORITY_BULK = 3000; const PRIORITY_IMPORT = 4000; + /** + * Special owner indicating that the task has yielded. + */ + const YIELD_OWNER = '(yield)'; /* -( Configuring Retries and Failures )----------------------------------- */ @@ -77,6 +82,23 @@ abstract class PhabricatorWorker extends Phobject { return null; } + public function setCurrentWorkerTask(PhabricatorWorkerTask $task) { + $this->currentWorkerTask = $task; + return $this; + } + + public function getCurrentWorkerTask() { + return $this->currentWorkerTask; + } + + public function getCurrentWorkerTaskID() { + $task = $this->getCurrentWorkerTask(); + if (!$task) { + return null; + } + return $task->getID(); + } + abstract protected function doWork(); final public function __construct($data) { @@ -105,6 +127,14 @@ abstract class PhabricatorWorker extends Phobject { $data, $options = array()) { + PhutilTypeSpec::checkMap( + $options, + array( + 'priority' => 'optional int|null', + 'objectPHID' => 'optional string|null', + 'delayUntil' => 'optional int|null', + )); + $priority = idx($options, 'priority'); if ($priority === null) { $priority = self::PRIORITY_DEFAULT; @@ -208,4 +238,49 @@ abstract class PhabricatorWorker extends Phobject { return $this->queuedTasks; } + + /** + * Awaken tasks that have yielded. + * + * Reschedules the specified tasks if they are currently queued in a yielded, + * unleased, unretried state so they'll execute sooner. This can let the + * queue avoid unnecessary waits. + * + * This method does not provide any assurances about when these tasks will + * execute, or even guarantee that it will have any effect at all. + * + * @param list List of task IDs to try to awaken. + * @return void + */ + final public static function awakenTaskIDs(array $ids) { + if (!$ids) { + return; + } + + $table = new PhabricatorWorkerActiveTask(); + $conn_w = $table->establishConnection('w'); + + // NOTE: At least for now, we're keeping these tasks yielded, just + // pretending that they threw a shorter yield than they really did. + + // Overlap the windows here to handle minor client/server time differences + // and because it's likely correct to push these tasks to the head of their + // respective priorities. There is a good chance they are ready to execute. + $window = phutil_units('1 hour in seconds'); + $epoch_ago = (PhabricatorTime::getNow() - $window); + + queryfx( + $conn_w, + 'UPDATE %T SET leaseExpires = %d + WHERE id IN (%Ld) + AND leaseOwner = %s + AND leaseExpires > %d + AND failureCount = 0', + $table->getTableName(), + $epoch_ago, + $ids, + self::YIELD_OWNER, + $epoch_ago); + } + } diff --git a/src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementWorkflow.php b/src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementWorkflow.php index 9189489208..be2408e54a 100644 --- a/src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementWorkflow.php +++ b/src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementWorkflow.php @@ -39,6 +39,15 @@ abstract class PhabricatorWorkerManagementWorkflow } } + // When we lock tasks properly, this gets populated as a side effect. Just + // fake it when doing manual CLI stuff. This makes sure CLI yields have + // their expires times set properly. + foreach ($tasks as $task) { + if ($task instanceof PhabricatorWorkerActiveTask) { + $task->setServerTime(PhabricatorTime::getNow()); + } + } + return $tasks; } diff --git a/src/infrastructure/daemon/workers/storage/PhabricatorWorkerActiveTask.php b/src/infrastructure/daemon/workers/storage/PhabricatorWorkerActiveTask.php index 0705a27780..6c2f5b4461 100644 --- a/src/infrastructure/daemon/workers/storage/PhabricatorWorkerActiveTask.php +++ b/src/infrastructure/daemon/workers/storage/PhabricatorWorkerActiveTask.php @@ -86,15 +86,23 @@ final class PhabricatorWorkerActiveTask extends PhabricatorWorkerTask { } protected function checkLease() { - if ($this->leaseOwner) { - $current_server_time = $this->serverTime + (time() - $this->localTime); - if ($current_server_time >= $this->leaseExpires) { - throw new Exception( - pht( - 'Trying to update Task %d (%s) after lease expiration!', - $this->getID(), - $this->getTaskClass())); - } + $owner = $this->leaseOwner; + + if (!$owner) { + return; + } + + if ($owner == PhabricatorWorker::YIELD_OWNER) { + return; + } + + $current_server_time = $this->serverTime + (time() - $this->localTime); + if ($current_server_time >= $this->leaseExpires) { + throw new Exception( + pht( + 'Trying to update Task %d (%s) after lease expiration!', + $this->getID(), + $this->getTaskClass())); } } @@ -141,6 +149,7 @@ final class PhabricatorWorkerActiveTask extends PhabricatorWorkerTask { $worker = null; try { $worker = $this->getWorkerInstance(); + $worker->setCurrentWorkerTask($this); $maximum_failures = $worker->getMaximumRetryCount(); if ($maximum_failures !== null) { @@ -175,6 +184,8 @@ final class PhabricatorWorkerActiveTask extends PhabricatorWorkerTask { } catch (PhabricatorWorkerYieldException $ex) { $this->setExecutionException($ex); + $this->setLeaseOwner(PhabricatorWorker::YIELD_OWNER); + $retry = $ex->getDuration(); $retry = max($retry, 5); @@ -211,7 +222,7 @@ final class PhabricatorWorkerActiveTask extends PhabricatorWorkerTask { $class, $data, array( - 'priority' => $this->getPriority(), + 'priority' => (int)$this->getPriority(), )); } } diff --git a/src/infrastructure/daemon/workers/storage/PhabricatorWorkerTask.php b/src/infrastructure/daemon/workers/storage/PhabricatorWorkerTask.php index 3d8c6887c6..b0937c4e6d 100644 --- a/src/infrastructure/daemon/workers/storage/PhabricatorWorkerTask.php +++ b/src/infrastructure/daemon/workers/storage/PhabricatorWorkerTask.php @@ -60,7 +60,10 @@ abstract class PhabricatorWorkerTask extends PhabricatorWorkerDAO { $id = $this->getID(); $class = $this->getTaskClass(); - if (!class_exists($class)) { + try { + // NOTE: If the class does not exist, libphutil will throw an exception. + class_exists($class); + } catch (PhutilMissingSymbolException $ex) { throw new PhabricatorWorkerPermanentFailureException( pht( "Task class '%s' does not exist!", diff --git a/src/infrastructure/edges/type/PhabricatorEdgeType.php b/src/infrastructure/edges/type/PhabricatorEdgeType.php index c86af5c027..1729256050 100644 --- a/src/infrastructure/edges/type/PhabricatorEdgeType.php +++ b/src/infrastructure/edges/type/PhabricatorEdgeType.php @@ -12,17 +12,7 @@ abstract class PhabricatorEdgeType extends Phobject { final public function getEdgeConstant() { - $class = new ReflectionClass($this); - - $const = $class->getConstant('EDGECONST'); - if ($const === false) { - throw new Exception( - pht( - '%s class "%s" must define an %s property.', - __CLASS__, - get_class($this), - 'EDGECONST')); - } + $const = $this->getPhobjectClassConstant('EDGECONST'); if (!is_int($const) || ($const <= 0)) { throw new Exception( diff --git a/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php b/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php index 7a5f8b78dd..055b85f0df 100644 --- a/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php +++ b/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php @@ -1388,6 +1388,16 @@ final class PhabricatorUSEnglishTranslation 'Mail sent in the last %s days.', ), + '%s Day(s)' => array( + '%s Day', + '%s Days', + ), + + 'Setting retention policy for "%s" to %s day(s).' => array( + 'Setting retention policy for "%s" to one day.', + 'Setting retention policy for "%s" to %s days.', + ), + ); } diff --git a/src/infrastructure/markup/rule/PhabricatorObjectRemarkupRule.php b/src/infrastructure/markup/rule/PhabricatorObjectRemarkupRule.php index 896c52276f..d5addbc556 100644 --- a/src/infrastructure/markup/rule/PhabricatorObjectRemarkupRule.php +++ b/src/infrastructure/markup/rule/PhabricatorObjectRemarkupRule.php @@ -209,13 +209,14 @@ abstract class PhabricatorObjectRemarkupRule extends PhutilRemarkupRule { $boundary = '\\B'; } - // The "(?