Federated calendar sync deletion crashes in CalDavBackend with CachedCalendarObjectDeletedEvent TypeError

オープン 初心者向け
#61,290 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
2/5
見積もり時間
1〜3時間
初心者へのやさしさ
82/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
静か
技術スタック
php
領域
backend

調査の方向性

apps/dav/lib/CalDAV/CalDavBackend.php の deleteCalendarObject() から始め、カレンダータイプの処理を createCalendarObject() および updateCalendarObject() と比較します。言及されている occ バックグラウンドジョブコマンドを使って、FederatedCalendarSyncService::syncOne() 経由でジョブを再現するか強制的に実行します。フェデレーションされた削除が TypeError なしで完了し、同期トークンが進めば完了です。

索引モデルが issue の本文から書いたものです。

説明

0. Needs triage 33-feedback bug feature: caldav feature: federation
Steps to reproduce
  1. Run Nextcloud 33.0.5 with at least one federated calendar in oc_calendars_federated.
  2. Let OCA\DAV\BackgroundJob\FederatedCalendarPeriodicSyncJob sync a remote calendar where the sync report contains deleted objects.
  3. The job calls FederatedCalendarSyncService::syncOne(), which calls:
$this->backend->deleteCalendarObject(
    $calendar->getId(),
    $objectUri,
    CalDavBackend::CALENDAR_TYPE_FEDERATED,
    true
);
Expected behaviour

Federated-calendar object deletion should complete without trying to look up a calendar subscription.

Actual behaviour

The background job fails with a TypeError because CalDavBackend::deleteCalendarObject() treats every non-CALENDAR_TYPE_CALENDAR object as a calendar subscription deletion:

if ($calendarType === self::CALENDAR_TYPE_CALENDAR) {
    ...
} else {
    $subscriptionRow = $this->getSubscriptionById($calendarId);

    $this->dispatcher->dispatchTyped(new CachedCalendarObjectDeletedEvent($calendarId, $subscriptionRow, [], $data));
}

For CALENDAR_TYPE_FEDERATED (2), getSubscriptionById($calendarId) returns null, and CachedCalendarObjectDeletedEvent requires an array for $subscriptionData.

Sanitized stack trace excerpt:

Error while running background job OCA\DAV\BackgroundJob\FederatedCalendarPeriodicSyncJob

TypeError: OCA\DAV\Events\CachedCalendarObjectDeletedEvent::__construct():
Argument #2 ($subscriptionData) must be of type array, null given,
called in apps/dav/lib/CalDAV/CalDavBackend.php on line 1588

#0 apps/dav/lib/CalDAV/CalDavBackend.php(1588): OCA\DAV\Events\CachedCalendarObjectDeletedEvent->__construct()
#1 lib/public/AppFramework/Db/TTransactional.php(45): OCA\DAV\CalDAV\CalDavBackend->{closure:...}()
#2 apps/dav/lib/CalDAV/CalDavBackend.php(1564): OCA\DAV\CalDAV\CalDavBackend->atomic()
#3 apps/dav/lib/CalDAV/Federation/FederatedCalendarSyncService.php(122): OCA\DAV\CalDAV\CalDavBackend->deleteCalendarObject()
#4 apps/dav/lib/BackgroundJob/FederatedCalendarPeriodicSyncJob.php(47): OCA\DAV\CalDAV\Federation\FederatedCalendarSyncService->syncOne()
#5 lib/public/BackgroundJob/Job.php(50): OCA\DAV\BackgroundJob\FederatedCalendarPeriodicSyncJob->run()
#6 lib/public/BackgroundJob/TimedJob.php(85): OCP\BackgroundJob\Job->start()
#7 core/Service/CronService.php(176): OCP\BackgroundJob\TimedJob->start()
#8 core/Service/CronService.php(98): OC\Core\Service\CronService->runCli()
#9 cron.php(52): OC\Core\Service\CronService->run()
Diagnosis

This does not appear to be orphaned subscription data in the database. On the affected instance:

calendarobjects grouped by calendartype:
  calendartype=0 count=564
  calendartype=2 count=1210

orphan cached subscription objects:
  calendartype=1 objects without calendarsubscriptions row = 0

federated calendars:
  2 rows in oc_calendars_federated

The error path is therefore the federated delete branch being routed into the cached-subscription event branch.

createCalendarObject() and updateCalendarObject() already distinguish the three cases:

if ($calendarType === self::CALENDAR_TYPE_CALENDAR) {
    ...
} elseif ($calendarType === self::CALENDAR_TYPE_SUBSCRIPTION) {
    ...
} elseif ($calendarType === self::CALENDAR_TYPE_FEDERATED) {
    // TODO: implement custom event for federated calendars
}

deleteCalendarObject() appears to need the same structure.

Suggested fix

Change the delete branch from else to elseif ($calendarType === self::CALENDAR_TYPE_SUBSCRIPTION), and add an explicit no-op CALENDAR_TYPE_FEDERATED branch until there is a dedicated federated-calendar deleted event:

if ($calendarType === self::CALENDAR_TYPE_CALENDAR) {
    $calendarRow = $this->getCalendarById($calendarId);
    $shares = $this->getShares($calendarId);

    $this->dispatcher->dispatchTyped(new CalendarObjectDeletedEvent($calendarId, $calendarRow, $shares, $data));
} elseif ($calendarType === self::CALENDAR_TYPE_SUBSCRIPTION) {
    $subscriptionRow = $this->getSubscriptionById($calendarId);

    $this->dispatcher->dispatchTyped(new CachedCalendarObjectDeletedEvent($calendarId, $subscriptionRow, [], $data));
} elseif ($calendarType === self::CALENDAR_TYPE_FEDERATED) {
    // TODO: implement custom event for federated calendars
}

I tested that local change on the affected instance by forcing the previously failing job with:

occ background-job:execute --force-execute <job-id>

After the local patch, the job completed, the federated sync token advanced, and cached federated objects dropped from 1210 to 1200, indicating that the remote deletions were processed.

Server configuration
  • Nextcloud version: 33.0.5.1 / version string 33.0.5
  • Database: PostgreSQL
  • Background jobs: cron
  • App/code path involved: apps/dav/lib/CalDAV/CalDavBackend.php, apps/dav/lib/CalDAV/Federation/FederatedCalendarSyncService.php, apps/dav/lib/BackgroundJob/FederatedCalendarPeriodicSyncJob.php
主要言語
PHP
スター
36.9k
フォーク
5.2k
平均マージ
2日
マージ済み PR(30日)
725

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

nextcloud/server のほかの issue

nextcloud/server の issue をすべて見る

似ている issue

PHP の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。