Merge branch 'debug-whitelist-console' of https://github.com/appwrite/appwrite into multi-region-support

This commit is contained in:
shimon 2024-11-21 22:00:17 +02:00
commit 3d333633e4
11 changed files with 163 additions and 141 deletions

View file

@ -127,6 +127,11 @@ jobs:
Messaging, Messaging,
Migrations Migrations
] ]
tables-mode: [
'Project',
'Shared V1',
'Shared V2',
]
steps: steps:
- name: checkout - name: checkout
@ -145,11 +150,11 @@ jobs:
docker compose up -d docker compose up -d
sleep 30 sleep 30
- name: Run ${{matrix.service}} Tests - name: Run ${{ matrix.service }} tests with ${{ matrix.tables-mode }} table mode
run: docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{matrix.service}} --debug run: docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug
env:
- name: Run ${{matrix.service}} Shared Tables Tests _APP_DATABASE_SHARED_TABLES: ${{ matrix.table_mode == 'Shared V1' || matrix.table_mode == 'Shared V2' && 'database_db_main' || '' }}
run: _APP_DATABASE_SHARED_TABLES=database_db_main docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{matrix.service}} --debug _APP_DATABASE_SHARED_TABLES_V1: ${{ matrix.table_mode == 'Shared V1' && 'database_db_main' || '' }}
benchmarking: benchmarking:
name: Benchmark name: Benchmark

View file

@ -686,7 +686,7 @@ return [
], ],
Exception::ATTRIBUTE_LIMIT_EXCEEDED => [ Exception::ATTRIBUTE_LIMIT_EXCEEDED => [
'name' => Exception::ATTRIBUTE_LIMIT_EXCEEDED, 'name' => Exception::ATTRIBUTE_LIMIT_EXCEEDED,
'description' => 'The maximum number of attributes has been reached.', 'description' => 'The maximum number or size of attributes for this collection has been reached.',
'code' => 400, 'code' => 400,
], ],
Exception::ATTRIBUTE_VALUE_INVALID => [ Exception::ATTRIBUTE_VALUE_INVALID => [

View file

@ -153,7 +153,7 @@ function createAttribute(string $databaseId, string $collectionId, Document $att
} catch (DuplicateException) { } catch (DuplicateException) {
throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS); throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS);
} catch (LimitException) { } catch (LimitException) {
throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED, 'Attribute limit exceeded'); throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED);
} catch (\Throwable $e) { } catch (\Throwable $e) {
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collectionId); $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collectionId);
$dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $collection->getInternalId()); $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $collection->getInternalId());
@ -197,7 +197,7 @@ function createAttribute(string $databaseId, string $collectionId, Document $att
throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS); throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS);
} catch (LimitException) { } catch (LimitException) {
$dbForProject->deleteDocument('attributes', $attribute->getId()); $dbForProject->deleteDocument('attributes', $attribute->getId());
throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED, 'Attribute limit exceeded'); throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED);
} catch (\Throwable $e) { } catch (\Throwable $e) {
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedCollection->getId()); $dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedCollection->getId());
$dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedCollection->getInternalId()); $dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedCollection->getInternalId());
@ -393,6 +393,8 @@ function updateAttribute(
throw new Exception(Exception::ATTRIBUTE_INVALID_RESIZE); throw new Exception(Exception::ATTRIBUTE_INVALID_RESIZE);
} catch (NotFoundException) { } catch (NotFoundException) {
throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); throw new Exception(Exception::ATTRIBUTE_NOT_FOUND);
} catch (LimitException) {
throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED);
} }
} }
@ -2627,7 +2629,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes')
$validator = new IndexValidator( $validator = new IndexValidator(
$collection->getAttribute('attributes'), $collection->getAttribute('attributes'),
$dbForProject->getAdapter()->getMaxIndexLength() $dbForProject->getAdapter()->getMaxIndexLength(),
$dbForProject->getAdapter()->getInternalIndexesKeys(),
); );
if (!$validator->isValid($index)) { if (!$validator->isValid($index)) {
throw new Exception(Exception::INDEX_INVALID, $validator->getDescription()); throw new Exception(Exception::INDEX_INVALID, $validator->getDescription());

View file

@ -136,6 +136,16 @@ App::post('/v1/projects')
$dsn = $databases[array_rand($databases)]; $dsn = $databases[array_rand($databases)];
} }
$dsns = [
'fra' => 'database_db_fra1_self_hosted_0_0',
'syd' => 'database_db_syd1_self_hosted_0_0',
'nyc' => 'database_db_nyc1_self_hosted_0_0',
];
if (isset($dsns[$region])) {
$dsn = $dsns[$region];
}
// TODO: Temporary until all projects are using shared tables. // TODO: Temporary until all projects are using shared tables.
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));

View file

@ -63,7 +63,7 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
} }
if (System::getEnv('_APP_OPTIONS_ROUTER_PROTECTION', 'disabled') === 'enabled') { if (System::getEnv('_APP_OPTIONS_ROUTER_PROTECTION', 'disabled') === 'enabled') {
if ($host !== 'localhost' && $host !== APP_HOSTNAME_INTERNAL) { // localhost allowed for proxy, APP_HOSTNAME_INTERNAL allowed for migrations if ($host !== 'localhost' && $host !== APP_HOSTNAME_INTERNAL && $host !== System::getEnv('_APP_CONSOLE_DOMAIN', '')) {
throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'Router protection does not allow accessing Appwrite over this domain. Please add it as custom domain to your project or disable _APP_OPTIONS_ROUTER_PROTECTION environment variable.'); throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'Router protection does not allow accessing Appwrite over this domain. Please add it as custom domain to your project or disable _APP_OPTIONS_ROUTER_PROTECTION environment variable.');
} }
} }
@ -438,7 +438,7 @@ App::init()
}); });
App::init() App::init()
->groups(['api', 'web']) ->groups(['api'])
->inject('utopia') ->inject('utopia')
->inject('swooleRequest') ->inject('swooleRequest')
->inject('request') ->inject('request')

View file

