feat(server): improve calendar sync queue (#14783)

#### PR Dependency Tree


* **PR #14783** 👈

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

* **New Features**
  * Configurable request timeout for calendar integrations.
* Calendar polling now enqueues per-subscription sync jobs (larger
batch) for improved throughput.

* **Bug Fixes / Improvements**
* Persisted next-sync timestamps and retry counts for more reliable
scheduling and retry behavior.
* Exponential backoff and webhook renewal now update scheduling
consistently.

* **Refactor**
* Calendar sync flow moved to a job-queue-driven design for better
concurrency and observability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
DarkSky
2026-04-05 10:52:18 +08:00
committed by GitHub
parent bfcf7fc2ba
commit d975bf46fb
17 changed files with 352 additions and 307 deletions
@@ -152,9 +152,26 @@ export abstract class CalendarProvider {
}
}
protected get requestTimeoutMs() {
const timeout = (this.config as { requestTimeoutMs?: number } | undefined)
?.requestTimeoutMs;
return typeof timeout === 'number' && timeout > 0 ? timeout : undefined;
}
protected withTimeout(signal?: AbortSignal | null) {
const timeoutMs = this.requestTimeoutMs;
if (!timeoutMs) return signal;
const timeoutSignal = AbortSignal.timeout(timeoutMs);
if (!signal) return timeoutSignal;
return AbortSignal.any([signal, timeoutSignal]);
}
protected async fetchJson<T>(url: string, init?: RequestInit) {
const response = await fetch(url, {
...init,
signal: this.withTimeout(init?.signal),
headers: { ...init?.headers, Accept: 'application/json' },
});
const body = await response.text();