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