@ -61,8 +61,6 @@ include __DIR__ . '/controllers/general.php';
$http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $register) { $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $register) {
$app = new App('UTC'); $app = new App('UTC');
$app->setCompression(true);
$app->setCompressionMinSize(intval(System::getEnv('_APP_COMPRESSION_MIN_SIZE_BYTES', '1024'))); // 1KB
go(function () use ($register, $app) { go(function () use ($register, $app) {
$pools = $register->get('pools'); $pools = $register->get('pools');

View file

@ -51,7 +51,7 @@
"utopia-php/cache": "0.11.*", "utopia-php/cache": "0.11.*",
"utopia-php/cli": "0.15.*", "utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*", "utopia-php/config": "0.2.*",
"utopia-php/database": "0.53.18", "utopia-php/database": "0.53.20",
"utopia-php/domains": "0.5.*", "utopia-php/domains": "0.5.*",
"utopia-php/dsn": "0.2.1", "utopia-php/dsn": "0.2.1",
"utopia-php/framework": "0.33.*", "utopia-php/framework": "0.33.*",

52
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "bd9b8f5f8fe295deb07002ca0a953949", "content-hash": "217b0c1b6c156d51bf5a2674f87a7630",
"packages": [ "packages": [
{ {
"name": "adhocore/jwt", "name": "adhocore/jwt",
@ -1770,16 +1770,16 @@
}, },
{ {
"name": "utopia-php/database", "name": "utopia-php/database",
"version": "0.53.18", "version": "0.53.20",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/utopia-php/database.git", "url": "https://github.com/utopia-php/database.git",
"reference": "895176b61b969d326bf2e36695606b8a33132094" "reference": "e43f8ee26e06ee8812737e63642dbd7ee7c9dc04"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/895176b61b969d326bf2e36695606b8a33132094", "url": "https://api.github.com/repos/utopia-php/database/zipball/e43f8ee26e06ee8812737e63642dbd7ee7c9dc04",
"reference": "895176b61b969d326bf2e36695606b8a33132094", "reference": "e43f8ee26e06ee8812737e63642dbd7ee7c9dc04",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1820,9 +1820,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/utopia-php/database/issues", "issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.53.18" "source": "https://github.com/utopia-php/database/tree/0.53.20"
}, },
"time": "2024-11-08T04:02:13+00:00" "time": "2024-11-12T00:23:36+00:00"
}, },
{ {
"name": "utopia-php/domains", "name": "utopia-php/domains",
@ -2222,16 +2222,16 @@
}, },
{ {
"name": "utopia-php/migration", "name": "utopia-php/migration",
"version": "0.6.11", "version": "0.6.12",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/utopia-php/migration.git", "url": "https://github.com/utopia-php/migration.git",
"reference": "4d167914d3f7fa1fe816b2b2c6f221e70166bfd7" "reference": "9a8c905af4cece5c5ec9542a5b534befce067260"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/4d167914d3f7fa1fe816b2b2c6f221e70166bfd7", "url": "https://api.github.com/repos/utopia-php/migration/zipball/9a8c905af4cece5c5ec9542a5b534befce067260",
"reference": "4d167914d3f7fa1fe816b2b2c6f221e70166bfd7", "reference": "9a8c905af4cece5c5ec9542a5b534befce067260",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2272,9 +2272,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/utopia-php/migration/issues", "issues": "https://github.com/utopia-php/migration/issues",
"source": "https://github.com/utopia-php/migration/tree/0.6.11" "source": "https://github.com/utopia-php/migration/tree/0.6.12"
}, },
"time": "2024-10-31T06:19:57+00:00" "time": "2024-11-12T00:31:53+00:00"
}, },
{ {
"name": "utopia-php/mongo", "name": "utopia-php/mongo",
@ -2542,16 +2542,16 @@
}, },
{ {
"name": "utopia-php/queue", "name": "utopia-php/queue",
"version": "0.7.1", "version": "0.7.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/utopia-php/queue.git", "url": "https://github.com/utopia-php/queue.git",
"reference": "94c240d9f6383829807ce7b2d737f04b159fd3e8" "reference": "40fdd9799d0a11dd33fca06f8223032a47dce2f6"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/utopia-php/queue/zipball/94c240d9f6383829807ce7b2d737f04b159fd3e8", "url": "https://api.github.com/repos/utopia-php/queue/zipball/40fdd9799d0a11dd33fca06f8223032a47dce2f6",
"reference": "94c240d9f6383829807ce7b2d737f04b159fd3e8", "reference": "40fdd9799d0a11dd33fca06f8223032a47dce2f6",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2597,9 +2597,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/utopia-php/queue/issues", "issues": "https://github.com/utopia-php/queue/issues",
"source": "https://github.com/utopia-php/queue/tree/0.7.1" "source": "https://github.com/utopia-php/queue/tree/0.7.2"
}, },
"time": "2024-11-05T17:00:38+00:00" "time": "2024-11-11T10:04:02+00:00"
}, },
{ {
"name": "utopia-php/registry", "name": "utopia-php/registry",
@ -2817,16 +2817,16 @@
}, },
{ {
"name": "utopia-php/vcs", "name": "utopia-php/vcs",
"version": "0.8.3", "version": "0.8.5",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/utopia-php/vcs.git", "url": "https://github.com/utopia-php/vcs.git",
"reference": "a032ed0611a8f4467aeaa9484f73223074457337" "reference": "7622330628d53844a3873ca873338150756bab82"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/a032ed0611a8f4467aeaa9484f73223074457337", "url": "https://api.github.com/repos/utopia-php/vcs/zipball/7622330628d53844a3873ca873338150756bab82",
"reference": "a032ed0611a8f4467aeaa9484f73223074457337", "reference": "7622330628d53844a3873ca873338150756bab82",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2860,9 +2860,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/utopia-php/vcs/issues", "issues": "https://github.com/utopia-php/vcs/issues",
"source": "https://github.com/utopia-php/vcs/tree/0.8.3" "source": "https://github.com/utopia-php/vcs/tree/0.8.5"
}, },
"time": "2024-11-05T17:10:09+00:00" "time": "2024-11-11T18:33:10+00:00"
}, },
{ {
"name": "utopia-php/websocket", "name": "utopia-php/websocket",
@ -7052,7 +7052,7 @@
], ],
"aliases": [], "aliases": [],
"minimum-stability": "stable", "minimum-stability": "stable",
"stability-flags": [], "stability-flags": {},
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {

View file

@ -92,6 +92,7 @@ class Databases extends Action
* @throws Authorization * @throws Authorization
* @throws Conflict * @throws Conflict
* @throws \Exception * @throws \Exception
* @throws \Throwable
*/ */
private function createAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForConsole, Database $dbForProject): void private function createAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForConsole, Database $dbForProject): void
{ {
@ -169,7 +170,6 @@ class Databases extends Action
$dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'available')); $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'available'));
} catch (\Throwable $e) { } catch (\Throwable $e) {
// TODO: Send non DatabaseExceptions to Sentry
Console::error($e->getMessage()); Console::error($e->getMessage());
if ($e instanceof DatabaseException) { if ($e instanceof DatabaseException) {
@ -192,15 +192,17 @@ class Databases extends Action
$relatedAttribute->setAttribute('status', 'failed') $relatedAttribute->setAttribute('status', 'failed')
); );
} }
throw $e;
} finally { } finally {
$this->trigger($database, $collection, $attribute, $project, $projectId, $events); $this->trigger($database, $collection, $attribute, $project, $projectId, $events);
}
if ($type === Database::VAR_RELATIONSHIP && $options['twoWay']) { if ($type === Database::VAR_RELATIONSHIP && $options['twoWay']) {
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId()); $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId());
} }
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId);
}
} }
/** /**
@ -214,6 +216,7 @@ class Databases extends Action
* @throws Authorization * @throws Authorization
* @throws Conflict * @throws Conflict
* @throws \Exception * @throws \Exception
* @throws \Throwable
**/ **/
private function deleteAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForConsole, Database $dbForProject): void private function deleteAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForConsole, Database $dbForProject): void
{ {
@ -247,113 +250,116 @@ class Databases extends Action
// - stuck: attribute was available but cannot be removed // - stuck: attribute was available but cannot be removed
try { try {
if ($status !== 'failed') { try {
if ($type === Database::VAR_RELATIONSHIP) { if ($status !== 'failed') {
if ($options['twoWay']) { if ($type === Database::VAR_RELATIONSHIP) {
$relatedCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']); if ($options['twoWay']) {
if ($relatedCollection->isEmpty()) { $relatedCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']);
throw new DatabaseException('Collection not found'); if ($relatedCollection->isEmpty()) {
throw new DatabaseException('Collection not found');
}
$relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']);
} }
$relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']);
}
if (!$dbForProject->deleteRelationship('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { if (!$dbForProject->deleteRelationship('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) {
$dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'stuck')); $dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'stuck'));
throw new DatabaseException('Failed to delete Relationship'); throw new DatabaseException('Failed to delete Relationship');
}
} elseif (!$dbForProject->deleteAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) {
throw new DatabaseException('Failed to delete Attribute');
} }
} elseif (!$dbForProject->deleteAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) {
throw new DatabaseException('Failed to delete Attribute');
} }
}
$dbForProject->deleteDocument('attributes', $attribute->getId()); $dbForProject->deleteDocument('attributes', $attribute->getId());
if (!$relatedAttribute->isEmpty()) {
$dbForProject->deleteDocument('attributes', $relatedAttribute->getId());
}
} catch (\Throwable $e) {
// TODO: Send non DatabaseExceptions to Sentry
Console::error($e->getMessage());
if ($e instanceof DatabaseException) {
$attribute->setAttribute('error', $e->getMessage());
if (!$relatedAttribute->isEmpty()) { if (!$relatedAttribute->isEmpty()) {
$relatedAttribute->setAttribute('error', $e->getMessage()); $dbForProject->deleteDocument('attributes', $relatedAttribute->getId());
}
} catch (\Throwable $e) {
Console::error($e->getMessage());
if ($e instanceof DatabaseException) {
$attribute->setAttribute('error', $e->getMessage());
if (!$relatedAttribute->isEmpty()) {
$relatedAttribute->setAttribute('error', $e->getMessage());
}
} }
}
$dbForProject->updateDocument(
'attributes',
$attribute->getId(),
$attribute->setAttribute('status', 'stuck')
);
if (!$relatedAttribute->isEmpty()) {
$dbForProject->updateDocument( $dbForProject->updateDocument(
'attributes', 'attributes',
$relatedAttribute->getId(), $attribute->getId(),
$relatedAttribute->setAttribute('status', 'stuck') $attribute->setAttribute('status', 'stuck')
); );
if (!$relatedAttribute->isEmpty()) {
$dbForProject->updateDocument(
'attributes',
$relatedAttribute->getId(),
$relatedAttribute->setAttribute('status', 'stuck')
);
}
throw $e;
} finally {
$this->trigger($database, $collection, $attribute, $project, $projectId, $events);
} }
} finally {
$this->trigger($database, $collection, $attribute, $project, $projectId, $events);
}
// The underlying database removes/rebuilds indexes when attribute is removed // The underlying database removes/rebuilds indexes when attribute is removed
// Update indexes table with changes // Update indexes table with changes
/** @var Document[] $indexes */ /** @var Document[] $indexes */
$indexes = $collection->getAttribute('indexes', []); $indexes = $collection->getAttribute('indexes', []);
foreach ($indexes as $index) { foreach ($indexes as $index) {
/** @var string[] $attributes */ /** @var string[] $attributes */
$attributes = $index->getAttribute('attributes'); $attributes = $index->getAttribute('attributes');
$lengths = $index->getAttribute('lengths'); $lengths = $index->getAttribute('lengths');
$orders = $index->getAttribute('orders'); $orders = $index->getAttribute('orders');
$found = \array_search($key, $attributes); $found = \array_search($key, $attributes);
if ($found !== false) { if ($found !== false) {
// If found, remove entry from attributes, lengths, and orders // If found, remove entry from attributes, lengths, and orders
// array_values wraps array_diff to reindex array keys // array_values wraps array_diff to reindex array keys
// when found attribute is removed from array // when found attribute is removed from array
$attributes = \array_values(\array_diff($attributes, [$attributes[$found]])); $attributes = \array_values(\array_diff($attributes, [$attributes[$found]]));
$lengths = \array_values(\array_diff($lengths, isset($lengths[$found]) ? [$lengths[$found]] : [])); $lengths = \array_values(\array_diff($lengths, isset($lengths[$found]) ? [$lengths[$found]] : []));
$orders = \array_values(\array_diff($orders, isset($orders[$found]) ? [$orders[$found]] : [])); $orders = \array_values(\array_diff($orders, isset($orders[$found]) ? [$orders[$found]] : []));
if (empty($attributes)) { if (empty($attributes)) {
$dbForProject->deleteDocument('indexes', $index->getId()); $dbForProject->deleteDocument('indexes', $index->getId());
} else {
$index
->setAttribute('attributes', $attributes, Document::SET_TYPE_ASSIGN)
->setAttribute('lengths', $lengths, Document::SET_TYPE_ASSIGN)
->setAttribute('orders', $orders, Document::SET_TYPE_ASSIGN);
// Check if an index exists with the same attributes and orders
$exists = false;
foreach ($indexes as $existing) {
if (
$existing->getAttribute('key') !== $index->getAttribute('key') // Ignore itself
&& $existing->getAttribute('attributes') === $index->getAttribute('attributes')
&& $existing->getAttribute('orders') === $index->getAttribute('orders')
) {
$exists = true;
break;
}
}
if ($exists) { // Delete the duplicate if created, else update in db
$this->deleteIndex($database, $collection, $index, $project, $dbForConsole, $dbForProject);
} else { } else {
$dbForProject->updateDocument('indexes', $index->getId(), $index); $index
->setAttribute('attributes', $attributes, Document::SET_TYPE_ASSIGN)
->setAttribute('lengths', $lengths, Document::SET_TYPE_ASSIGN)
->setAttribute('orders', $orders, Document::SET_TYPE_ASSIGN);
// Check if an index exists with the same attributes and orders
$exists = false;
foreach ($indexes as $existing) {
if (
$existing->getAttribute('key') !== $index->getAttribute('key') // Ignore itself
&& $existing->getAttribute('attributes') === $index->getAttribute('attributes')
&& $existing->getAttribute('orders') === $index->getAttribute('orders')
) {
$exists = true;
break;
}
}
if ($exists) { // Delete the duplicate if created, else update in db
$this->deleteIndex($database, $collection, $index, $project, $dbForConsole, $dbForProject);
} else {
$dbForProject->updateDocument('indexes', $index->getId(), $index);
}
} }
} }
} }
} } finally {
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId);
$dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId());
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); if (!$relatedCollection->isEmpty() && !$relatedAttribute->isEmpty()) {
$dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId());
$dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId());
if (!$relatedCollection->isEmpty() && !$relatedAttribute->isEmpty()) { }
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId());
$dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId());
} }
} }
@ -369,6 +375,7 @@ class Databases extends Action
* @throws Conflict * @throws Conflict
* @throws Structure * @throws Structure
* @throws DatabaseException * @throws DatabaseException
* @throws \Throwable
*/ */
private function createIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForConsole, Database $dbForProject): void private function createIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForConsole, Database $dbForProject): void
{ {
@ -400,9 +407,7 @@ class Databases extends Action
} }
$dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'available')); $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'available'));
} catch (\Throwable $e) { } catch (\Throwable $e) {
// TODO: Send non DatabaseExceptions to Sentry
Console::error($e->getMessage()); Console::error($e->getMessage());
if ($e instanceof DatabaseException) { if ($e instanceof DatabaseException) {
$index->setAttribute('error', $e->getMessage()); $index->setAttribute('error', $e->getMessage());
} }
@ -411,11 +416,12 @@ class Databases extends Action
$index->getId(), $index->getId(),
$index->setAttribute('status', 'failed') $index->setAttribute('status', 'failed')
); );
throw $e;
} finally { } finally {
$this->trigger($database, $collection, $index, $project, $projectId, $events); $this->trigger($database, $collection, $index, $project, $projectId, $events);
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId);
} }
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId);
} }
/** /**
@ -430,6 +436,7 @@ class Databases extends Action
* @throws Conflict * @throws Conflict
* @throws Structure * @throws Structure
* @throws DatabaseException * @throws DatabaseException
* @throws \Throwable
*/ */
private function deleteIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForConsole, Database $dbForProject): void private function deleteIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForConsole, Database $dbForProject): void
{ {
@ -458,7 +465,6 @@ class Databases extends Action
$dbForProject->deleteDocument('indexes', $index->getId()); $dbForProject->deleteDocument('indexes', $index->getId());
$index->setAttribute('status', 'deleted'); $index->setAttribute('status', 'deleted');
} catch (\Throwable $e) { } catch (\Throwable $e) {
// TODO: Send non DatabaseExceptions to Sentry
Console::error($e->getMessage()); Console::error($e->getMessage());
if ($e instanceof DatabaseException) { if ($e instanceof DatabaseException) {
@ -469,11 +475,13 @@ class Databases extends Action
$index->getId(), $index->getId(),
$index->setAttribute('status', 'stuck') $index->setAttribute('status', 'stuck')
); );
throw $e;
} finally { } finally {
$this->trigger($database, $collection, $index, $project, $projectId, $events); $this->trigger($database, $collection, $index, $project, $projectId, $events);
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collection->getId());
} }
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collection->getId());
} }
/** /**

View file

@ -268,8 +268,6 @@ class Migrations extends Action
$transfer = $source = $destination = null; $transfer = $source = $destination = null;
try { try {
$migration = $this->dbForProject->getDocument('migrations', $migration->getId());
if ( if (
$migration->getAttribute('source') === SourceAppwrite::getName() && $migration->getAttribute('source') === SourceAppwrite::getName() &&
empty($migration->getAttribute('credentials', [])) empty($migration->getAttribute('credentials', []))

View file

@ -1362,7 +1362,7 @@ class DatabasesCustomServerTest extends Scope
]); ]);
$this->assertEquals(400, $tooWide['headers']['status-code']); $this->assertEquals(400, $tooWide['headers']['status-code']);
$this->assertEquals('Attribute limit exceeded', $tooWide['body']['message']); $this->assertEquals('attribute_limit_exceeded', $tooWide['body']['type']);
} }
public function testIndexLimitException() public function testIndexLimitException()