Skip to content

Don't delete conversations that arrive while a list sync is in flight - #6749

Open
AndyScherzinger wants to merge 1 commit into
masterfrom
fix/noid/no-conversation-loss-on-concurrent-sync
Open

AndyScherzinger wants to merge 1 commit into
masterfrom
fix/noid/no-conversation-loss-on-concurrent-sync

Conversation

@AndyScherzinger

Copy link
Copy Markdown
Member

A conversation list sync reads the locally known conversations after the server has answered, and treats every one the response does not mention as a conversation the user left. A conversation that reached the database while the request was in flight — a single-room fetch, a room joined on another device, one the user just created — was never in that response and could not have been, so it was deleted, taking its cached messages and chat blocks with it through the foreign key cascade.

What it does

Takes the set of known conversation ids before the request goes out, and reconciles removals only against those. A conversation that appeared afterwards is left alone until the next full sync, which is the first one whose response could actually speak about it.

Why not simply read the conversations earlier

The same read feeds preservePendingLocalState, which needs the freshest local state to protect an in-flight favourite or read marker. Moving it earlier would trade this bug for that one. Only the removal reconcile wants the older snapshot, so only it gets one — as an id-only query, so the extra read does not load every conversation row.

Note for reviewers

detekt is red on this branch. It is red on master too, at the same count: 111 weighted issues against a maxIssues of 110, measured on master with this branch stashed. This change adds none.

🚧 TODO

  • ...

🏁 Checklist

  • ⛑️ Tests (unit and/or integration) are included or not needed
  • 🔖 Capability is checked or not needed
  • 🔙 Backport requests are created or not needed: /backport to stable-xx.x
  • 📅 Milestone is set
  • 🌸 PR title is meaningful (if it should be in the changelog: is it meaningful to users?)

🤖 AI (if applicable)

  • The content of this PR was partly or fully generated using AI

@AndyScherzinger AndyScherzinger added this to the 25.1.0 milestone Sep 21, 2026
@github-actions

github-actions Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

📱 QA build

Download app-qa-debug.apk
QR code Open the QR code for this download
Commit 54a04a5
Version 6749
Available until 7 days after this build

The QA build installs alongside a released Nextcloud app, so you can keep
using your existing install while testing.

Downloading the file requires a GitHub account, so open this link on the
device you want to test on, or transfer the APK to it.

@AndyScherzinger AndyScherzinger added the 2. developing Work in progress label Sep 21, 2026
@AndyScherzinger
AndyScherzinger force-pushed the fix/noid/no-conversation-loss-on-concurrent-sync branch 15 times, most recently from cd90e54 to 88d6996 Compare September 28, 2026 23:03
@AndyScherzinger
AndyScherzinger force-pushed the fix/noid/no-conversation-loss-on-concurrent-sync branch 2 times, most recently from d94e69b to 554f690 Compare October 1, 2026 06:20
@AndyScherzinger
AndyScherzinger marked this pull request as ready for review October 1, 2026 06:21
@AndyScherzinger AndyScherzinger added 3. to review Waiting for reviews and removed 2. developing Work in progress labels Oct 1, 2026
@AndyScherzinger AndyScherzinger self-assigned this Oct 1, 2026
@AndyScherzinger
AndyScherzinger requested a review from mahibi October 1, 2026 06:21
@coderabbitai

coderabbitai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

🧰 Additional context used
📚 Code guidelines (1)
AGENTS.md — auto-discovered

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 985dbad3-cbfe-4148-aa1f-74012b880d20

📥 Commits

Reviewing files that changed from the base of the PR and between 554f690 and 54a04a5.

📒 Files selected for processing (3)
  • app/src/main/java/com/nextcloud/talk/conversationlist/data/network/OfflineFirstConversationsRepository.kt
  • app/src/main/java/com/nextcloud/talk/utils/preview/ComposePreviewUtilsDaos.kt
  • app/src/test/java/com/nextcloud/talk/conversationlist/data/network/OfflineFirstConversationsRepositoryTest.kt

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 2 remain after this review.


📝 Walkthrough

Walkthrough

The repository now captures an account’s conversation IDs before requesting rooms from the server. It limits sync deletion candidates to conversations in that snapshot. ConversationsDao adds an account-scoped ID query, and the preview DAO returns an empty list. Repository tests cover a conversation that appears in a later row read during the request.

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to 54a04

Sync protects conversations added during an in-flight room request, and the regression test exercises deletion reconciliation. No identified issue blocks merging.

Security Architecture Review

Security architecture risk: ⚪ Minimal · up to 54a04

The change narrows which conversations a sync may delete, protecting conversations that arrive during the request. Account isolation and atomic database updates remain intact. No material security risk introduced or worsened by this change was identified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The directly affected destructive outcome is removal of an account’s local conversations and their cascading cached messages and chat blocks. The new membership filter reduces the set of rows exposed to that outcome; it does not expand deletion authority.

Trust Boundaries and Controls

  • observed — The same supplied user identity is used for the snapshot, server request, entity account assignment, later local read, and reconciliation. The existing empty-response guard declines destructive reconciliation when cached conversations exist.

Resilience and Maintainability Implications

  • inferred — The snapshot protects later arrivals but does not establish ordering between overlapping sync responses. The repository launches independent jobs without a visible generation check. This limitation predates the reviewed change; external caller serialization was not established, so complete overlapping-sync safety is not claimed.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 14 functions across 4 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: preventing deletion of conversations that arrive during an in-flight list sync.
Description check ✅ Passed The description explains the defect, the solution, the design rationale, the detekt status, and the checklist. The screenshots section is omitted and the TODO remains a placeholder, but these omission…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: f13dcaf9-a12c-479b-b7ad-5e14bac0aa90

📥 Commits

Reviewing files that changed from the base of the PR and between ab89e1d and 554f690.

📒 Files selected for processing (4)
  • app/src/main/java/com/nextcloud/talk/conversationlist/data/network/OfflineFirstConversationsRepository.kt
  • app/src/main/java/com/nextcloud/talk/data/database/dao/ConversationsDao.kt
  • app/src/main/java/com/nextcloud/talk/utils/preview/ComposePreviewUtilsDaos.kt
  • app/src/test/java/com/nextcloud/talk/conversationlist/data/network/OfflineFirstConversationsRepositoryTest.kt

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 2 remain after this review.

A conversation list sync reads the locally known conversations after the
server has answered, and treats every one the response does not mention
as a conversation the user left. A conversation that reached the
database while the request was in flight - a single-room fetch, a room
joined on another device, one the user just created - was never in that
response and could not have been, so it was deleted, taking its cached
messages and chat blocks with it through the foreign key cascade.

Take the known conversation ids before the request goes out and
reconcile removals only against those. Anything that appeared afterwards
is left alone until the next full sync, which is the first one whose
response can speak about it at all.

Reading the conversations themselves earlier would not do: that same
read feeds the pending local state guard, which needs the freshest state
to protect a favourite or read marker that is still on its way to the
server. Only the removal reconcile wants the older snapshot, so only it
gets one, as an id query that does not load the rows.

Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
@AndyScherzinger
AndyScherzinger force-pushed the fix/noid/no-conversation-loss-on-concurrent-sync branch from 554f690 to 54a04a5 Compare October 1, 2026 19:24
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Codacy

Lint

TypemasterPR
Warnings138138
Errors1815

SpotBugs

CategoryBaseNew
Bad practice77
Correctness1111
Dodgy code4040
Internationalization33
Malicious code vulnerability33
Performance88
Security1111
Total8383

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3. to review Waiting for reviews AI assisted

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant