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.cache;
021
022import ca.uhn.fhir.interceptor.model.RequestPartitionId;
023import ca.uhn.fhir.jpa.entity.Search;
024
025import java.time.Instant;
026import java.util.Optional;
027
028public interface ISearchCacheSvc {
029
030        /**
031         * Places a new search of some sort in the cache, or updates an existing search. The search passed in is guaranteed to have
032         * a {@link Search#getUuid() UUID} so that is a good candidate for consistent identification.
033         *
034         * @param theSearch The search to store
035         * @return Returns a copy of the search as it was saved. Callers should use the returned Search object for any further processing.
036         */
037        Search save(Search theSearch, RequestPartitionId theRequestPartitionId);
038
039        /**
040         * Fetch a search using its UUID. The search should be fully loaded when it is returned (i.e. includes are fetched, so that access to its
041         * fields will not cause database errors if the current tranaction scope ends.
042         *
043         * @param theUuid The search UUID
044         * @return The search if it exists
045         */
046        Optional<Search> fetchByUuid(String theUuid, RequestPartitionId theRequestPartitionId);
047
048        /**
049         * TODO: this is perhaps an inappropriate responsibility for this service
050         *
051         * <p>
052         * This method marks a search as in progress, but should only allow exactly one call to do so across the cluster. This
053         * is done so that if two client threads request the next page at the exact same time (which is unlikely but not
054         * impossible) only one will actually proceed to load the next results and the other will just wait for them
055         * to arrive.
056         *
057         * @param theSearch The search to mark
058         * @return This method should return an empty optional if the search was not marked (meaning that another thread
059         * succeeded in marking it). If the search doesn't exist or some other error occurred, an exception will be thrown
060         * instead of {@link Optional#empty()}
061         */
062        Optional<Search> tryToMarkSearchAsInProgress(Search theSearch, RequestPartitionId theRequestPartitionId);
063
064        /**
065         * Look for any existing searches matching the given resource type and query string.
066         * <p>
067         * This method is allowed to perform a "best effort" return, so it can return searches that don't match the query string exactly, or
068         * which have a created timestamp before <code>theCreatedAfter</code> date. The caller is responsible for removing
069         * any inappropriate Searches and picking the most relevant one.
070         * </p>
071         *
072         * @param theResourceType The resource type of the search. Results MUST match this type
073         * @param theQueryString  The query string. Results SHOULD match this type
074         * @param theCreatedAfter Results SHOULD not include any searches created before this cutoff timestamp
075         * @param theRequestPartitionId Search should examine only the requested partitions. Cache MUST not return results matching the given partition IDs
076         * @return A collection of candidate searches
077         */
078        Optional<Search> findCandidatesForReuse(
079                        String theResourceType,
080                        String theQueryString,
081                        Instant theCreatedAfter,
082                        RequestPartitionId theRequestPartitionId);
083
084        /**
085         * This method will be called periodically to delete stale searches. Implementations are not required to do anything
086         * if they have some other mechanism for expiring stale results other than manually looking for them
087         * and deleting them.
088         */
089        void pollForStaleSearchesAndDeleteThem(RequestPartitionId theRequestPartitionId, Instant theDeadline);
090}