Skip to content

Move message search cap requests off the Swing EDT - #470

Open
Arthur031221 wants to merge 3 commits into
OpenIntegrationEngine:mainfrom
Arthur031221:fix/message-search-edt-s45
Open

Arthur031221 wants to merge 3 commits into
OpenIntegrationEngine:mainfrom
Arthur031221:fix/message-search-edt-s45

Conversation

@Arthur031221

Copy link
Copy Markdown

Message Browser froze the administrator while fetching a channel's maximum message ID on the Swing event thread.

The fetch now runs in a background worker. Searches and result actions are guarded while it runs, and results are discarded if the channel changes. This picks up #317 by @NicoPiel and adds the requested reentrance guard.

Added MessageBrowserSearchTest for request threading, repeated searches, failure recovery, and cancellation. ./gradlew --no-daemon :client:test --tests '*MessageBrowserSearchTest' -PdisableSigning=true passes on Java 17.

Co-authored-by: Nico Piel 16973767+NicoPiel@users.noreply.github.com

Share the search worker with page and count requests. Capture the channel and filter before requesting the maximum message ID, and discard superseded completions when the browser changes channels.

Co-authored-by: Nico Piel <16973767+NicoPiel@users.noreply.github.com>
Signed-off-by: Arthur031221 <levi74108520963@gmail.com>
@Arthur031221
Arthur031221 marked this pull request as ready for review October 4, 2026 17:24

@gibson9583 gibson9583 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Had a look. Couple of issues that need to be resolved ranker by priority.

— Existing safety gap remains: after a new search’s page request fails, old rows remain visible while Export/Remove/Reprocess Results use the new filter. Remove Results can therefore delete a different set from the displayed rows. The new readiness guard doesn’t detect this. This also occurs on the base commit.

— New regression: Search becomes stuck. Entering an accepted 19-digit ID such as 9999999999999999999 causes numeric overflow. Search is disabled before parsing and never restored, so correcting the value doesn’t permit retry.

— New regression: deletion refreshes are lost. Removing a selected message while Count runs causes the refresh guard to discard the completion refresh. The deleted row remains displayed after counting finishes.

Signed-off-by: Arthur031221 <levi74108520963@gmail.com>
@Arthur031221

Copy link
Copy Markdown
Author

Fixed all three. Failed page loads clear the old rows and keep result actions disabled, and an overflowing ID leaves Search available for retry. A deletion refresh during Count now cancels the count worker and reloads the page, and a late count response cannot overwrite the refreshed count. I added regression tests for these cases, and the full test and build run passes.

@tonygermano tonygermano added the enhancement New feature or request label Oct 7, 2026
@gibson9583

Copy link
Copy Markdown
Contributor

Fixed all three. Failed page loads clear the old rows and keep result actions disabled, and an overflowing ID leaves Search available for retry. A deletion refresh during Count now cancels the count worker and reloads the page, and a late count response cannot overwrite the refreshed count. I added regression tests for these cases, and the full test and build run passes.

  • I havent reviewed yet, but you'll need to fix the DCO on the changes you pushed.

@Arthur031221
Arthur031221 force-pushed the fix/message-search-edt-s45 branch from 17451e6 to 3d4478a Compare October 7, 2026 12:33
@Arthur031221

Copy link
Copy Markdown
Author

Added the missing sign-off to the second commit. The tree is unchanged, only the message gained the trailer.

@mgaffigan mgaffigan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not sure whether there are any issues, but it is challenging to review given the existing design.

Added some questions. Suggestions to permit review would be:

  • Minimize changes (especially all the new state tracking for command visibility)

or

  • Extract complexity to a pure function that can be unit tested. The swing soup reduces to two functions that: start the search, update the state of the controlled UI elements after any change

I know the complexity you're adding is not the problem, but it still makes it challenging to review.

Comment on lines +3887 to +3889
if (!messageTasks.getContentPane().getComponent(3).isVisible()) {
return;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

These indices seem easy to get wrong/go stale. How can we avoid?

Please fix uniformly across the changes to this file.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done. The only change left in Frame.java is that Remove Results reads its channel and filter when it's chosen, so there are no task pane indices.

Comment on lines +731 to +736
try {
advancedSearchPopup.applySelectionsToFilter(messageFilter);
} catch (NumberFormatException e) {
parent.alertError(parent, "Invalid numeric search value.");
return false;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This validation sends out of place. Where is the rest of the filter validated?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

That catch is gone. generateMessageFilter only lost its maximum ID request, and the form is validated there like on main.

Comment on lines +747 to +755
private void cancelSearchWorker() {
SwingWorker<?, Void> previousWorker = worker;
worker = null;
counting = false;
if (previousWorker != null && !previousWorker.isDone()) {
parent.mirthClient.getServerConnection().abort(getAbortOperations());
previousWorker.cancel(true);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this safe? I don't see us checking/handling thread interrupted.

This seems like it will abort unrelated calls in progress in the app - not just those we started.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Removed that one too, nothing aborts the maximum ID request now. It isn't one of the abortable operations of MessageServletInterface, so an abort wouldn't stop it anyway. A search that gets dropped because a channel was loaded or the browser was left lets its request finish and ignores the answer. The abort in ancestorRemoved is the one main already has.

When Search was pressed and the form set no maximum message ID, the message
browser asked the server for one on the event dispatch thread, so the window
froze until the server answered. The request now runs in a background worker
and the search starts when the answer arrives. The filter is still built when
Search is pressed, and a search whose form sets a maximum ID makes no request.

Until the answer arrives the results that are listed keep their filter and
keep working, and a second Search or Enter does nothing. Loading a channel or
leaving the message browser drops a waiting search. If the request fails the
error is shown and no search starts, as before. Opening a channel still asks
on the event dispatch thread, because nothing is listed yet that could stay
usable while it waits.

Because a search can now finish while a confirmation is open, Remove Results
reads its channel and filter when the command is chosen and not after the
confirmation.

This replaces the search worker, the readiness checks and the state they
needed in the previous commits.

Co-authored-by: Nico Piel <16973767+NicoPiel@users.noreply.github.com>
Signed-off-by: Arthur031221 <levi74108520963@gmail.com>
@Arthur031221

Copy link
Copy Markdown
Author

I went back to the smallest version. Only the maximum ID request behind the Search button moves off the event thread. The task pane indices, the command visibility state, the validation catch and the abort are gone. While the request is pending the listed results keep their filter and commands, so there's nothing to hide. A Search or Enter during the request does nothing, which is the reentrance guard asked for on #317.

The only change in Frame.java is that Remove Results reads its channel and filter when the command is chosen, since a pending search can now finish while its confirmation is open.

Opening a channel still asks on the event thread, as on main. Nothing is listed then, so waiting safely would need the command state you wanted out. I can send that separately.

Against main: MessageBrowser.java +93 -23 (+78 -8 ignoring whitespace), Frame.java +5 -1, one test class with 11 tests. The description above still says result actions are guarded, which isn't true anymore.

@Arthur031221

Copy link
Copy Markdown
Author

@gibson9583 on your three points about the version you reviewed. The second and third came from disabling Search before the form was parsed and from the readiness guard that discarded a refresh. The current head has neither. A 19-digit ID still throws in the parse, same as on main, but it no longer leaves Search disabled.

The first one isn't fixed here. loadPageNumber and refresh aren't in this diff, so a failed page request after a new search can still leave the old rows listed while the result commands use the new filter, as on main.

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants