/// <summary>
        /// Parses the RTable configuration blobs.
        /// Returns the list of views, the list of configured tables and the lease duration.
        /// If null is returned, then the value of tableConfigList/leaseDuration are not relevant.
        /// </summary>
        /// <param name="blobs"></param>
        /// <param name="useHttps"></param>
        /// <param name="tableConfigList"></param>
        /// <param name="leaseDuration"></param>
        /// <returns></returns>
        public List <View> ParseBlob(
            List <CloudBlockBlob> blobs,
            Action <ReplicaInfo> SetConnectionString,
            out List <ReplicatedTableConfiguredTable> tableConfigList,
            out int leaseDuration,
            out Guid configId)
        {
            tableConfigList = null;
            leaseDuration   = 0;
            configId        = Guid.Empty;

            ReplicatedTableConfiguration configuration;
            List <string> eTags;

            ReplicatedTableQuorumReadResult result = CloudBlobHelpers.TryReadBlobQuorum(
                blobs,
                out configuration,
                out eTags,
                ReplicatedTableConfiguration.FromJson);

            if (result.Code != ReplicatedTableQuorumReadCode.Success)
            {
                ReplicatedTableLogger.LogError("Unable to refresh views, \n{0}", result.ToString());
                return(null);
            }


            /**
             * Views:
             */
            var viewList = new List <View>();

            foreach (var entry in configuration.viewMap)
            {
                ReplicatedTableConfigurationStore configurationStore = entry.Value;

                var view = View.InitFromConfigVer2(entry.Key, configurationStore, SetConnectionString);
                view.RefreshTime = DateTime.UtcNow;

                if (view.ViewId <= 0)
                {
                    ReplicatedTableLogger.LogError("ViewId={0} of  ViewName={1} is invalid. Must be >= 1.", view.ViewId, view.Name);
                    continue;
                }

                if (view.IsEmpty)
                {
                    ReplicatedTableLogger.LogError("ViewName={0} is empty, skipping ...", view.Name);
                    continue;
                }

                // - ERROR!
                if (view.ReadHeadIndex > view.TailIndex)
                {
                    ReplicatedTableLogger.LogError("ReadHeadIndex={0} of  ViewName={1} is out of range. Must be <= {2}", view.ReadHeadIndex, view.Name, view.TailIndex);
                    continue;
                }

                viewList.Add(view);
            }

            if (!viewList.Any())
            {
                ReplicatedTableLogger.LogError("Config has no active Views !");
                return(null);
            }


            /**
             * Tables:
             */
            tableConfigList = configuration.tableList.ToList();


            // - lease duration
            leaseDuration = configuration.LeaseDuration;

            // - ConfigId
            configId = configuration.GetConfigId();

            return(viewList);
        }
Пример #2
0
        public static ReplicatedTableQuorumWriteResult TryWriteBlobQuorum <T>(List <CloudBlockBlob> blobs, T configuration, Func <string, T> ParseBlobFunc, Func <T, T, bool> ConfigIdComparer, Func <T, T> GenerateConfigId)
            where T : ReplicatedTableConfigurationBase, new()
        {
            // Fetch all blobs ...
            List <T>      valuesArray;
            List <string> eTagsArray;
            List <ReplicatedTableReadBlobResult> resultArray = TryReadAllBlobs(blobs, out valuesArray, out eTagsArray, ParseBlobFunc);

            // Find majority ...
            int quorumIndex;
            ReplicatedTableQuorumReadResult majority = FindMajority(resultArray.AsReadOnly(), valuesArray.AsReadOnly(), out quorumIndex);
            string readDetails = majority.ToString();

            switch (majority.Code)
            {
            case ReplicatedTableQuorumReadCode.NotFound:
                // Create blobs ...
                break;

            case ReplicatedTableQuorumReadCode.UpdateInProgress:
                return(new ReplicatedTableQuorumWriteResult(ReplicatedTableQuorumWriteCode.ConflictDueToUpdateInProgress, readDetails));

            case ReplicatedTableQuorumReadCode.Exception:
                return(new ReplicatedTableQuorumWriteResult(ReplicatedTableQuorumWriteCode.ReadExceptions, readDetails));

            case ReplicatedTableQuorumReadCode.NullOrLowSuccessRate:
                return(new ReplicatedTableQuorumWriteResult(ReplicatedTableQuorumWriteCode.ReadNoQuorum, readDetails));

            case ReplicatedTableQuorumReadCode.Success:
                // Blob has changed since ...
                if (ConfigIdComparer(valuesArray[quorumIndex], configuration) == false)
                {
                    return(new ReplicatedTableQuorumWriteResult(ReplicatedTableQuorumWriteCode.ConflictDueToBlobChange, readDetails));
                }

                // Update blobs ...
                break;

            case ReplicatedTableQuorumReadCode.BlobsNotInSyncOrTransitioning:
                return(new ReplicatedTableQuorumWriteResult(ReplicatedTableQuorumWriteCode.BlobsNotInSyncOrTransitioning, readDetails));

            case ReplicatedTableQuorumReadCode.NullObject:
            default:
            {
                var msg = string.Format("Unexpected value majority=\'{0}\' ", majority.Code);
                throw new Exception(msg);
            }
            }

            // Generate a new Id for the copy of the input configuration
            T      newConfiguration = GenerateConfigId(configuration);
            string content          = newConfiguration.ToString();

            // Update blobs ...
            int numberOfBlobs = blobs.Count;

            ReplicatedTableWriteBlobResult[] writeResultArray = new ReplicatedTableWriteBlobResult[numberOfBlobs];

            Parallel.For(0, numberOfBlobs, index =>
            {
                T currentValue     = valuesArray[index];
                string currentETag = eTagsArray[index];

                if (currentValue == null)
                {
                    currentETag = null;
                }
                else if (!ConfigIdComparer(currentValue, configuration))
                {
                    currentETag = "*";
                }

                writeResultArray[index] = TryWriteBlob(blobs[index], content, currentETag);
            });

            int successRate = writeResultArray.Count(e => e.Success == true);
            int quorum      = (numberOfBlobs / 2) + 1;

            if (successRate >= quorum)
            {
                // Return new config Id to the caller for record
                string newConfId = newConfiguration.GetConfigId().ToString();
                return(new ReplicatedTableQuorumWriteResult(ReplicatedTableQuorumWriteCode.Success, newConfId, writeResultArray.ToList()));
            }

            return(new ReplicatedTableQuorumWriteResult(ReplicatedTableQuorumWriteCode.QuorumWriteFailure, writeResultArray.ToList()));
        }
Пример #3
0
        /*
         * Configuration management APIs
         */
        public ReplicatedTableQuorumReadResult RetrieveConfiguration(out ReplicatedTableConfiguration configuration)
        {
            List <string> eTags;

            ReplicatedTableQuorumReadResult
                result = CloudBlobHelpers.TryReadBlobQuorum(
                this.configManager.GetBlobs(),
                out configuration,
                out eTags,
                ReplicatedTableConfiguration.FromJson);

            if (result.Code != ReplicatedTableQuorumReadCode.Success)
            {
                ReplicatedTableLogger.LogError("Failed to read configuration, \n{0}", result.ToString());
            }

            return(result);
        }
Пример #4
0
        public void TurnReplicaOff(string storageAccountName)
        {
            if (string.IsNullOrEmpty(storageAccountName))
            {
                throw new ArgumentNullException("storageAccountName");
            }

            ReplicatedTableConfiguration configuration = null;

            // - Retrieve configuration ...
            ReplicatedTableQuorumReadResult readResult = RetrieveConfiguration(out configuration);

            if (readResult.Code != ReplicatedTableQuorumReadCode.Success)
            {
                var msg = string.Format("TurnReplicaOff={0}: failed to read configuration, \n{1}", storageAccountName, readResult.ToString());

                ReplicatedTableLogger.LogError(msg);
                throw new Exception(msg);
            }

            // - Parse/Update all views ...
            foreach (var viewConf in configuration.viewMap.Values)
            {
                var foundReplicas = viewConf.GetCurrentReplicaChain()
                                    .FindAll(r => r.StorageAccountName == storageAccountName);

                if (!foundReplicas.Any())
                {
                    continue;
                }

                foreach (var replica in foundReplicas)
                {
                    replica.Status            = ReplicaStatus.None;
                    replica.ViewWhenTurnedOff = viewConf.ViewId;
                }

                // Update view id
                viewConf.ViewId++;
            }

            // - Write back configuration ...
            ReplicatedTableQuorumWriteResult writeResult = UpdateConfigurationInternal(configuration, true);

            if (writeResult.Code != ReplicatedTableQuorumWriteCode.Success)
            {
                var msg = string.Format("TurnReplicaOff={0}: failed to update configuration, \n{1}", storageAccountName, writeResult.ToString());

                ReplicatedTableLogger.LogError(msg);
                throw new Exception(msg);
            }
        }
Пример #5
0
        public void TurnReplicaOn(string storageAccountName, List <string> tablesToRepair, out List <ReplicatedTableRepairResult> failures)
        {
            if (string.IsNullOrEmpty(storageAccountName))
            {
                throw new ArgumentNullException("storageAccountName");
            }

            if (tablesToRepair == null)
            {
                throw new ArgumentNullException("tablesToRepair");
            }

            ReplicatedTableConfiguration configuration = null;

            failures = new List <ReplicatedTableRepairResult>();

            // - Retrieve configuration ...
            ReplicatedTableQuorumReadResult readResult = RetrieveConfiguration(out configuration);

            if (readResult.Code != ReplicatedTableQuorumReadCode.Success)
            {
                var msg = string.Format("TurnReplicaOn={0}: failed to read configuration, \n{1}", storageAccountName, readResult.ToString());

                ReplicatedTableLogger.LogError(msg);
                throw new Exception(msg);
            }


            /* - Phase 1:
             *      Move the *replica* to the front and set it to None.
             *      Make the view ReadOnly.
             **/
            #region Phase 1

            configuration.MoveReplicaToHeadAndSetViewToReadOnly(storageAccountName);

            // - Write back configuration, refresh its Id with the new one,
            //   but don't validate it is loaded bcz if all views of the config are empty, the config won't be refreshed by RefreshReadAndWriteViewsFromBlobs() thread!
            SaveConfigAndRefreshItsIdAndValidateIsLoaded(configuration, "Phase 1", false);

            #endregion

            /**
             * Chain is such: [None] -> [RO] -> ... -> [RO]
             *            or: [None] -> [None] -> ... -> [None]
             **/


            // - Wait for L + CF to make sure no pending transaction working on old views
            Thread.Sleep(TimeSpan.FromSeconds(Constants.LeaseDurationInSec + Constants.ClockFactorInSec));


            /* - Phase 2:
             *      Set the *replica* (head) to WriteOnly and other active replicas to ReadWrite.
             *   Or, in case of one replic chain
             *      Set only the *replica* (head) to ReadWrite.
             **/
            #region Phase 2

            configuration.EnableWriteOnReplicas(storageAccountName);

            // - Write back configuration, refresh its Id with the new one,
            //   and then validate it is loaded now (it has to be working since next Phase is "Repair")
            SaveConfigAndRefreshItsIdAndValidateIsLoaded(configuration, "Phase 2");

            #endregion

            /**
             * Chain is such: [W] -> [RW] -> ... -> [RW]
             *            or: [RW] -> [None] -> ... -> [None]
             **/


            // To be safe:
            // - Wait for L + CF to make sure no pending transaction working on old views
            Thread.Sleep(TimeSpan.FromSeconds(Constants.LeaseDurationInSec + Constants.ClockFactorInSec));


            /* - Phase 3:
             *      Repair all tables
             **/
            #region Phase 3

            foreach (var tableName in tablesToRepair)
            {
                ReplicatedTableRepairResult result = RepairTable(tableName, storageAccountName, configuration);
                if (result.Code != ReplicatedTableRepairCode.Error)
                {
                    ReplicatedTableLogger.LogInformational(result.ToString());
                    continue;
                }

                ReplicatedTableLogger.LogError(result.ToString());

                // List of tables (and corresponding views) failed to repair!
                failures.Add(result);
            }

            #endregion


            /* - Phase 4:
             *      Set the *replica* (head) to ReadWrite.
             **/
            #region Phase 4

            configuration.EnableReadWriteOnReplicas(storageAccountName, failures.Select(r => r.ViewName).ToList());

            // - Write back configuration, refresh its Id with the new one,
            //   and then validate it is loaded now (i.e. it is a working config)
            SaveConfigAndRefreshItsIdAndValidateIsLoaded(configuration, "Phase 4");

            #endregion

            /**
             * Chain is such: [RW] -> [RW] -> ... -> [RW] if all configured table repaired
             *            or:  [W] -> [RW] -> ... -> [RW] if at least one configured table failed repair !
             **/
        }
