Skip to content

Commit 9d30c67

Browse files
committed
feat(config): add serverid configuration and use it for Snowflakes
Signed-off-by: Benjamin Gaussorgues <[email protected]>
1 parent da13836 commit 9d30c67

File tree

7 files changed

+96
-3
lines changed

7 files changed

+96
-3
lines changed

apps/settings/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@
132132
'OCA\\Settings\\SetupChecks\\ReadOnlyConfig' => $baseDir . '/../lib/SetupChecks/ReadOnlyConfig.php',
133133
'OCA\\Settings\\SetupChecks\\SchedulingTableSize' => $baseDir . '/../lib/SetupChecks/SchedulingTableSize.php',
134134
'OCA\\Settings\\SetupChecks\\SecurityHeaders' => $baseDir . '/../lib/SetupChecks/SecurityHeaders.php',
135+
'OCA\\Settings\\SetupChecks\\ServerIdConfig' => $baseDir . '/../lib/SetupChecks/ServerIdConfig.php',
135136
'OCA\\Settings\\SetupChecks\\SupportedDatabase' => $baseDir . '/../lib/SetupChecks/SupportedDatabase.php',
136137
'OCA\\Settings\\SetupChecks\\SystemIs64bit' => $baseDir . '/../lib/SetupChecks/SystemIs64bit.php',
137138
'OCA\\Settings\\SetupChecks\\TaskProcessingPickupSpeed' => $baseDir . '/../lib/SetupChecks/TaskProcessingPickupSpeed.php',

apps/settings/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ class ComposerStaticInitSettings
147147
'OCA\\Settings\\SetupChecks\\ReadOnlyConfig' => __DIR__ . '/..' . '/../lib/SetupChecks/ReadOnlyConfig.php',
148148
'OCA\\Settings\\SetupChecks\\SchedulingTableSize' => __DIR__ . '/..' . '/../lib/SetupChecks/SchedulingTableSize.php',
149149
'OCA\\Settings\\SetupChecks\\SecurityHeaders' => __DIR__ . '/..' . '/../lib/SetupChecks/SecurityHeaders.php',
150+
'OCA\\Settings\\SetupChecks\\ServerIdConfig' => __DIR__ . '/..' . '/../lib/SetupChecks/ServerIdConfig.php',
150151
'OCA\\Settings\\SetupChecks\\SupportedDatabase' => __DIR__ . '/..' . '/../lib/SetupChecks/SupportedDatabase.php',
151152
'OCA\\Settings\\SetupChecks\\SystemIs64bit' => __DIR__ . '/..' . '/../lib/SetupChecks/SystemIs64bit.php',
152153
'OCA\\Settings\\SetupChecks\\TaskProcessingPickupSpeed' => __DIR__ . '/..' . '/../lib/SetupChecks/TaskProcessingPickupSpeed.php',

