fix(server): race condition for sync (#14770)

#### PR Dependency Tree


* **PR #14770** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Release Notes

* **New Features**
* Implemented batch processing for calendar synchronization to improve
performance and resource utilization.
* Added distributed locking to prevent concurrent operations in
multi-instance environments.

* **Bug Fixes**
* Improved reliability by preventing duplicate synchronization attempts.

* **Tests**
  * Enhanced test coverage for batch processing and locking mechanisms.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
DarkSky
2026-04-03 03:37:09 +08:00
committed by GitHub
parent 233004f867
commit 0da32d61ae
5 changed files with 121 additions and 28 deletions
@@ -33,19 +33,37 @@ test('should not index workspace if indexer is disabled', async t => {
const count = module.queue.count('indexer.indexWorkspace');
// @ts-expect-error ignore missing fields
await indexerEvent.indexWorkspace({ id: 'test-workspace' });
await indexerEvent.indexWorkspace({
workspaceId: 'test-workspace',
docId: 'test-workspace',
});
t.is(module.queue.count('indexer.indexWorkspace'), count);
});
test('should index workspace if indexer is enabled', async t => {
test('should index workspace when root snapshot is updated', async t => {
// @ts-expect-error ignore missing fields
await indexerEvent.indexWorkspace({ id: 'test-workspace' });
await indexerEvent.indexWorkspace({
workspaceId: 'test-workspace',
docId: 'test-workspace',
});
const { payload } = await module.queue.waitFor('indexer.indexWorkspace');
t.is(payload.workspaceId, 'test-workspace');
});
test('should not index workspace when non-root snapshot is updated', async t => {
const count = module.queue.count('indexer.indexWorkspace');
// @ts-expect-error ignore missing fields
await indexerEvent.indexWorkspace({
workspaceId: 'test-workspace',
docId: 'child-doc',
});
t.is(module.queue.count('indexer.indexWorkspace'), count);
});
test('should not delete workspace if indexer is disabled', async t => {
Sinon.stub(config.indexer, 'enabled').value(false);
const count = module.queue.count('indexer.deleteWorkspace');
@@ -29,21 +29,20 @@ export class IndexerEvent {
);
}
@OnEvent('workspace.updated')
async indexWorkspace({ id }: Events['workspace.updated']) {
@OnEvent('doc.snapshot.updated')
async indexWorkspace({ workspaceId, docId }: Events['doc.snapshot.updated']) {
if (!this.config.indexer.enabled) {
return;
}
if (workspaceId !== docId) {
return;
}
await this.queue.add(
'indexer.indexWorkspace',
{
workspaceId: id,
},
{
jobId: `indexWorkspace/${id}`,
priority: 100,
}
{ workspaceId },
{ jobId: `indexWorkspace/${workspaceId}`, priority: 100 }
);
}