/* * RHQ Management Platform * Copyright (C) 2005-2014 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation, and/or the GNU Lesser * General Public License, version 2.1, also as published by the Free * Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License and the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.rhq.core.domain.util; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.HibernateException; import org.hibernate.ScrollableResults; import org.hibernate.engine.spi.QueryParameters; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.event.spi.EventSource; import org.hibernate.hql.internal.ast.QueryTranslatorImpl; /** * This class can be used to quickly identify and analyze usages of JOIN FETCH together with limits on JPA queries. It * will log the JPA, generated SQL and a filtered stacktrace for each such usage. This is to enhance the diagnostics * that Hibernate itself offers that merely dumps a message about in-memory filtering of results resulting from the use * of JOIN FETCH together with limits. * * @author Lukas Krejci */ public class JoinFetchReportingQueryTranslator extends QueryTranslatorImpl { private static Log LOG = LogFactory.getLog("JOIN FETCH Performance"); public JoinFetchReportingQueryTranslator(String queryIdentifier, String query, Map enabledFilters, SessionFactoryImplementor factory) { super(queryIdentifier, query, enabledFilters, factory); } private class JoinFetchUsage { private long time; private Integer firstRow; private Integer maxRows; public JoinFetchUsage(QueryParameters queryParameters) { boolean collect = containsCollectionFetches(); boolean hasLimit = queryParameters.getRowSelection() != null && queryParameters.getRowSelection().definesLimits(); if (collect && hasLimit) { firstRow = queryParameters.getRowSelection().getFirstRow(); maxRows = queryParameters.getRowSelection().getMaxRows(); time = System.currentTimeMillis(); } } public void report(String method) { if (time != 0) { time = System.currentTimeMillis() - time; LOG.warn("Encountered a query with potentially bad performance. While this is not a bug and the " + "system functions as designed, please report this to RHQ community so that we can reimplement our" + " code to work better.\n" + method + "() with first: " + firstRow + ", max: " + maxRows + " took " + time + "ms:\n" + getQueryString() + "\n\nSQL:\n" + getSQLString() + "\n" + extractRHQCalls(new Exception())); } } private String extractRHQCalls(Throwable t) { StringBuilder bld = new StringBuilder(); StackTraceElement[] elements = t.getStackTrace(); //skip the report() and list() calls, hence 2 for (int i = 2; i < elements.length; ++i) { StackTraceElement e = elements[i]; if (e.getClassName().startsWith("org.rhq")) { bld.append("\n").append(e.toString()); } } return bld.toString(); } } @Override public List list(SessionImplementor session, QueryParameters queryParameters) throws HibernateException { JoinFetchUsage usage = new JoinFetchUsage(queryParameters); List ret = super.list(session, queryParameters); usage.report("list"); return ret; } @Override public Iterator iterate(QueryParameters queryParameters, EventSource session) throws HibernateException { JoinFetchUsage usage = new JoinFetchUsage(queryParameters); Iterator ret = super.iterate(queryParameters, session); usage.report("iterate"); return ret; } @Override public ScrollableResults scroll(QueryParameters queryParameters, SessionImplementor session) throws HibernateException { JoinFetchUsage usage = new JoinFetchUsage(queryParameters); ScrollableResults ret = super.scroll(queryParameters, session); usage.report("scroll"); return ret; } }