Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15285651
D19135.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
8 KB
Referenced Files
None
Subscribers
None
D19135.diff
View Options
diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuildLog.php b/src/applications/harbormaster/storage/build/HarbormasterBuildLog.php
--- a/src/applications/harbormaster/storage/build/HarbormasterBuildLog.php
+++ b/src/applications/harbormaster/storage/build/HarbormasterBuildLog.php
@@ -16,6 +16,7 @@
private $buildTarget = self::ATTACHABLE;
private $rope;
private $isOpen;
+ private $lock;
const CHUNK_BYTE_LIMIT = 1048576;
@@ -27,6 +28,12 @@
if ($this->isOpen) {
$this->closeBuildLog();
}
+
+ if ($this->lock) {
+ if ($this->lock->isLocked()) {
+ $this->lock->unlock();
+ }
+ }
}
public static function initializeNewBuildLog(
@@ -35,37 +42,7 @@
return id(new HarbormasterBuildLog())
->setBuildTargetPHID($build_target->getPHID())
->setDuration(null)
- ->setLive(0);
- }
-
- public function openBuildLog() {
- if ($this->isOpen) {
- throw new Exception(pht('This build log is already open!'));
- }
-
- $this->isOpen = true;
-
- return $this
- ->setLive(1)
- ->save();
- }
-
- public function closeBuildLog() {
- if (!$this->isOpen) {
- throw new Exception(pht('This build log is not open!'));
- }
-
- $start = $this->getDateCreated();
- $now = PhabricatorTime::getNow();
-
- $this
- ->setDuration($now - $start)
- ->setLive(0)
- ->save();
-
- $this->scheduleRebuild(false);
-
- return $this;
+ ->setLive(1);
}
public function scheduleRebuild($force) {
@@ -120,72 +97,6 @@
return pht('Build Log');
}
- public function append($content) {
- if (!$this->getLive()) {
- throw new PhutilInvalidStateException('openBuildLog');
- }
-
- $content = (string)$content;
-
- $this->rope->append($content);
- $this->flush();
-
- return $this;
- }
-
- private function flush() {
-
- // TODO: Maybe don't flush more than a couple of times per second. If a
- // caller writes a single character over and over again, we'll currently
- // spend a lot of time flushing that.
-
- $chunk_table = id(new HarbormasterBuildLogChunk())->getTableName();
- $chunk_limit = self::CHUNK_BYTE_LIMIT;
- $encoding_text = HarbormasterBuildLogChunk::CHUNK_ENCODING_TEXT;
-
- $rope = $this->rope;
-
- while (true) {
- $length = $rope->getByteLength();
- if (!$length) {
- break;
- }
-
- $conn_w = $this->establishConnection('w');
- $last = $this->loadLastChunkInfo();
-
- $can_append =
- ($last) &&
- ($last['encoding'] == $encoding_text) &&
- ($last['size'] < $chunk_limit);
- if ($can_append) {
- $append_id = $last['id'];
- $prefix_size = $last['size'];
- } else {
- $append_id = null;
- $prefix_size = 0;
- }
-
- $data_limit = ($chunk_limit - $prefix_size);
- $append_data = $rope->getPrefixBytes($data_limit);
- $data_size = strlen($append_data);
-
- if ($append_id) {
- queryfx(
- $conn_w,
- 'UPDATE %T SET chunk = CONCAT(chunk, %B), size = %d WHERE id = %d',
- $chunk_table,
- $append_data,
- $prefix_size + $data_size,
- $append_id);
- } else {
- $this->writeChunk($encoding_text, $data_size, $append_data);
- }
-
- $rope->removeBytesFromHead($data_size);
- }
- }
-
public function newChunkIterator() {
return id(new HarbormasterBuildLogChunkIterator($this))
->setPageSize(8);
@@ -216,6 +127,16 @@
return implode('', $full_text);
}
+
+ public function getURI() {
+ $id = $this->getID();
+ return "/harbormaster/log/view/{$id}/";
+ }
+
+
+/* -( Chunks )------------------------------------------------------------- */
+
+
public function canCompressLog() {
return function_exists('gzdeflate');
}
@@ -229,6 +150,13 @@
}
private function processLog($mode) {
+ if (!$this->getLock()->isLocked()) {
+ throw new Exception(
+ pht(
+ 'You can not process build log chunks unless the log lock is '.
+ 'held.'));
+ }
+
$chunks = $this->newChunkIterator();
// NOTE: Because we're going to insert new chunks, we need to stop the
@@ -292,12 +220,145 @@
->save();
}
- public function getURI() {
- $id = $this->getID();
- return "/harbormaster/log/view/{$id}/";
+
+/* -( Writing )------------------------------------------------------------ */
+
+
+ public function getLock() {
+ if (!$this->lock) {
+ $phid = $this->getPHID();
+ $phid_key = PhabricatorHash::digestToLength($phid, 14);
+ $lock_key = "build.log({$phid_key})";
+ $lock = PhabricatorGlobalLock::newLock($lock_key);
+ $this->lock = $lock;
+ }
+
+ return $this->lock;
+ }
+
+
+ public function openBuildLog() {
+ if ($this->isOpen) {
+ throw new Exception(pht('This build log is already open!'));
+ }
+
+ $is_new = !$this->getID();
+ if ($is_new) {
+ $this->save();
+ }
+
+ $this->getLock()->lock();
+ $this->isOpen = true;
+
+ $this->reload();
+
+ if (!$this->getLive()) {
+ $this->setLive(1)->save();
+ }
+
+ return $this;
+ }
+
+ public function closeBuildLog($forever = true) {
+ if (!$this->isOpen) {
+ throw new Exception(
+ pht(
+ 'You must openBuildLog() before you can closeBuildLog().'));
+ }
+
+ $this->flush();
+
+ if ($forever) {
+ $start = $this->getDateCreated();
+ $now = PhabricatorTime::getNow();
+
+ $this
+ ->setDuration($now - $start)
+ ->setLive(0)
+ ->save();
+ }
+
+ $this->getLock()->unlock();
+ $this->isOpen = false;
+
+ if ($forever) {
+ $this->scheduleRebuild(false);
+ }
+
+ return $this;
+ }
+
+ public function append($content) {
+ if (!$this->isOpen) {
+ throw new Exception(
+ pht(
+ 'You must openBuildLog() before you can append() content to '.
+ 'the log.'));
+ }
+
+ $content = (string)$content;
+
+ $this->rope->append($content);
+ $this->flush();
+
+ return $this;
+ }
+
+ private function flush() {
+
+ // TODO: Maybe don't flush more than a couple of times per second. If a
+ // caller writes a single character over and over again, we'll currently
+ // spend a lot of time flushing that.
+
+ $chunk_table = id(new HarbormasterBuildLogChunk())->getTableName();
+ $chunk_limit = self::CHUNK_BYTE_LIMIT;
+ $encoding_text = HarbormasterBuildLogChunk::CHUNK_ENCODING_TEXT;
+
+ $rope = $this->rope;
+
+ while (true) {
+ $length = $rope->getByteLength();
+ if (!$length) {
+ break;
+ }
+
+ $conn_w = $this->establishConnection('w');
+ $last = $this->loadLastChunkInfo();
+
+ $can_append =
+ ($last) &&
+ ($last['encoding'] == $encoding_text) &&
+ ($last['size'] < $chunk_limit);
+ if ($can_append) {
+ $append_id = $last['id'];
+ $prefix_size = $last['size'];
+ } else {
+ $append_id = null;
+ $prefix_size = 0;
+ }
+
+ $data_limit = ($chunk_limit - $prefix_size);
+ $append_data = $rope->getPrefixBytes($data_limit);
+ $data_size = strlen($append_data);
+
+ if ($append_id) {
+ queryfx(
+ $conn_w,
+ 'UPDATE %T SET chunk = CONCAT(chunk, %B), size = %d WHERE id = %d',
+ $chunk_table,
+ $append_data,
+ $prefix_size + $data_size,
+ $append_id);
+ } else {
+ $this->writeChunk($encoding_text, $data_size, $append_data);
+ }
+
+ $rope->removeBytesFromHead($data_size);
+ }
}
+
/* -( PhabricatorPolicyInterface )----------------------------------------- */
diff --git a/src/applications/harbormaster/worker/HarbormasterLogWorker.php b/src/applications/harbormaster/worker/HarbormasterLogWorker.php
--- a/src/applications/harbormaster/worker/HarbormasterLogWorker.php
+++ b/src/applications/harbormaster/worker/HarbormasterLogWorker.php
@@ -8,9 +8,18 @@
$data = $this->getTaskData();
$log_phid = idx($data, 'logPHID');
- $phid_key = PhabricatorHash::digestToLength($log_phid, 14);
- $lock_key = "build.log({$phid_key})";
- $lock = PhabricatorGlobalLock::newLock($lock_key);
+ $log = id(new HarbormasterBuildLogQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($log_phid))
+ ->executeOne();
+ if (!$log) {
+ throw new PhabricatorWorkerPermanentFailureException(
+ pht(
+ 'Invalid build log PHID "%s".',
+ $log_phid));
+ }
+
+ $lock = $log->getLock();
try {
$lock->lock();
@@ -20,16 +29,7 @@
$caught = null;
try {
- $log = id(new HarbormasterBuildLogQuery())
- ->setViewer($viewer)
- ->withPHIDs(array($log_phid))
- ->executeOne();
- if (!$log) {
- throw new PhabricatorWorkerPermanentFailureException(
- pht(
- 'Invalid build log PHID "%s".',
- $log_phid));
- }
+ $log->reload();
if ($log->getLive()) {
throw new PhabricatorWorkerPermanentFailureException(
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Mar 5, 3:05 PM (2 w, 3 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7225849
Default Alt Text
D19135.diff (8 KB)
Attached To
Mode
D19135: Manage log locks on the Log object to prepare for multiple writers
Attached
Detach File
Event Timeline
Log In to Comment