Skip to content

Commit 6fe6a43

Browse files
committed
fixup! feat(jobs): introduce background job classes register
1 parent a2ffcb9 commit 6fe6a43

3 files changed

Lines changed: 40 additions & 25 deletions

File tree

‎core/Migrations/Version34000Date20260518163022.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt
2525
$changed = false;
2626
if (!$schema->hasTable('job_classes_registry')) {
2727
$table = $schema->createTable('job_classes_registry');
28-
$table->addColumn('class_id', Types::SMALLINT, [
28+
$table->addColumn('class_id', Types::BIGINT, [
2929
'autoincrement' => true,
3030
'notnull' => true,
3131
'unsigned' => true,

‎lib/private/BackgroundJob/JobClassesRegistry.php‎

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,40 +12,42 @@
1212
use InvalidArgumentException;
1313
use OCP\BackgroundJob\IJob;
1414
use OCP\IDBConnection;
15+
use OCP\Snowflake\ISnowflakeGenerator;
1516

1617
/**
1718
* Map background job classes and their ID in database
1819
*
1920
* Uses a rapid hash to speed-up lookups
2021
*/
2122
final class JobClassesRegistry {
22-
private array $register = [];
23+
private array $registry = [];
2324

2425
private const TABLE = 'job_classes_registry';
2526

2627
public function __construct(
2728
private readonly IDBConnection $connection,
29+
private readonly ISnowflakeGenerator $snowflakeGenerator,
2830
) {
2931
}
3032

3133
private function loadRegistry(): void {
32-
if ($this->register !== []) {
34+
if ($this->registry !== []) {
3335
return;
3436
}
3537
$qb = $this->connection->getQueryBuilder();
3638
$result = $qb->select('class_id', 'class_name')->from(self::TABLE)->executeQuery();
3739
foreach ($result->iterateAssociative() as $row) {
38-
$this->register[$row['class_name']] = (int)$row['class_id'];
40+
$this->registry[$row['class_name']] = (string)$row['class_id'];
3941
}
4042
}
4143

4244
/**
4345
* Resolve current ID or generates a new one
4446
*/
45-
public function getId(string $className): int {
47+
public function getId(string $className): string {
4648
$this->loadRegistry();
47-
if (isset($this->register[$className])) {
48-
return $this->register[$className];
49+
if (isset($this->registry[$className])) {
50+
return $this->registry[$className];
4951
}
5052

5153
if (!class_exists($className)) {
@@ -58,12 +60,18 @@ public function getId(string $className): int {
5860
$qb = $this->connection->getQueryBuilder();
5961
$hashedName = $this->hashName($className);
6062
try {
63+
$classId = $this->snowflakeGenerator->nextId();
6164
$qb
6265
->insert(self::TABLE)
63-
->setValue('class_name', $qb->expr()->literal($className))
64-
->setValue('class_hash', $qb->expr()->literal($hashedName))
66+
->values([
67+
'class_id' => $qb->createNamedParameter($classId),
68+
'class_name' => $qb->createNamedParameter($className),
69+
'class_hash' => $qb->createNamedParameter($hashedName),
70+
])
6571
->executeStatement();
66-
return $qb->getLastInsertId();
72+
$this->registry[$className] = $classId;
73+
74+
return $classId;
6775
} catch (UniqueConstraintViolationException) {
6876
// Class was probably added by a concurrent process
6977
// Try to load it
@@ -74,16 +82,20 @@ public function getId(string $className): int {
7482
->andWhere($qb->expr()->eq('class_name', $className))
7583
->executeQuery();
7684
if ($classId = $result->fetchOne()) {
77-
return (int)$classId;
85+
$classId = (string)$classId;
86+
$this->registry[$className] = $classId;
87+
88+
return $classId;
7889
}
7990
}
8091

8192
throw new \Exception('Fail to retrieve ' . $className . ' ID');
8293
}
8394

84-
public function getName(int $classId): string {
95+
public function getName(string|int $classId): string {
8596
$this->loadRegistry();
86-
$className = array_search($classId, $this->register, true);
97+
$classId = (string)$classId;
98+
$className = array_search($classId, $this->registry, true);
8799
if ($className === false) {
88100
throw new InvalidArgumentException('Class ID ' . $classId . ' doesn’t match any class name');
89101
}

‎tests/lib/BackgroundJob/JobClassesRegistryTest.php‎

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use OC\BackgroundJob\JobClassesRegistry;
1414
use OCP\IDBConnection;
1515
use OCP\Server;
16+
use OCP\Snowflake\ISnowflakeGenerator;
1617
use Override;
1718
use Test\TestCase;
1819

@@ -21,51 +22,53 @@
2122
*/
2223
#[\PHPUnit\Framework\Attributes\Group('DB')]
2324
class JobClassesRegistryTest extends TestCase {
24-
private IDBConnection $connection;
25-
private JobClassesRegistry $register;
25+
private readonly IDBConnection $connection;
26+
private readonly ISnowflakeGenerator $snowflakeGenerator;
27+
private JobClassesRegistry $registry;
2628

2729
#[Override]
2830
protected function setUp(): void {
2931
parent::setUp();
3032

3133
$this->connection = Server::get(IDBConnection::class);
32-
$this->register = new JobClassesRegistry($this->connection);
34+
$this->snowflakeGenerator = Server::get(ISnowflakeGenerator::class);
35+
$this->registry = new JobClassesRegistry($this->connection, $this->snowflakeGenerator);
3336
}
3437

3538
public function testResolveNonExistingClass() {
3639
$className = 'invalid_class_name_122278';
3740

3841
$this->expectException(InvalidArgumentException::class);
3942
$this->expectExceptionMessage('Class ' . $className . ' doesn’t exists');
40-
$this->register->getId($className);
43+
$this->registry->getId($className);
4144
}
4245

4346
public function testResolveInvalidClass() {
4447
$className = self::class;
4548

4649
$this->expectException(InvalidArgumentException::class);
4750
$this->expectExceptionMessage('Class ' . $className . ' isn’t an instance of OCP\BackgroundJob\IJob');
48-
$this->register->getId($className);
51+
$this->registry->getId($className);
4952
}
5053

5154
public function testResolveValidClass() {
5255
$className = DummyJob::class;
5356

54-
$classId = $this->register->getId($className);
55-
$this->assertIsInt($classId);
57+
$classId = $this->registry->getId($className);
58+
$this->assertIsString($classId);
5659
$this->assertGreaterThan(0, $classId);
5760

5861
// Renew register. ID should stay the same
59-
$this->register = new JobClassesRegistry($this->connection);
60-
$newId = $this->register->getId($className);
62+
$this->registry = new JobClassesRegistry($this->connection, $this->snowflakeGenerator);
63+
$newId = $this->registry->getId($className);
6164
$this->assertEquals($classId, $newId);
6265
}
6366

6467
public function testResolveValidId() {
6568
$className = DummyJob::class;
6669

67-
$classId = $this->register->getId($className);
68-
$resolvedClass = $this->register->getName($classId);
70+
$classId = $this->registry->getId($className);
71+
$resolvedClass = $this->registry->getName($classId);
6972

7073
$this->assertEquals($className, $resolvedClass);
7174
}
@@ -74,6 +77,6 @@ public function testResolveInvalidId() {
7477
$classId = PHP_INT_MAX;
7578
$this->expectException(InvalidArgumentException::class);
7679
$this->expectExceptionMessage('Class ID ' . $classId . ' doesn’t match any class name');
77-
$this->register->getName($classId);
80+
$this->registry->getName($classId);
7881
}
7982
}

0 commit comments

Comments
 (0)