IGNITE-7301 .NET: Baseline topology
authorPavel Tupitsyn <ptupitsyn@apache.org>
Wed, 17 Jan 2018 12:38:20 +0000 (15:38 +0300)
committerPavel Tupitsyn <ptupitsyn@apache.org>
Wed, 17 Jan 2018 12:38:20 +0000 (15:38 +0300)
This closes #3352

28 files changed:
modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformContextImpl.java
modules/core/src/main/java/org/apache/ignite/internal/processors/platform/PlatformProcessorImpl.java
modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cluster/PlatformClusterGroup.java
modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformUtils.java
modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Common/TestUtils.DotNetCore.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
modules/platforms/dotnet/Apache.Ignite.Core.Tests/ApiParity/ClusterNodeParityTest.cs [new file with mode: 0644]
modules/platforms/dotnet/Apache.Ignite.Core.Tests/ApiParity/ClusterParityTest.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/ApiParity/ParityTest.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/DataStorageMetricsTest.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/PersistenceTest.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Compute/ComputeApiTest.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationTest.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestUtils.Common.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestUtils.Windows.cs
modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
modules/platforms/dotnet/Apache.Ignite.Core/Cluster/IBaselineNode.cs [new file with mode: 0644]
modules/platforms/dotnet/Apache.Ignite.Core/Cluster/ICluster.cs
modules/platforms/dotnet/Apache.Ignite.Core/Cluster/IClusterNode.cs
modules/platforms/dotnet/Apache.Ignite.Core/IIgnite.cs
modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemHandlers.cs
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryTypeId.cs
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/OptimizedMarshallerObject.cs [new file with mode: 0644]
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cluster/BaselineNode.cs [new file with mode: 0644]
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cluster/ClusterNodeImpl.cs
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Ignite.cs
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/Jni/Env.cs

index b141313..9e22f38 100644 (file)
@@ -72,10 +72,7 @@ import org.jetbrains.annotations.Nullable;
 import java.sql.Timestamp;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
@@ -205,27 +202,14 @@ public class PlatformContextImpl implements PlatformContext {
             BinaryRawWriterEx w = writer(out);
 
             w.writeUuid(node.id());
-
-            Map<String, Object> attrs = new HashMap<>(node.attributes());
-
-            Iterator<Map.Entry<String, Object>> attrIter = attrs.entrySet().iterator();
-
-            while (attrIter.hasNext()) {
-                Map.Entry<String, Object> entry = attrIter.next();
-
-                Object val = entry.getValue();
-
-                if (val != null && !val.getClass().getName().startsWith("java.lang"))
-                    attrIter.remove();
-            }
-
-            w.writeMap(attrs);
+            PlatformUtils.writeNodeAttributes(w, node.attributes());
             w.writeCollection(node.addresses());
             w.writeCollection(node.hostNames());
             w.writeLong(node.order());
             w.writeBoolean(node.isLocal());
             w.writeBoolean(node.isDaemon());
             w.writeBoolean(node.isClient());
+            w.writeObjectDetached(node.consistentId());
             writeClusterMetrics(w, node.metrics());
 
             out.synchronize();
index 0d88fbb..de51b3d 100644 (file)
@@ -23,6 +23,7 @@ import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteDataStreamer;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.cluster.BaselineNode;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.configuration.NearCacheConfiguration;
 import org.apache.ignite.configuration.PlatformConfiguration;
@@ -30,6 +31,7 @@ import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.binary.BinaryRawReaderEx;
 import org.apache.ignite.internal.binary.BinaryRawWriterEx;
+import org.apache.ignite.internal.cluster.DetachedClusterNode;
 import org.apache.ignite.internal.logger.platform.PlatformLogger;
 import org.apache.ignite.internal.processors.GridProcessorAdapter;
 import org.apache.ignite.internal.processors.cache.IgniteCacheProxy;
@@ -57,6 +59,7 @@ import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lang.IgniteFuture;
 import org.jetbrains.annotations.Nullable;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -140,6 +143,15 @@ public class PlatformProcessorImpl extends GridProcessorAdapter implements Platf
     /** */
     private static final int OP_ADD_CACHE_CONFIGURATION = 23;
 
+    /** */
+    private static final int OP_SET_BASELINE_TOPOLOGY_VER = 24;
+
+    /** */
+    private static final int OP_SET_BASELINE_TOPOLOGY_NODES = 25;
+
+    /** */
+    private static final int OP_GET_BASELINE_TOPOLOGY = 26;
+
     /** Start latch. */
     private final CountDownLatch startLatch = new CountDownLatch(1);
 
@@ -408,6 +420,12 @@ public class PlatformProcessorImpl extends GridProcessorAdapter implements Platf
 
                 return 0;
             }
+
+            case OP_SET_BASELINE_TOPOLOGY_VER: {
+                ctx.grid().cluster().setBaselineTopology(val);
+
+                return 0;
+            }
         }
 
         return PlatformAbstractTarget.throwUnsupported(type);
@@ -428,6 +446,22 @@ public class PlatformProcessorImpl extends GridProcessorAdapter implements Platf
                 return 0;
             }
 
