aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/DB/QueryBuilder
diff options
context:
space:
mode:
authorRobin Appelman <robin@icewind.nl>2024-10-10 17:00:09 +0200
committerbackportbot[bot] <backportbot[bot]@users.noreply.github.com>2025-02-25 21:43:54 +0000
commitea678c02802c2ab6f5eea3fa5235521a2778e548 (patch)
tree681de821578f8e9d3f31624fb46aad2e3ed59ee1 /lib/private/DB/QueryBuilder
parent77db5c76e8338c410afc2def3ca45575277569e7 (diff)
downloadnextcloud-server-backport/48795/stable31.tar.gz
nextcloud-server-backport/48795/stable31.zip
feat: support migrating an instance to shardingbackport/48795/stable31
Signed-off-by: Robin Appelman <robin@icewind.nl>
Diffstat (limited to 'lib/private/DB/QueryBuilder')
-rw-r--r--lib/private/DB/QueryBuilder/Sharded/AutoIncrementHandler.php21
-rw-r--r--lib/private/DB/QueryBuilder/Sharded/ShardConnectionManager.php11
-rw-r--r--lib/private/DB/QueryBuilder/Sharded/ShardDefinition.php22
-rw-r--r--lib/private/DB/QueryBuilder/Sharded/ShardQueryRunner.php3
4 files changed, 43 insertions, 14 deletions
diff --git a/lib/private/DB/QueryBuilder/Sharded/AutoIncrementHandler.php b/lib/private/DB/QueryBuilder/Sharded/AutoIncrementHandler.php
index caedaa71c97..3a230ea544d 100644
--- a/lib/private/DB/QueryBuilder/Sharded/AutoIncrementHandler.php
+++ b/lib/private/DB/QueryBuilder/Sharded/AutoIncrementHandler.php
@@ -108,7 +108,7 @@ class AutoIncrementHandler {
}
// discard the encoded initial shard
- $current = $this->getMaxFromDb($shardDefinition) >> 8;
+ $current = $this->getMaxFromDb($shardDefinition);
$next = max($current, self::MIN_VALID_KEY) + 1;
if ($cache->cas($shardDefinition->table, 'empty-placeholder', $next)) {
return $next;
@@ -131,19 +131,22 @@ class AutoIncrementHandler {
}
/**
- * Get the maximum primary key value from the shards
+ * Get the maximum primary key value from the shards, note that this has already stripped any embedded shard id
*/
private function getMaxFromDb(ShardDefinition $shardDefinition): int {
- $max = 0;
+ $max = $shardDefinition->fromFileId;
+ $query = $this->shardConnectionManager->getConnection($shardDefinition, 0)->getQueryBuilder();
+ $query->select($shardDefinition->primaryKey)
+ ->from($shardDefinition->table)
+ ->orderBy($shardDefinition->primaryKey, 'DESC')
+ ->setMaxResults(1);
foreach ($shardDefinition->getAllShards() as $shard) {
$connection = $this->shardConnectionManager->getConnection($shardDefinition, $shard);
- $query = $connection->getQueryBuilder();
- $query->select($shardDefinition->primaryKey)
- ->from($shardDefinition->table)
- ->orderBy($shardDefinition->primaryKey, 'DESC')
- ->setMaxResults(1);
- $result = $query->executeQuery()->fetchOne();
+ $result = $query->executeQuery($connection)->fetchOne();
if ($result) {
+ if ($result > $shardDefinition->fromFileId) {
+ $result = $result >> 8;
+ }
$max = max($max, $result);
}
}
diff --git a/lib/private/DB/QueryBuilder/Sharded/ShardConnectionManager.php b/lib/private/DB/QueryBuilder/Sharded/ShardConnectionManager.php
index 87cac58bc57..74358e3ca96 100644
--- a/lib/private/DB/QueryBuilder/Sharded/ShardConnectionManager.php
+++ b/lib/private/DB/QueryBuilder/Sharded/ShardConnectionManager.php
@@ -28,8 +28,17 @@ class ShardConnectionManager {
public function getConnection(ShardDefinition $shardDefinition, int $shard): IDBConnection {
$connectionKey = $shardDefinition->table . '_' . $shard;
- if (!isset($this->connections[$connectionKey])) {
+
+ if (isset($this->connections[$connectionKey])) {
+ return $this->connections[$connectionKey];
+ }
+
+ if ($shard === ShardDefinition::MIGRATION_SHARD) {
+ $this->connections[$connectionKey] = \OC::$server->get(IDBConnection::class);
+ } elseif (isset($shardDefinition->shards[$shard])) {
$this->connections[$connectionKey] = $this->createConnection($shardDefinition->shards[$shard]);
+ } else {
+ throw new \InvalidArgumentException("invalid shard key $shard only " . count($shardDefinition->shards) . ' configured');
}
return $this->connections[$connectionKey];
diff --git a/lib/private/DB/QueryBuilder/Sharded/ShardDefinition.php b/lib/private/DB/QueryBuilder/Sharded/ShardDefinition.php
index ebccbb639a6..4f98079d92d 100644
--- a/lib/private/DB/QueryBuilder/Sharded/ShardDefinition.php
+++ b/lib/private/DB/QueryBuilder/Sharded/ShardDefinition.php
@@ -15,7 +15,9 @@ use OCP\DB\QueryBuilder\Sharded\IShardMapper;
*/
class ShardDefinition {
// we reserve the bottom byte of the primary key for the initial shard, so the total shard count is limited to what we can fit there
- public const MAX_SHARDS = 256;
+ // additionally, shard id 255 is reserved for migration purposes
+ public const MAX_SHARDS = 255;
+ public const MIGRATION_SHARD = 255;
public const PRIMARY_KEY_MASK = 0x7F_FF_FF_FF_FF_FF_FF_00;
public const PRIMARY_KEY_SHARD_MASK = 0x00_00_00_00_00_00_00_FF;
@@ -37,8 +39,10 @@ class ShardDefinition {
public array $companionKeys,
public string $shardKey,
public IShardMapper $shardMapper,
- public array $companionTables = [],
- public array $shards = [],
+ public array $companionTables,
+ public array $shards,
+ public int $fromFileId,
+ public int $fromStorageId,
) {
if (count($this->shards) >= self::MAX_SHARDS) {
throw new \Exception('Only allowed maximum of ' . self::MAX_SHARDS . ' shards allowed');
@@ -53,11 +57,21 @@ class ShardDefinition {
}
public function getShardForKey(int $key): int {
+ if ($key < $this->fromStorageId) {
+ return self::MIGRATION_SHARD;
+ }
return $this->shardMapper->getShardForKey($key, count($this->shards));
}
+ /**
+ * @return list<int>
+ */
public function getAllShards(): array {
- return array_keys($this->shards);
+ if ($this->fromStorageId !== 0) {
+ return array_merge(array_keys($this->shards), [self::MIGRATION_SHARD]);
+ } else {
+ return array_keys($this->shards);
+ }
}
public function isKey(string $column): bool {
diff --git a/lib/private/DB/QueryBuilder/Sharded/ShardQueryRunner.php b/lib/private/DB/QueryBuilder/Sharded/ShardQueryRunner.php
index c020e72868e..25e2a3d5f2d 100644
--- a/lib/private/DB/QueryBuilder/Sharded/ShardQueryRunner.php
+++ b/lib/private/DB/QueryBuilder/Sharded/ShardQueryRunner.php
@@ -55,6 +55,9 @@ class ShardQueryRunner {
private function getLikelyShards(array $primaryKeys): array {
$shards = [];
foreach ($primaryKeys as $primaryKey) {
+ if ($primaryKey < $this->shardDefinition->fromFileId && !in_array(ShardDefinition::MIGRATION_SHARD, $shards)) {
+ $shards[] = ShardDefinition::MIGRATION_SHARD;
+ }
$encodedShard = $primaryKey & ShardDefinition::PRIMARY_KEY_SHARD_MASK;
if ($encodedShard < count($this->shardDefinition->shards) && !in_array($encodedShard, $shards)) {
$shards[] = $encodedShard;