Skip to content

fix: synchronize CsiManager.activate() and flush full queue atomically#3316

Open
dwd wants to merge 5 commits into
igniterealtime:mainfrom
dwd:csi-fix
Open

fix: synchronize CsiManager.activate() and flush full queue atomically#3316
dwd wants to merge 5 commits into
igniterealtime:mainfrom
dwd:csi-fix

Conversation

@dwd
Copy link
Copy Markdown
Member

@dwd dwd commented May 7, 2026

activate() was not synchronized, causing a race with queueOrPush() on the shared queue field. It also used an indirect flush (pollLast + re- deliver) that could lose stanzas queued between pollLast and the re- entry into queueOrPush.

Fix: drain the entire queue atomically under the lock, set active=true, then deliver all stanzas outside the lock so I/O does not block other threads from queuing new stanzas.

Summary by CodeRabbit

  • New Features

    • Improved client delivery path to reliably push queued stanzas to connections, preserving stream management behavior.
  • Bug Fixes

    • More consistent activation/reactivation handling when delivering queued packets; queue flushes at capacity and race conditions during activation/deactivation are reduced.
  • Tests

    • Modernized test suite and greatly expanded coverage across stanza types, presence/nonza cases, queuing/flush scenarios, and delayability rules.

Copilot AI review requested due to automatic review settings May 7, 2026 10:53
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

activate() in CsiManager now sets active = true and runs a gated repeated drain loop that empties the delay queue via a new drainQueue(), pushing drained stanzas until the queue is empty or inactive; process() lost synchronized, deactivate() became synchronized; queue flush triggers at size >= capacity; LocalClientSession adds pushPackets(); tests moved to JUnit5 and expanded.

Changes

CSI Queue Delivery Refactor

Layer / File(s) Summary
Activation: gated repeated drain & deliver
xmppserver/src/main/java/org/jivesoftware/openfire/csi/CsiManager.java
activate() sets active = true, gates a single activation flush loop with flushingOnActivate, repeatedly drains the delay queue and pushes drained stanzas until empty or inactive; I/O performed outside the CSI lock; per-delivery UnauthorizedException handling retained.
Fields & process signature
xmppserver/src/main/java/org/jivesoftware/openfire/csi/CsiManager.java
Added flushingOnActivate field and removed synchronized from process(Element).
Deactivation synchronization
xmppserver/src/main/java/org/jivesoftware/openfire/csi/CsiManager.java
deactivate() is now synchronized.
Delay queue capacity threshold
xmppserver/src/main/java/org/jivesoftware/openfire/csi/CsiManager.java
queueOrPush() now flushes when queue.size() >= DELAY_QUEUE_CAPACITY (was >).
Drain helper
xmppserver/src/main/java/org/jivesoftware/openfire/csi/CsiManager.java
Added private drainQueue() that copies queued stanzas, clears the queue, and returns the drained list for batch delivery.
LocalClientSession deliver / push
xmppserver/src/main/java/org/jivesoftware/openfire/session/LocalClientSession.java
deliver(Packet) delegates to csiManager.queueOrPush(...) and returns early when nothing to send; added public pushPackets(List<Packet>) to perform actual transmission and stream-management recording.
Tests: header & imports
xmppserver/src/test/java/org/jivesoftware/openfire/csi/CsiManagerTest.java
Updated header year range and migrated imports to JUnit 5/Mockito.
Tests: JUnit5 + Mockito wiring
xmppserver/src/test/java/org/jivesoftware/openfire/csi/CsiManagerTest.java
Added @ExtendWith(MockitoExtension.class), @MockitoSettings(strictness = LENIENT), a mocked LocalClientSession, and @BeforeEach that stubs pushPackets(...) to forward stanzas into csiManager.queueOrPush(...).
Tests: activation/deactivation & queue behavior
xmppserver/src/test/java/org/jivesoftware/openfire/csi/CsiManagerTest.java
Expanded tests for active/inactive state, process(...) CSI nonzas, queue sizing, flush-on-activate behavior, enqueue vs immediate push, non-delayable stanza handling, and concurrent scenarios.
Tests: canDelay matrix
xmppserver/src/test/java/org/jivesoftware/openfire/csi/CsiManagerTest.java
Large matrix covering delayability across IQ, presence types, messages (bodies vs chat-state), receipts, MUC subjects/invites, OMEMO, Jingle variants, headline messages, HTTP upload-slot IQ, and other explicit cases.
Tests: isCsiNonza & helpers
xmppserver/src/test/java/org/jivesoftware/openfire/csi/CsiManagerTest.java
Expanded isCsiNonza(...) tests for valid/invalid CSI elements and retained helper methods used across tests.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 A tail polled gently, freed from the lock,
Hopped out to send, then scurried off the clock,
Tests sprouted meadows, each case in a row,
Threshold nudged — the queue lets stanzas go,
CSI hops lighter and bravely says "go."

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: synchronizing CsiManager.activate() and implementing atomic queue flushing to fix race conditions and stanza loss.
Docstring Coverage ✅ Passed Docstring coverage is 97.96% which is sufficient. The required threshold is 80.00%.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
xmppserver/src/main/java/org/jivesoftware/openfire/csi/CsiManager.java (1)

