1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-12 00:26:13 +01:00
phorge-phorge/src/applications/differential/storage/DifferentialHunk.php
epriestley d15fb20fe6 Support storage of Differential hunk data in Files
Summary:
Ref T12932. For long-lived installs, one of the largest tables tends to be the hunk data table. Although it doesn't grow tremendously fast, it's also well suited to storage in Files instead of the database (infrequent access, relatively large blobs of data, mostly one-at-a-time access), and earlier work anticipated eventually adding support for Files storage.

Make Files storage work, and provide `bin/differential migrate-hunk` to manually test/migrate hunks. This is currently the only way hunks get moved to file storage, but I expect to add a GC step which moves them to File storage after 30 days shortly.

The immediate motivation for this is to relieve storage pressure on db001/db002 so we have more headroom for deploying the Ferret engine and its larger indexes (see also T12819).

Test Plan:
  - Used `bin/differential migrate-hunk` to move a hunk to and from file storage, verified it survived intact.
  - Downloaded the actual stored file, sanity-checked it. Verified permissions.
  - Destroyed a diff with `bin/remove destroy`, saw the hunk and file storage destroyed.
  - Verified that going from file -> text destroys the old file properly with `migrate-hunk --trace ...`.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T12932

Differential Revision: https://secure.phabricator.com/D18584
2017-09-11 16:09:02 -07:00

244 lines
5.6 KiB
PHP

<?php
abstract class DifferentialHunk
extends DifferentialDAO
implements
PhabricatorPolicyInterface,
PhabricatorDestructibleInterface {
protected $changesetID;
protected $oldOffset;
protected $oldLen;
protected $newOffset;
protected $newLen;
private $changeset;
private $splitLines;
private $structuredLines;
private $structuredFiles = array();
const FLAG_LINES_ADDED = 1;
const FLAG_LINES_REMOVED = 2;
const FLAG_LINES_STABLE = 4;
public function getAddedLines() {
return $this->makeContent($include = '+');
}
public function getRemovedLines() {
return $this->makeContent($include = '-');
}
public function makeNewFile() {
return implode('', $this->makeContent($include = ' +'));
}
public function makeOldFile() {
return implode('', $this->makeContent($include = ' -'));
}
public function makeChanges() {
return implode('', $this->makeContent($include = '-+'));
}
public function getStructuredOldFile() {
return $this->getStructuredFile('-');
}
public function getStructuredNewFile() {
return $this->getStructuredFile('+');
}
private function getStructuredFile($kind) {
if ($kind !== '+' && $kind !== '-') {
throw new Exception(
pht(
'Structured file kind should be "+" or "-", got "%s".',
$kind));
}
if (!isset($this->structuredFiles[$kind])) {
if ($kind == '+') {
$number = $this->newOffset;
} else {
$number = $this->oldOffset;
}
$lines = $this->getStructuredLines();
// NOTE: We keep the "\ No newline at end of file" line if it appears
// after a line which is not excluded. For example, if we're constructing
// the "+" side of the diff, we want to ignore this one since it's
// relevant only to the "-" side of the diff:
//
// - x
// \ No newline at end of file
// + x
//
// ...but we want to keep this one:
//
// - x
// + x
// \ No newline at end of file
$file = array();
$keep = true;
foreach ($lines as $line) {
switch ($line['type']) {
case ' ':
case $kind:
$file[$number++] = $line;
$keep = true;
break;
case '\\':
if ($keep) {
// Strip the actual newline off the line's text.
$text = $file[$number - 1]['text'];
$text = rtrim($text, "\r\n");
$file[$number - 1]['text'] = $text;
$file[$number++] = $line;
$keep = false;
}
break;
default:
$keep = false;
break;
}
}
$this->structuredFiles[$kind] = $file;
}
return $this->structuredFiles[$kind];
}
public function getSplitLines() {
if ($this->splitLines === null) {
$this->splitLines = phutil_split_lines($this->getChanges());
}
return $this->splitLines;
}
public function getStructuredLines() {
if ($this->structuredLines === null) {
$lines = $this->getSplitLines();
$structured = array();
foreach ($lines as $line) {
if (empty($line[0])) {
// TODO: Can we just get rid of this?
continue;
}
$structured[] = array(
'type' => $line[0],
'text' => substr($line, 1),
);
}
$this->structuredLines = $structured;
}
return $this->structuredLines;
}
public function getContentWithMask($mask) {
$include = array();
if (($mask & self::FLAG_LINES_ADDED)) {
$include[] = '+';
}
if (($mask & self::FLAG_LINES_REMOVED)) {
$include[] = '-';
}
if (($mask & self::FLAG_LINES_STABLE)) {
$include[] = ' ';
}
$include = implode('', $include);
return implode('', $this->makeContent($include));
}
final private function makeContent($include) {
$lines = $this->getSplitLines();
$results = array();
$include_map = array();
for ($ii = 0; $ii < strlen($include); $ii++) {
$include_map[$include[$ii]] = true;
}
if (isset($include_map['+'])) {
$n = $this->newOffset;
} else {
$n = $this->oldOffset;
}
$use_next_newline = false;
foreach ($lines as $line) {
if (!isset($line[0])) {
continue;
}
if ($line[0] == '\\') {
if ($use_next_newline) {
$results[last_key($results)] = rtrim(end($results), "\n");
}
} else if (empty($include_map[$line[0]])) {
$use_next_newline = false;
} else {
$use_next_newline = true;
$results[$n] = substr($line, 1);
}
if ($line[0] == ' ' || isset($include_map[$line[0]])) {
$n++;
}
}
return $results;
}
public function getChangeset() {
return $this->assertAttached($this->changeset);
}
public function attachChangeset(DifferentialChangeset $changeset) {
$this->changeset = $changeset;
return $this;
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
public function getPolicy($capability) {
return $this->getChangeset()->getPolicy($capability);
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return $this->getChangeset()->hasAutomaticCapability($capability, $viewer);
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$this->delete();
}
}