apps/settings/lib/AppInfo/Application.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
use OCA\Settings\SetupChecks\ReadOnlyConfig;
7070
use OCA\Settings\SetupChecks\SchedulingTableSize;
7171
use OCA\Settings\SetupChecks\SecurityHeaders;
72+
use OCA\Settings\SetupChecks\ServerIdConfig;
7273
use OCA\Settings\SetupChecks\SupportedDatabase;
7374
use OCA\Settings\SetupChecks\SystemIs64bit;
7475
use OCA\Settings\SetupChecks\TaskProcessingPickupSpeed;
@@ -207,6 +208,7 @@ public function register(IRegistrationContext $context): void {
207208
$context->registerSetupCheck(RandomnessSecure::class);
208209
$context->registerSetupCheck(ReadOnlyConfig::class);
209210
$context->registerSetupCheck(SecurityHeaders::class);
211+
$context->registerSetupCheck(ServerIdConfig::class);
210212
$context->registerSetupCheck(SchedulingTableSize::class);
211213
$context->registerSetupCheck(SupportedDatabase::class);
212214
$context->registerSetupCheck(SystemIs64bit::class);
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
namespace OCA\Settings\SetupChecks;
10+
11+
use OCP\IConfig;
12+
use OCP\IL10N;
13+
use OCP\IURLGenerator;
14+
use OCP\SetupCheck\ISetupCheck;
15+
use OCP\SetupCheck\SetupResult;
16+
use Override;
17+
18+
final class ServerIdConfig implements ISetupCheck {
19+
public function __construct(
20+
private readonly IL10N $l10n,
21+
private readonly IConfig $config,
22+
private readonly IURLGenerator $urlGenerator,
23+
) {
24+
}
25+
26+
#[Override]
27+
public function getName(): string {
28+
return $this->l10n->t('Configuration server ID');
29+
}
30+
31+
#[Override]
32+
public function getCategory(): string {
33+
return 'config';
34+
}
35+
36+
#[Override]
37+
public function run(): SetupResult {
38+
$serverid = $this->config->getSystemValueInt('serverid', PHP_INT_MIN);
39+
$linkToDoc = $this->urlGenerator->linkToDocs('admin-update');
40+
41+
if ($serverid === PHP_INT_MIN) {
42+
return SetupResult::info(
43+
$this->l10n->t('server identifier isn’t configured. It is recommended if your Nextcloud instance is running on several PHP servers. Add a serverid in your configuration.'),
44+
$linkToDoc,
45+
);
46+
}
47+
48+
if ($serverid < 0 || $serverid > 1023) {
49+
return SetupResult::error(
50+
$this->l10n->t('"%d" is not a valid server identifier. It must be between 0 and 1023.', [$serverid]),
51+
$linkToDoc,
52+
);
53+
}
54+
55+
return SetupResult::success($this->l10n->t('server identifier is configured and valid.'));
56+
}
57+
}

config/config.sample.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,20 @@
4545
*/
4646
'instanceid' => '',
4747

48+
/**
49+
* This is a unique identifier for your server.
50+
* It is useful when your Nextcloud instance is using different PHP servers.
51+
* Once it's set it shouldn't be changed.
52+
*
53+
* Value must be an integer, comprised between 0 and 1023.
54+
*
55+
* This value should be overriden on each server with "NC_serverid=<value>"
56+
* Be careful, it must be overriden for CLI and for your webserver.
57+
*
58+
* Example for CLI: NC_serverid=42 occ config:list system
59+
*/
60+
'serverid' => -1,
61+
4862
/**
4963
* The salt used to hash all passwords, auto-generated by the Nextcloud
5064
* installer. (There are also per-user salts.) If you lose this salt, you lose

lib/private/Snowflake/Generator.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
namespace OC\Snowflake;
1111

1212
use OCP\AppFramework\Utility\ITimeFactory;
13+
use OCP\IConfig;
1314
use OCP\Snowflake\IGenerator;
1415
use Override;
1516

@@ -23,6 +24,7 @@
2324
final class Generator implements IGenerator {
2425
public function __construct(
2526
private readonly ITimeFactory $timeFactory,
27+
private readonly IConfig $config,
2628
) {
2729
}
2830

@@ -99,8 +101,14 @@ private function getCurrentTime(): array {
99101
];
100102
}
101103

104+
/**
105+
* Return configured serverid or generate one if not set
106+
*/
102107
private function getServerId(): int {
103-
return crc32(gethostname() ?: random_bytes(8));
108+
$serverid = $this->config->getSystemValueInt('serverid', -1);
109+
return $serverid > 0
110+
? $serverid
111+
: crc32(gethostname() ?: random_bytes(8));
104112
}
105113

106114
private function isCli(): bool {

tests/lib/Snowflake/GeneratorTest.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use OC\Snowflake\Decoder;
1414
use OC\Snowflake\Generator;
1515
use OCP\AppFramework\Utility\ITimeFactory;
16+
use OCP\IConfig;
1617
use OCP\Snowflake\IGenerator;
1718
use PHPUnit\Framework\Attributes\DataProvider;
1819
use Test\TestCase;
@@ -22,12 +23,17 @@
2223
*/
2324
class GeneratorTest extends TestCase {
2425
private Decoder $decoder;
26+
private IConfig|MockObject $config;
2527

2628
public function setUp():void {
2729
$this->decoder = new Decoder();
30+
$this->config = $this->createMock(IConfig::class);
31+
$this->config->method('getSystemValueInt')
32+
->with('serverid')
33+
->willReturn(42);
2834
}
2935
public function testGenerator(): void {
30-
$generator = new Generator(new TimeFactory());
36+
$generator = new Generator(new TimeFactory(), $this->config);
3137
$snowflakeId = $generator->nextId();
3238
$data = $this->decoder->decode($generator->nextId());
3339

@@ -45,6 +51,9 @@ public function testGenerator(): void {
4551

4652
// Check CLI
4753
$this->assertTrue($data['isCli']);
54+
55+
// Check serverId
56+
$this->assertEquals(42, $data['serverId']);
4857
}
4958

5059
#[DataProvider('provideSnowflakeData')]
@@ -53,11 +62,12 @@ public function testGeneratorWithFixedTime(string $date, int $expectedSeconds, i
5362
$timeFactory = $this->createMock(ITimeFactory::class);
5463
$timeFactory->method('now')->willReturn($dt);
5564

56-
$generator = new Generator($timeFactory);
65+
$generator = new Generator($timeFactory, $this->config);
5766
$data = $this->decoder->decode($generator->nextId());
5867

5968
$this->assertEquals($expectedSeconds, ($data['createdAt']->format('U') - IGenerator::TS_OFFSET));
6069
$this->assertEquals($expectedMilliseconds, (int)$data['createdAt']->format('v'));
70+
$this->assertEquals(42, $data['serverId']);
6171
}
6272

6373
public static function provideSnowflakeData(): array {

0 commit comments

Comments
 (0)