121-126: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

process() still holds the same monitor during activate() delivery.

Line 121 keeps this locked for the full process() call, so even though activate() releases its inner lock before delivery, queueOrPush() remains blocked while session.deliver(...) runs (Line 155). That defeats the “deliver outside lock” goal for the primary call path (processactivate).

Suggested fix
-public synchronized void process(`@Nonnull` final Element nonza)
+public void process(`@Nonnull` final Element nonza)
 {
     switch(nonza.getName()) {
         case "active":
             activate();
             break;
         case "inactive":
             deactivate();
             break;
         default:
             Log.warn("Unable to process element that was expected to be a CSI nonza for {}: {}", session, nonza);
     }
 }
@@
-public void deactivate()
+public synchronized void deactivate()
 {
     Log.trace("Session for '{}' to CSI 'inactive'", session.getAddress());
     active = false;
 }

Also applies to: 142-159

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@xmppserver/src/main/java/org/jivesoftware/openfire/csi/CsiManager.java`
around lines 121 - 126, process() currently holds the CsiManager instance
monitor for the entire method which means calling activate() (and similar flows)
keeps queueOrPush/session.deliver blocked; change process() to avoid holding
synchronized(this) around the whole switch: read nonza.getName(), then for
"active"/"inactive" call activate()/deactivate() outside any synchronized block;
for branches that must touch shared state (the code around queueOrPush and the
session.deliver path referenced in queueOrPush and lines 142-159) acquire the
lock only to snapshot/modify minimal state, then release the lock before calling
session.deliver; alternatively refactor queueOrPush to compute/collect the
delivery payload under lock and invoke session.deliver after releasing the lock
so no session.deliver occurs while holding this.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@xmppserver/src/main/java/org/jivesoftware/openfire/csi/CsiManager.java`:
- Around line 121-126: process() currently holds the CsiManager instance monitor
for the entire method which means calling activate() (and similar flows) keeps
queueOrPush/session.deliver blocked; change process() to avoid holding
synchronized(this) around the whole switch: read nonza.getName(), then for
"active"/"inactive" call activate()/deactivate() outside any synchronized block;
for branches that must touch shared state (the code around queueOrPush and the
session.deliver path referenced in queueOrPush and lines 142-159) acquire the
lock only to snapshot/modify minimal state, then release the lock before calling
session.deliver; alternatively refactor queueOrPush to compute/collect the
delivery payload under lock and invoke session.deliver after releasing the lock
so no session.deliver occurs while holding this.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 80589b7e-17bc-4bb1-aea5-9a932e390f3d

📥 Commits

Reviewing files that changed from the base of the PR and between 98147d6 and d1c7f00.

📒 Files selected for processing (1)
  • xmppserver/src/main/java/org/jivesoftware/openfire/csi/CsiManager.java

@guusdk
Copy link
Copy Markdown
Member

guusdk commented May 7, 2026

