Skip to content

Commit 6321ebb

Browse files
authored
Merge pull request #576 from nextcloud/feat/concurrent-pre-generate
feat: allow running multiple instances of pre-generate
2 parents 45b9ae2 + 81b47e3 commit 6321ebb

File tree

1 file changed

+29
-56
lines changed

1 file changed

+29
-56
lines changed

lib/Command/PreGenerate.php

Lines changed: 29 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
use OCA\PreviewGenerator\Service\NoMediaService;
1313
use OCA\PreviewGenerator\SizeHelper;
14+
use OCP\AppFramework\Db\TTransactional;
1415
use OCP\AppFramework\Utility\ITimeFactory;
1516
use OCP\Encryption\IManager;
1617
use OCP\Files\File;
@@ -26,6 +27,8 @@
2627
use Symfony\Component\Console\Output\OutputInterface;
2728

2829
class PreGenerate extends Command {
30+
use TTransactional;
31+
2932
/* @return array{width: int, height: int, crop: bool} */
3033
protected array $specifications;
3134

@@ -87,13 +90,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
8790
return 1;
8891
}
8992

90-
if ($this->checkAlreadyRunning()) {
91-
$output->writeln('Command is already running.');
92-
return 2;
93-
}
94-
95-
$this->setPID();
96-
9793
// Set timestamp output
9894
$formatter = new TimestampFormatter($this->config, $output->getFormatter());
9995
$output->setFormatter($formatter);
@@ -105,38 +101,44 @@ protected function execute(InputInterface $input, OutputInterface $output): int
105101
}
106102
$this->startProcessing();
107103

108-
$this->clearPID();
109-
110104
return 0;
111105
}
112106

113107
private function startProcessing(): void {
114108
while (true) {
115-
$qb = $this->connection->getQueryBuilder();
116-
$qb->select('*')
117-
->from('preview_generation')
118-
->orderBy('id')
119-
->setMaxResults(1000);
120-
$cursor = $qb->executeQuery();
121-
$rows = $cursor->fetchAll();
122-
$cursor->closeCursor();
123-
124-
if ($rows === []) {
125-
break;
126-
}
109+
/*
110+
* Get and delete the row so that if preview generation fails for some reason the next
111+
* run can just continue. Wrap in transaction to make sure that one row is handled by
112+
* one process only.
113+
*/
114+
$row = $this->atomic(function () {
115+
$qb = $this->connection->getQueryBuilder();
116+
$qb->select('*')
117+
->from('preview_generation')
118+
->orderBy('id')
119+
->setMaxResults(1);
120+
$result = $qb->executeQuery();
121+
$row = $result->fetch();
122+
$result->closeCursor();
123+
124+
if (!$row) {
125+
return null;
126+
}
127127

128-
foreach ($rows as $row) {
129-
/*
130-
* First delete the row so that if preview generation fails for some reason
131-
* the next run can just continue
132-
*/
133128
$qb = $this->connection->getQueryBuilder();
134129
$qb->delete('preview_generation')
135130
->where($qb->expr()->eq('id', $qb->createNamedParameter($row['id'])));
136131
$qb->executeStatement();
137132

138-
$this->processRow($row);
133+
return $row;
134+
}, $this->connection);
135+
136+
137+
if (!$row) {
138+
break;
139139
}
140+
141+
$this->processRow($row);
140142
}
141143
}
142144

@@ -198,33 +200,4 @@ private function processFile(File $file): void {
198200
}
199201
}
200202
}
201-
202-
private function setPID(): void {
203-
$this->config->setAppValue($this->appName, 'pid', posix_getpid());
204-
}
205-
206-
private function clearPID(): void {
207-
$this->config->deleteAppValue($this->appName, 'pid');
208-
}
209-
210-
private function getPID(): int {
211-
return (int)$this->config->getAppValue($this->appName, 'pid', -1);
212-
}
213-
214-
private function checkAlreadyRunning(): bool {
215-
$pid = $this->getPID();
216-
217-
// No PID set so just continue
218-
if ($pid === -1) {
219-
return false;
220-
}
221-
222-
// Get the gid of non running processes so continue
223-
if (posix_getpgid($pid) === false) {
224-
return false;
225-
}
226-
227-
// Seems there is already a running process generating previews
228-
return true;
229-
}
230203
}

0 commit comments

Comments
 (0)