Example #1
0
        /// <summary>Adds a node to the ring</summary>
        /// <remarks>This is only called on the master.  Data nodes will
        /// simply reload everything when something changes</remarks>
        public void AddNode(CacheNode node)
        {
            this.ringLock.EnterWriteLock();
            try
            {
                // Make sure this node isn't already in the ring
                string nodeName = node.GetName();
                if (this.Nodes.ContainsKey(nodeName))
                {
                    throw new Exception("Already added node " + nodeName);
                }

                this.Nodes.Add(nodeName, node);

                DetermineNodeLocations();
                LookupEndPoints();
            }
            finally
            {
                this.ringLock.ExitWriteLock();
            }
        }
Example #2
0
        /// <summary>Test basic ring functionality.</summary>
        /// <remarks>Throws an exception or returns false on failure</remarks>
        public static bool Test()
        {
            CacheRing ring = new CacheRing();

            CacheNode nodeA = new CacheNode();

            nodeA.HostName    = "localhost";
            nodeA.PortNumber  = 1;
            nodeA.MaxNumBytes = CacheConfig.ParseMaxNumBytes("48Mb");
            ring.Nodes.Add(nodeA.GetName(), nodeA);

            CacheNode nodeB = new CacheNode();

            nodeB.HostName    = "localhost";
            nodeB.PortNumber  = 2;
            nodeB.MaxNumBytes = CacheConfig.ParseMaxNumBytes("12Mb");
            ring.Nodes.Add(nodeB.GetName(), nodeB);

            CacheNode nodeC = new CacheNode();

            nodeC.HostName    = "localhost";
            nodeC.PortNumber  = 3;
            nodeC.MaxNumBytes = CacheConfig.ParseMaxNumBytes("64Mb");
            ring.Nodes.Add(nodeC.GetName(), nodeC);

            // Hard-code some locations so we can make sure objects get assigned
            // to the correct virtual node.

            ring.SortedLocations.Add(10, nodeA);
            nodeA.Locations.Add(10);
            ring.SortedLocations.Add(-10, nodeB);
            nodeB.Locations.Add(-10);
            ring.SortedLocations.Add(20, nodeC);
            nodeC.Locations.Add(20);
            ring.SortedLocations.Add(50, nodeA);
            nodeA.Locations.Add(50);
            ring.SortedLocations.Add(60, nodeC);
            nodeC.Locations.Add(60);

            var node = ring.GetNodeForHash(5);

            if (node != nodeA)
            {
                throw new Exception(string.Format
                                        ("Hash 5 should belong to nodeA, not {0}", node.GetName()));
            }

            node = ring.GetNodeForHash(int.MaxValue);
            if (node != nodeB)
            {
                throw new Exception(string.Format
                                        ("Hash Integer.MAX should belong to nodeB, not {0}", node.GetName()));
            }

            node = ring.GetNodeForHash(20);
            if (node != nodeC)
            {
                throw new Exception(string.Format
                                        ("Hash 20 should belong to nodeC, not {0}", node.GetName()));
            }

            node = ring.GetNodeForHash(25);
            if (node != nodeA)
            {
                throw new Exception(string.Format
                                        ("Hash 25 should belong to nodeA, not {0}", node.GetName()));
            }

            // Now get rid of those hard coded locations and let the algorithm decide
            ring.DetermineNodeLocations();

            // Make sure the master list and the nodes agree
            foreach (var n in ring.Nodes.Values)
            {
                foreach (var location in n.Locations)
                {
                    if (!ring.SortedLocations.ContainsKey(location) ||
                        ring.SortedLocations[location] != n)
                    {
                        throw new Exception(string.Format
                                                ("Location {0} in node {1} not found", location, node.GetName()));
                    }
                }
            }

            foreach (var loc in ring.SortedLocations)
            {
                string nodeName = loc.Value.GetName();
                if (!ring.Nodes.ContainsKey(nodeName))
                {
                    throw new Exception(string.Format
                                            ("ring.Nodes missing {0}", nodeName));
                }
                if (!loc.Value.Locations.Contains(loc.Key))
                {
                    throw new Exception(string.Format
                                            ("node {0} does not have {1}", nodeName, loc.Key));
                }
            }

            //Console.WriteLine(ring.GetTrace());

            // Now let's place a bunch of values and see how many of them change
            // when we make changes to the node configuration.

            //Console.WriteLine("About to place objects");

            // nodeName => List of hashes
            Dictionary <string, List <int> > map = new Dictionary <string, List <int> >();

            for (int i = 0; i < 100000; i++)
            {
                Guid       g        = Guid.NewGuid();
                int        hash     = CacheHelper.GetConsistentHashCode(g.ToString());
                CacheNode  n        = ring.GetNodeForHash(hash);
                string     nodeName = n.GetName();
                List <int> hashes;
                if (!map.TryGetValue(nodeName, out hashes))
                {
                    hashes        = new List <int>();
                    map[nodeName] = hashes;
                }
                hashes.Add(hash);
            }

            //foreach (var nodeName in map.Keys)
            //{
            //	Console.WriteLine("{0} has {1} hashes",
            //			nodeName, map[nodeName].Count);
            //}

            //Console.WriteLine("Modifying sizes and replacing objects");
            nodeC.MaxNumBytes = CacheConfig.ParseMaxNumBytes("48Mb");             // was 64Mb
            ring.PopulateNodes();
            int numChanged = 0;
            int numTotal   = 0;

            foreach (var nodeName in map.Keys)
            {
                foreach (int hash in map[nodeName])
                {
                    numTotal++;
                    CacheNode n = ring.GetNodeForHash(hash);
                    if (!(nodeName.Equals(n.GetName())))
                    {
                        numChanged++;
                    }
                }
            }

            if (numChanged >= numTotal)
            {
                throw new Exception("Number of changed hases >= total number of hashes");
            }

            // TODO - Caclulate an acceptable percentage

            //Console.WriteLine("{0} hashes changed which node they were assigned to",
            //		numChanged);

            return(true);
        }