modules/core/domain/src/main/java/org/rhq/core/domain/measurement/Availability.java
| 2
modules/core/domain/src/main/java/org/rhq/core/domain/measurement/ResourceAvailability.java
| 14 ++
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/AvailabilityManagerBean.java
| 58 ++++++++--
3 files changed, 65 insertions(+), 9 deletions(-)
New commits:
commit 0beeff7ee267456050c572124b5e4103bd377c1c
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Thu Dec 5 11:37:30 2013 -0500
[1037616] Pull calls to find current availability out of the processing loop
Part 2 - batch the pull of ResourceAvailability records that need to
be updated due to changed availability. Pull only the necessary records.
diff --git
a/modules/core/domain/src/main/java/org/rhq/core/domain/measurement/Availability.java
b/modules/core/domain/src/main/java/org/rhq/core/domain/measurement/Availability.java
index a44a2ff..6d94181 100644
--- a/modules/core/domain/src/main/java/org/rhq/core/domain/measurement/Availability.java
+++ b/modules/core/domain/src/main/java/org/rhq/core/domain/measurement/Availability.java
@@ -67,7 +67,7 @@ import org.rhq.core.domain.resource.Resource;
@NamedQuery(name = Availability.FIND_LATEST_BY_RESOURCE_IDS, query = "" //
+ " SELECT av " //
+ " FROM Availability av " //
- + " WHERE av.resource.id IN :resourceIds " //
+ + " WHERE av.resource.id IN ( :resourceIds ) " //
+ " AND av.endTime IS NULL "), //
@NamedQuery(name = Availability.FIND_BY_RESOURCE, query = "" //
+ " SELECT av " //
diff --git
a/modules/core/domain/src/main/java/org/rhq/core/domain/measurement/ResourceAvailability.java
b/modules/core/domain/src/main/java/org/rhq/core/domain/measurement/ResourceAvailability.java
index 4a58f99..7d442a4 100644
---
a/modules/core/domain/src/main/java/org/rhq/core/domain/measurement/ResourceAvailability.java
+++
b/modules/core/domain/src/main/java/org/rhq/core/domain/measurement/ResourceAvailability.java
@@ -51,6 +51,8 @@ import org.rhq.core.domain.resource.Resource;
@NamedQueries( //
{ @NamedQuery(name = ResourceAvailability.QUERY_FIND_BY_RESOURCE_ID, query = ""
//
+ " SELECT ra FROM ResourceAvailability ra WHERE ra.resourceId = :resourceId
"),
+ @NamedQuery(name = ResourceAvailability.QUERY_FIND_BY_RESOURCE_IDS, query =
"" //
+ + " SELECT ra FROM ResourceAvailability ra WHERE ra.resourceId IN (
:resourceIds ) "),
@NamedQuery(name = ResourceAvailability.UPDATE_PLATFORM_BY_AGENT_ID, query =
"" //
+ " UPDATE ResourceAvailability " //
+ " SET availabilityType = :availabilityType " //
@@ -70,11 +72,11 @@ import org.rhq.core.domain.resource.Resource;
* Platform plugins always return up for availability. Platforms are
* only down if the check-suspect-agent's backfiller sets them down.
* Thus this agent has been backfilled if it's platform is not up.
- *
+ *
* Returns 0 if the agent has NOT been backfilled, non-zero if it is.
*/
@NamedQuery(name = ResourceAvailability.QUERY_IS_AGENT_BACKFILLED, query =
"" //
- + "SELECT COUNT(avail.id) " // return count of
+ + "SELECT COUNT(avail.id) " // return count of
+ " FROM Resource res " //
+ " JOIN res.currentAvailability avail " // we only want the current
availability
+ " WHERE res.agent.id = :agentId " // use id not name to prevent an
unnecessary join to agent table
@@ -88,6 +90,7 @@ public class ResourceAvailability implements Serializable {
public static final String TABLE_NAME = "RHQ_RESOURCE_AVAIL";
public static final String QUERY_FIND_BY_RESOURCE_ID =
"ResourceAvailability.findByResourceId";
+ public static final String QUERY_FIND_BY_RESOURCE_IDS =
"ResourceAvailability.findByResourceIds";
public static final String UPDATE_CHILD_BY_AGENT_ID =
"ResourceAvailability.updateChildByAgentId";
public static final String UPDATE_PLATFORM_BY_AGENT_ID =
"ResourceAvailability.updatePlatformByAgentId";
public static final String QUERY_IS_AGENT_BACKFILLED =
"ResourceAvailability.isAgentBackfilled";
@@ -137,6 +140,13 @@ public class ResourceAvailability implements Serializable {
}
/**
+ * Lightweight way to get only the resource ID, as getResource goes through a lazy
proxy
+ */
+ public int getResourceId() {
+ return resourceId;
+ }
+
+ /**
* @return availability type
*/
public AvailabilityType getAvailabilityType() {
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/AvailabilityManagerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/AvailabilityManagerBean.java
index e54b167..0e73794 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/AvailabilityManagerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/AvailabilityManagerBean.java
@@ -822,7 +822,8 @@ public class AvailabilityManagerBean implements
AvailabilityManagerLocal, Availa
}
q.setParameter("resourceIds", resourceIds);
List<Availability> latestAvailabilitiesList = q.getResultList();
- resourceIds.clear(); // perhaps helps GC
+ resourceIds.clear(); // done with this, perhaps helps GC
+ resourceIds = null;
// populate Map of resourceIds to latestAvailability
// there should be a single latest avail per resource. mark any situation where
we have multiple
@@ -837,6 +838,9 @@ public class AvailabilityManagerBean implements
AvailabilityManagerLocal, Availa
}
}
+ // keep track of the changes in availability so we can update the relevant
ResourceAvailabilities in a batch
+ List<Availability> changedAvailabilities = new
ArrayList<Availability>(availabilities.size());
+
for (Availability reported : availabilities) {
// availability reports only tell us the current state at the start time; end
time is ignored/must be null
@@ -959,7 +963,7 @@ public class AvailabilityManagerBean implements
AvailabilityManagerLocal, Availa
latest.setEndTime(reported.getStartTime());
latest = entityManager.merge(latest);
- updateResourceAvailability(reported);
+ changedAvailabilities.add(reported);
}
// our last known state was unknown, ask for a full report to ensure we
are in sync with agent
@@ -980,7 +984,13 @@ public class AvailabilityManagerBean implements
AvailabilityManagerLocal, Availa
}
}
- latestAvailabilities.clear(); // perhaps helps GC
+ // update the affected ResourceAvailabilities
+ updateResourceAvailabilities(changedAvailabilities);
+
+ latestAvailabilities.clear(); // done with these, perhaps helps GC
+ latestAvailabilities = null;
+ changedAvailabilities.clear();
+ changedAvailabilities = null;
// notify alert condition cache manager for all reported avails for for enabled
resources
availabilities.removeAll(disabledAvailabilities);
@@ -991,12 +1001,15 @@ public class AvailabilityManagerBean implements
AvailabilityManagerLocal, Availa
}
private void updateResourceAvailability(Availability reported) {
- // update the last known availability data for this resource
ResourceAvailability currentAvailability =
resourceAvailabilityManager.getLatestAvailability(reported
.getResource().getId());
+ updateResourceAvailabilityIfNecessary(reported, currentAvailability);
+ }
+
+ // update the current availability data for this resource but only if necessary
(actually changed)
+ private void updateResourceAvailabilityIfNecessary(Availability reported,
ResourceAvailability currentAvailability) {
if (currentAvailability != null &&
currentAvailability.getAvailabilityType() != reported.getAvailabilityType()) {
- // but only update the record if necessary (if the AvailabilityType changed)
currentAvailability.setAvailabilityType(reported.getAvailabilityType());
entityManager.merge(currentAvailability);
@@ -1004,11 +1017,44 @@ public class AvailabilityManagerBean implements
AvailabilityManagerLocal, Availa
// This should not happen unless the Resource in the report is stale, which
can happen in certain
// sync scenarios. A Resource is given its initial ResourceAvailability when
it is persisted so it
// is guaranteed to have currentAvailability, so, the Resource must not
exist.
- log.info("Skipping updateResourceAvailability() for stale resource
[" + reported.getResource()
+ log.info("Skipping updateResourceAvailabilityIfNecessary() for stale
resource [" + reported.getResource()
+ "]. These messages should go away after the next agent
synchronization with the server.");
}
}
+ // pulls all the ResourceAvailabilities in one query to reduce DB round trips
+ private void updateResourceAvailabilities(List<Availability> reportedChanges)
{
+ if (null == reportedChanges || reportedChanges.isEmpty()) {
+ return;
+ }
+
+ Query q =
entityManager.createNamedQuery(ResourceAvailability.QUERY_FIND_BY_RESOURCE_IDS);
+ List<Integer> resourceIds = new
ArrayList<Integer>(reportedChanges.size());
+ for (Availability reported : reportedChanges) {
+ resourceIds.add(reported.getResource().getId());
+ }
+ q.setParameter("resourceIds", resourceIds);
+ List<ResourceAvailability> resourceAvailabilityList = q.getResultList();
+ resourceIds.clear(); // done with this, perhaps helps GC
+ resourceIds = null;
+
+ // populate Map of resourceIds to resourceAvailability
+ Map<Integer, ResourceAvailability> resourceAvailabilities = new
HashMap(reportedChanges.size());
+ for (ResourceAvailability resourceAvailability : resourceAvailabilityList) {
+ resourceAvailabilities.put(resourceAvailability.getResourceId(),
resourceAvailability);
+ }
+
+ for (Availability reported : reportedChanges) {
+ ResourceAvailability currentAvailability =
resourceAvailabilities.get(reported.getResource().getId());
+
+ // update the last known availability data for this resource but only if
necessary (actually changed)
+ updateResourceAvailabilityIfNecessary(reported, currentAvailability);
+ }
+
+ resourceAvailabilities.clear(); // done with these, perhaps helps GC
+ resourceAvailabilities = null;
+ }
+
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void updateLastAvailabilityReportInNewTransaction(int agentId) {
// should we catch exceptions here, or allow them to bubble up and be caught?