Can we fix the synchronization issue without flushing the entire queue under lock? If Stream Management taught me anything, it's that long-lived locks cause trouble.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request updates CsiManager.activate() to avoid races with queueOrPush() by atomically draining the CSI delay queue under the manager lock, then delivering drained stanzas after releasing the lock to avoid holding the lock during I/O.

Changes:

  • Atomically drains and clears the delayed-stanza queue during activation under synchronization.
  • Sets CSI state to active before delivering drained stanzas.
  • Delivers drained stanzas outside the lock to reduce lock contention during network I/O.
Comments suppressed due to low confidence (1)

xmppserver/src/main/java/org/jivesoftware/openfire/csi/CsiManager.java:159

  • activate() now calls session.deliver() once per queued stanza. Each call re-enters queueOrPush(), which re-adds the stanza to the queue only to immediately flush it again, causing extra synchronization/logging overhead compared to flushing the whole drained queue in one pass. Consider delivering the drained list without re-queuing each packet (eg by refactoring LocalClientSession/CsiManager to support delivering a batch that bypasses CSI queueing while still applying stream management accounting).
        for (final Packet packet : toDeliver) {
            try {
                session.deliver(packet);
            } catch (UnauthorizedException e) {
                Log.error("Unexpected exception while activating CSI.", e);
            }
        }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread xmppserver/src/main/java/org/jivesoftware/openfire/csi/CsiManager.java Outdated
Comment thread xmppserver/src/main/java/org/jivesoftware/openfire/csi/CsiManager.java Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
xmppserver/src/main/java/org/jivesoftware/openfire/csi/CsiManager.java (1)

138-152: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

activate() still holds the lock during I/O and only delivers one queued stanza

On Line 138-152, this implementation does not flush the full delayed queue: queue.pollLast() removes only the tail, leaving earlier entries queued. Also, because activate() is synchronized, session.deliver(...) runs while holding this lock.

