Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Setup background job workers as described here: https://docs.nextcloud.com/serve
Note:
Refer to the [Context Chat Backend's readme](https://github.com/nextcloud/context_chat_backend/?tab=readme-ov-file) and the [AppAPI's documentation](https://cloud-py-api.github.io/app_api/) for help with setup of AppAPI's deploy daemon.
]]></description>
<version>5.3.1</version>
<version>5.3.2</version>
<licence>agpl</licence>
<author>Julien Veyssier</author>
<author>Anupam Kumar</author>
Expand Down
4 changes: 2 additions & 2 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace OCA\ContextChat\AppInfo;

use OCA\ContextChat\Listener\AddMissingIndicesListener;
use OCA\ContextChat\Listener\AddMissingIndicesListenerFsEvents;
use OCA\ContextChat\Listener\AppDisableListener;
use OCA\ContextChat\Listener\FileListener;
use OCA\ContextChat\Listener\ShareListener;
Expand Down Expand Up @@ -68,7 +68,7 @@ public function __construct(array $urlParams = []) {
}

public function register(IRegistrationContext $context): void {
$context->registerEventListener(AddMissingIndicesEvent::class, AddMissingIndicesListener::class);
$context->registerEventListener(AddMissingIndicesEvent::class, AddMissingIndicesListenerFsEvents::class);
$context->registerEventListener(BeforeNodeDeletedEvent::class, FileListener::class);
$context->registerEventListener(NodeCreatedEvent::class, FileListener::class);
$context->registerEventListener(CacheEntryInsertedEvent::class, FileListener::class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
/**
* @template-implements IEventListener<AddMissingIndicesEvent>
*/
class AddMissingIndicesListener implements IEventListener {
class AddMissingIndicesListenerFsEvents implements IEventListener {
#[\Override]
public function handle(Event $event): void {
if (!($event instanceof AddMissingIndicesEvent)) {
Expand Down
59 changes: 59 additions & 0 deletions lib/Migration/Version005003002Date20260320093626.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\ContextChat\Migration;

use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
use Override;

class Version005003002Date20260320093626 extends SimpleMigrationStep {
/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
* @return null|ISchemaWrapper
*/
#[Override]
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
$schemaChanged = false;

if ($schema->hasTable('context_chat_content_queue')) {
$table = $schema->getTable('context_chat_content_queue');
$appIdCol = $table->getColumn('app_id');
if ($appIdCol->getLength() !== 32) {
// appId has the same length in other tables like oc_appconfig
$appIdCol->setLength(32);
$schemaChanged = true;
}

$providerIdCol = $table->getColumn('provider_id');
if ($providerIdCol->getLength() !== 63) {
// limit length of provider id, almost double of appId length
// 63 instead of 64 to avoid extra byte in utf8mb4 from varchar's length overhead
$providerIdCol->setLength(63);
$schemaChanged = true;
}

$itemIdCol = $table->getColumn('item_id');
// max length of item id so far is in mail at 41 bytes "bigint:bigint"
// for bigint ids converted to string 20 should be enough
if ($itemIdCol->getLength() !== 63) {
$itemIdCol->setLength(63);
Comment thread
kyteinsky marked this conversation as resolved.
$schemaChanged = true;
}
}

return $schemaChanged ? $schema : null;
}
}
98 changes: 98 additions & 0 deletions lib/Migration/Version005003002Date20260320093628.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\ContextChat\Migration;

use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\IDBConnection;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
use Override;

class Version005003002Date20260320093628 extends SimpleMigrationStep {
public function __construct(
private IDBConnection $db,
) {
}

/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
* @return null|ISchemaWrapper
*/
#[Override]
public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options) {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();

if (!$schema->hasTable('context_chat_content_queue')) {
$output->info('Table context_chat_content_queue does not exist, skipping de-duplication.');
return;
}

$totalRowsDeleted = 0;
$qb = $this->db->getQueryBuilder();
$qb->select('app_id', 'provider_id', 'item_id')
->selectAlias($qb->func()->count('*'), 'count')
->selectAlias($qb->func()->max('id'), 'keep_id')
->from('context_chat_content_queue')
->groupBy('app_id', 'provider_id', 'item_id')
->having($qb->expr()->gt('count', $qb->createNamedParameter(1)));
$selectQuery = $qb->executeQuery();

$qb2 = $this->db->getQueryBuilder();
$qb2->delete('context_chat_content_queue')
Copy link
Copy Markdown
Member

@marcelklehr marcelklehr Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will not utilize the new index, I think. That may be ok, though.

Copy link
Copy Markdown
Contributor Author

@kyteinsky kyteinsky Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, a unique index cannot be added without de-duplicating it first, we do it in another migration.

->where(
$qb2->expr()->eq('app_id', $qb2->createParameter('appId')),
$qb2->expr()->eq('provider_id', $qb2->createParameter('providerId')),
$qb2->expr()->eq('item_id', $qb2->createParameter('itemId')),
$qb2->expr()->neq('id', $qb2->createParameter('keepId')),
);

try {
while ($row = $selectQuery->fetch()) {
$qb2->setParameter('appId', $row['app_id'])
->setParameter('providerId', $row['provider_id'])
->setParameter('itemId', $row['item_id'])
->setParameter('keepId', $row['keep_id']);

$rowsDeleted = $qb2->executeStatement();
$totalRowsDeleted += $rowsDeleted;
}
} catch (\Exception $e) {
$selectQuery->closeCursor();
throw $e;
}

$output->info("Removed $totalRowsDeleted duplicate content provider entries.");
}

/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
* @return null|ISchemaWrapper
*/
#[Override]
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
$schemaChanged = false;

if ($schema->hasTable('context_chat_content_queue')) {
$table = $schema->getTable('context_chat_content_queue');
$table->addUniqueIndex(['app_id', 'provider_id', 'item_id'], 'ccc_q_provider');
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On large instances this will take a while. What made you give up the way of the AddMissingIndeces event?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we didn't have a way before to de-duplicate, now that we do that, it would be good to add the unique index just after since the table might not remain unique if there is time between the de-duplication and the index addition by the admin.
I suppose a unique index made sense as opposed to a normal index, it reduces the index and table size, and is much better to reason about, wdyt?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not against a unique index, it makes sense. I'm just afraid that we're not being a nice citizen by forcing the creation of the index on people with a large table which will cause a long downtime for them with this update. But might be acceptable.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true but the subset of the affected people might be quite small since this only affects the instances with analytics and/or mail apps that have turned on the context chat indexing.
we can mention this in the changelog, it might help.

$schemaChanged = true;
}

return $schemaChanged ? $schema : null;
}
}
21 changes: 20 additions & 1 deletion lib/Public/ContentManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@ public function registerContentProvider(string $appId, string $providerId, strin
return;
}

if (mb_strlen($appId) > 32) {
$this->logger->warning('App ID is too long, maximum length is 32 characters.', ['appId' => $appId]);
return;
}

if (mb_strlen($providerId) > 63) {
$this->logger->warning('Provider ID is too long, maximum length is 63 characters.', ['providerId' => $providerId]);
return;
}

$this->providerConfig->updateProvider($appId, $providerId, $providerClass);

if (!$this->jobList->has(InitialContentImportJob::class, $providerClass)) {
Expand Down Expand Up @@ -108,6 +118,15 @@ public function submitContent(string $appId, array $items): void {
$this->collectAllContentProviders();

foreach ($items as $item) {
if (mb_strlen($item->itemId) > 63) {
$this->logger->warning('Item ID is too long, maximum length is 63 characters.', [
'appId' => $appId,
'providerId' => $item->providerId,
'itemId' => $item->itemId,
]);
continue;
}

$dbItem = new QueueContentItem();
$dbItem->setItemId($item->itemId);
$dbItem->setAppId($appId);
Expand All @@ -119,7 +138,7 @@ public function submitContent(string $appId, array $items): void {
$dbItem->setUsers(implode(',', $item->users));

try {
$this->mapper->insert($dbItem);
$this->mapper->insertOrUpdate($dbItem);
} catch (Exception $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
}
Expand Down
Loading