Skip to content

O3-5765: Automatically clear queue entries on a schedule - #119

Merged
wikumChamith merged 15 commits into
openmrs:mainfrom
UjjawalPrabhat:O3-5765-scheduled-queue-clear
Oct 5, 2026
Merged

wikumChamith merged 15 commits into
openmrs:mainfrom
UjjawalPrabhat:O3-5765-scheduled-queue-clear

Conversation

@UjjawalPrabhat

@UjjawalPrabhat UjjawalPrabhat commented Jul 19, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds AutoCloseQueueEntryTask. Each minute it ends active entries whose startedAt is on or before the most recent occurrence of the configured time of day. So a run missed at that time is caught up by the next one, and entries started since then stay in the queue.

Both this and the existing visit-close task are now registered with core's scheduler as TaskDefinitions from QueueModuleActivator, replacing the module's own QueueTaskExecutor and QueueTimerTask. They appear on the Manage Scheduler page, where the interval can be changed or a task stopped.

Two new global properties:

queue.autoCloseQueueEntriesAtTime - HH:mm, blank by default, which disables clearing. An outpatient clinic would set 23:59.
queue.autoCloseQueueEntriesForQueues - comma-separated queue uuids; blank means all queues.

Blank by default so nothing changes for an existing deployment until an implementer opts in (O3-2443).

Related Issue

O3-5765

Comment thread api/src/main/java/org/openmrs/module/queue/tasks/AutoCloseQueueEntryTask.java Outdated
Comment thread api/src/main/java/org/openmrs/module/queue/tasks/AutoCloseQueueEntryTask.java Outdated
Comment thread omod/src/main/resources/config.xml
@UjjawalPrabhat
UjjawalPrabhat force-pushed the O3-5765-scheduled-queue-clear branch from b9b4520 to fb2a13d Compare July 24, 2026 13:23
@UjjawalPrabhat
UjjawalPrabhat requested a review from dkayiwa July 24, 2026 13:24
Comment thread omod/src/main/resources/config.xml
Comment thread api/src/main/java/org/openmrs/module/queue/tasks/AutoCloseQueueEntryTask.java Outdated
Comment thread api/src/main/java/org/openmrs/module/queue/tasks/AutoCloseQueueEntryTask.java Outdated
Comment thread api/src/test/java/org/openmrs/module/queue/tasks/AutoCloseQueueEntryTaskTest.java Outdated
@UjjawalPrabhat
UjjawalPrabhat requested a review from dkayiwa July 27, 2026 07:23

@dkayiwa dkayiwa left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The PR description still describes an earlier version of this. It says the default is 23:59, warns that existing deployments will begin clearing all queues at end of day once deployed, and describes the task as acting once the clock passes the configured time. None of that holds any more: the property defaults to blank, and the task works back from the most recent occurrence of the configured time. Could you refresh it? It is the first thing anyone coming to this PR reads, and as written it advertises the behaviour that got changed.

@UjjawalPrabhat
UjjawalPrabhat requested a review from dkayiwa July 28, 2026 06:06
Comment thread api/src/main/java/org/openmrs/module/queue/tasks/AutoCloseQueueEntryTask.java Outdated
@UjjawalPrabhat
UjjawalPrabhat requested a review from dkayiwa July 28, 2026 12:26

@ibacher ibacher left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

While I realize this wasn't added by this module, it would be better to use core's task scheduling features rather than ScheduledExecutorTask. That said, if we are going to use Spring scheduling, then do not call Context.getService() but inject the service from Spring.

Both tasks are now AbstractTasks registered as TaskDefinitions when the module
starts, following the pattern the reference application and chartsearchai
activators use. That retires QueueTaskExecutor and QueueTimerTask along with
the daemon token plumbing, since core runs scheduled tasks as the daemon user
itself, and replaces the two hand rolled re-entrancy flags with the isExecuting
guard AbstractTask provides.

Implementers get both tasks on the Manage Scheduler page, where the interval
can be changed or a task stopped. Nothing here overrides that afterwards: the
scheduler starts tasks with startOnStartup at server startup and restores them
across a module being started or stopped, so registration only happens once.
The definition is scheduled as it is created, which is the one case the
scheduler does not cover, of this module being installed into a running server.

Services stay behind Context lookups because a task built by TaskFactory is not
a Spring bean, which is what core's own AutoCloseVisitsTask does.
@UjjawalPrabhat

Copy link
Copy Markdown
Contributor Author

it would be better to use core's task scheduling features rather than ScheduledExecutorTask.

@ibacher Tried out using core's scheduling feat....have a look!!

@UjjawalPrabhat
UjjawalPrabhat requested a review from ibacher August 1, 2026 10:12
The unit tests stub the services the task talks through, so nothing checked
that the search criteria filter the way they assume, that the save survives
validation, or that endedAt reaches the database. This builds the task the way
the scheduler does, from the class name on a TaskDefinition, and reads the
entry back from the database rather than from the session the task used.
@jwnasambu

Copy link
Copy Markdown

@claude review

@NethmiRodrigo NethmiRodrigo 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.

Thanks @UjjawalPrabhat! A few suggestions -

Comment thread README.md Outdated
Comment thread api/src/main/java/org/openmrs/module/queue/QueueModuleActivator.java Outdated
Comment thread api/src/main/java/org/openmrs/module/queue/tasks/AutoCloseQueueEntryTask.java Outdated
Comment thread api/src/main/java/org/openmrs/module/queue/tasks/AutoCloseQueueEntryTask.java Outdated
Close queue entries through a new QueueEntryService.closeQueueEntry,
which reloads the entry and writes it with optimistic locking, so an
entry transitioned between the query and its turn in the loop is left
alone instead of being overwritten from a stale snapshot. Both tasks
use it, and the unreachable closeActiveQueueEntries is removed.

