fix(proposal): avoid UID collision when converting a proposal without…#8284
fix(proposal): avoid UID collision when converting a proposal without…#8284shbb-asdea wants to merge 1 commit into
Conversation
… a known blocker convertProposal assigned the proposal's UUID to the new VEVENT before deciding between "PUT over an existing blocker" and "INSERT a new calendar object". When findCalendarBlocker missed the blocker (calendar not enumerated by the principal lookup, search backend lag, etc.) the INSERT collided with oc_calendarobjects.calobjects_by_uid_index because the orphan blocker still held the same UID, surfacing as a 500 with SQLSTATE[23505] and leaving the proposal unconverted. Defer the UID assignment until the blocker lookup result is known: when a blocker is found, keep reusing the proposal UUID so the in-place PUT preserves iCal threading; when no blocker is found, allocate a fresh UUID so the INSERT cannot clash with a stale row. Refs: nextcloud#7941 Signed-off-by: shbb-asdea <b.shestakov@asdea.net>
dec5b19 to
4384f83
Compare
|
Hi @shbb-asdea Please link a proper issue ticket, the reference link currently, links to a dependency update PR |
|
@SebastianKrupinski apologies for the confusion — I had pasted the wrong issue number. The PR description is now updated to reference the correct issue (#7941). Could you take another look? |
|
The issue you linked should have been resolved by this PR #8092 The issue was the original code would delete the existing event, but due to our trash bin logic the event was not actually deleted, so when the original code created a new event there was a collision in the index. But the above PR resolves this, by updating the blocker, is this still an issue? |
|
Hi @SebastianKrupinski, thanks for pointing to #8092 — I dug into its diff and the relationship with this issue is clearer now. #8092 (which closed #8049 — same symptom: duplicate key on But the same PR introduced an $result = $this->findCalendarBlocker($user, $proposal);
if ($result !== null) {
$this->applyCalendarBlockersOrganizer(...); // PUT — safe after #8092
} else {
$userCalendar->createFromString(...); // INSERT — still collides
}When findCalendarBlocker (L796-L805) returns null but a blocker row still exists in oc_calendarobjects, the else branch INSERTs with the proposal's UID and crashes on calobjects_by_uid_index exactly the way #8049 / #7941 described. findCalendarBlocker is built on getCalendarsForPrincipal() + $calendar->search('', [], ['uid' => …]). The unique index is on (calendarid, calendartype, uid) — deleted_at is not part of it. So a blocker that has been trash-binned (or sits in a calendar/state the principal lookup or the search backend doesn't surface) is invisible to findCalendarBlocker, but still claims the UID in the table. That's why #8049 / #7941 keep reproducing after #8092: when the trash-bin row exists, we fall through to the else branch and the same collision happens. This is consistent with the report by @sobeaa in this thread, where the crash still reproduces on 32.0.6 / Calendar 6.2.0 — i.e. with #8092 already merged. The regression test added in this PR (testConvertProposalUsesFreshUuidWhenBlockerNotFound) reproduces the failure deterministically by mocking getCalendarsForPrincipal as empty (the precise "blocker invisible to the lookup" condition). It fails on current main and passes after the patch. The fix is small: defer UID assignment until the lookup result is known. Blocker found → reuse proposal UUID (PUT in place — unchanged behaviour from #8092, iCal threading preserved). |
… a known blocker
convertProposal assigned the proposal's UUID to the new VEVENT before deciding between "PUT over an existing blocker" and "INSERT a new calendar object". When findCalendarBlocker missed the blocker (calendar not enumerated by the principal lookup, search backend lag, etc.) the INSERT collided with oc_calendarobjects.calobjects_by_uid_index because the orphan blocker still held the same UID, surfacing as a 500 with SQLSTATE[23505] and leaving the proposal unconverted.
Defer the UID assignment until the blocker lookup result is known: when a blocker is found, keep reusing the proposal UUID so the in-place PUT preserves iCal threading; when no blocker is found, allocate a fresh UUID so the INSERT cannot clash with a stale row.
Refs: #7941