You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by se...@apache.org on 2021/08/11 13:15:43 UTC
[ignite] branch master updated: IGNITE-15225 Improve logging of the
peer class loading error message - Fixes #9293.
This is an automated email from the ASF dual-hosted git repository.
sergeychugunov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new 6c04182 IGNITE-15225 Improve logging of the peer class loading error message - Fixes #9293.
6c04182 is described below
commit 6c04182ea64f2f68221a41a550dd394a93a7aa89
Author: denis-chudov <mo...@gmail.com>
AuthorDate: Tue Aug 10 17:50:15 2021 +0300
IGNITE-15225 Improve logging of the peer class loading error message - Fixes #9293.
Signed-off-by: Sergey Chugunov <se...@gmail.com>
---
.../deployment/GridDeploymentClassLoader.java | 208 ++++++++++++---------
.../deployment/GridDeploymentCommunication.java | 189 +++++++++++--------
.../ignite/internal/util/GridLogThrottle.java | 13 ++
.../internal/TestRecordingCommunicationSpi.java | 2 +-
...loymentRequestOfUnknownClassProcessingTest.java | 4 +-
.../ClassLoadingProblemExtendedLoggingTest.java | 178 ++++++++++++++++++
.../apache/ignite/tests/p2p/P2PTestPredicate.java | 1 -
.../testsuites/IgniteUriDeploymentTestSuite.java | 2 +
8 files changed, 430 insertions(+), 167 deletions(-)
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentClassLoader.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentClassLoader.java
index bb5458c..925c033 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentClassLoader.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentClassLoader.java
@@ -26,6 +26,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@@ -42,7 +43,9 @@ import org.apache.ignite.internal.util.GridByteArrayList;
import org.apache.ignite.internal.util.tostring.GridToStringExclude;
import org.apache.ignite.internal.util.tostring.GridToStringInclude;
import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.internal.util.typedef.internal.LT;
import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.SB;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteUuid;
import org.jetbrains.annotations.Nullable;
@@ -124,6 +127,9 @@ class GridDeploymentClassLoader extends ClassLoader implements GridDeploymentInf
/** */
private final Object mux = new Object();
+ /** */
+ private final String clsLdrHierarchy = classLoadersHierarchy();
+
/**
* Creates a new peer class loader.
* <p>
@@ -595,9 +601,11 @@ class GridDeploymentClassLoader extends ClassLoader implements GridDeploymentInf
synchronized (mux) {
// Skip requests for the previously missed classes.
- if (missedRsrcs != null && missedRsrcs.contains(path))
- throw new ClassNotFoundException("Failed to peer load class [class=" + name + ", nodeClsLdrIds=" +
- nodeLdrMap + ", parentClsLoader=" + getParent() + ']');
+ if (missedRsrcs != null && missedRsrcs.contains(path)) {
+ throw new ClassNotFoundException("Failed to peer load class, previous request for the same class " +
+ "has failed [class=" + name + ", nodeClsLdrIds=" +
+ nodeLdrMap + ", clsLoadersHierarchy=" + clsLdrHierarchy + ']');
+ }
// If single-node mode, then node cannot change and we simply reuse list and map.
// Otherwise, make copies that can be used outside synchronization.
@@ -605,9 +613,7 @@ class GridDeploymentClassLoader extends ClassLoader implements GridDeploymentInf
nodeLdrMapCp = singleNode ? nodeLdrMap : new HashMap<>(nodeLdrMap);
}
- IgniteCheckedException err = null;
-
- TimeoutException te = null;
+ List<IgniteException> classRequestExceptions = new ArrayList<>();
for (UUID nodeId : nodeListCp) {
if (nodeId.equals(ctx.discovery().localNode().id()))
@@ -626,80 +632,89 @@ class GridDeploymentClassLoader extends ClassLoader implements GridDeploymentInf
}
try {
- GridDeploymentResponse res = null;
-
- try {
- res = comm.sendResourceRequest(path, ldrId, node, endTime);
- }
- catch (TimeoutException e) {
- te = e;
- }
-
- if (res == null) {
- String msg = "Failed to send class-loading request to node (is node alive?) [node=" +
- node.id() + ", clsName=" + name + ", clsPath=" + path + ", clsLdrId=" + ldrId +
- ", parentClsLdr=" + getParent() + ']';
-
- if (!quiet)
- U.warn(log, msg);
- else if (log.isDebugEnabled())
- log.debug(msg);
-
- err = new IgniteCheckedException(msg);
-
- continue;
- }
+ GridDeploymentResponse res = comm.sendResourceRequest(path, ldrId, node, endTime);
if (res.success())
return res.byteSource();
+ else {
+ // In case of shared resources/classes all nodes should have it.
+ String msg = "Failed to find class on remote node [class=" + name + ", nodeId=" + node.id() +
+ ", clsLdrId=" + ldrId + ", classLoadersHierarchy=" + clsLdrHierarchy +
+ ", reason=" + res.errorMessage() + ']';
- // In case of shared resources/classes all nodes should have it.
- if (log.isDebugEnabled())
- log.debug("Failed to find class on remote node [class=" + name + ", nodeId=" + node.id() +
- ", clsLdrId=" + ldrId + ", reason=" + res.errorMessage() + ']');
+ LT.warn(log, msg);
- synchronized (mux) {
- if (missedRsrcs != null)
- missedRsrcs.add(path);
- }
+ classRequestExceptions.add(new IgniteException(msg));
+
+ synchronized (mux) {
+ if (missedRsrcs != null)
+ missedRsrcs.add(path);
+ }
- throw new ClassNotFoundException("Failed to peer load class [class=" + name + ", nodeClsLdrs=" +
- nodeLdrMapCp + ", parentClsLoader=" + getParent() + ", reason=" + res.errorMessage() + ']');
+ break;
+ }
}
catch (IgniteCheckedException e) {
// This thread should be interrupted again in communication if it
// got interrupted. So we assume that thread can be interrupted
// by processing cancellation request.
if (Thread.currentThread().isInterrupted()) {
+ String msg = "Failed to find class probably due to task/job cancellation [name=" + name +
+ ", clsLdrId=" + ldrId +
+ ", nodeId=" + nodeId +
+ ", clsLoadersHierarchy=" + clsLdrHierarchy + ']';
+
if (!quiet)
- U.error(log, "Failed to find class probably due to task/job cancellation: " + name, e);
+ U.error(log, msg, e);
else if (log.isDebugEnabled())
- log.debug("Failed to find class probably due to task/job cancellation [name=" + name +
- ", err=" + e + ']');
+ log.debug(msg);
}
else {
+ String msg = "Failed to send class-loading request to node (is node alive?) [" +
+ "node=" + node.id() +
+ ", clsName=" + name +
+ ", clsPath=" + path +
+ ", clsLdrId=" + ldrId +
+ ", clsLoadersHierarchy=" + clsLdrHierarchy +
+ ", err=" + e + ']';
+
if (!quiet)
- U.warn(log, "Failed to send class-loading request to node (is node alive?) [node=" +
- node.id() + ", clsName=" + name + ", clsPath=" + path + ", clsLdrId=" + ldrId +
- ", parentClsLdr=" + getParent() + ", err=" + e + ']');
+ U.warn(log, msg, e);
else if (log.isDebugEnabled())
- log.debug("Failed to send class-loading request to node (is node alive?) [node=" +
- node.id() + ", clsName=" + name + ", clsPath=" + path + ", clsLdrId=" + ldrId +
- ", parentClsLdr=" + getParent() + ", err=" + e + ']');
+ log.debug(msg);
- err = e;
+ classRequestExceptions.add(new IgniteException(msg, e));
}
}
+ catch (TimeoutException e) {
+ classRequestExceptions.add(new IgniteException("Failed to send class-loading request to node (is node alive?) " +
+ "[node=" + node.id() + ", clsName=" + name + ", clsPath=" + path + ", clsLdrId=" + ldrId +
+ ", clsLoadersHierarchy=" + clsLdrHierarchy + ']', e));
+ }
}
- if (te != null) {
- err.addSuppressed(te);
+ if (!classRequestExceptions.isEmpty()) {
+ IgniteException exception = classRequestExceptions.remove(0);
- throw new IgniteException(err);
+ for (Exception e : classRequestExceptions)
+ exception.addSuppressed(e);
+
+ LT.warn(log, exception.getMessage(), exception);
+
+ throw exception;
}
+ else {
+ ClassNotFoundException cnfe = new ClassNotFoundException("Failed to peer load class [" +
+ "class=" + name +
+ ", nodeClsLdrs=" + nodeLdrMapCp +
+ ", clsLoadersHierarchy=" + clsLdrHierarchy +
+ ']'
+ );
+
+ LT.warn(log, cnfe.getMessage(), cnfe);
- throw new ClassNotFoundException("Failed to peer load class [class=" + name + ", nodeClsLdrs=" +
- nodeLdrMapCp + ", parentClsLoader=" + getParent() + ']', err);
+ throw cnfe;
+ }
}
/** {@inheritDoc} */
@@ -792,12 +807,11 @@ class GridDeploymentClassLoader extends ClassLoader implements GridDeploymentInf
// Request is sent with timeout that is why we can use synchronization here.
GridDeploymentResponse res = comm.sendResourceRequest(name, ldrId, node, endTime);
- if (res == null) {
- U.warn(log, "Failed to get resource from node (is node alive?) [nodeId=" +
- node.id() + ", clsLdrId=" + ldrId + ", resName=" +
- name + ", parentClsLdr=" + getParent() + ']');
+ if (res.success()) {
+ return new ByteArrayInputStream(res.byteSource().internalArray(), 0,
+ res.byteSource().size());
}
- else if (!res.success()) {
+ else {
synchronized (mux) {
// Cache unsuccessfully loaded resource.
if (missedRsrcs != null)
@@ -807,45 +821,44 @@ class GridDeploymentClassLoader extends ClassLoader implements GridDeploymentInf
// Some frameworks like Spring often ask for the resources
// just in case - none will happen if there are no such
// resources. So we print out INFO level message.
- if (!quiet) {
- if (log.isInfoEnabled())
- log.info("Failed to get resource from node [nodeId=" +
- node.id() + ", clsLdrId=" + ldrId + ", resName=" +
- name + ", parentClsLdr=" + getParent() + ", msg=" + res.errorMessage() + ']');
- }
- else if (log.isDebugEnabled())
- log.debug("Failed to get resource from node [nodeId=" +
- node.id() + ", clsLdrId=" + ldrId + ", resName=" +
- name + ", parentClsLdr=" + getParent() + ", msg=" + res.errorMessage() + ']');
+ String msg = "Failed to get resource from node [" +
+ "nodeId=" + node.id() +
+ ", clsLdrId=" + ldrId +
+ ", resName=" + name +
+ ", classLoadersHierarchy=" + classLoadersHierarchy() +
+ ", msg=" + res.errorMessage() + ']';
+
+ LT.info(log, msg);
// Do not ask other nodes in case of shared mode all of them should have the resource.
return null;
}
- else {
- return new ByteArrayInputStream(res.byteSource().internalArray(), 0,
- res.byteSource().size());
- }
}
- catch (IgniteCheckedException e) {
+ catch (IgniteCheckedException | TimeoutException e) {
// This thread should be interrupted again in communication if it
// got interrupted. So we assume that thread can be interrupted
// by processing cancellation request.
if (Thread.currentThread().isInterrupted()) {
- if (!quiet)
- U.error(log, "Failed to get resource probably due to task/job cancellation: " + name, e);
- else if (log.isDebugEnabled())
- log.debug("Failed to get resource probably due to task/job cancellation: " + name);
+ String msg = "Failed to get resource probably due to task/job cancellation [name=" + name +
+ ", clsLdrId=" + ldrId +
+ ", nodeId=" + nodeId +
+ ", clsLoadersHierarchy=" + classLoadersHierarchy() + ']';
+
+ LT.error(log, e, msg);
}
else {
- if (!quiet)
- U.warn(log, "Failed to get resource from node (is node alive?) [nodeId=" +
- node.id() + ", clsLdrId=" + ldrId + ", resName=" +
- name + ", parentClsLdr=" + getParent() + ", err=" + e + ']');
- else if (log.isDebugEnabled())
- log.debug("Failed to get resource from node (is node alive?) [nodeId=" +
- node.id() + ", clsLdrId=" + ldrId + ", resName=" +
- name + ", parentClsLdr=" + getParent() + ", err=" + e + ']');
+ String msg = "Failed to get resource from node (is node alive?) [" +
+ "node=" + node.id() +
+ ", resName=" + name +
+ ", clsLdrId=" + ldrId +
+ ", clsLoadersHierarchy=" + classLoadersHierarchy() +
+ ", err=" + e + ']';
+
+ LT.warn(log, msg, e);
}
+
+ if (e instanceof TimeoutException)
+ throw (TimeoutException) e;
}
}
@@ -858,4 +871,27 @@ class GridDeploymentClassLoader extends ClassLoader implements GridDeploymentInf
return S.toString(GridDeploymentClassLoader.class, this);
}
}
+
+ /**
+ * @return Hierarchy of all parents of this class loader.
+ */
+ private String classLoadersHierarchy() {
+ SB sb = new SB();
+
+ sb.a(getClass().getName());
+
+ ClassLoader ldr = this;
+
+ int iterations = 100;
+
+ while (ldr.getParent() != null && iterations > 0) {
+ sb.a("->").a(ldr.getParent().getClass().getName());
+
+ ldr = ldr.getParent();
+
+ iterations--;
+ }
+
+ return sb.toString();
+ }
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentCommunication.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentCommunication.java
index 3cd5861..44a3661 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentCommunication.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentCommunication.java
@@ -37,6 +37,7 @@ import org.apache.ignite.internal.util.GridBusyLock;
import org.apache.ignite.internal.util.GridByteArrayList;
import org.apache.ignite.internal.util.lang.GridTuple;
import org.apache.ignite.internal.util.tostring.GridToStringExclude;
+import org.apache.ignite.internal.util.typedef.internal.SB;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteNotPeerDeployable;
import org.apache.ignite.lang.IgniteUuid;
@@ -183,14 +184,15 @@ class GridDeploymentCommunication {
*/
private void processResourceRequest(UUID nodeId, GridDeploymentRequest req) {
if (log.isDebugEnabled())
- log.debug("Received peer class/resource loading request [node=" + nodeId + ", req=" + req + ']');
+ log.debug("Received peer class/resource loading request [originatingNodeId=" + nodeId + ", req=" + req + ']');
if (req.responseTopic() == null) {
try {
req.responseTopic(U.unmarshal(marsh, req.responseTopicBytes(), U.resolveClassLoader(ctx.config())));
}
catch (IgniteCheckedException e) {
- U.error(log, "Failed to process deployment request (will ignore): " + req, e);
+ U.error(log, "Failed to process deployment request (will ignore) [" +
+ "originatingNodeId=" + nodeId + ", req=" + req + ']', e);
return;
}
@@ -217,7 +219,7 @@ class GridDeploymentCommunication {
Class<?> cls = Class.forName(clsName, true, ldr);
if (U.getAnnotation(cls, IgniteNotPeerDeployable.class) != null) {
- String errMsg = "Attempt to peer deploy class that has @GridNotPeerDeployable " +
+ String errMsg = "Attempt to peer deploy class that has @IgniteNotPeerDeployable " +
"annotation: " + clsName;
U.error(log, errMsg);
@@ -231,7 +233,9 @@ class GridDeploymentCommunication {
}
}
catch (LinkageError | ClassNotFoundException e) {
- U.warn(log, "Failed to resolve class: " + clsName, e);
+ U.warn(log, "Failed to resolve class [originatingNodeId=" + nodeId + ", class=" + clsName +
+ ", req=" + req + ']', e
+ );
// Defined errors can be safely ignored here, because of resource which is able to be not a class name.
// Unsuccessful response will be sent below if the resource failed to be loaded.
}
@@ -240,7 +244,7 @@ class GridDeploymentCommunication {
InputStream in = ldr.getResourceAsStream(req.resourceName());
if (in == null) {
- String errMsg = "Requested resource not found (ignoring locally): " + req.resourceName();
+ String errMsg = "Requested resource not found (ignoring locally) " + resourceRequestDetails(nodeId, req);
// Java requests the same class with BeanInfo suffix during
// introspection automatically. Usually nobody uses this kind
@@ -265,7 +269,7 @@ class GridDeploymentCommunication {
res.byteSource(bytes);
}
catch (IOException e) {
- String errMsg = "Failed to read resource due to IO failure: " + req.resourceName();
+ String errMsg = "Failed to read resource due to IO failure " + resourceRequestDetails(nodeId, req);
U.error(log, errMsg, e);
@@ -278,7 +282,8 @@ class GridDeploymentCommunication {
}
}
else {
- String errMsg = "Failed to find local deployment for peer request: " + req;
+ String errMsg = "Failed to find local deployment for peer request [" +
+ "originatingNodeId=" + nodeId + ", req=" + req + ']';
U.warn(log, errMsg);
@@ -289,6 +294,16 @@ class GridDeploymentCommunication {
sendResponse(nodeId, req.responseTopic(), res);
}
+ /** */
+ private String resourceRequestDetails(UUID nodeId, GridDeploymentRequest req) {
+ return new SB()
+ .a("[originatingNodeId=").a(nodeId)
+ .a(", resourceName=").a(req.resourceName())
+ .a(", classLoaderId=").a(req.classLoaderId())
+ .a(']')
+ .toString();
+ }
+
/**
* @param nodeId Destination node ID.
* @param topic Response topic.
@@ -346,14 +361,14 @@ class GridDeploymentCommunication {
* Sends request to the remote node and wait for response. If there is
* no response until threshold time, method returns null.
*
- *
* @param rsrcName Resource name.
* @param clsLdrId Class loader ID.
* @param dstNode Remote node request should be sent to.
* @param threshold Time in milliseconds when request is decided to
* be obsolete.
- * @return Either response value or {@code null} if timeout occurred.
+ * @return Response value.
* @throws IgniteCheckedException Thrown if there is no connection with remote node.
+ * @throws TimeoutException If request timed out.
*/
GridDeploymentResponse sendResourceRequest(final String rsrcName, IgniteUuid clsLdrId,
final ClusterNode dstNode, long threshold) throws IgniteCheckedException, TimeoutException {
@@ -364,19 +379,9 @@ class GridDeploymentCommunication {
Collection<UUID> nodeIds = activeReqNodeIds.get();
if (nodeIds != null && nodeIds.contains(dstNode.id())) {
- if (log.isDebugEnabled())
- log.debug("Node attempts to load resource from one of the requesters " +
- "[rsrcName=" + rsrcName + ", dstNodeId=" + dstNode.id() +
- ", requesters=" + nodeIds + ']');
-
- GridDeploymentResponse fake = new GridDeploymentResponse();
-
- fake.success(false);
- fake.errorMessage("Node attempts to load resource from one of the requesters " +
+ throw new IgniteCheckedException("Node attempts to load resource from one of the requesters " +
"[rsrcName=" + rsrcName + ", dstNodeId=" + dstNode.id() +
- ", requesters=" + nodeIds + ']');
-
- return fake;
+ ", requesters=" + nodeIds + ']');
}
Object resTopic = TOPIC_CLASSLOAD.topic(IgniteUuid.fromUuid(ctx.localNodeId()));
@@ -390,57 +395,9 @@ class GridDeploymentCommunication {
final GridTuple<GridDeploymentResponse> res = new GridTuple<>();
- GridLocalEventListener discoLsnr = new GridLocalEventListener() {
- @Override public void onEvent(Event evt) {
- assert evt instanceof DiscoveryEvent;
-
- assert evt.type() == EVT_NODE_LEFT || evt.type() == EVT_NODE_FAILED;
-
- DiscoveryEvent discoEvt = (DiscoveryEvent)evt;
-
- UUID nodeId = discoEvt.eventNode().id();
+ GridLocalEventListener discoLsnr = resourceDiscoListener(res, dstNode, rsrcName, qryMux);
- if (!nodeId.equals(dstNode.id()))
- // Not a destination node.
- return;
-
- GridDeploymentResponse fake = new GridDeploymentResponse();
-
- String errMsg = "Originating node left grid (resource will not be peer loaded) " +
- "[nodeId=" + dstNode.id() + ", rsrc=" + rsrcName + ']';
-
- U.warn(log, errMsg);
-
- fake.success(false);
- fake.errorMessage(errMsg);
-
- // We put fake result here to interrupt waiting peer-to-peer thread
- // because originating node has left grid.
- synchronized (qryMux) {
- res.set(fake);
-
- qryMux.notifyAll();
- }
- }
- };
-
- GridMessageListener resLsnr = new GridMessageListener() {
- @Override public void onMessage(UUID nodeId, Object msg, byte plc) {
- assert nodeId != null;
- assert msg != null;
-
- synchronized (qryMux) {
- if (!(msg instanceof GridDeploymentResponse)) {
- U.error(log, "Received unknown peer class loading response [node=" + nodeId + ", msg=" +
- msg + ']');
- }
- else
- res.set((GridDeploymentResponse)msg);
-
- qryMux.notifyAll();
- }
- }
- };
+ GridMessageListener resLsnr = resourceMessageListener(res, qryMux);
try {
ctx.io().addMessageListener(resTopic, resLsnr);
@@ -491,12 +448,7 @@ class GridDeploymentCommunication {
}
}
- if (res.get() == null) {
- U.warn(log, "Failed to receive peer response from node within duration [node=" + dstNode.id() +
- ", duration=" + (U.currentTimeMillis() - start) + ']');
- }
- else if (log.isDebugEnabled())
- log.debug("Received peer loading response [node=" + dstNode.id() + ", res=" + res.get() + ']');
+ assert res.get() != null;
return res.get();
}
@@ -506,4 +458,87 @@ class GridDeploymentCommunication {
ctx.io().removeMessageListener(resTopic, resLsnr);
}
}
+
+ /**
+ * @param res Result holder.
+ * @param dstNode Destination node.
+ * @param rsrcName Resource name.
+ * @param qryMux Mutex.
+ * @return Listener for discovery events {@code EVT_NODE_LEFT} and {@code EVT_NODE_FAILED} for class loading
+ * requests.
+ */
+ public GridLocalEventListener resourceDiscoListener(
+ GridTuple<GridDeploymentResponse> res,
+ ClusterNode dstNode,
+ String rsrcName,
+ Object qryMux
+ ) {
+ return new GridLocalEventListener() {
+ @Override public void onEvent(Event evt) {
+ assert evt instanceof DiscoveryEvent;
+
+ assert evt.type() == EVT_NODE_LEFT || evt.type() == EVT_NODE_FAILED;
+
+ DiscoveryEvent discoEvt = (DiscoveryEvent)evt;
+
+ UUID nodeId = discoEvt.eventNode().id();
+
+ if (!nodeId.equals(dstNode.id()))
+ // Not a destination node.
+ return;
+
+ GridDeploymentResponse fake = new GridDeploymentResponse();
+
+ String errMsg = "Originating node left grid (resource will not be peer loaded) " +
+ "[nodeId=" + dstNode.id() + ", rsrc=" + rsrcName + ']';
+
+ U.warn(log, errMsg);
+
+ fake.success(false);
+ fake.errorMessage(errMsg);
+
+ // We put fake result here to interrupt waiting peer-to-peer thread
+ // because originating node has left grid.
+ synchronized (qryMux) {
+ res.set(fake);
+
+ qryMux.notifyAll();
+ }
+ }
+ };
+ }
+
+ /**
+ * @param res Result holder.
+ * @param qryMux Mutex.
+ * @return Listener for response message for class loading requests.
+ */
+ public GridMessageListener resourceMessageListener(GridTuple<GridDeploymentResponse> res, Object qryMux) {
+ return new GridMessageListener() {
+ @Override public void onMessage(UUID nodeId, Object msg, byte plc) {
+ assert nodeId != null;
+ assert msg != null;
+
+ synchronized (qryMux) {
+ if (!(msg instanceof GridDeploymentResponse)) {
+ GridDeploymentResponse fake = new GridDeploymentResponse();
+
+ String errMsg = "Received unknown peer class loading response [node=" + nodeId +
+ ", msg=" + msg + ']';
+
+ U.error(log, errMsg);
+
+ fake.success(false);
+ fake.errorMessage(errMsg);
+
+ res.set(fake);
+ }
+ else
+ res.set((GridDeploymentResponse)msg);
+
+ qryMux.notifyAll();
+ }
+ }
+ };
+ }
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/GridLogThrottle.java b/modules/core/src/main/java/org/apache/ignite/internal/util/GridLogThrottle.java
index ac9c335..27fe9da 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/GridLogThrottle.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/GridLogThrottle.java
@@ -150,6 +150,19 @@ public class GridLogThrottle {
}
/**
+ * Logs warning if needed.
+ *
+ * @param log Logger.
+ * @param msg Message.
+ * @param e Error..
+ */
+ public static void warn(@Nullable IgniteLogger log, String msg, Throwable e) {
+ assert !F.isEmpty(msg);
+
+ log(log, e, msg, LogLevel.WARN, false, false);
+ }
+
+ /**
* Logs info if needed.
*
* @param log Logger.
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/TestRecordingCommunicationSpi.java b/modules/core/src/test/java/org/apache/ignite/internal/TestRecordingCommunicationSpi.java
index 8348147..95a8266 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/TestRecordingCommunicationSpi.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/TestRecordingCommunicationSpi.java
@@ -284,7 +284,7 @@ public class TestRecordingCommunicationSpi extends TcpCommunicationSpi {
/**
* @param cls Message class.
- * @param nodeName Node name.
+ * @param nodeName Name of the node where message is sent to.
*/
public void blockMessages(Class<?> cls, String nodeName) {
synchronized (this) {
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/managers/deployment/DeploymentRequestOfUnknownClassProcessingTest.java b/modules/core/src/test/java/org/apache/ignite/internal/managers/deployment/DeploymentRequestOfUnknownClassProcessingTest.java
index 1c6027f..7997e88 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/managers/deployment/DeploymentRequestOfUnknownClassProcessingTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/managers/deployment/DeploymentRequestOfUnknownClassProcessingTest.java
@@ -97,7 +97,7 @@ public class DeploymentRequestOfUnknownClassProcessingTest extends GridCommonAbs
final GridFutureAdapter<Void> testResultFut = new GridFutureAdapter<>();
final LogListener remNodeLogLsnr = LogListener
- .matches(s -> s.startsWith("Failed to resolve class: " + UNKNOWN_CLASS_NAME)).build();
+ .matches(s -> s.matches("Failed to resolve class.*?" + UNKNOWN_CLASS_NAME + ".*")).build();
remNodeLog.registerListener(remNodeLogLsnr);
@@ -115,7 +115,7 @@ public class DeploymentRequestOfUnknownClassProcessingTest extends GridCommonAbs
assertNotNull("Response should contain an error message.", errMsg);
assertTrue("Response contains unexpected error message, errorMessage=" + errMsg,
- errMsg.startsWith("Requested resource not found (ignoring locally): " + UNKNOWN_CLASS_NAME));
+ errMsg.matches("Requested resource not found \\(ignoring locally\\).*?" + UNKNOWN_CLASS_NAME + ".*"));
testResultFut.onDone();
}
diff --git a/modules/core/src/test/java/org/apache/ignite/p2p/ClassLoadingProblemExtendedLoggingTest.java b/modules/core/src/test/java/org/apache/ignite/p2p/ClassLoadingProblemExtendedLoggingTest.java
new file mode 100644
index 0000000..0aae23e
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/p2p/ClassLoadingProblemExtendedLoggingTest.java
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ignite.p2p;
+
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.TestRecordingCommunicationSpi;
+import org.apache.ignite.internal.managers.deployment.GridDeploymentRequest;
+import org.apache.ignite.internal.managers.deployment.GridDeploymentResponse;
+import org.apache.ignite.internal.util.typedef.internal.LT;
+import org.apache.ignite.testframework.ListeningTestLogger;
+import org.apache.ignite.testframework.LogListener;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import static com.google.common.primitives.Ints.asList;
+import static org.apache.ignite.configuration.DeploymentMode.SHARED;
+import static org.apache.ignite.internal.TestRecordingCommunicationSpi.spi;
+import static org.apache.ignite.testframework.GridTestUtils.setFieldValue;
+
+/**
+ * Tests of extended logging of class loading problems.
+ */
+@RunWith(Parameterized.class)
+public class ClassLoadingProblemExtendedLoggingTest extends GridCommonAbstractTest {
+ /** */
+ private ListeningTestLogger listeningLog = new ListeningTestLogger(log);
+
+ /** */
+ private IgniteEx ignite;
+
+ /** */
+ private IgniteEx client;
+
+ /** */
+ @Parameterized.Parameter(0)
+ public Integer allowSuccessfulClassRequestsCnt;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static List<Integer> allowSuccessfulClassRequestsCntList() {
+ return asList(0, 1);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+ return super.getConfiguration(igniteInstanceName)
+ .setPeerClassLoadingEnabled(true)
+ .setDeploymentMode(SHARED)
+ .setCommunicationSpi(new TestRecordingCommunicationSpi())
+ .setGridLogger(listeningLog)
+ .setNetworkTimeout(1000);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ super.beforeTest();
+
+ stopAllGrids();
+
+ LT.clear();
+
+ listeningLog.clearListeners();
+
+ ignite = startGrid(0);
+
+ client = startClientGrid(1);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+ stopAllGrids();
+
+ LT.clear();
+
+ super.afterTest();
+ }
+
+ /** Tests logging when executing job with communication problems. */
+ @Test
+ public void testTimeout() throws ClassNotFoundException {
+ LogListener lsnr1 = LogListener
+ .matches(msg -> msg
+ .replace("\n", "")
+ .matches(".*?Failed to get resource from node \\(is node alive\\?\\).*?" +
+ TimeoutException.class.getName() + ".*")
+ )
+ .build();
+
+ LogListener lsnr2 = LogListener
+ .matches("Failed to send class-loading request to node")
+ .build();
+
+ listeningLog.registerListener(lsnr1);
+ listeningLog.registerListener(lsnr2);
+
+ TestRecordingCommunicationSpi clientSpi = spi(client);
+
+ AtomicInteger reqCntr = new AtomicInteger(0);
+
+ spi(ignite).closure((node, msg) -> {
+ if (msg instanceof GridDeploymentRequest && allowSuccessfulClassRequestsCnt - reqCntr.get() <= 0)
+ clientSpi.blockMessages(GridDeploymentResponse.class, ignite.name());
+
+ reqCntr.incrementAndGet();
+ });
+
+ Class cls = getExternalClassLoader()
+ .loadClass("org.apache.ignite.tests.p2p.P2PTestTaskExternalPath1");
+
+ try {
+ client.compute().execute(cls, ignite.cluster().localNode().id());
+ }
+ catch (Exception ignored) {
+ /* No-op. */
+ }
+
+ doSleep(1500);
+
+ assertTrue(lsnr1.check() || lsnr2.check());
+
+ clientSpi.stopBlock();
+ }
+
+ /** Tests logging when executing job and class is not found on initiator. */
+ @Test
+ public void testCNFE() throws Exception {
+ LogListener srvLsnr1 = LogListener.matches("Failed to get resource from node").build();
+ LogListener srvLsnr2 = LogListener.matches("Failed to find class on remote node").build();
+ LogListener clientLsnr = LogListener.matches("Failed to resolve class").build();
+
+ listeningLog.registerListener(srvLsnr1);
+ listeningLog.registerListener(srvLsnr2);
+ listeningLog.registerListener(clientLsnr);
+
+ AtomicInteger reqCntr = new AtomicInteger(0);
+
+ spi(ignite).closure((node, msg) -> {
+ if (msg instanceof GridDeploymentRequest && allowSuccessfulClassRequestsCnt - reqCntr.get() <= 0)
+ setFieldValue(msg, "rsrcName", "asdf");
+
+ reqCntr.incrementAndGet();
+ });
+
+ Class cls = getExternalClassLoader()
+ .loadClass("org.apache.ignite.tests.p2p.P2PTestTaskExternalPath1");
+
+ try {
+ client.compute().execute(cls, ignite.cluster().localNode().id());
+ }
+ catch (Exception ignored) {
+ /* No-op. */
+ }
+
+ assertTrue(srvLsnr1.check() || srvLsnr2.check());
+ assertTrue(clientLsnr.check());
+
+ spi(ignite).closure(null);
+ }
+}
diff --git a/modules/extdata/p2p/src/main/java/org/apache/ignite/tests/p2p/P2PTestPredicate.java b/modules/extdata/p2p/src/main/java/org/apache/ignite/tests/p2p/P2PTestPredicate.java
index 1bd6ea8..f650337 100644
--- a/modules/extdata/p2p/src/main/java/org/apache/ignite/tests/p2p/P2PTestPredicate.java
+++ b/modules/extdata/p2p/src/main/java/org/apache/ignite/tests/p2p/P2PTestPredicate.java
@@ -25,7 +25,6 @@ import org.apache.ignite.lang.IgniteBiPredicate;
/**
* Test predicate for scan queries in p2p deployment tests.
*/
-@SuppressWarnings("unused")
public class P2PTestPredicate extends GridCacheIdMessage implements GridCacheDeployable, IgniteBiPredicate, Serializable {
/** */
private static final long serialVersionUID = 0L;
diff --git a/modules/urideploy/src/test/java/org/apache/ignite/testsuites/IgniteUriDeploymentTestSuite.java b/modules/urideploy/src/test/java/org/apache/ignite/testsuites/IgniteUriDeploymentTestSuite.java
index 7f975ce..a5b49fa 100644
--- a/modules/urideploy/src/test/java/org/apache/ignite/testsuites/IgniteUriDeploymentTestSuite.java
+++ b/modules/urideploy/src/test/java/org/apache/ignite/testsuites/IgniteUriDeploymentTestSuite.java
@@ -20,6 +20,7 @@ package org.apache.ignite.testsuites;
import org.apache.ignite.internal.GridTaskUriDeploymentDeadlockSelfTest;
import org.apache.ignite.internal.UriDeploymentAbsentProcessorClassTest;
import org.apache.ignite.p2p.ClassLoadingProblemExceptionTest;
+import org.apache.ignite.p2p.ClassLoadingProblemExtendedLoggingTest;
import org.apache.ignite.p2p.GridP2PDisabledSelfTest;
import org.apache.ignite.spi.deployment.uri.GridUriDeploymentClassLoaderMultiThreadedSelfTest;
import org.apache.ignite.spi.deployment.uri.GridUriDeploymentClassLoaderSelfTest;
@@ -63,6 +64,7 @@ import org.junit.runners.Suite;
GridTaskUriDeploymentDeadlockSelfTest.class,
GridP2PDisabledSelfTest.class,
ClassLoadingProblemExceptionTest.class,
+ ClassLoadingProblemExtendedLoggingTest.class,
UriDeploymentAbsentProcessorClassTest.class
})
public class IgniteUriDeploymentTestSuite {