Skip to content

Fix: send buffered events when process terminates - #380

Open
buongarzoni wants to merge 6 commits into
masterfrom
fix/send-buffered-events-when-process-terminates
Open

buongarzoni wants to merge 6 commits into
masterfrom
fix/send-buffered-events-when-process-terminates

Conversation

@buongarzoni

Copy link
Copy Markdown
Collaborator

Description of the change

Please include a summary of the change and which issues are fixed.
Please also include relevant motivation and context.

Type of change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Maintenance
  • New release

Related issues

Shortcut stories and GitHub issues (delete irrelevant)

Checklists

Development

  • Lint rules pass locally
  • The code changed/added as part of this pull request has been covered with tests
  • All tests related to the changed code pass in development

Code review

  • This pull request has a descriptive title and information useful to a reviewer. There may be a screenshot or screencast attached
  • "Ready for review" label attached to the PR and reviewers assigned
  • Issue from task tracker has a link to this pull request
  • Changes have been reviewed by at least one other engineer

@buongarzoni buongarzoni self-assigned this Sep 21, 2026

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6050a31abc

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

if (sender == null) {
return;
}
sender.close(true);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Wait for any in-flight buffered send before returning

When shutdown begins while BufferedSender's scheduled daemon has already polled a payload and is still sending it, this close(true) call finds the queue empty and returns after BufferedSender.close() merely calls executorService.shutdown() without awaiting the active task. The hook then finishes, allowing the JVM to terminate the daemon mid-request and lose precisely the recent occurrence this change is intended to preserve. The shutdown path must also wait, within the configured timeout, for an already-running sender task to complete.

Useful? React with 👍 / 👎.

@rollbar-circleci-machine rollbar-circleci-machine 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.

AI Agent Review (openai, openai-astra)

Overall: The change is sound. I read every changed file and its collaborators in the checkout and found no correctness defect in production code. I did not run the tests, so I can't say whether they pass.

What I checked:

  • Hook lifecycle: Rollbar registers the hook only when flushOnShutdown() is true and there is a sender (Rollbar.java:51-53,66-87). close(boolean) removes it before closing the sender (Rollbar.java:753-757). An IllegalStateException (JVM already shutting down) and a SecurityException are both caught on add and on remove (Rollbar.java:79-86,130-137).
  • Current sender at run time: CurrentSenderProvider reads the live config under configReadLock (Rollbar.java:94-104). This matches how RollbarBase.configure swaps the config under the write lock (RollbarBase.java:141-153).
  • Timeout: SenderShutdownHook flushes on a daemon thread and waits for it with join(timeoutMillis). It deliberately avoids join(0), which would wait forever (SenderShutdownHook.java:52-74).
  • BufferedSender.close(true): it now shuts down the executor and waits for any in-flight run before draining the queue (BufferedSender.java:129-147). That closes the real gap where a run had already taken payloads off the queue while they were still being sent. A periodic task is not rescheduled after shutdown(), so the wait ends once the current run finishes.
  • Config: both new settings are carried through withConfig and ConfigImpl (ConfigBuilder.java:169-170,785-786).
  • Android: it opts out because DiskQueue already persists payloads (rollbar-android/.../Rollbar.java:489-493).
  • Style: the new Javadoc follows the existing <p> style that google_checks.xml already accepts, and the new lines are within the 100-character limit.

Notes outside the findings (these are design or out-of-diff observations, not defects):

  • Un-closed notifiers are kept alive: the registered hook holds a non-static inner CurrentSenderProvider, which holds a reference to the whole Rollbar (Rollbar.java:71,94). Any notifier never passed to close(boolean) is therefore kept until JVM exit. With the default config this adds little, because each un-closed BufferedSender already leaves a live executor thread behind. Apps that create many Rollbar instances sharing one custom sender would now accumulate hooks, though. Consider saying in the flushOnShutdown Javadoc that notifiers should be closed.
  • The 2 s bound applies only to this PR's own hook: an explicit close(true) is still unbounded (BufferedSender.java:141 plus SyncSender, which sets no socket timeouts). The logback and log4j2 appenders call it from stop() (rollbar-logback/.../RollbarAppender.java:140, rollbar-log4j2/.../RollbarAppender.java:145; neither file is in this diff). This was already true before the PR.
  • Busy loop while sending is suspended: flushQueue spins when a SenderFailureStrategy has sending suspended (BufferedSender.java:155-158,320-321). This was already true before the PR. The only implementation is Android's, and Android opts out of the hook.
  • RollbarFilter.destroy() never closes the notifier (rollbar-web/.../RollbarFilter.java:92-95; not in this diff).

Comment on lines +73 to +80
public void shouldNotPropagateFlushFailures() throws Exception {
Sender sender = mock(Sender.class);
doThrow(new IllegalStateException("network down")).when(sender).close(true);

new SenderShutdownHook(providerOf(sender), TIMEOUT).run();

verify(sender).close(true);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Low: This test can't fail, so it doesn't check what its Javadoc says. SenderShutdownHook.run() calls sender.close(true) on a separate rollbar-shutdown-flush thread (SenderShutdownHook.java:53-56,84). An exception thrown there can never reach the thread that called run(). If the catch (Exception) / catch (Throwable) blocks in FlushTask (SenderShutdownHook.java:85-91) were deleted, the IllegalStateException would go to the flush thread's uncaught-exception handler and this test would still pass. shouldDoNothingWhenThereIsNoSender (lines 117-123) has the same problem: it says 'No exception', but an NPE on the flush thread wouldn't be seen either.

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.

2 participants