Also flush and clear the session every 250 entries so the first sweep
after a close time is configured stays linear, wrap the whole task
registration so a lookup failure cannot skip the second task, and
tighten a couple of log messages.
…e its visit

closeQueueEntry used dao.get, which returns the instance already in the
task's session, so its state checks saw the copy loaded at the start of
the run and a transition made in the meantime was overwritten. The entry
and its visit are now refreshed from the database first.

The direct UPDATE in updateIfUnmodified skips QueueEntryValidator, so the
visit rule is applied in closeQueueEntry: an entry whose visit stopped
before the close time ends at the visit stop time. If that leaves no time
after the entry started, the entry is left alone with one warning instead
of a rejected write every minute.

The catch (ValidationException) blocks in both tasks could not fire on
this path and are removed. Integration tests cover the concurrent end and
the stopped visit against the real session.
@NethmiRodrigo

Copy link
Copy Markdown
Contributor

@wikumChamith can you review this?

Comment thread api/src/main/java/org/openmrs/module/queue/api/dao/impl/QueueEntryDaoImpl.java Outdated

@ibacher ibacher left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks @UjjawalPrabhat! A random smattering of curmudgeonly comments.

Comment thread api/src/main/java/org/openmrs/module/queue/api/dao/impl/QueueEntryDaoImpl.java Outdated
Comment thread api/src/main/java/org/openmrs/module/queue/tasks/AutoCloseQueueEntryTask.java Outdated
Comment thread api/src/main/java/org/openmrs/module/queue/tasks/AutoCloseQueueEntryTask.java Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated
@NethmiRodrigo

Copy link
Copy Markdown
Contributor

@UjjawalPrabhat let me know if you'll be addressing the review, else I'll pick it up :D

Set date_changed / changed_by in QueueEntryDaoImpl.updateIfUnmodified,
which the bulk update bypasses AuditableInterceptor for, and refuse to
write an end over a row another transaction has already ended. Re-opening
an entry deliberately targets an ended row, so it stays exempt. The query
is now built with the JPA Criteria API rather than by string munging.

Restore the deprecated QueueEntryService.closeActiveQueueEntries() rather
than deleting public API someone may be relying on.

Resolve queue references in queue.autoCloseQueueEntriesForQueues by name
as well as uuid, the way concept and location references already resolve,
so the property can be configured in terms an implementer can read.

Parse the configured close time with a static DateTimeFormatter instead
of SimpleDateFormat, and trim the README section down.
Name the criteria field literals that updateIfUnmodified repeated, and
return an Optional from getQueuesToClear rather than null. An empty
Optional means no queues are configured and every queue is swept; a
present but empty list means queues were configured and none of them
resolved, which is the case that must not turn into a sweep of
everything.

Drop the evict-and-continue test in each task that only repeated its
neighbour with a different exception class, now that the two tasks catch
exceptions in one place, and stop the warning in closeQueueEntry from
blaming a visit that may not exist.
@NethmiRodrigo

Copy link
Copy Markdown
Contributor

Ping @wikumChamith can you re-review this?

Read the lazy visit before stopping it in the visit-refresh test, stop
the scheduled tasks after the activator tests, drop tests that only
exercised their own stubs, and shorten the README entries.
@UjjawalPrabhat
UjjawalPrabhat requested a review from dkayiwa October 1, 2026 07:46
Comment thread api/src/main/java/org/openmrs/module/queue/api/QueueServicesWrapper.java Outdated

@wikumChamith wikumChamith left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

A few minor nits with the code style. Not merge blocking.

Comment thread api/src/main/java/org/openmrs/module/queue/api/dao/impl/QueueEntryDaoImpl.java Outdated
Comment thread api/src/main/java/org/openmrs/module/queue/api/dao/impl/QueueEntryDaoImpl.java Outdated
Comment thread api/src/main/java/org/openmrs/module/queue/tasks/AutoCloseQueueEntryTask.java Outdated
@sonarqubecloud

sonarqubecloud Bot commented Oct 1, 2026

Copy link
Copy Markdown

@wikumChamith

Copy link
Copy Markdown
Member

@UjjawalPrabhat what about the 4 new issues Sonar is pointing out?

@UjjawalPrabhat

Copy link
Copy Markdown
Contributor Author

@UjjawalPrabhat what about the 4 new issues Sonar is pointing out?

@wikumChamith 2 of them are just reminders to remove the deprecated code someday. The other 2 are code quality improvements and aren't touched by this PR. I did rewrite them, but Daniel suggested keeping the PR scoped, so I put them back.

@wikumChamith
wikumChamith merged commit 3229748 into openmrs:main Oct 5, 2026
9 checks passed
dkayiwa added a commit to dkayiwa/openmrs-module-queue that referenced this pull request Oct 7, 2026
Brings in openmrs#119 (O3-5765) and the 3.1.0 release, and ports openmrs#119 to
Platform 3:
- updateIfUnmodified keeps openmrs#119's CriteriaUpdate, on jakarta.persistence
  and createMutationQuery.
- openmrs#119's tests move to JUnit 5. QueueModuleActivatorTest and
  AutoCloseQueueEntryTaskIntegrationTest extend the Jupiter
  BaseModuleContextSensitiveTest, and the integration test runs the task
  through core's LegacyTask, which is how the JobRunr scheduler runs a
  task definition now that TaskFactory is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

6 participants