mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-26 23:02:57 +08:00
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:
@@ -1,18 +1,27 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Cron, CronExpression } from '@nestjs/schedule';
|
||||
import { chunk } from 'lodash-es';
|
||||
|
||||
import { Mutex } from '../../base';
|
||||
import { Models } from '../../models';
|
||||
import { CalendarService } from './service';
|
||||
|
||||
const CALENDAR_POLL_LOCK_KEY = 'calendar:poll-accounts';
|
||||
const CALENDAR_POLL_BATCH_SIZE = 10;
|
||||
|
||||
@Injectable()
|
||||
export class CalendarCronJobs {
|
||||
constructor(
|
||||
private readonly models: Models,
|
||||
private readonly calendar: CalendarService
|
||||
private readonly calendar: CalendarService,
|
||||
private readonly mutex: Mutex
|
||||
) {}
|
||||
|
||||
@Cron(CronExpression.EVERY_MINUTE)
|
||||
async pollAccounts() {
|
||||
await using lock = await this.mutex.acquire(CALENDAR_POLL_LOCK_KEY);
|
||||
if (!lock) return;
|
||||
|
||||
const subscriptions =
|
||||
await this.models.calendarSubscription.listAllWithAccountForSync();
|
||||
|
||||
@@ -46,16 +55,18 @@ export class CalendarCronJobs {
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
await Promise.allSettled(
|
||||
Array.from(accountDueAt.entries()).map(([accountId, info]) => {
|
||||
if (
|
||||
const dueAccountIds = Array.from(accountDueAt.entries())
|
||||
.filter(
|
||||
([, info]) =>
|
||||
!info.lastSyncAt ||
|
||||
now - info.lastSyncAt.getTime() >= info.refreshInterval * 60 * 1000
|
||||
) {
|
||||
return this.calendar.syncAccount(accountId);
|
||||
}
|
||||
return Promise.resolve();
|
||||
})
|
||||
);
|
||||
)
|
||||
.map(([accountId]) => accountId);
|
||||
|
||||
for (const accountIds of chunk(dueAccountIds, CALENDAR_POLL_BATCH_SIZE)) {
|
||||
await Promise.allSettled(
|
||||
accountIds.map(accountId => this.calendar.syncAccount(accountId))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user