+            case OP_SET_BASELINE_TOPOLOGY_NODES: {
+                int cnt = reader.readInt();
+                Collection<BaselineNode> nodes = new ArrayList<>(cnt);
+
+                for (int i = 0; i < cnt; i++) {
+                    Object consId = reader.readObjectDetached();
+                    Map<String, Object> attrs = PlatformUtils.readNodeAttributes(reader);
+
+                    nodes.add(new DetachedClusterNode(consId, attrs));
+                }
+
+                ctx.grid().cluster().setBaselineTopology(nodes);
+
+                return 0;
+            }
+
             case OP_ADD_CACHE_CONFIGURATION:
                 CacheConfiguration cfg = PlatformConfigurationUtils.readCacheConfiguration(reader);
 
@@ -614,6 +648,18 @@ public class PlatformProcessorImpl extends GridProcessorAdapter implements Platf
 
                 return;
             }
+
+            case OP_GET_BASELINE_TOPOLOGY: {
+                Collection<BaselineNode> blt = ignite().cluster().currentBaselineTopology();
+                writer.writeInt(blt.size());
+
+                for (BaselineNode n : blt) {
+                    writer.writeObjectDetached(n.consistentId());
+                    PlatformUtils.writeNodeAttributes(writer, n.attributes());
+                }
+
+                return;
+            }
         }
 
         PlatformAbstractTarget.throwUnsupported(type);
index ef382d6..e0fff66 100644 (file)
 
 package org.apache.ignite.internal.processors.platform.cluster;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.UUID;
-
 import org.apache.ignite.DataRegionMetrics;
 import org.apache.ignite.DataStorageMetrics;
 import org.apache.ignite.IgniteCache;
@@ -31,9 +27,9 @@ import org.apache.ignite.PersistenceMetrics;
 import org.apache.ignite.binary.BinaryRawWriter;
 import org.apache.ignite.cluster.ClusterMetrics;
 import org.apache.ignite.cluster.ClusterNode;
-import org.apache.ignite.internal.cluster.ClusterGroupEx;
 import org.apache.ignite.internal.binary.BinaryRawReaderEx;
 import org.apache.ignite.internal.binary.BinaryRawWriterEx;
+import org.apache.ignite.internal.cluster.ClusterGroupEx;
 import org.apache.ignite.internal.processors.platform.PlatformAbstractTarget;
 import org.apache.ignite.internal.processors.platform.PlatformContext;
 import org.apache.ignite.internal.processors.platform.PlatformTarget;
@@ -46,6 +42,10 @@ import org.apache.ignite.internal.processors.platform.utils.PlatformUtils;
 import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
 import org.jetbrains.annotations.Nullable;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.UUID;
+
 /**
  * Interop projection.
  */
@@ -344,7 +344,7 @@ public class PlatformClusterGroup extends PlatformAbstractTarget {
             case OP_PING_NODE:
                 return pingNode(reader.readUuid()) ? TRUE : FALSE;
 
-            case OP_RESET_LOST_PARTITIONS:
+            case OP_RESET_LOST_PARTITIONS: {
                 int cnt = reader.readInt();
 
                 Collection<String> cacheNames = new ArrayList<>(cnt);
@@ -356,6 +356,7 @@ public class PlatformClusterGroup extends PlatformAbstractTarget {
                 platformCtx.kernalContext().grid().resetLostPartitions(cacheNames);
 
                 return TRUE;
+            }
 
             default:
                 return super.processInStreamOutLong(type, reader);
index aa11dfa..b65ca04 100644 (file)
@@ -1225,6 +1225,47 @@ public class PlatformUtils {
     }
 
     /**
+     * Writes node attributes.
+     *
+     * @param writer Writer.
+     * @param attrs Attributes.
+     */
+    public static void writeNodeAttributes(BinaryRawWriterEx writer, Map<String, Object> attrs) {
+        assert writer != null;
+        assert attrs != null;
+
+        if (attrs != null) {
+            writer.writeInt(attrs.size());
+
+            for (Map.Entry<String, Object> e : attrs.entrySet()) {
+                writer.writeString(e.getKey());
+                writer.writeObjectDetached(e.getValue());
+            }
+        } else {
+            writer.writeInt(0);
+        }
+    }
+
+    /**
+     * Reads node attributes.
+     *
+     * @param reader Reader.
+     * @return Attributes.
+     */
+    public static Map<String, Object> readNodeAttributes(BinaryRawReaderEx reader) {
+        assert reader != null;
+
+        int attrCnt = reader.readInt();
+        Map<String, Object> attrs = new HashMap<>(attrCnt);
+
+        for (int j = 0; j < attrCnt; j++) {
+            attrs.put(reader.readString(), reader.readObjectDetached());
+        }
+
+        return attrs;
+    }
+
+    /**
      * Private constructor.
      */
     private PlatformUtils() {
index 99f732d..08cae96 100644 (file)
@@ -41,7 +41,8 @@ namespace Apache.Ignite.Core.Tests
                 Localhost = "127.0.0.1",
                 JvmOptions = TestJavaOptions(),
                 IgniteInstanceName = name,
-                Logger = TestLogger.Instance
+                Logger = TestLogger.Instance,
+                WorkDirectory = WorkDir
             };
         }
 
index 22f4ad9..ab16bf1 100644 (file)
@@ -75,6 +75,7 @@
     <Compile Include="ApiParity\CacheParityTest.cs" />
     <Compile Include="ApiParity\ClientConnectorConfigurationParityTest.cs" />
     <Compile Include="ApiParity\ClusterMetricsParityTest.cs" />
+    <Compile Include="ApiParity\ClusterNodeParityTest.cs" />
     <Compile Include="ApiParity\ClusterParityTest.cs" />
     <Compile Include="ApiParity\ComputeParityTest.cs" />
     <Compile Include="ApiParity\DataRegionConfigurationParityTest.cs" />
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ApiParity/ClusterNodeParityTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ApiParity/ClusterNodeParityTest.cs
new file mode 100644 (file)
index 0000000..fcf7f89
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+ namespace Apache.Ignite.Core.Tests.ApiParity
+{
+    using Apache.Ignite.Core.Cluster;
+    using NUnit.Framework;
+
+    /// <summary>
+    /// Tests that <see cref="IClusterNode"/> has all APIs from Java Ignite interface.
+    /// </summary>
+    public class ClusterNodeParityTest
+    {
+        /** Members that are missing on .NET side and should be added in future. */
+        private static readonly string[] MissingMembers =
+        {
+            "version"  // IGNITE-7101
+        };
+
+        /// <summary>
+        /// Tests the API parity.
+        /// </summary>
+        [Test]
+        public void TestClusterNode()
+        {
+            ParityTest.CheckInterfaceParity(
+                @"modules\core\src\main\java\org\apache\ignite\cluster\ClusterNode.java",
+                typeof(IClusterNode), MissingMembers);
+        }
+    }
+}
\ No newline at end of file
index 4b4279f..b940d09 100644 (file)
         /** Members that are missing on .NET side and should be added in future. */
         private static readonly string[] MissingMembers =
         {
-            "enableStatistics",  // IGNITE-7276
-            
-            // IGNITE-7301
-            "active",
-            "setBaselineTopology"
+            "enableStatistics"  // IGNITE-7276
         };
 
         /// <summary>
index d30d6d1..c9116af 100644 (file)
@@ -80,7 +80,7 @@ namespace Apache.Ignite.Core.Tests.ApiParity
         {
             var path = GetFullPath(javaFilePath);
 
-            var dotNetMembers = type.GetMembers()
+            var dotNetMembers = GetMembers(type)
                 .GroupBy(x => x.Name)
                 .ToDictionary(x => x.Key, x => x.First(), StringComparer.OrdinalIgnoreCase);
 
@@ -91,6 +91,30 @@ namespace Apache.Ignite.Core.Tests.ApiParity
         }
 
         /// <summary>
+        /// Gets the members.
+        /// </summary>
+        private static IEnumerable<MemberInfo> GetMembers(Type type)
+        {
+            var types = new Stack<Type>();
+            types.Push(type);
+
+            while (types.Count > 0)
+            {
+                var t = types.Pop();
+
+                foreach (var m in t.GetMembers())
+                {
+                    yield return m;
+                }
+
+                foreach (var i in t.GetInterfaces())
+                {
+                    types.Push(i);
+                }
+            }
+        }
+
+        /// <summary>
         /// Gets the full path.
         /// </summary>
         private static string GetFullPath(string javaFilePath)
index 1bc0218..d98254d 100644 (file)
@@ -56,7 +56,7 @@ namespace Apache.Ignite.Core.Tests.Cache
 
             using (var ignite = Ignition.Start(cfg))
             {
-                ignite.SetActive(true);
+                ignite.GetCluster().SetActive(true);
 
                 var cache = ignite.CreateCache<int, object>("c");
 
@@ -79,8 +79,8 @@ namespace Apache.Ignite.Core.Tests.Cache
                 Assert.AreEqual(0, metrics.WalArchiveSegments);
                 Assert.AreEqual(0, metrics.WalFsyncTimeAverage);
 
-                Assert.AreEqual(89, metrics.LastCheckpointTotalPagesNumber);
-                Assert.AreEqual(10, metrics.LastCheckpointDataPagesNumber);
+                Assert.Greater(metrics.LastCheckpointTotalPagesNumber, 26);
+                Assert.AreEqual(0, metrics.LastCheckpointDataPagesNumber);
                 Assert.AreEqual(0, metrics.LastCheckpointCopiedOnWritePagesNumber);
                 Assert.AreEqual(TimeSpan.Zero, metrics.LastCheckpointLockWaitDuration);
 
index 4a60f7b..e3c0acf 100644 (file)
@@ -19,6 +19,7 @@ namespace Apache.Ignite.Core.Tests.Cache
 {
     using System;
     using System.IO;
+    using System.Linq;
     using Apache.Ignite.Core.Cache.Configuration;
     using Apache.Ignite.Core.Common;
     using Apache.Ignite.Core.Configuration;
@@ -34,6 +35,15 @@ namespace Apache.Ignite.Core.Tests.Cache
         private readonly string _tempDir = TestUtils.GetTempDirectoryName();
 
         /// <summary>
+        /// Sets up the test.
+        /// </summary>
+        [SetUp]
+        public void SetUp()
+        {
+            TestUtils.ClearWorkDir();
+        }
+
+        /// <summary>
         /// Tears down the test.
         /// </summary>
         [TearDown]
@@ -45,6 +55,8 @@ namespace Apache.Ignite.Core.Tests.Cache
             {
                 Directory.Delete(_tempDir, true);
             }
+
+            TestUtils.ClearWorkDir();
         }
 
         /// <summary>
@@ -84,7 +96,7 @@ namespace Apache.Ignite.Core.Tests.Cache
             // Start Ignite, put data, stop.
             using (var ignite = Ignition.Start(cfg))
             {
-                ignite.SetActive(true);
+                ignite.GetCluster().SetActive(true);
 
                 // Create cache with default region (persistence enabled), add data.
                 var cache = ignite.CreateCache<int, int>(cacheName);
@@ -110,7 +122,7 @@ namespace Apache.Ignite.Core.Tests.Cache
             // Start Ignite, verify data survival.
             using (var ignite = Ignition.Start(cfg))
             {
-                ignite.SetActive(true);
+                ignite.GetCluster().SetActive(true);
 
                 // Persistent cache already exists and contains data.
                 var cache = ignite.GetCache<int, int>(cacheName);
@@ -127,7 +139,7 @@ namespace Apache.Ignite.Core.Tests.Cache
             // Start Ignite, verify data loss.
             using (var ignite = Ignition.Start(cfg))
             {
-                ignite.SetActive(true);
+                ignite.GetCluster().SetActive(true);
 
                 Assert.IsFalse(ignite.GetCacheNames().Contains(cacheName));
             }
@@ -149,27 +161,17 @@ namespace Apache.Ignite.Core.Tests.Cache
         [Test]
         public void TestGridActivationWithPersistence()
         {
-            var cfg = new IgniteConfiguration(TestUtils.GetTestConfiguration())
-            {
-                DataStorageConfiguration = new DataStorageConfiguration
-                {
-                    DefaultDataRegionConfiguration = new DataRegionConfiguration
-                    {
-                        PersistenceEnabled = true,
-                        Name = "foo"
-                    }
-                }
-            };
+            var cfg = GetPersistentConfiguration();
 
             // Default config, inactive by default (IsActiveOnStart is ignored when persistence is enabled).
             using (var ignite = Ignition.Start(cfg))
             {
                 CheckIsActive(ignite, false);
 
-                ignite.SetActive(true);
+                ignite.GetCluster().SetActive(true);
                 CheckIsActive(ignite, true);
 
-                ignite.SetActive(false);
+                ignite.GetCluster().SetActive(false);
                 CheckIsActive(ignite, false);
             }
         }
@@ -187,10 +189,10 @@ namespace Apache.Ignite.Core.Tests.Cache
             {
                 CheckIsActive(ignite, true);
 
-                ignite.SetActive(false);
+                ignite.GetCluster().SetActive(false);
                 CheckIsActive(ignite, false);
 
-                ignite.SetActive(true);
+                ignite.GetCluster().SetActive(true);
                 CheckIsActive(ignite, true);
             }
 
@@ -200,20 +202,84 @@ namespace Apache.Ignite.Core.Tests.Cache
             {
                 CheckIsActive(ignite, false);
 
-                ignite.SetActive(true);
+                ignite.GetCluster().SetActive(true);
                 CheckIsActive(ignite, true);
 
-                ignite.SetActive(false);
+                ignite.GetCluster().SetActive(false);
                 CheckIsActive(ignite, false);
             }
         }
 
         /// <summary>
+        /// Tests the baseline topology.
+        /// </summary>
+        [Test]
+        public void TestBaselineTopology()
+        {
+            var cfg1 = new IgniteConfiguration(GetPersistentConfiguration())
+            {
+                ConsistentId = "node1"
+            };
+            var cfg2 = new IgniteConfiguration(GetPersistentConfiguration())
+            {
+                ConsistentId = "node2",
+                IgniteInstanceName = "2"
+            };
+
+            using (var ignite = Ignition.Start(cfg1))
+            {
+                // Start and stop to bump topology version.
+                Ignition.Start(cfg2);
+                Ignition.Stop(cfg2.IgniteInstanceName, true);
+
+                var cluster = ignite.GetCluster();
+                Assert.AreEqual(3, cluster.TopologyVersion);
+
+                // Can not set baseline while inactive.
+                var ex = Assert.Throws<IgniteException>(() => cluster.SetBaselineTopology(2));
+                Assert.AreEqual("Changing BaselineTopology on inactive cluster is not allowed.", ex.Message);
+
+                // Set with version.
+                cluster.SetActive(true);
+                cluster.SetBaselineTopology(2);
+
+                var res = cluster.GetBaselineTopology();
+                CollectionAssert.AreEquivalent(new[] {"node1", "node2"}, res.Select(x => x.ConsistentId));
+
+                cluster.SetBaselineTopology(1);
+                Assert.AreEqual("node1", cluster.GetBaselineTopology().Single().ConsistentId);
+
+                // Set with nodes.
+                cluster.SetBaselineTopology(res);
+                
+                res = cluster.GetBaselineTopology();
+                CollectionAssert.AreEquivalent(new[] { "node1", "node2" }, res.Select(x => x.ConsistentId));
+
+                cluster.SetBaselineTopology(cluster.GetTopology(1));
+                Assert.AreEqual("node1", cluster.GetBaselineTopology().Single().ConsistentId);
+
+                // Set to two nodes.
+                cluster.SetBaselineTopology(cluster.GetTopology(2));
+            }
+
+            // Check auto activation on cluster restart.
+            using (var ignite = Ignition.Start(cfg1))
+            using (Ignition.Start(cfg2))
+            {
+                var cluster = ignite.GetCluster();
+                Assert.IsTrue(cluster.IsActive());
+                
+                var res = cluster.GetBaselineTopology();
+                CollectionAssert.AreEquivalent(new[] { "node1", "node2" }, res.Select(x => x.ConsistentId));
+            }
+        }
+
+        /// <summary>
         /// Checks active state.
         /// </summary>
         private static void CheckIsActive(IIgnite ignite, bool isActive)
         {
-            Assert.AreEqual(isActive, ignite.IsActive());
+            Assert.AreEqual(isActive, ignite.GetCluster().IsActive());
 
             if (isActive)
             {
@@ -228,5 +294,23 @@ namespace Apache.Ignite.Core.Tests.Cache
                     ex.Message.Substring(0, 62));
             }
         }
+
+        /// <summary>
+        /// Gets the persistent configuration.
+        /// </summary>
+        private static IgniteConfiguration GetPersistentConfiguration()
+        {
+            return new IgniteConfiguration(TestUtils.GetTestConfiguration())
+            {
+                DataStorageConfiguration = new DataStorageConfiguration
+                {
+                    DefaultDataRegionConfiguration = new DataRegionConfiguration
+                    {
+                        PersistenceEnabled = true,
+                        Name = "foo"
+                    }
+                }
+            };
+        }
     }
 }
index cf0ad40..d0c576d 100644 (file)
@@ -142,9 +142,13 @@ namespace Apache.Ignite.Core.Tests.Compute
                 Assert.IsTrue(node.Addresses.Count > 0);
                 Assert.Throws<NotSupportedException>(() => node.Addresses.Add("addr"));
 
-                Assert.NotNull(node.GetAttributes());
-                Assert.IsTrue(node.GetAttributes().Count > 0);
-                Assert.Throws<NotSupportedException>(() => node.GetAttributes().Add("key", "val"));
+                Assert.NotNull(node.Attributes);
+                Assert.IsTrue(node.Attributes.Count > 0);
+                Assert.Throws<NotSupportedException>(() => node.Attributes.Add("key", "val"));
+
+#pragma warning disable 618
+                Assert.AreSame(node.Attributes, node.GetAttributes());
+#pragma warning restore 618
 
                 Assert.NotNull(node.HostNames);
                 Assert.Throws<NotSupportedException>(() => node.HostNames.Add("h"));
index 0bc4e0f..734b0cf 100644 (file)
@@ -426,6 +426,7 @@ namespace Apache.Ignite.Core.Tests
                 using (var ignite = Ignition.Start(cfg))
                 {
                     Assert.AreEqual(id, ignite.GetConfiguration().ConsistentId);
+                    Assert.AreEqual(id ?? "127.0.0.1:47500", ignite.GetCluster().GetLocalNode().ConsistentId);
                 }
             }
         }
index c00ca49..bf2849e 100644 (file)
@@ -20,7 +20,9 @@ namespace Apache.Ignite.Core.Tests
     using System;
     using System.Collections.Concurrent;
     using System.Collections.Generic;
+    using System.IO;
     using System.Linq;
+    using System.Reflection;
     using System.Threading;
     using Apache.Ignite.Core.Binary;
     using Apache.Ignite.Core.Cluster;
@@ -44,6 +46,11 @@ namespace Apache.Ignite.Core.Tests
         /** */
         private const int DfltBusywaitSleepInterval = 200;
 
+        /** Work dir. */
+        private static readonly string WorkDir = 
+            // ReSharper disable once AssignNullToNotNullAttribute
+            Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "ignite_work");
+
         /** */
         private static readonly IList<string> TestJvmOpts = Environment.Is64BitProcess
             ? new List<string>
@@ -349,5 +356,38 @@ namespace Apache.Ignite.Core.Tests
 
             return marsh.Unmarshal<T>(marsh.Marshal(obj));
         }
+
+        /// <summary>
+        /// Clears the work dir.
+        /// </summary>
+        public static void ClearWorkDir()
+        {
+            if (!Directory.Exists(WorkDir))
+            {
+                return;
+            }
+
+            // Delete everything we can. Some files may be locked.
+            foreach (var e in Directory.GetFileSystemEntries(WorkDir, "*", SearchOption.AllDirectories))
+            {
+                try
+                {
+                    File.Delete(e);
+                }
+                catch (Exception)
+                {
+                    // Ignore
+                }
+
+                try
+                {
+                    Directory.Delete(e, true);
+                }
+                catch (Exception)
+                {
+                    // Ignore
+                }
+            }
+        }
     }
 }
index 2169630..f8ff874 100644 (file)
@@ -78,7 +78,8 @@ namespace Apache.Ignite.Core.Tests
                             ? DataRegionConfiguration.DefaultMaxSize
                             : 256 * 1024 * 1024
                     }
-                }
+                },
+                WorkDirectory = WorkDir
             };
         }
 
index f9b22fc..adae2b1 100644 (file)
@@ -56,6 +56,7 @@
     <Compile Include="Cache\Configuration\DataPageEvictionMode.cs" />
     <Compile Include="Client\Cache\CacheClientConfiguration.cs" />
     <Compile Include="Client\IgniteClientConfigurationSection.cs" />
+    <Compile Include="Cluster\IBaselineNode.cs" />
     <Compile Include="Configuration\CheckpointWriteOrder.cs" />
     <Compile Include="Configuration\DataPageEvictionMode.cs" />
     <Compile Include="Configuration\DataRegionConfiguration.cs" />
@@ -79,6 +80,7 @@
     <Compile Include="Impl\Binary\IBinaryRawWriteAware.cs" />
     <Compile Include="Impl\Binary\MultidimensionalArrayHolder.cs" />
     <Compile Include="Impl\Binary\MultidimensionalArraySerializer.cs" />
+    <Compile Include="Impl\Binary\OptimizedMarshallerObject.cs" />
     <Compile Include="Impl\Client\Cache\CacheFlags.cs" />
     <Compile Include="Impl\Client\Cache\ClientCacheConfigurationSerializer.cs" />
     <Compile Include="Impl\Client\Cache\Query\ClientFieldsQueryCursor.cs" />
@@ -90,6 +92,7 @@
     <Compile Include="Impl\Client\Cache\Query\StatementType.cs" />
     <Compile Include="Client\ClientStatusCode.cs" />
     <Compile Include="Events\LocalEventListener.cs" />
+    <Compile Include="Impl\Cluster\BaselineNode.cs" />
     <Compile Include="Impl\DataStorageMetrics.cs" />
     <Compile Include="Impl\IIgniteInternal.cs" />
     <Compile Include="Impl\Client\Cache\CacheClient.cs" />
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Cluster/IBaselineNode.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Cluster/IBaselineNode.cs
new file mode 100644 (file)
index 0000000..d0c5f81
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+namespace Apache.Ignite.Core.Cluster
+{
+    using System.Collections.Generic;
+
+    /// <summary>
+    /// Baseline topology node (see <see cref="ICluster.GetBaselineTopology"/>).
+    /// </summary>
+    public interface IBaselineNode
+    {
+        /// <summary>
+        /// Gets the consistent ID from <see cref="IgniteConfiguration.ConsistentId"/>.
+        /// </summary>
+        object ConsistentId { get; }
+
+        /// <summary>
+        /// Gets all node attributes. Attributes are assigned to nodes at startup.
+        /// </summary>
+        IDictionary<string, object> Attributes { get; }
+    }
+}
\ No newline at end of file
index 812a644..f9bbdf7 100644 (file)
@@ -88,6 +88,38 @@ namespace Apache.Ignite.Core.Cluster
         /// <value>
         /// The reconnect task.
         /// </value>
-        Task<bool> ClientReconnectTask { get; } 
+        Task<bool> ClientReconnectTask { get; }
+
+        /// <summary>
+        /// Changes Ignite grid state to active or inactive.
+        /// </summary>
+        void SetActive(bool isActive);
+
+        /// <summary>
+        /// Determines whether this grid is in active state.
+        /// </summary>
+        /// <returns>
+        ///   <c>true</c> if the grid is active; otherwise, <c>false</c>.
+        /// </returns>
+        bool IsActive();
+
+        /// <summary>
+        /// Sets the baseline topology from the cluster topology of the given version.
+        /// This method requires active cluster (<see cref="IsActive"/>).
+        /// </summary>
+        /// <param name="topologyVersion">The topology version.</param>
+        void SetBaselineTopology(long topologyVersion);
+
+        /// <summary>
+        /// Sets the baseline topology nodes.
+        /// </summary>
+        /// <param name="nodes">The nodes.</param>
+        void SetBaselineTopology(IEnumerable<IBaselineNode> nodes);
+
+        /// <summary>
+        /// Gets the baseline topology.
+        /// Returns null if <see cref="SetBaselineTopology(long)"/> has not been called.
+        /// </summary>
+        ICollection<IBaselineNode> GetBaselineTopology();
     }
 }
\ No newline at end of file
index 5f14116..ec22d18 100644 (file)
@@ -33,7 +33,7 @@ namespace Apache.Ignite.Core.Cluster
     /// <para/>
     /// All members are thread-safe and may be used concurrently from multiple threads.
     /// </summary>
-    public interface IClusterNode
+    public interface IClusterNode : IBaselineNode
     {
         /// <summary>
         /// Globally unique node ID. A new ID is generated every time a node restarts.
@@ -66,6 +66,7 @@ namespace Apache.Ignite.Core.Cluster
         /// </summary>
         /// <returns>All node attributes.</returns>
         [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Semantics.")]
+        [Obsolete("Use Attributes property.")]
         IDictionary<string, object> GetAttributes();
 
         /// <summary>
index 035d4b5..52323c8 100644 (file)
@@ -357,6 +357,7 @@ namespace Apache.Ignite.Core
         /// <summary>
         /// Changes Ignite grid state to active or inactive.
         /// </summary>
+        [Obsolete("Use GetCluster().SetActive instead.")]
         void SetActive(bool isActive);
 
         /// <summary>
@@ -365,6 +366,7 @@ namespace Apache.Ignite.Core
         /// <returns>
         ///   <c>true</c> if the grid is active; otherwise, <c>false</c>.
         /// </returns>
+        [Obsolete("Use GetCluster().IsActive instead.")]
         bool IsActive();
 
         /// <summary>
index f63d959..0ddf9a8 100644 (file)
@@ -1095,7 +1095,7 @@ namespace Apache.Ignite.Core
         /// <summary>
         /// Gets or sets the user attributes for this node.
         /// <para />
-        /// These attributes can be retrieved later via <see cref="IClusterNode.GetAttributes"/>.
+        /// These attributes can be retrieved later via <see cref="IBaselineNode.Attributes"/>.
         /// Environment variables are added to node attributes automatically.
         /// NOTE: attribute names starting with "org.apache.ignite" are reserved for internal use.
         /// </summary>
@@ -1377,7 +1377,7 @@ namespace Apache.Ignite.Core
 
         /// <summary>
         /// Gets or sets a value indicating whether grid should be active on start.
-        /// See also <see cref="IIgnite.IsActive"/> and <see cref="IIgnite.SetActive"/>.
+        /// See also <see cref="ICluster.IsActive"/> and <see cref="ICluster.SetActive"/>.
         /// <para />
         /// This property is ignored when <see cref="DataStorageConfiguration"/> is present:
         /// cluster is always inactive on start when Ignite Persistence is enabled.
index 430b426..cae0f5f 100644 (file)
@@ -113,6 +113,9 @@ namespace Apache.Ignite.Core.Impl.Binary
 
             // 14. Enum. Should be read as Array, see WriteEnumArray implementation.
             ReadHandlers[BinaryTypeId.ArrayEnum] = new BinarySystemReader(ReadArray);
+
+            // 15. Optimized marshaller objects.
+            ReadHandlers[BinaryTypeId.OptimizedMarshaller] = new BinarySystemReader(ReadOptimizedMarshallerObject);
         }
 
         /// <summary>
@@ -229,6 +232,11 @@ namespace Apache.Ignite.Core.Impl.Binary
                 return new BinarySystemWriteHandler<object>(WriteArray, true);
             }
 
+            if (type == typeof(OptimizedMarshallerObject))
+            {
+                return new BinarySystemWriteHandler<OptimizedMarshallerObject>((w, o) => o.Write(w.Stream), false);
+            }
+
             return null;
         }
 
@@ -533,6 +541,14 @@ namespace Apache.Ignite.Core.Impl.Binary
             ctx.Stream.WriteByte(BinaryUtils.HdrNull);
         }
 
+        /// <summary>
+        /// Reads the optimized marshaller object.
+        /// </summary>
+        private static object ReadOptimizedMarshallerObject(BinaryReader ctx, Type type)
+        {
+            return new OptimizedMarshallerObject(ctx.Stream);
+        }
+
         /**
          * <summary>Read delegate.</summary>
          * <param name="ctx">Read context.</param>
index 1d3d9c4..8067a39 100644 (file)
@@ -164,6 +164,9 @@ namespace Apache.Ignite.Core.Impl.Binary
         /** Type: platform object proxy. */
         public const byte PlatformJavaObjectFactoryProxy = 99;
 
+        /** Type: object written with Java OptimizedMarshaller. */
+        public const byte OptimizedMarshaller = 254;
+
         /** Type: platform object proxy. */
         public const int IgniteUuid = 2018070327;
 
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/OptimizedMarshallerObject.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/OptimizedMarshallerObject.cs
new file mode 100644 (file)
index 0000000..5d2ec05
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+namespace Apache.Ignite.Core.Impl.Binary
+{
+    using System.Diagnostics;
+    using Apache.Ignite.Core.Impl.Binary.IO;
+
+    /// <summary>
+    /// Object written with Java OptimizedMarshaller.
+    /// We just hold it as a byte array.
+    /// </summary>
+    internal class OptimizedMarshallerObject
+    {
+        /** */
+        private readonly byte[] _data;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="OptimizedMarshallerObject"/> class.
+        /// </summary>
+        public OptimizedMarshallerObject(IBinaryStream stream)
+        {
+            Debug.Assert(stream != null);
+
+            _data = stream.ReadByteArray(stream.ReadInt());
+        }
+
+        /// <summary>
+        /// Writes to the specified writer.
+        /// </summary>
+        public void Write(IBinaryStream stream)
+        {
+            Debug.Assert(stream != null);
+
+            stream.WriteByte(BinaryTypeId.OptimizedMarshaller);
+            stream.WriteInt(_data.Length);
+            stream.WriteByteArray(_data);
+        }
+    }
+}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cluster/BaselineNode.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Cluster/BaselineNode.cs
new file mode 100644 (file)
index 0000000..23ec724
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+namespace Apache.Ignite.Core.Impl.Cluster
+{
+    using System.Collections.Generic;
+    using System.Diagnostics;
+    using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Cluster;
+    using Apache.Ignite.Core.Impl.Binary;
+
+    /// <summary>
+    /// Baseline node.
+    /// </summary>
+    internal class BaselineNode : IBaselineNode
+    {
+        /** Attributes. */
+        private readonly IDictionary<string, object> _attributes;
+
+        /** Consistent ID. */
+        private readonly object _consistentId;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BaselineNode"/> class.
+        /// </summary>
+        /// <param name="reader">The reader.</param>
+        public BaselineNode(IBinaryRawReader reader)
+        {
+            Debug.Assert(reader != null);
+
+            _consistentId = reader.ReadObject<object>();
+            _attributes = ClusterNodeImpl.ReadAttributes(reader);
+        }
+
+        /** <inheritdoc /> */
+        public object ConsistentId
+        {
+            get { return _consistentId; }
+        }
+
+        /** <inheritdoc /> */
+        public IDictionary<string, object> Attributes
+        {
+            get { return _attributes; }
+        }
+
+        /// <summary>
+        /// Writes this instance to specified writer.
+        /// </summary>
+        public static void Write(BinaryWriter writer, IBaselineNode node)
+        {
+            Debug.Assert(writer != null);
+            Debug.Assert(node != null);
+
+            writer.WriteObjectDetached(node.ConsistentId);
+            
+            var attrs = node.Attributes;
+
+            if (attrs != null)
+            {
+                writer.WriteInt(attrs.Count);
+
+                foreach (var attr in attrs)
+                {
+                    writer.WriteString(attr.Key);
+                    writer.WriteObjectDetached(attr.Value);
+                }
+            }
+            else
+            {
+                writer.WriteInt(0);
+            }
+        }
+    }
+}
index b658353..fbf4764 100644 (file)
@@ -55,6 +55,9 @@ namespace Apache.Ignite.Core.Impl.Cluster
         /** Client flag. */
         private readonly bool _isClient;
 
+        /** Consistent id. */
+        private readonly object _consistentId;
+
         /** Metrics. */
         private volatile ClusterMetricsImpl _metrics;
         
@@ -73,13 +76,14 @@ namespace Apache.Ignite.Core.Impl.Cluster
 
             _id = id.Value;
 
-            _attrs = reader.ReadDictionaryAsGeneric<string, object>().AsReadOnly();
+            _attrs = ReadAttributes(reader);
             _addrs = reader.ReadCollectionAsList<string>().AsReadOnly();
             _hosts = reader.ReadCollectionAsList<string>().AsReadOnly();
             _order = reader.ReadLong();
             _isLocal = reader.ReadBoolean();
             _isDaemon = reader.ReadBoolean();
             _isClient = reader.ReadBoolean();
+            _consistentId = reader.ReadObject<object>();
 
             _metrics = reader.ReadBoolean() ? new ClusterMetricsImpl(reader) : null;
         }
@@ -119,7 +123,7 @@ namespace Apache.Ignite.Core.Impl.Cluster
         /** <inheritDoc /> */
         public IDictionary<string, object> GetAttributes()
         {
-            return _attrs.AsReadOnly();
+            return _attrs;
         }
 
         /** <inheritDoc /> */
@@ -181,6 +185,18 @@ namespace Apache.Ignite.Core.Impl.Cluster
         }
 
         /** <inheritDoc /> */
+        public object ConsistentId
+        {
+            get { return _consistentId; }
+        }
+
+        /** <inheritDoc /> */
+        public IDictionary<string, object> Attributes
+        {
+            get { return _attrs; }
+        }
+
+        /** <inheritDoc /> */
         public bool IsClient
         {
             get { return _isClient; }
@@ -218,5 +234,23 @@ namespace Apache.Ignite.Core.Impl.Cluster
         {
             _igniteRef = new WeakReference(grid);
         }
+
+        /// <summary>
+        /// Reads the attributes.
+        /// </summary>
+        internal static IDictionary<string, object> ReadAttributes(IBinaryRawReader reader)
+        {
+            Debug.Assert(reader != null);
+
+            var count = reader.ReadInt();
+            var res = new Dictionary<string, object>(count);
+
+            for (var i = 0; i < count; i++)
+            {
+                res[reader.ReadString()] = reader.ReadObject<object>();
+            }
+
+            return res.AsReadOnly();
+        }
     }
 }
index eb7f627..10b083b 100644 (file)
@@ -84,7 +84,10 @@ namespace Apache.Ignite.Core.Impl
             LoggerLog = 20,
             GetBinaryProcessor = 21,
             ReleaseStart = 22,
-            AddCacheConfiguration = 23
+            AddCacheConfiguration = 23,
+            SetBaselineTopologyVersion = 24,
+            SetBaselineTopologyNodes = 25,
+            GetBaselineTopology = 26
         }
 
         /** */
@@ -781,6 +784,40 @@ namespace Apache.Ignite.Core.Impl
         }
 
         /** <inheritdoc /> */
+        public void SetBaselineTopology(long topologyVersion)
+        {
+            DoOutInOp((int) Op.SetBaselineTopologyVersion, topologyVersion);
+        }
+
+        /** <inheritdoc /> */
+        public void SetBaselineTopology(IEnumerable<IBaselineNode> nodes)
+        {
+            IgniteArgumentCheck.NotNull(nodes, "nodes");
+
+            DoOutOp((int) Op.SetBaselineTopologyNodes, w =>
+            {
+                var pos = w.Stream.Position;
+                w.WriteInt(0);
+                var cnt = 0;
+
+                foreach (var node in nodes)
+                {
+                    cnt++;
+                    BaselineNode.Write(w, node);
+                }
+
+                w.Stream.WriteInt(pos, cnt);
+            });
+        }
+
+        /** <inheritdoc /> */
+        public ICollection<IBaselineNode> GetBaselineTopology()
+        {
+            return DoInOp((int) Op.GetBaselineTopology,
+                s => Marshaller.StartUnmarshal(s).ReadCollectionRaw(r => (IBaselineNode) new BaselineNode(r)));
+        }
+
+        /** <inheritdoc /> */
 #pragma warning disable 618
         public IPersistentStoreMetrics GetPersistentStoreMetrics()
         {
index f3632ce..2f0a271 100644 (file)
@@ -19,11 +19,9 @@ namespace Apache.Ignite.Core.Impl.Unmanaged.Jni
 {
     using System;
     using System.Diagnostics;
-    using System.Reflection;
     using System.Runtime.InteropServices;
     using System.Security;
     using Apache.Ignite.Core.Common;
-    using Apache.Ignite.Core.Impl.Common;
 
     /// <summary>
     /// JNIEnv.