✅ Suggested fix (atomic drain under lock, deliver outside lock)
-public synchronized void activate()
+public void activate()
 {
-    Log.trace("Session for '{}' to CSI 'active'", session.getAddress());
-    active = true;
-
-    // Re-submit the tail of the queue through the normal queueOrPush path. Because active is now true, queueOrPush
-    // will flush the entire queue (including all preceding stanzas) in one atomic operation.
-    final Packet tail = queue.pollLast();
-    if (tail != null) {
-        try {
-            session.deliver(tail);
-        } catch (UnauthorizedException e) {
-            Log.error("Unexpected exception while activating CSI.", e);
-        }
-    }
+    final List<Packet> toDeliver;
+    synchronized (this) {
+        Log.trace("Session for '{}' to CSI 'active'", session.getAddress());
+        active = true;
+        toDeliver = new LinkedList<>(queue);
+        queue.clear();
+        lastPush = Instant.now();
+    }
+
+    for (final Packet packet : toDeliver) {
+        try {
+            session.deliver(packet);
+        } catch (UnauthorizedException e) {
+            Log.error("Unexpected exception while activating CSI.", e);
+        }
+    }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@xmppserver/src/main/java/org/jivesoftware/openfire/csi/CsiManager.java`
around lines 138 - 152, activate() currently is synchronized and only calls
queue.pollLast() then session.deliver(...) while holding the lock, which both
leaves earlier queued stanzas and performs I/O under the object's monitor;
change activate() to set active = true under the synchronized block and then
atomically drain the entire queue into a local List (e.g., loop polling queue
until empty) while still synchronized, release the lock, and then iterate that
local list calling session.deliver(...) for each stanza outside of the
synchronized block to avoid holding the lock during I/O and ensure the whole
queue is flushed; update references to activate(), queue.pollLast(), and
session.deliver(...) accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@xmppserver/src/main/java/org/jivesoftware/openfire/csi/CsiManager.java`:
- Around line 138-152: activate() currently is synchronized and only calls
queue.pollLast() then session.deliver(...) while holding the lock, which both
leaves earlier queued stanzas and performs I/O under the object's monitor;
change activate() to set active = true under the synchronized block and then
atomically drain the entire queue into a local List (e.g., loop polling queue
until empty) while still synchronized, release the lock, and then iterate that
local list calling session.deliver(...) for each stanza outside of the
synchronized block to avoid holding the lock during I/O and ensure the whole
queue is flushed; update references to activate(), queue.pollLast(), and
session.deliver(...) accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2a3ee7c0-cc04-4bd7-a643-534e6f1e0aa5

📥 Commits

Reviewing files that changed from the base of the PR and between d1c7f00 and 62c1af7.

📒 Files selected for processing (1)
  • xmppserver/src/main/java/org/jivesoftware/openfire/csi/CsiManager.java

dwd and others added 2 commits May 7, 2026 21:50
activate() was not synchronized, causing a race with queueOrPush() on
the shared queue field. It also used an indirect flush (pollLast + re-
deliver) that could lose stanzas queued between pollLast and the re-
entry into queueOrPush.

Fix: drain the entire queue atomically under the lock, set active=true,
then deliver all stanzas outside the lock so I/O does not block other
threads from queuing new stanzas.

Co-authored-by: Junie <junie@jetbrains.com>
The implementation was partially synchronized, but lacked synchronization on activate/deactivate. By marking both methods as synchronized, there no longer is a data race.

In activate, the rescheduling of queued stanzas was previously performed outside of a lock, although its execution (in `queueOrPush`) was fully synchronized. Because of that, I don't expect this commit to introduce significant more (possibly problematic, remember Stream Management?) locking.

This commit also addresses a minor off-by-one error when checking the queue capacity.
@guusdk
Copy link
Copy Markdown
Member

guusdk commented May 7, 2026

I have rebased this to the latest head, and added changes to hopefully address all issues raised.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
xmppserver/src/test/java/org/jivesoftware/openfire/csi/CsiManagerTest.java (1)

331-343: 💤 Low value

Typo in method name: "Withother" should be "WithOther".

Minor naming issue for consistency with other test method names.

📝 Suggested fix
-    public void testCanDelayPresenceWithotherTypeReturnsFalse() throws Exception
+    public void testCanDelayPresenceWithOtherTypeReturnsFalse() throws Exception
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@xmppserver/src/test/java/org/jivesoftware/openfire/csi/CsiManagerTest.java`
around lines 331 - 343, Rename the test method in CsiManagerTest from
testCanDelayPresenceWithotherTypeReturnsFalse to
testCanDelayPresenceWithOtherTypeReturnsFalse for consistent naming; update the
method declaration and any references (the `@Test-annotated` method name) so the
signature remains public void testCanDelayPresenceWithOtherTypeReturnsFalse()
throws Exception and ensure the body still calls
CsiManager.canDelay(presenceSubscribed) and asserts assertFalse(result, ...).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@xmppserver/src/test/java/org/jivesoftware/openfire/csi/CsiManagerTest.java`:
- Around line 594-608: The test method name
testCanDelayProceedingToGroupChatReturnsTrue contradicts its assertion; update
the test in CsiManagerTest so the name matches behavior (either rename
testCanDelayProceedingToGroupChatReturnsFalse or similar) and keep the logic
that calls CsiManager.canDelay(groupchatMessage) and asserts false; ensure the
method name change is applied to the test method declaration and any references
so the test intent is clear.

---

Nitpick comments:
In `@xmppserver/src/test/java/org/jivesoftware/openfire/csi/CsiManagerTest.java`:
- Around line 331-343: Rename the test method in CsiManagerTest from
testCanDelayPresenceWithotherTypeReturnsFalse to
testCanDelayPresenceWithOtherTypeReturnsFalse for consistent naming; update the
method declaration and any references (the `@Test-annotated` method name) so the
signature remains public void testCanDelayPresenceWithOtherTypeReturnsFalse()
throws Exception and ensure the body still calls
CsiManager.canDelay(presenceSubscribed) and asserts assertFalse(result, ...).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 485d967a-3505-4f27-870e-e74da02dea7e

📥 Commits

Reviewing files that changed from the base of the PR and between 62c1af7 and 9f5ff4b.

📒 Files selected for processing (2)
  • xmppserver/src/main/java/org/jivesoftware/openfire/csi/CsiManager.java
  • xmppserver/src/test/java/org/jivesoftware/openfire/csi/CsiManagerTest.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • xmppserver/src/main/java/org/jivesoftware/openfire/csi/CsiManager.java

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
xmppserver/src/test/java/org/jivesoftware/openfire/csi/CsiManagerTest.java (1)

331-343: 💤 Low value

Typo in method name: "Withother" → "WithOther".

Minor naming inconsistency for readability.

📝 Suggested fix
-    public void testCanDelayPresenceWithotherTypeReturnsFalse() throws Exception
+    public void testCanDelayPresenceWithOtherTypeReturnsFalse() throws Exception
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@xmppserver/src/test/java/org/jivesoftware/openfire/csi/CsiManagerTest.java`
around lines 331 - 343, The test method name has a typo: rename
CsiManagerTest.testCanDelayPresenceWithotherTypeReturnsFalse to
testCanDelayPresenceWithOtherTypeReturnsFalse to fix the casing and readability;
update the method declaration in class CsiManagerTest (keep the `@Test` annotation
and body unchanged) and ensure any references (e.g., IDE-run configurations or
other tests) are updated to the new name so compilation and test discovery
continue to work.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@xmppserver/src/test/java/org/jivesoftware/openfire/csi/CsiManagerTest.java`:
- Around line 594-608: The test comment is incorrect: in
CsiManagerTest.testCanDelayProceedingToGroupChatReturnsFalse the fixture
includes a <body> and the assertion checks CsiManager.canDelay(...) returns
false, so update the comment to state that a normal groupchat message with a
body (and without subject/invites) is NOT delayable (e.g., "Normal groupchat
message with body and without subject/invites should not be delayable") so the
comment matches the test intent and the assertion message.

---

Nitpick comments:
In `@xmppserver/src/test/java/org/jivesoftware/openfire/csi/CsiManagerTest.java`:
- Around line 331-343: The test method name has a typo: rename
CsiManagerTest.testCanDelayPresenceWithotherTypeReturnsFalse to
testCanDelayPresenceWithOtherTypeReturnsFalse to fix the casing and readability;
update the method declaration in class CsiManagerTest (keep the `@Test` annotation
and body unchanged) and ensure any references (e.g., IDE-run configurations or
other tests) are updated to the new name so compilation and test discovery
continue to work.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 11373f37-1c58-44ef-9b47-34fed3f92bda

📥 Commits

Reviewing files that changed from the base of the PR and between 9f5ff4b and 6f1fb28.

📒 Files selected for processing (1)
  • xmppserver/src/test/java/org/jivesoftware/openfire/csi/CsiManagerTest.java

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.

Comment thread xmppserver/src/main/java/org/jivesoftware/openfire/csi/CsiManager.java Outdated
Comment thread xmppserver/src/test/java/org/jivesoftware/openfire/csi/CsiManagerTest.java Outdated
Comment thread xmppserver/src/test/java/org/jivesoftware/openfire/csi/CsiManagerTest.java Outdated
Comment thread xmppserver/src/test/java/org/jivesoftware/openfire/csi/CsiManagerTest.java Outdated
Comment thread xmppserver/src/main/java/org/jivesoftware/openfire/csi/CsiManager.java Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.

Comment thread xmppserver/src/main/java/org/jivesoftware/openfire/csi/CsiManager.java Outdated
Comment thread xmppserver/src/test/java/org/jivesoftware/openfire/csi/CsiManagerTest.java Outdated
@guusdk
Copy link
Copy Markdown
Member

guusdk commented May 8, 2026

@coderabbitai please review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

@guusdk Sure! I'll kick off a fresh review of the updated changes right away.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@guusdk guusdk requested a review from Copilot May 8, 2026 10:04
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comment thread xmppserver/src/test/java/org/jivesoftware/openfire/csi/CsiManagerTest.java Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
xmppserver/src/main/java/org/jivesoftware/openfire/session/LocalClientSession.java (1)

998-1005: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't mark stanzas as sent when no connection exists.

In the stream-management branch, streamManager.sentStanza(stanzaToPush) runs even when conn == null. That records the stanza as sent/ackable although nothing went over the wire, which can break SM replay/ack state after detach/resume. Keep the bookkeeping inside the same conn != null guard as the actual delivery.

Suggested fix
                 for (final Packet stanzaToPush : stanzasToPush) {
                     if (conn != null) {
                         conn.deliver(stanzaToPush);
+                        streamManager.sentStanza(stanzaToPush);
                     }
-                    streamManager.sentStanza(stanzaToPush);
                 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@xmppserver/src/main/java/org/jivesoftware/openfire/session/LocalClientSession.java`
around lines 998 - 1005, The stream-management bookkeeping currently calls
streamManager.sentStanza(stanzaToPush) even when conn is null; update
LocalClientSession so that inside the synchronized(streamManager) block the call
to streamManager.sentStanza(...) is moved into the same conn != null guard that
calls conn.deliver(...), ensuring sentStanza is only invoked when deliver
actually occurred; preserve the synchronization and ordering (call
conn.deliver(...) then streamManager.sentStanza(...)) for each Packet in the
stanzasToPush loop.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@xmppserver/src/main/java/org/jivesoftware/openfire/csi/CsiManager.java`:
- Around line 163-182: The loop in CsiManager (around drainQueue() and
session.pushPackets) can push stanzas after deactivate() wins the race; after
exiting the synchronized(this) block but before calling
session.pushPackets(stanzasToPush) re-check the CSI state (the active flag)
under the same lock and abort (set flushingOnActivate=false and return) or
re-queue the drained stanzas if the session is no longer active; specifically
update the code paths around drainQueue(), flushingOnActivate, active and
session.pushPackets(...) so you perform a synchronized check of active
immediately before calling session.pushPackets and handle the inactive case
(stop flushing or re-enqueue) to prevent delivering batches during deactivation.
- Around line 236-242: The current mustPush boolean disables all safety triggers
while flushingOnActivate is true, allowing unbounded queue growth; update the
mustPush logic (the declaration of mustPush in CsiManager) so that
flushingOnActivate only prevents regular immediate sends but does not ignore the
queue capacity or max-duration conditions—i.e., preserve checks for queue.size()
>= DELAY_QUEUE_CAPACITY.getValue() and
Instant.now().isAfter(lastPush.plus(DELAY_MAX_DURATION.getValue())) even when
flushingOnActivate is true, keeping the other conditions (DELAY_ENABLED, active,
canDelay(packet)) as before; ensure references to flushingOnActivate,
DELAY_QUEUE_CAPACITY, DELAY_MAX_DURATION, lastPush, canDelay(...), active,
DELAY_ENABLED, and queue are used to implement this adjusted boolean.

---

Outside diff comments:
In
`@xmppserver/src/main/java/org/jivesoftware/openfire/session/LocalClientSession.java`:
- Around line 998-1005: The stream-management bookkeeping currently calls
streamManager.sentStanza(stanzaToPush) even when conn is null; update
LocalClientSession so that inside the synchronized(streamManager) block the call
to streamManager.sentStanza(...) is moved into the same conn != null guard that
calls conn.deliver(...), ensuring sentStanza is only invoked when deliver
actually occurred; preserve the synchronization and ordering (call
conn.deliver(...) then streamManager.sentStanza(...)) for each Packet in the
stanzasToPush loop.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 02240cf6-a561-4cd9-b383-e7b26ce743f7

📥 Commits

Reviewing files that changed from the base of the PR and between 6f1fb28 and 4fcf386.

📒 Files selected for processing (3)
  • xmppserver/src/main/java/org/jivesoftware/openfire/csi/CsiManager.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/session/LocalClientSession.java
  • xmppserver/src/test/java/org/jivesoftware/openfire/csi/CsiManagerTest.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • xmppserver/src/test/java/org/jivesoftware/openfire/csi/CsiManagerTest.java

…n flush

Refactor CSI activation flushing to avoid holding the CsiManager monitor during delivery I/O and preserve stanza ordering across activation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants