/// <summary>
        /// Handles the shard deletion request and processes it against the shard set driver.
        /// </summary>
        /// <param name="request">The shard deletion request.</param>
        /// <param name="queue">The queue.</param>
        public static void HandleShardDeletionRequest(ShardDeletionRequest request,
                                                      IShardSetActionQueue queue)
        {
            var config = GetCurrentShardSetConfig(request.ShardSetName);
            var driver = GetShardSetDriver(config);

            Action <ShardDeletionRequest> process = (x =>
            {
                var shard = new RangeShard
                {
                    Catalog = request.Catalog,
                    ServerInstanceName = request.ServerInstanceName,
                };
                //Do not delete the database if it is populated for now, as the move shardlet code could error leaving shardlets behind
                //TODO: Allow this to delete populated databases, by orchestrating that a failed shardlet move stops this step for that database.
                driver.DeleteShard(shard, config);
            });

            Action <ShardDeletionRequest> complete =
                (x => queue.SendQueueProcessingEvent(GetCompletionMessage("ShardDeletionRequest", x)));
            Action <ShardDeletionRequest> error =
                (x => queue.SendQueueProcessingEvent(GetErrorMessage("ShardDeletionRequest", x)));

            request.Process(process, complete, error);
        }
        /// <summary>
        /// Handles the shardlet move request from tje request queue.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="queue">The queue.</param>
        /// <param name="uniqueProcessID">A unique process identifier that can be set to maintain state between moves.</param>
        public static void HandleShardletMoveRequest(ShardletMoveRequest request, IShardSetActionQueue queue,
                                                     Guid uniqueProcessID)
        {
            Action <ShardletMoveRequest> process =
                x =>
            {
                var shardlet         = Load(x.ShardSetName, x.ShardingKey);
                var destinationShard =
                    new RangeShard
                {
                    Catalog            = x.DestinationCatalog,
                    ServerInstanceName = x.DestinationServerInstanceName
                };

                shardlet.MoveToShard(destinationShard, x.Pin, x.DeleteOnMove, uniqueProcessID);
            };

            Action <ShardletMoveRequest> complete =
                x =>
            {
                var message = GetCompletionMessage("ShardletMoveRequest", x);
                queue.SendQueueProcessingEvent(message);
            };

            Action <ShardletMoveRequest> error =
                x =>
            {
                var message = GetErrorMessage("ShardletMoveRequest", x);
                queue.SendQueueProcessingEvent(message);
            };

            request.Process(process, complete, error);
        }
        /// <summary>
        /// Handles the shard synchronize request and processes it against the shard set driver.
        /// </summary>
        /// <param name="request">The shard synchronize request.</param>
        /// <param name="queue">The queue.</param>
        public static void HandleShardSyncRequest(ShardSyncRequest request, BaseShardSetActionQueue queue)
        {
            var config = GetCurrentShardSetConfig(request.ShardSetName);
            var driver = GetShardSetDriver(config);

            Action <ShardSyncRequest> process = (
                x =>
            {
                var shard = new RangeShard
                {
                    Catalog = request.Catalog,
                    ServerInstanceName = request.ServerInstanceName,
                };

                driver.SyncShard(shard, config);
            });

            Action <ShardSyncRequest> complete =
                (x => queue.SendQueueProcessingEvent(GetCompletionMessage("ShardSyncRequest", x)));
            Action <ShardSyncRequest> error =
                (x => queue.SendQueueProcessingEvent(GetErrorMessage("ShardSyncRequest", x)));

            request.Process(process, complete, error);
        }
        private void UpdateRangedBaseShardMap(Settings settings)
        {
            //Rollback available shards on each server for the recalculation
            foreach (var server in Servers)
            {
                if (server.MaxShardsAllowed > 0)
                {
                    //TODO: in order for this to work "right" we need an accurate calculation of available shards from the DAL.. Until then we will pretend that setting is max shards per shard set.
                    server.AvailableShards = server.MaxShardsAllowed;
                    //server.AvailableShards + ShardMap.Shards.Count(x => x.ServerInstanceName == server.ServerInstanceName);
                }
            }
            var newShardCount             = TargetShardCount - ShardMap.Shards.Count();
            IList <RangeShard> shardsUsed = new List <RangeShard>();

            if (newShardCount > 0)
            {
                //If Max Shards < 1 then unlimited shards allowed on the server.
                var availableShards = Servers.Sum(x => (x.MaxShardsAllowed < 1 ? 1000000 : x.AvailableShards));
                if (availableShards < newShardCount)
                {
                    throw new Exception("Not enough Shards available on servers used in shard set");
                }
            }
            var increment      = (long.MaxValue / TargetShardCount) * 2;
            var rangeLowValue  = Int64.MinValue;
            var rangeHighValue = Int64.MaxValue;

            if (increment != -2)
            {
                rangeHighValue = rangeLowValue + increment;
            }
            var counter         = 0;
            var currServerIndex = 0;

            var currentServer = Servers[currServerIndex];

            while (counter < TargetShardCount)
            {
                if ((currentServer.AvailableShards <= 0) &&
                    (currentServer.MaxShardsAllowed > 0))
                //unlimited shards allowed maxshardsallowed < 1 (or in this case > 0)
                {
                    currServerIndex++;
                    currentServer = Servers[currServerIndex];
                }
                else
                {
                    if (currentServer.MaxShardsAllowed > 0)
                    {
                        currentServer.AvailableShards--;
                    }
                }
                counter++;
                var databaseName = settings.ShardPrefix + ShardSetName + (counter.ToString("000000"));
                var isNewShard   = false;
                var shard        = ShardMap.Shards.FirstOrDefault(x => x.Catalog == databaseName);
                if (shard == null)
                {
                    shard = new RangeShard();
                    ShardMap.ShardMapID = -1;
                    isNewShard          = true;
                }

                //Is anything changing?
                if (
                    shard.Catalog != databaseName ||
                    shard.LowDistributionKey != rangeLowValue ||
                    shard.HighDistributionKey != rangeHighValue ||
                    shard.ServerInstanceName != currentServer.ServerInstanceName
                    )
                {
                    shard.Catalog             = databaseName;
                    shard.LowDistributionKey  = rangeLowValue;
                    shard.HighDistributionKey = rangeHighValue;
                    shard.ServerInstanceName  = currentServer.ServerInstanceName;
                    //If the shard has changed then time for a new map.
                    ShardMap.ShardMapID = -1;
                    shard.ShardID       = -1;
                }

                //Only add the shards that are new.
                if (isNewShard)
                {
                    ShardMap.Shards.Add(shard);
                }
                shardsUsed.Add(shard);

                //Either there can be a near infinite number of shards in the server, or we need to calculate how many shards remaining for the shard set
                //TODO: don't query just the current shard set, figure out total number of used shards across all work groups.
                //TODO: don't query just the current shard set, figure out total number of used shards across all work groups.
                currentServer.AvailableShards = (currentServer.MaxShardsAllowed == -1
                    ? 100000
                    : (currentServer.MaxShardsAllowed -
                       shardsUsed.Count(x => x.ServerInstanceName == currentServer.ServerInstanceName)));

                rangeLowValue = rangeHighValue + 1;
                if (counter + 1 == TargetShardCount)
                {
                    rangeHighValue = Int64.MaxValue;
                }
                else
                {
                    rangeHighValue += increment;
                }
            }
            var shardsToDelete = ShardMap.Shards.Count(x => !shardsUsed.Contains(x));

            if (shardsToDelete > 0)
            {
                //If we are deleting shards, then its time for a new shard map.
                ShardMap.ShardMapID = -1;
            }

            ShardMap.Shards = shardsUsed;

            // if the shard map is new all of the shards must be new too...
            if (ShardMap.ShardMapID == -1)
            {
                ShardMap.Shards.ToList().ForEach(x => x.ShardID = -1);
            }
        }
Exemple #5
0
 /// <summary>
 /// Removes the shard from the live shard map.
 /// </summary>
 /// <param name="shardSetName">Name of the shard set.</param>
 /// <param name="rangeShard">The shard.</param>
 public abstract void RemoveShard(string shardSetName, RangeShard rangeShard);
Exemple #6
0
 /// <summary>
 /// Publishes the shard into the live shard map.
 /// </summary>
 /// <param name="shardSetName">Name of the shard set.</param>
 /// <param name="rangeShard">The shard.</param>
 public abstract void PublishShard(string shardSetName, RangeShard rangeShard);
        /// <summary>
        /// Moves to shard.
        /// </summary>
        /// <param name="shard">The shard to move the ShardLet data to.</param>
        /// <param name="pin">if set to <c>true</c> add the shard to the pinned shard list.</param>
        /// <param name="deleteOnMove">if set to <c>true</c> delete the Shardlet data from the existing Shard after move.</param>
        /// <param name="uniqueProcessID">A unique process identifier that can be set to maintain state between moves.</param>
        /// <param name="queueRequest">if set to <c>true</c> queue the shard move rather than executing it immediately.</param>
        public void MoveToShard(ShardBase shard, bool pin, bool deleteOnMove, Guid uniqueProcessID,
                                bool queueRequest = false)
        {
            if (queueRequest)
            {
                var request =
                    new ShardletMoveRequest
                {
                    DestinationCatalog            = shard.Catalog,
                    DestinationServerInstanceName = shard.ServerInstanceName,
                    SourceCatalog            = Catalog,
                    SourceServerInstanceName = ServerInstanceName,
                    DistributionKey          = DistributionKey,
                    ShardingKey     = ShardingKey,
                    ShardSetName    = ShardSetName,
                    DeleteOnMove    = deleteOnMove,
                    UniqueProcessID = uniqueProcessID,
                    Pin             = pin
                };

                request.Save();

                return;
            }

            var shardSetConnectionDriver = ScaleOutShardletManager.GetManager().GetShardletConnectionDriver(ShardSetName);

            try
            {
                // change the state to read only in the map and update th epin status
                Status = ShardletStatus.Moving;
                Pinned = pin;
                shardSetConnectionDriver.PublishShardlet(this);

                // terminate all existing connections
                shardSetConnectionDriver.TerminateConnections(this);

                // copy the shardlet data
                var shardSetConfig = ShardSetConfig.LoadCurrent(ShardSetName);

                var currentShard =
                    new RangeShard
                {
                    Catalog            = Catalog,
                    ServerInstanceName = ServerInstanceName,
                };

                shardSetConfig.CopyShardlet(currentShard, shard, ShardingKey, uniqueProcessID);

                // update the status and set the new catalog and instance in the shard map
                Catalog            = shard.Catalog;
                ServerInstanceName = shard.ServerInstanceName;
                Status             = ShardletStatus.Active;

                shardSetConnectionDriver.PublishShardlet(this);

                if (deleteOnMove)
                {
                    shardSetConfig.DeleteShardlet(currentShard, ShardingKey);
                }
            }
            catch (Exception)
            {
                Status = ShardletStatus.Active;
                shardSetConnectionDriver.PublishShardlet(this);

                throw;
            }
        }