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.util;
021
022import java.util.ArrayList;
023import java.util.Collections;
024import java.util.List;
025
026/*
027        This class encapsulate the implementation providing a workaround to a known issue involving Hibernate. If queries are used with "in" clauses with large and varying
028        numbers of parameters, this can overwhelm Hibernate's QueryPlanCache and deplete heap space. See the following link for more info:
029        https://stackoverflow.com/questions/31557076/spring-hibernate-query-plan-cache-memory-usage.
030
031        Normalizing the number of parameters in the "in" clause stabilizes the size of the QueryPlanCache, so long as the number of
032        arguments never exceeds the maximum specified below.
033*/
034public class InClauseNormalizer {
035
036        public static List<Long> normalizeIdListForInClause(List<Long> theResourceIds) {
037
038                List<Long> retVal = theResourceIds;
039
040                int listSize = theResourceIds.size();
041
042                if (listSize > 1 && listSize < 10) {
043                        retVal = padIdListWithPlaceholders(theResourceIds, 10);
044                } else if (listSize > 10 && listSize < 50) {
045                        retVal = padIdListWithPlaceholders(theResourceIds, 50);
046                } else if (listSize > 50 && listSize < 100) {
047                        retVal = padIdListWithPlaceholders(theResourceIds, 100);
048                } else if (listSize > 100 && listSize < 200) {
049                        retVal = padIdListWithPlaceholders(theResourceIds, 200);
050                } else if (listSize > 200 && listSize < 500) {
051                        retVal = padIdListWithPlaceholders(theResourceIds, 500);
052                } else if (listSize > 500 && listSize < 800) {
053                        retVal = padIdListWithPlaceholders(theResourceIds, 800);
054                }
055
056                return retVal;
057        }
058
059        private static List<Long> padIdListWithPlaceholders(List<Long> theIdList, int preferredListSize) {
060                List<Long> retVal = theIdList;
061
062                if (isUnmodifiableList(theIdList)) {
063                        retVal = new ArrayList<>(preferredListSize);
064                        retVal.addAll(theIdList);
065                }
066
067                while (retVal.size() < preferredListSize) {
068                        retVal.add(-1L);
069                }
070
071                return retVal;
072        }
073
074        private static boolean isUnmodifiableList(List<Long> theList) {
075                try {
076                        theList.addAll(Collections.emptyList());
077                } catch (Exception e) {
078                        return true;
079                }
080                return false;
081        }
082
083        private InClauseNormalizer() {}
084}