Пример #6
0
        /// <summary>
        /// Parses the RTable configuration blobs.
        /// Returns the list of views, the list of configured tables and the lease duration.
        /// If null is returned, then the value of tableConfigList/leaseDuration are not relevant.
        /// </summary>
        /// <param name="blobs"></param>
        /// <param name="useHttps"></param>
        /// <param name="tableConfigList"></param>
        /// <param name="leaseDuration"></param>
        /// <returns></returns>
        public List <View> ParseBlob(
            List <CloudBlockBlob> blobs,
            Action <ReplicaInfo> SetConnectionString,
            out List <ReplicatedTableConfiguredTable> tableConfigList,
            out int leaseDuration,
            out Guid configId)
        {
            tableConfigList = null;
            leaseDuration   = 0;
            configId        = Guid.Empty;

            ReplicatedTableConfigurationStore configurationStore;
            List <string> eTags;

            ReplicatedTableQuorumReadResult result = CloudBlobHelpers.TryReadBlobQuorum(
                blobs,
                out configurationStore,
                out eTags,
                JsonStore <ReplicatedTableConfigurationStore> .Deserialize);

            if (result.Code != ReplicatedTableQuorumReadCode.Success)
            {
                ReplicatedTableLogger.LogError("Unable to refresh view, \n{0}", result.ToString());
                return(null);
            }


            /**
             * View:
             */
            var view = View.InitFromConfigVer1(DefaultViewName, configurationStore, SetConnectionString);

            view.RefreshTime = DateTime.UtcNow;

            if (view.ViewId <= 0)
            {
                ReplicatedTableLogger.LogError("ViewId={0} is invalid. Must be >= 1.", view.ViewId);
                return(null);
            }

            if (view.IsEmpty)
            {
                ReplicatedTableLogger.LogError("ViewName={0} is empty, skipping ...", view.Name);
                return(null);
            }


            /**
             * Tables:
             */
            tableConfigList = new List <ReplicatedTableConfiguredTable>
            {
                new ReplicatedTableConfiguredTable
                {
                    TableName       = AllTables,
                    ViewName        = DefaultViewName,
                    ConvertToRTable = configurationStore.ConvertXStoreTableMode,
                }
            };


            // - lease duration
            leaseDuration = configurationStore.LeaseDuration;

            return(new List <View> {
                view
            });
        }
        public void TurnReplicaOff(string storageAccountName)
        {
            if (string.IsNullOrEmpty(storageAccountName))
            {
                throw new ArgumentNullException("storageAccountName");
            }

            ReplicatedTableConfiguration configuration = null;

            // - Retrieve configuration ...
            ReplicatedTableQuorumReadResult readResult = RetrieveConfiguration(out configuration);

            if (readResult.Code != ReplicatedTableQuorumReadCode.Success)
            {
                var msg = string.Format("TurnReplicaOff={0}: failed to read configuration, \n{1}", storageAccountName, readResult.ToString());

                ReplicatedTableLogger.LogError(msg);
                throw new Exception(msg);
            }

            // - Update all views ...
            configuration.TurnReplicaOff(storageAccountName);

            // - Write back configuration ...
            ReplicatedTableQuorumWriteResult writeResult = UpdateConfigurationInternal(configuration, true);

            if (writeResult.Code != ReplicatedTableQuorumWriteCode.Success)
            {
                var msg = string.Format("TurnReplicaOff={0}: failed to update configuration, \n{1}", storageAccountName, writeResult.ToString());

                ReplicatedTableLogger.LogError(msg);
                throw new Exception(msg);
            }
        }