It is a common requirement to perform some action on resource after it is saved, but in some specific time in the future. One option would be to use Micronauts @Scheduled annotation and periodically run searches, but this does not cover all cases and adds stress on the search engine.
To solve this issue Scheduler module was created.
After scheduling some task KeFHIR will handle task execution and will do it only once.
implementation "com.kodality.kefhir:kefhir-scheduler:${kefhirVersion}"
By default scheduler will use default
database configurations as in Storage, but you can define separate database configuration with name scheduler-app
datasources:
scheduler-app:
url: jdbc:postgresql://localhost:5151/kefhirdb
username: kefhir_scheduler
password: test
maximum.pool.size: 1
driver.class.name: org.postgresql.Driver
Api service to schedule tasks.
class SchedulerService {
void schedule(String type, String identifier, Date scheduled);
void reschedule(String type, String identifier, Date scheduled);
void unschedule(String type, String identifier)
}
And for every task you need an implementation, where getType
is same as type
in service, and identifier
will be same, as provided to service when scheduling. It can be any unique string, usually it is a resource id.
interface ScheduleJobRunner {
String getType();
String run(String identifier);
}
For example every time you save a MedicationRequest
, you want to make sure it will expire after some time by changing its status.
First, you will need to implement this job:
@Singleton
public class MedicationExpireJob implements ScheduleJobRunner {
@Inject
private ResourceService resourceService;
@Inject
private ResourceFormatService resourceFormatService;
@Override
public String getType() {
return "medication-expire";
}
@Override
public String run(String identifier) {
ResourceId id = ResourceUtil.parseReference(identifier);
MedicationRequest mr = resourceFormatService.parse(resourceService.load(id).getContent());
if (mr.getStatus() != MedicationRequestStatus.ACTIVE) {
// medication already change its status, no need to cancel
return "medication request unchanged";
}
mr.setStatus(MedicationRequestStatus.STOPPED);
resourceService.save(id, resourceFormatService.compose(mr, "json"), InteractionType.UPDATE);
return "medication request cancelled";
}
}
Now after MedicationRequest is saved, we need to tell scheduler to remember it. We can take advantage of ResourceAfterSaveInterceptor to do so.
public class MedicationExpireSchedulePlugin extends ResourceAfterSaveInterceptor {
@Inject
private ResourceFormatService formatService;
@Inject
private SchedulerService schedulerService;
public MedicationExpireSchedulePlugin() {
super(ResourceAfterSaveInterceptor.FINALIZATION);
}
@Override
public void handle(ResourceVersion version) {
if (!version.getId().getResourceType().equals(ResourceType.MedicationRequest.name())) {
return;
}
MedicationRequest request = formatService.parse(version.getContent());
String reference = ResourceType.MedicationRequest.name() + "/" + request.getId();
if (request.getStatus() == MedicationRequestStatus.ACTIVE) {
schedulerService.reschedule("medication-expire", reference, DateUtils.addHours(new Date(), 72));
} else {
schedulerService.unschedule("medication-expire", reference);
}
}
}
And thats all. Now, after every MedicationRequest save or update scheduler be ready to cancel it right after 72 hours passes.