details: https://code.openbravo.com/erp/devel/pi/rev/4524f5da317f changeset: 32968:4524f5da317f user: Inigo Sanchez <inigo.sanchez <at> openbravo.com> date: Wed Nov 08 12:25:15 2017 +0100 summary: Fixes issue 35838: Improved the performance in Alert Management window
When you had several alerts (the problem was reported with +20000 alerts) and the Alert Management window was opening, there was a performance problem: The alerts weren't show, java was consuming the memory,etc. Several refactors and improvements have been done in order to improve the performance in Alert Management window: - The getWhereAndFilterClause method was performing twice per alert status. It is removed the invocation from the fetch method in ADAlertDatasourceService because it is done in the fetch method of the super class. - It is not neccesary to retrieves the full alert object when only the alert ID is needed. The same for AlertRule. - Now the status of the alert is take into account in the queries in order to retrieves from the DB only the alerts that should be needed. - StringBuilder is used instead of String when concatenate several IDs. Now the performance of the window is improvement a lot. For example, having more than 20000 alerts, it has been reduce the time from 40.1 seconds to 0.6 seconds (Times in DataSourceServlet.doFetch) showing to the user the same information. diffstat: modules/org.openbravo.client.application/src/org/openbravo/client/application/ADAlertDatasourceService.java | 170 +++++---- 1 files changed, 87 insertions(+), 83 deletions(-) diffs (259 lines): diff -r f1b82b7deb61 -r 4524f5da317f modules/org.openbravo.client.application/src/org/openbravo/client/application/ADAlertDatasourceService.java --- a/modules/org.openbravo.client.application/src/org/openbravo/client/application/ADAlertDatasourceService.java Wed Nov 08 18:32:01 2017 +0100 +++ b/modules/org.openbravo.client.application/src/org/openbravo/client/application/ADAlertDatasourceService.java Wed Nov 08 12:25:15 2017 +0100 @@ -11,7 +11,7 @@ * under the License. * The Original Code is Openbravo ERP. * The Initial Developer of the Original Code is Openbravo SLU - * All portions are Copyright (C) 2015-2016 Openbravo SLU + * All portions are Copyright (C) 2015-2017 Openbravo SLU * All Rights Reserved. * Contributor(s): ______________________________________. ************************************************************************ @@ -19,8 +19,11 @@ package org.openbravo.client.application; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import javax.servlet.ServletException; @@ -34,11 +37,7 @@ import org.openbravo.client.kernel.RequestContext; import org.openbravo.dal.core.OBContext; import org.openbravo.dal.service.OBDal; -import org.openbravo.dal.service.OBQuery; import org.openbravo.erpCommon.utility.UsedByLink; -import org.openbravo.model.ad.alert.Alert; -import org.openbravo.model.ad.alert.AlertRecipient; -import org.openbravo.model.ad.alert.AlertRule; import org.openbravo.service.datasource.DefaultDataSourceService; import org.openbravo.service.json.JsonConstants; import org.slf4j.Logger; @@ -51,6 +50,8 @@ private static final String AD_TABLE_ID = "594"; private static final String ALERT_STATUS = "_alertStatus"; private static final String ALERT_RULE_TAB = "alertRule.tab.id"; + private static final int ALERT_RULE_ID = 0; + private static final int ALERT_RULE_FILTERCLAUSE = 1; private static final Logger log = LoggerFactory.getLogger(ADAlertDatasourceService.class); @Override @@ -70,9 +71,6 @@ alertStatus = parameters.get(ALERT_STATUS); alertStatus = StringUtils.isEmpty(alertStatus) ? "" : alertStatus.toUpperCase(); - String whereClause = getWhereAndFilterClause(parameters); - parameters.put(JsonConstants.WHERE_AND_FILTER_CLAUSE, whereClause); - if (parameters.get(JsonConstants.DISTINCT_PARAMETER) == null) { // Also return the tab id of the alert rule, just when loading the grid from the server. // This is used in the Alert Management window to navigate to the record related to an alert @@ -95,68 +93,77 @@ // Alerts. Alerts are filtered based on each user/role. } - private List<String> getAlertIds() { + private List<String> getAlertIds(String alertStatus) { // Get alert rules visible for context's the role/user. - try { - OBContext.setAdminMode(false); - StringBuffer whereClause = new StringBuffer(); - whereClause.append(" as ar "); - whereClause.append("\nwhere exists (select 1 from ar." - + AlertRule.PROPERTY_ADALERTRECIPIENTLIST + " as arr"); - whereClause.append("\n where arr." + AlertRecipient.PROPERTY_USERCONTACT + ".id = :user"); - whereClause.append("\n or ("); - whereClause.append("arr." + AlertRecipient.PROPERTY_USERCONTACT + " is null"); - whereClause.append("\n and arr." + AlertRecipient.PROPERTY_ROLE + ".id = :role))"); - - OBQuery<AlertRule> alertRulesQuery = OBDal.getInstance().createQuery(AlertRule.class, - whereClause.toString()); - alertRulesQuery.setNamedParameter("user", OBContext.getOBContext().getUser().getId()); - alertRulesQuery.setNamedParameter("role", OBContext.getOBContext().getRole().getId()); - - return getAlertIdsFromAlertRules(alertRulesQuery.list()); - } finally { - OBContext.restorePreviousMode(); - } + final String sql = "SELECT arule.ad_alertrule_id, arule.filterclause" + + " FROM ad_alertrule arule " + + " WHERE (EXISTS(SELECT 1 FROM ad_alertrecipient arecipient" + + " WHERE arule.ad_alertrule_id = arecipient.ad_alertrule_id" + + " AND (arecipient.ad_user_id = :userId OR (arecipient.ad_user_id is null)" + + " AND arecipient.ad_role_id = :roleId)))"// + + " AND arule.ad_client_id " + OBDal.getInstance().getReadableClientsInClause() + + " AND ad_org_id " + OBDal.getInstance().getReadableOrganizationsInClause() + + " AND arule.isactive='Y'"; + final SQLQuery alertRules = OBDal.getInstance().getSession().createSQLQuery(sql); + alertRules.setParameter("userId", OBContext.getOBContext().getUser().getId()); + alertRules.setParameter("roleId", OBContext.getOBContext().getRole().getId()); + return getAlertIdsFromAlertRules(getAlertRulesGroupedByFilterClause(alertRules), alertStatus); } - private List<String> getAlertIdsFromAlertRules(List<AlertRule> alertRules) { - List<String> alertIds = new ArrayList<String>(); - for (AlertRule alertRule : alertRules) { - // Adding alert rule if it has not filter clause. In case it has, it will be added only in - // case it returns data after applying the filter clause. - if (alertRule.getFilterClause() == null) { - for (Alert alert : alertRule.getADAlertList()) { - alertIds.add(alert.getId()); + /** + * The method groups the AlertRule IDS by taking into account when they have the same filter + * clause. + */ + private Map<String, List<String>> getAlertRulesGroupedByFilterClause(SQLQuery alertRules) { + Map<String, List<String>> alertRulesIdGroupByFilterClauses = new HashMap<>(); + try { + for (Object resultObject : alertRules.list()) { + final Object[] resultAlertRules = (Object[]) resultObject; + String alertRuleId = resultAlertRules[ALERT_RULE_ID].toString(); + String alertRuleFilterClause = resultAlertRules[ALERT_RULE_FILTERCLAUSE] == null ? "" + : resultAlertRules[ALERT_RULE_FILTERCLAUSE].toString(); + if (alertRulesIdGroupByFilterClauses.containsKey(alertRuleFilterClause)) { + List<String> oldIds = alertRulesIdGroupByFilterClauses.get(alertRuleFilterClause); + oldIds.add(alertRuleId); + alertRulesIdGroupByFilterClauses.put(alertRuleFilterClause, oldIds); + } else { + List<String> newFilterClause = new ArrayList<>(); + newFilterClause.add(alertRuleId); + alertRulesIdGroupByFilterClauses.put(alertRuleFilterClause, newFilterClause); } } + } catch (SQLGrammarException e) { + log.error("An error has ocurred when trying to process the alert rules: " + e.getMessage(), e); + } + return alertRulesIdGroupByFilterClauses; + } - String filterClause = null; - if (alertRule.getFilterClause() != null) { - try { - filterClause = new UsedByLink().getWhereClause(RequestContext.get() - .getVariablesSecureApp(), "", alertRule.getFilterClause()); - } catch (ServletException e) { - throw new IllegalStateException(e); - } - final String sql = "select * from AD_ALERT where ISACTIVE='Y'" + " AND AD_CLIENT_ID " - + OBDal.getInstance().getReadableClientsInClause() + " AND AD_ORG_ID " - + OBDal.getInstance().getReadableOrganizationsInClause() + " AND AD_ALERTRULE_ID = ? " - + (filterClause == null ? "" : filterClause); - final SQLQuery sqlQuery = OBDal.getInstance().getSession().createSQLQuery(sql) - .addEntity(Alert.ENTITY_NAME); - sqlQuery.setParameter(0, alertRule.getId()); - - try { - @SuppressWarnings("unchecked") - List<Alert> alertsWithFilterClause = sqlQuery.list(); - log.debug("Alert " + alertRule.getName() + " (" + alertRule.getId() + ") - SQL:'" + sql - + "' - Rows: " + alertsWithFilterClause.size()); - for (Alert alert : alertsWithFilterClause) { - alertIds.add(alert.getId()); - } - } catch (SQLGrammarException e) { - log.error("An error has ocurred when trying to process the alerts: " + e.getMessage(), e); - } + private List<String> getAlertIdsFromAlertRules( + Map<String, List<String>> alertRulesGroupByFilterClause, String alertStatus) { + List<String> alertIds = new ArrayList<String>(); + for (Entry<String, List<String>> alertRuleList : alertRulesGroupByFilterClause.entrySet()) { + String filterClause; + try { + filterClause = new UsedByLink().getWhereClause( + RequestContext.get().getVariablesSecureApp(), "", alertRuleList.getKey()); + } catch (ServletException e) { + throw new IllegalStateException(e); + } + final String sql = "SELECT ad_alert_id FROM ad_alert WHERE isactive='Y'" + + " AND ad_client_id " + OBDal.getInstance().getReadableClientsInClause() + + " AND ad_org_id " + OBDal.getInstance().getReadableOrganizationsInClause() + + " AND ad_alertrule_id IN (" + toStringList(alertRuleList.getValue()) + ")" + + filterClause + " AND coalesce(to_char(status), 'NEW') = '" + + StringEscapeUtils.escapeSql(alertStatus) + "'"; + final SQLQuery sqlQuery = OBDal.getInstance().getSession().createSQLQuery(sql); + try { + @SuppressWarnings("unchecked") + List<String> alertsFound = sqlQuery.list(); + log.debug("Alerts ID: " + alertRuleList.getValue().toString() + ") - SQL:'" + sql + + "' - Rows: " + alertsFound.size()); + alertIds.addAll(alertsFound); + } catch (SQLGrammarException e) { + log.error("An error has ocurred when trying to process the alerts: " + e.getMessage(), e); } } return alertIds; @@ -168,11 +175,9 @@ alertStatus = parameters.get(ALERT_STATUS); alertStatus = StringUtils.isEmpty(alertStatus) ? "" : alertStatus.toUpperCase(); - List<String> alertList = getAlertIds(); + List<String> alertList = getAlertIds(alertStatus); int chunkSize = 1000; - String filterClause; - String whereClause = "coalesce(to_char(status), 'NEW') = '" - + StringEscapeUtils.escapeSql(alertStatus) + "'"; + String whereClause = ""; ArrayList<String> alertListToRemove; if (alertList.size() == 0) { @@ -180,40 +185,39 @@ } if (alertList.size() <= chunkSize) { - whereClause += " and e.id in (" + toStringList(alertList) + ")"; - return whereClause; + return "e.id in (" + toStringList(alertList) + ")"; } // There are more than 1000 alerts to include in the where clause, Oracle doesn't // support it, so let's split them in chunks with <=1000 elements each alertListToRemove = new ArrayList<String>(); - filterClause = ""; while (alertList.size() > chunkSize) { alertListToRemove = new ArrayList<String>(alertList.subList(0, chunkSize - 1)); - if (StringUtils.isEmpty(filterClause)) { - filterClause = " and (e.id in (" + toStringList(alertListToRemove) + ")"; + if (StringUtils.isEmpty(whereClause)) { + whereClause = "(e.id in (" + toStringList(alertListToRemove) + ")"; } else { - filterClause += " or e.id in (" + toStringList(alertListToRemove) + ")"; + whereClause += " or e.id in (" + toStringList(alertListToRemove) + ")"; } alertList.removeAll(alertListToRemove); } if (!alertList.isEmpty()) { - filterClause += " or e.id in (" + toStringList(alertList) + "))"; + whereClause += " or e.id in (" + toStringList(alertList) + "))"; } else { - filterClause += ")"; + whereClause += ")"; } - whereClause += filterClause; return whereClause; } private String toStringList(List<String> list) { - String result = ""; - for (String s : list) { - if (!StringUtils.isEmpty(result)) { - result += ", "; + StringBuilder result = new StringBuilder(list.size() * 35); + Iterator<String> iterator = list.iterator(); + while (iterator.hasNext()) { + String item = iterator.next(); + result.append("'").append(item).append("'"); + if (iterator.hasNext()) { + result.append(","); } - result += "'" + s + "'"; } - return result; + return result.toString(); } } \ No newline at end of file ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot _______________________________________________ Openbravo-commits mailing list Openbravo-commits@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openbravo-commits