You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by pt...@apache.org on 2019/04/11 07:02:14 UTC

[ignite] branch master updated: IGNITE-11690 .NET: Fix peer assembly loading endless loop when assembly can't be found on any node

This is an automated email from the ASF dual-hosted git repository.

ptupitsyn 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 313ae88  IGNITE-11690 .NET: Fix peer assembly loading endless loop when assembly can't be found on any node
313ae88 is described below

commit 313ae8863b722dd06ed96c45595aee6244497705
Author: Aleksandr Shapkin <14...@users.noreply.github.com>
AuthorDate: Thu Apr 11 10:02:04 2019 +0300

    IGNITE-11690 .NET: Fix peer assembly loading endless loop when assembly can't be found on any node
    
    #6427
---
 .../Deployment/PeerAssemblyLoadingTest.cs          | 37 +++++++++++++++++
 .../Impl/Deployment/GetAssemblyFunc.cs             | 11 ++++--
 .../Impl/Deployment/PeerAssemblyResolver.cs        | 46 +++++++++++++++++++++-
 3 files changed, 88 insertions(+), 6 deletions(-)

diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/PeerAssemblyLoadingTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/PeerAssemblyLoadingTest.cs
index da896dd..23be6dc 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/PeerAssemblyLoadingTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/PeerAssemblyLoadingTest.cs
@@ -26,6 +26,7 @@ namespace Apache.Ignite.Core.Tests.Deployment
     using Apache.Ignite.Core.Deployment;
     using Apache.Ignite.Core.Impl;
     using Apache.Ignite.Core.Impl.Common;
+    using Apache.Ignite.Core.Impl.Deployment;
     using Apache.Ignite.Core.Tests.Process;
     using Apache.Ignite.NLog;
     using NUnit.Framework;
@@ -217,6 +218,42 @@ namespace Apache.Ignite.Core.Tests.Deployment
         }
 
         /// <summary>
+        /// Tests that we are not entering endless loop while resolving an assembly from one node to another
+        /// https://issues.apache.org/jira/browse/IGNITE-11690
+        /// </summary>
+        [Test]
+        public void TestMissingAssemblyResolutionWithEnabledPeerLoadingFlag()
+        {
+            // We need to simulate two nodes that will be interacted with each other.
+            Ignition.Start(GetConfig());
+            var ignite = Ignition.Start(GetConfig());
+
+            // Create separate thread in order to avoid program block due to the endless loop.
+            var workerThread = new Thread(() =>
+            {
+                PeerAssemblyResolver.LoadAssemblyAndGetType("Unavailable.Assembly, unavailable, Ver=1",
+                    (IIgniteInternal) ignite, Guid.Empty);
+            });
+            workerThread.Start();
+
+            bool isAssemblyResolved = workerThread.Join(TimeSpan.FromSeconds(10));
+            if (!isAssemblyResolved)
+            {
+                // Ignite instances should be disposed in TearDown method.
+                workerThread.Abort();
+            }
+
+            Assert.IsTrue(isAssemblyResolved, "Execution cancelled by timeout.");
+        }
+
+        private static IgniteConfiguration GetConfig()
+        {
+            var cfg = TestUtils.GetTestConfiguration(null, Guid.NewGuid().ToString());
+            cfg.PeerAssemblyLoadingMode = PeerAssemblyLoadingMode.CurrentAppDomain;
+            return cfg;
+        }
+
+        /// <summary>
         /// Tears down the test.
         /// </summary>
         [TearDown]
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Deployment/GetAssemblyFunc.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Deployment/GetAssemblyFunc.cs
index 8f0c266..38fa6e6 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Deployment/GetAssemblyFunc.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Deployment/GetAssemblyFunc.cs
@@ -72,11 +72,14 @@ namespace Apache.Ignite.Core.Impl.Deployment
             // Assembly may be present but not loaded - attempt to load into main context.
             try
             {
-                asm = Assembly.Load(arg.AssemblyName);
-
-                if (asm != null)
+                using (PeerAssemblyResolver.Disable())
                 {
-                    return new AssemblyRequestResult(AssemblyLoader.GetAssemblyBytes(asm), null);
+                    asm = Assembly.Load(arg.AssemblyName);
+
+                    if (asm != null)
+                    {
+                        return new AssemblyRequestResult(AssemblyLoader.GetAssemblyBytes(asm), null);
+                    }
                 }
             }
             catch (FileNotFoundException)
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Deployment/PeerAssemblyResolver.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Deployment/PeerAssemblyResolver.cs
index a9e07d9..f0c221d 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Deployment/PeerAssemblyResolver.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Deployment/PeerAssemblyResolver.cs
@@ -43,9 +43,9 @@ namespace Apache.Ignite.Core.Impl.Deployment
         {
             Debug.Assert(ignite != null);
 
-            _handler = (sender, args) => GetAssembly(ignite, args.Name, originNodeId);
+            _handler = (sender, args) => RemoteAssemblyLoadingBlock.IsActive ? null : GetAssembly(ignite, args.Name, originNodeId);
 
-            // AssemblyResolve handler is called only when aseembly can't be found via normal lookup,
+            // AssemblyResolve handler is called only when assembly can't be found via normal lookup,
             // so we won't end up loading assemblies that are already present.
             AppDomain.CurrentDomain.AssemblyResolve += _handler;
         }
@@ -210,5 +210,47 @@ namespace Apache.Ignite.Core.Impl.Deployment
                 return null;
             }
         }
+
+        /// <summary>
+        /// Special class that indicates that there is no need in remote assembly request.
+        /// </summary>
+        private sealed class RemoteAssemblyLoadingBlock : IDisposable
+        {
+            /** Block status. */
+            [ThreadStatic]
+            private static bool _isActive;
+
+            /// <summary>
+            /// Gets the block status.
+            /// </summary>
+            public static bool IsActive
+            {
+                get { return _isActive; }
+            }
+
+            /// <summary>
+            /// Constructor.
+            /// </summary>
+            public RemoteAssemblyLoadingBlock()
+            {
+                _isActive = true;
+            }
+
+            /** <inheritdoc /> */
+            public void Dispose()
+            {
+                _isActive = false;
+            }
+        }
+
+        /// <summary>
+        /// Disable remote assembly loading, default functionality will be used instead.
+        /// This is useful to prevent cycled resolution, for sample consider <see cref="GetAssemblyFunc" />.
+        /// </summary>
+        /// <returns></returns>
+        public static IDisposable Disable()
+        {
+            return new RemoteAssemblyLoadingBlock();
+        }
     }
 }