001/*-
002 * #%L
003 * HAPI FHIR JPA Server
004 * %%
005 * Copyright (C) 2014 - 2024 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.jpa.search;
021
022import ca.uhn.fhir.interceptor.model.RequestPartitionId;
023import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
024import ca.uhn.fhir.jpa.model.sched.HapiJob;
025import ca.uhn.fhir.jpa.model.sched.IHasScheduledJobs;
026import ca.uhn.fhir.jpa.model.sched.ISchedulerService;
027import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition;
028import ca.uhn.fhir.jpa.search.cache.DatabaseSearchCacheSvcImpl;
029import ca.uhn.fhir.jpa.search.cache.ISearchCacheSvc;
030import org.quartz.JobExecutionContext;
031import org.springframework.beans.factory.annotation.Autowired;
032import org.springframework.transaction.annotation.Propagation;
033import org.springframework.transaction.annotation.Transactional;
034
035import java.time.Instant;
036import java.time.temporal.ChronoUnit;
037
038import static ca.uhn.fhir.jpa.search.cache.DatabaseSearchCacheSvcImpl.SEARCH_CLEANUP_JOB_INTERVAL_MILLIS;
039
040/**
041 * Deletes old searches
042 */
043//
044// NOTE: This is not a @Service because we manually instantiate
045// it in BaseConfig. This is so that we can override the definition
046// in Smile.
047//
048public class StaleSearchDeletingSvcImpl implements IStaleSearchDeletingSvc, IHasScheduledJobs {
049
050        @Autowired
051        private JpaStorageSettings myStorageSettings;
052
053        @Autowired
054        private ISearchCacheSvc mySearchCacheSvc;
055
056        @Override
057        @Transactional(propagation = Propagation.NEVER)
058        public void pollForStaleSearchesAndDeleteThem() {
059                mySearchCacheSvc.pollForStaleSearchesAndDeleteThem(RequestPartitionId.allPartitions(), getDeadline());
060        }
061
062        /**
063         * Calculate a deadline to finish before the next scheduled run.
064         */
065        protected Instant getDeadline() {
066                return Instant.ofEpochMilli(DatabaseSearchCacheSvcImpl.now())
067                                // target a 90% duty-cycle to avoid confusing quartz
068                                .plus((long) (SEARCH_CLEANUP_JOB_INTERVAL_MILLIS * 0.90), ChronoUnit.MILLIS);
069        }
070
071        @Override
072        public void scheduleJobs(ISchedulerService theSchedulerService) {
073                ScheduledJobDefinition jobDetail = new ScheduledJobDefinition();
074                jobDetail.setId(getClass().getName());
075                jobDetail.setJobClass(Job.class);
076                theSchedulerService.scheduleClusteredJob(SEARCH_CLEANUP_JOB_INTERVAL_MILLIS, jobDetail);
077        }
078
079        public static class Job implements HapiJob {
080                @Autowired
081                private IStaleSearchDeletingSvc myTarget;
082
083                @Override
084                public void execute(JobExecutionContext theContext) {
085                        myTarget.schedulePollForStaleSearches();
086                }
087        }
088
089        @Transactional(propagation = Propagation.NEVER)
090        @Override
091        public synchronized void schedulePollForStaleSearches() {
092                if (!myStorageSettings.isSchedulingDisabled() && myStorageSettings.isEnableTaskStaleSearchCleanup()) {
093                        pollForStaleSearchesAndDeleteThem();
094                }
095        }
096}