1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-23 22:10:55 +01:00

Implement an iterator for build log chunks

Summary: Ref T5822. This will make it easier to compress and archive chunks without needing to hold them in memory.

Test Plan: Ran a build, looked at some logs.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T5822

Differential Revision: https://secure.phabricator.com/D15378
This commit is contained in:
epriestley 2016-03-01 12:22:14 -08:00
parent e174cac1b4
commit 6514237c0e
4 changed files with 75 additions and 26 deletions

View file

@ -1045,6 +1045,7 @@ phutil_register_library_map(array(
'HarbormasterBuildLintMessage' => 'applications/harbormaster/storage/build/HarbormasterBuildLintMessage.php', 'HarbormasterBuildLintMessage' => 'applications/harbormaster/storage/build/HarbormasterBuildLintMessage.php',
'HarbormasterBuildLog' => 'applications/harbormaster/storage/build/HarbormasterBuildLog.php', 'HarbormasterBuildLog' => 'applications/harbormaster/storage/build/HarbormasterBuildLog.php',
'HarbormasterBuildLogChunk' => 'applications/harbormaster/storage/build/HarbormasterBuildLogChunk.php', 'HarbormasterBuildLogChunk' => 'applications/harbormaster/storage/build/HarbormasterBuildLogChunk.php',
'HarbormasterBuildLogChunkIterator' => 'applications/harbormaster/storage/build/HarbormasterBuildLogChunkIterator.php',
'HarbormasterBuildLogPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildLogPHIDType.php', 'HarbormasterBuildLogPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildLogPHIDType.php',
'HarbormasterBuildLogQuery' => 'applications/harbormaster/query/HarbormasterBuildLogQuery.php', 'HarbormasterBuildLogQuery' => 'applications/harbormaster/query/HarbormasterBuildLogQuery.php',
'HarbormasterBuildMessage' => 'applications/harbormaster/storage/HarbormasterBuildMessage.php', 'HarbormasterBuildMessage' => 'applications/harbormaster/storage/HarbormasterBuildMessage.php',
@ -5193,6 +5194,7 @@ phutil_register_library_map(array(
'PhabricatorPolicyInterface', 'PhabricatorPolicyInterface',
), ),
'HarbormasterBuildLogChunk' => 'HarbormasterDAO', 'HarbormasterBuildLogChunk' => 'HarbormasterDAO',
'HarbormasterBuildLogChunkIterator' => 'PhutilBufferedIterator',
'HarbormasterBuildLogPHIDType' => 'PhabricatorPHIDType', 'HarbormasterBuildLogPHIDType' => 'PhabricatorPHIDType',
'HarbormasterBuildLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'HarbormasterBuildLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'HarbormasterBuildMessage' => array( 'HarbormasterBuildMessage' => array(

View file

@ -16,11 +16,6 @@ final class HarbormasterBuildLog
const CHUNK_BYTE_LIMIT = 102400; const CHUNK_BYTE_LIMIT = 102400;
/**
* The log is encoded as plain text.
*/
const ENCODING_TEXT = 'text';
public function __construct() { public function __construct() {
$this->rope = new PhutilRope(); $this->rope = new PhutilRope();
} }
@ -129,6 +124,8 @@ final class HarbormasterBuildLog
$chunk_table = id(new HarbormasterBuildLogChunk())->getTableName(); $chunk_table = id(new HarbormasterBuildLogChunk())->getTableName();
$chunk_limit = self::CHUNK_BYTE_LIMIT; $chunk_limit = self::CHUNK_BYTE_LIMIT;
$encoding_text = HarbormasterBuildLogChunk::CHUNK_ENCODING_TEXT;
$rope = $this->rope; $rope = $this->rope;
while (true) { while (true) {
@ -147,7 +144,7 @@ final class HarbormasterBuildLog
$can_append = $can_append =
($tail) && ($tail) &&
($tail['encoding'] == self::ENCODING_TEXT) && ($tail['encoding'] == $encoding_text) &&
($tail['size'] < $chunk_limit); ($tail['size'] < $chunk_limit);
if ($can_append) { if ($can_append) {
$append_id = $tail['id']; $append_id = $tail['id'];
@ -176,7 +173,7 @@ final class HarbormasterBuildLog
VALUES (%d, %s, %d, %B)', VALUES (%d, %s, %d, %B)',
$chunk_table, $chunk_table,
$this->getID(), $this->getID(),
self::ENCODING_TEXT, $encoding_text,
$data_size, $data_size,
$append_data); $append_data);
} }
@ -185,29 +182,21 @@ final class HarbormasterBuildLog
} }
} }
public function getLogText() { public function newChunkIterator() {
// TODO: This won't cope very well if we're pulling like a 700MB return new HarbormasterBuildLogChunkIterator($this);
// log file out of the DB. We should probably implement some sort
// of optional limit parameter so that when we're rendering out only
// 25 lines in the UI, we don't wastefully read in the whole log.
// We have to read our content out of the database and stitch all of
// the log data back together.
$conn = $this->establishConnection('r');
$result = queryfx_all(
$conn,
'SELECT chunk '.
'FROM %T '.
'WHERE logID = %d '.
'ORDER BY id ASC',
id(new HarbormasterBuildLogChunk())->getTableName(),
$this->getID());
$content = '';
foreach ($result as $row) {
$content .= $row['chunk'];
} }
return $content;
public function getLogText() {
// TODO: Remove this method since it won't scale for big logs.
$all_chunks = $this->newChunkIterator();
$full_text = array();
foreach ($all_chunks as $chunk) {
$full_text[] = $chunk->getChunkDisplayText();
}
return implode('', $full_text);
} }

View file

@ -8,6 +8,12 @@ final class HarbormasterBuildLogChunk
protected $size; protected $size;
protected $chunk; protected $chunk;
/**
* The log is encoded as plain text.
*/
const CHUNK_ENCODING_TEXT = 'text';
protected function getConfiguration() { protected function getConfiguration() {
return array( return array(
self::CONFIG_TIMESTAMPS => false, self::CONFIG_TIMESTAMPS => false,
@ -29,4 +35,21 @@ final class HarbormasterBuildLogChunk
) + parent::getConfiguration(); ) + parent::getConfiguration();
} }
public function getChunkDisplayText() {
$data = $this->getChunk();
$encoding = $this->getEncoding();
switch ($encoding) {
case self::CHUNK_ENCODING_TEXT:
// Do nothing, data is already plaintext.
break;
default:
throw new Exception(
pht('Unknown log chunk encoding ("%s")!', $encoding));
}
return $data;
}
} }

View file

@ -0,0 +1,35 @@
<?php
final class HarbormasterBuildLogChunkIterator
extends PhutilBufferedIterator {
private $log;
private $cursor;
public function __construct(HarbormasterBuildLog $log) {
$this->log = $log;
}
protected function didRewind() {
$this->cursor = 0;
}
public function key() {
return $this->current()->getID();
}
protected function loadPage() {
$results = id(new HarbormasterBuildLogChunk())->loadAllWhere(
'logID = %d AND id > %d ORDER BY id ASC LIMIT %d',
$this->log->getID(),
$this->cursor,
$this->getPageSize());
if ($results) {
$this->cursor = last($results)->getID();
}
return $results;
}
}