Nikolay Izhikov created IGNITE-29181:
----------------------------------------

             Summary: P2P: in SHARED mode a job class is requested from a 
neighbour client that does not have it (regression of IGNITE-20861)
                 Key: IGNITE-29181
                 URL: https://issues.apache.org/jira/browse/IGNITE-29181
             Project: Ignite
          Issue Type: Bug
    Affects Versions: 2.17
            Reporter: Nikolay Izhikov
            Assignee: Nikolay Izhikov
         Attachments: GridP2PClientDeploymentSourceTest.java

Since IGNITE-20861 (2.17) a server in SHARED deployment mode can request a job 
class from a client node that does not have it, even though the client that 
submitted the task has it. The peer class loader gives up on the first node 
that reports the class missing, so the job fails.

h3. Scenario

Thick clients with partly overlapping class paths (different applications on 
each client):
* Client A has task {{T1}} with job {{J1}}, and task {{T2}}.
* Client B has task {{T2}} with its job, and may have {{T1}} but *not* {{J1}}.
* Peer class loading enabled, SHARED mode (default).

# A executes {{T2}}. The server creates a shared deployment {{D_A}} whose peer 
loader points at A.
# B executes {{T2}}. In {{GridDeploymentPerVersionStore#getDeployment}} the 
block added by IGNITE-20861 scans every existing shared deployment and calls 
{{dep0.deployedClass(T2)}} on {{D_A}}. {{D_A}} loads {{T2}} from A, so B is 
registered as a participant of {{D_A}} with 
{{GridDeploymentClassLoader#register}}, which puts B *first* in the loader's 
node list.
# A executes {{T1}}. {{GridDeploymentManager#getGlobalDeployment}} resolves A's 
request through {{searchDeploymentCache}} to {{D_A}} (A is its participant). 
{{D_A}}'s loader asks B first for {{T1}} and then for {{J1}}. B does not have 
{{J1}}, replies "not found", and {{GridDeploymentClassLoader#sendClassRequest}} 
breaks out of the node loop ("in case of shared resources/classes all nodes 
should have it") and caches the class as missed. The job fails with {{Failed to 
deserialize task attributes}} / {{P2PClassNotFoundException}}.

In 2.16 every submitting node got a shared deployment of its own in this case, 
so {{D_A}}'s loader only ever asked A.

Observed on a production cluster: "Ignite goes for the job code not to the 
initiator node, which has the classes, but to a neighbouring client node that 
has the task class but not the job class."

h3. Reproducer

{{GridP2PClientDeploymentSourceTest}} (attached, goes to 
{{org.apache.ignite.internal.managers.deployment}} because 
{{GridDeploymentRequest}} accessors are package-private; register it in 
{{IgniteP2PSelfTestSuite}}). It records every outgoing 
{{GridDeploymentRequest}} with its destination through 
{{TestRecordingCommunicationSpi}} and asserts that classes are requested only 
from nodes that hold them.

* {{testNeighbourClientWithoutJobClass}}: one server, client A with all classes 
of {{ignite-extdata-p2p}}, client B whose loader excludes 
{{SingleSplitTestTask$SingleSplitTestJob}} and 
{{GridSingleSplitTestJobTarget}}. A runs {{P2PTestTaskExternalPath1}}, B runs 
{{P2PTestTaskExternalPath2}}, A runs {{SingleSplitTestTask}}.
* {{testSharedMode}} / {{testContinuousMode}} / {{testPrivateMode}} / 
{{testIsolatedMode}}: two servers, two clients with the classes (separate 
loaders), two clients without; every request must target a client that has the 
classes, also after the first owner leaves.

Bisect:
||commit||testNeighbourClientWithoutJobClass||other 4||
|531eba2c86e (parent of IGNITE-20861)|pass|pass|
|be246366346 IGNITE-20861|fail: {{SingleSplitTestJob}} requested from B|pass|
|master|fail|pass|
|master with the IGNITE-20861 block in {{getDeployment}} disabled|pass|pass|

Log on the failing side:
{noformat}
Failed to find class on remote node 
[class=org.apache.ignite.tests.p2p.classic.SingleSplitTestTask$SingleSplitTestJob,
 nodeId=<client B>, clsLdrId=<client B loader>, ...]
Remote job threw user exception ...: Failed to deserialize task attributes 
[taskName=org.apache.ignite.tests.p2p.classic.SingleSplitTestTask, ..., 
taskClsLdr=GridDeploymentClassLoader [..., nodeLdrMap=HashMap {<client B>=..., 
<client A>=...}, ...]]
{noformat}

h3. Possible fixes

* Do not register a sender into a shared deployment it did not create just 
because that deployment can load the task class: a node holding the task class 
does not necessarily hold every class of the deployment.
* Or make {{GridDeploymentClassLoader#sendClassRequest}} continue with the next 
participant when one reports the class missing instead of breaking and caching 
the miss.




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to