Skip to content

Commit 6514237

Browse files
author
epriestley
committed
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
1 parent e174cac commit 6514237

File tree

4 files changed

+77
-28
lines changed

4 files changed

+77
-28
lines changed

src/__phutil_library_map__.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,7 @@
10451045
'HarbormasterBuildLintMessage' => 'applications/harbormaster/storage/build/HarbormasterBuildLintMessage.php',
10461046
'HarbormasterBuildLog' => 'applications/harbormaster/storage/build/HarbormasterBuildLog.php',
10471047
'HarbormasterBuildLogChunk' => 'applications/harbormaster/storage/build/HarbormasterBuildLogChunk.php',
1048+
'HarbormasterBuildLogChunkIterator' => 'applications/harbormaster/storage/build/HarbormasterBuildLogChunkIterator.php',
10481049
'HarbormasterBuildLogPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildLogPHIDType.php',
10491050
'HarbormasterBuildLogQuery' => 'applications/harbormaster/query/HarbormasterBuildLogQuery.php',
10501051
'HarbormasterBuildMessage' => 'applications/harbormaster/storage/HarbormasterBuildMessage.php',
@@ -5193,6 +5194,7 @@
51935194
'PhabricatorPolicyInterface',
51945195
),
51955196
'HarbormasterBuildLogChunk' => 'HarbormasterDAO',
5197+
'HarbormasterBuildLogChunkIterator' => 'PhutilBufferedIterator',
51965198
'HarbormasterBuildLogPHIDType' => 'PhabricatorPHIDType',
51975199
'HarbormasterBuildLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
51985200
'HarbormasterBuildMessage' => array(

src/applications/harbormaster/storage/build/HarbormasterBuildLog.php

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,6 @@ final class HarbormasterBuildLog
1616

1717
const CHUNK_BYTE_LIMIT = 102400;
1818

19-
/**
20-
* The log is encoded as plain text.
21-
*/
22-
const ENCODING_TEXT = 'text';
23-
2419
public function __construct() {
2520
$this->rope = new PhutilRope();
2621
}
@@ -129,6 +124,8 @@ private function flush() {
129124

130125
$chunk_table = id(new HarbormasterBuildLogChunk())->getTableName();
131126
$chunk_limit = self::CHUNK_BYTE_LIMIT;
127+
$encoding_text = HarbormasterBuildLogChunk::CHUNK_ENCODING_TEXT;
128+
132129
$rope = $this->rope;
133130

134131
while (true) {
@@ -147,7 +144,7 @@ private function flush() {
147144

148145
$can_append =
149146
($tail) &&
150-
($tail['encoding'] == self::ENCODING_TEXT) &&
147+
($tail['encoding'] == $encoding_text) &&
151148
($tail['size'] < $chunk_limit);
152149
if ($can_append) {
153150
$append_id = $tail['id'];
@@ -176,7 +173,7 @@ private function flush() {
176173
VALUES (%d, %s, %d, %B)',
177174
$chunk_table,
178175
$this->getID(),
179-
self::ENCODING_TEXT,
176+
$encoding_text,
180177
$data_size,
181178
$append_data);
182179
}
@@ -185,29 +182,21 @@ private function flush() {
185182
}
186183
}
187184

185+
public function newChunkIterator() {
186+
return new HarbormasterBuildLogChunkIterator($this);
187+
}
188+
188189
public function getLogText() {
189-
// TODO: This won't cope very well if we're pulling like a 700MB
190-
// log file out of the DB. We should probably implement some sort
191-
// of optional limit parameter so that when we're rendering out only
192-
// 25 lines in the UI, we don't wastefully read in the whole log.
193-
194-
// We have to read our content out of the database and stitch all of
195-
// the log data back together.
196-
$conn = $this->establishConnection('r');
197-
$result = queryfx_all(
198-
$conn,
199-
'SELECT chunk '.
200-
'FROM %T '.
201-
'WHERE logID = %d '.
202-
'ORDER BY id ASC',
203-
id(new HarbormasterBuildLogChunk())->getTableName(),
204-
$this->getID());
205-
206-
$content = '';
207-
foreach ($result as $row) {
208-
$content .= $row['chunk'];
190+
// TODO: Remove this method since it won't scale for big logs.
191+
192+
$all_chunks = $this->newChunkIterator();
193+
194+
$full_text = array();
195+
foreach ($all_chunks as $chunk) {
196+
$full_text[] = $chunk->getChunkDisplayText();
209197
}
210-
return $content;
198+
199+
return implode('', $full_text);
211200
}
212201

213202

src/applications/harbormaster/storage/build/HarbormasterBuildLogChunk.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ final class HarbormasterBuildLogChunk
88
protected $size;
99
protected $chunk;
1010

11+
12+
/**
13+
* The log is encoded as plain text.
14+
*/
15+
const CHUNK_ENCODING_TEXT = 'text';
16+
1117
protected function getConfiguration() {
1218
return array(
1319
self::CONFIG_TIMESTAMPS => false,
@@ -29,4 +35,21 @@ protected function getConfiguration() {
2935
) + parent::getConfiguration();
3036
}
3137

38+
public function getChunkDisplayText() {
39+
$data = $this->getChunk();
40+
$encoding = $this->getEncoding();
41+
42+
switch ($encoding) {
43+
case self::CHUNK_ENCODING_TEXT:
44+
// Do nothing, data is already plaintext.
45+
break;
46+
default:
47+
throw new Exception(
48+
pht('Unknown log chunk encoding ("%s")!', $encoding));
49+
}
50+
51+
return $data;
52+
}
53+
54+
3255
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
final class HarbormasterBuildLogChunkIterator
4+
extends PhutilBufferedIterator {
5+
6+
private $log;
7+
private $cursor;
8+
9+
public function __construct(HarbormasterBuildLog $log) {
10+
$this->log = $log;
11+
}
12+
13+
protected function didRewind() {
14+
$this->cursor = 0;
15+
}
16+
17+
public function key() {
18+
return $this->current()->getID();
19+
}
20+
21+
protected function loadPage() {
22+
$results = id(new HarbormasterBuildLogChunk())->loadAllWhere(
23+
'logID = %d AND id > %d ORDER BY id ASC LIMIT %d',
24+
$this->log->getID(),
25+
$this->cursor,
26+
$this->getPageSize());
27+
28+
if ($results) {
29+
$this->cursor = last($results)->getID();
30+
}
31+
32+
return $results;
33+
}
34+
35+
}

0 commit comments

Comments
 (0)