Beispiel #1
0
        private ReplicatedTableQuorumWriteResult UpdateConfigurationInternal(ReplicatedTableConfiguration configuration, bool useConditionalUpdate)
        {
            SanitizeConfiguration(configuration);

            // - Upload configuration ...
            Func <ReplicatedTableConfiguration, ReplicatedTableConfiguration, bool> comparer = (a, b) => a.Id == b.Id;

            if (!useConditionalUpdate)
            {
                comparer = (a, b) => true;
            }

            ReplicatedTableQuorumWriteResult
                result = CloudBlobHelpers.TryWriteBlobQuorum(
                this.configManager.GetBlobs(),
                configuration,
                ReplicatedTableConfiguration.FromJson,
                comparer,
                ReplicatedTableConfiguration.GenerateNewConfigId);

            if (result.Code == ReplicatedTableQuorumWriteCode.Success)
            {
                this.configManager.Invalidate();
            }
            else
            {
                ReplicatedTableLogger.LogError("Failed to update configuration, \n{0}", result.ToString());
            }

            return(result);
        }
Beispiel #2
0
        private void Initialize()
        {
            if ((this.blobLocations.Count % 2) == 0)
            {
                throw new ArgumentException("Number of blob locations must be odd");
            }

            foreach (var blobLocation in blobLocations)
            {
                string accountConnectionString = String.Format(Constants.ShortConnectioStringTemplate,
                                                               ((this.useHttps == true) ? "https" : "http"),
                                                               blobLocation.StorageAccountName,
                                                               blobLocation.StorageAccountKey);

                try
                {
                    CloudBlockBlob blob = CloudBlobHelpers.GetBlockBlob(accountConnectionString, blobLocation.BlobPath);
                    this.blobs.Add(blobLocation.StorageAccountName + ';' + blobLocation.BlobPath, blob);
                }
                catch (Exception e)
                {
                    ReplicatedTableLogger.LogError("Failed to init blob Acc={0}, Blob={1}. Exception: {2}",
                                                   blobLocation.StorageAccountName,
                                                   blobLocation.BlobPath,
                                                   e.Message);
                }
            }

            int quorumSize = (this.blobLocations.Count / 2) + 1;

            if (this.blobs.Count < quorumSize)
            {
                throw new Exception(string.Format("Retrieved blobs count ({0}) is less than quorum !", this.blobs.Count));
            }
        }
Beispiel #3
0
        public List <ReplicatedTableReadBlobResult> RetrieveConfiguration(out List <ReplicatedTableConfiguration> configuration)
        {
            List <string> eTagsArray;

            return(CloudBlobHelpers.TryReadAllBlobs(
                       this.configManager.GetBlobs(),
                       out configuration,
                       out eTagsArray,
                       ReplicatedTableConfiguration.FromJson));
        }
        public void UpdateConfiguration(List <ReplicaInfo> replicaChain, int readViewHeadIndex, bool convertXStoreTableMode = false)
        {
            Parallel.ForEach(this.blobs, blob =>
            {
                ReplicatedTableConfigurationStore configurationStore = null;
                long newViewId = 0;
                if (!CloudBlobHelpers.TryReadBlob <ReplicatedTableConfigurationStore>(blob.Value, out configurationStore))
                {
                    //This is the first time we are uploading the config
                    configurationStore = new ReplicatedTableConfigurationStore();
                }

                newViewId = configurationStore.ViewId + 1;

                configurationStore.LeaseDuration          = Constants.LeaseDurationInSec;
                configurationStore.Timestamp              = DateTime.UtcNow;
                configurationStore.ReplicaChain           = replicaChain;
                configurationStore.ReadViewHeadIndex      = readViewHeadIndex;
                configurationStore.ConvertXStoreTableMode = convertXStoreTableMode;

                configurationStore.ViewId = newViewId;

                //If the read view head index is not 0, this means we are introducing 1 or more replicas at the head. For
                //each such replica, update the view id in which it was added to the write view of the chain
                if (readViewHeadIndex != 0)
                {
                    for (int i = 0; i < readViewHeadIndex; i++)
                    {
                        replicaChain[i].ViewInWhichAddedToChain = newViewId;
                    }
                }

                try
                {
                    //Step 1: Delete the current configuration
                    blob.Value.UploadText(Constants.ConfigurationStoreUpdatingText);

                    //Step 2: Wait for L + CF to make sure no pending transaction working on old views
                    // Chunzhi: removed this, original code hangs here
                    // Matt: restore this: it's essential for consistency.
                    Thread.Sleep(TimeSpan.FromSeconds(Constants.LeaseDurationInSec +
                                                      Constants.ClockFactorInSec));

                    //Step 3: Update new config
                    blob.Value.UploadText(JsonStore <ReplicatedTableConfigurationStore> .Serialize(configurationStore));
                }
                catch (StorageException e)
                {
                    ReplicatedTableLogger.LogError("Updating the blob: {0} failed. Exception: {1}", blob.Value, e.Message);
                }
            });

            //Invalidate the lastViewRefreshTime so that updated views get returned
            this.lastViewRefreshTime = DateTime.MinValue;
        }
Beispiel #5
0
        /*
         * Class functions:
         */
        static internal protected CloudTableClient GetTableClientForReplica(ReplicaInfo replica)
        {
            CloudTableClient tableClient = null;

            if (!CloudBlobHelpers.TryCreateCloudTableClient(replica.ConnectionString, out tableClient))
            {
                ReplicatedTableLogger.LogError("No table client created for replica info: {0}", replica);
            }

            return(tableClient);
        }
        private CloudTableClient GetTableClientForReplica(ReplicaInfo replica)
        {
            string connectionString = String.Format(cloudStorageAccountTemplate,
                                                    this.useHttps ? "https" : "http",
                                                    replica.StorageAccountName,
                                                    replica.StorageAccountKey);

            CloudTableClient tableClient = null;

            if (!CloudBlobHelpers.TryCreateCloudTableClient(connectionString, out tableClient))
            {
                ReplicatedTableLogger.LogError("No table client created for replica info: {0}", replica);
            }

            return(tableClient);
        }
Beispiel #7
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);
        }
Beispiel #8
0
        public ReplicatedTableQuorumWriteResult UploadConfigurationToBlobs(List <int> blobIndexes, ReplicatedTableConfiguration configuration)
        {
            if (blobIndexes == null || !blobIndexes.Any())
            {
                throw new ArgumentNullException("blobIndexes");
            }

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

            List <CloudBlockBlob> blobs = new List <CloudBlockBlob>();

            foreach (var blobIndex in blobIndexes)
            {
                if (blobIndex < this.configManager.GetBlobs().Count)
                {
                    blobs.Add(this.configManager.GetBlobs()[blobIndex]);
                    continue;
                }

                var msg = string.Format("blobIndex={0} >= BlobCount={1}", blobIndex, this.configManager.GetBlobs().Count);

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

            SanitizeConfiguration(configuration);

            // - Upload to blobs ...
            ReplicatedTableQuorumWriteResult result = CloudBlobHelpers.TryUploadBlobs(blobs, configuration);

            this.configManager.Invalidate();

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

            return(result);
        }
        private void Initialize()
        {
            if ((this.blobLocations.Count % 2) == 0)
            {
                throw new ArgumentException("Number of blob locations must be odd");
            }

            foreach (var blobLocation in blobLocations)
            {
                string accountConnectionString =
                    String.Format(cloudStorageAccountTemplate, ((this.useHttps == true) ? "https" : "http"),
                                  blobLocation.StorageAccountName,
                                  blobLocation.StorageAccountKey);

                CloudBlockBlob blob = CloudBlobHelpers.GetBlockBlob(accountConnectionString, blobLocation.BlobPath);
                this.blobs.Add(blobLocation.StorageAccountName + ';' + blobLocation.BlobPath, blob);
            }

            this.quorumSize = (this.blobs.Count / 2) + 1;
        }
        /// <summary>
        /// True, if the read and write views are the same. False, otherwise.
        /// </summary>
        public bool IsViewStable()
        {
            int viewStableCount = 0;

            foreach (var blob in this.blobs)
            {
                ReplicatedTableConfigurationStore configurationStore;
                if (!CloudBlobHelpers.TryReadBlob <ReplicatedTableConfigurationStore>(blob.Value, out configurationStore))
                {
                    continue;
                }

                if (configurationStore.ReadViewHeadIndex == 0)
                {
                    viewStableCount++;
                }
            }

            return(viewStableCount >= this.quorumSize);
        }
        /// <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);
        }
Beispiel #12
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 UpdateConfiguration(List <ReplicaInfo> replicaChain, int readViewHeadIndex, bool convertXStoreTableMode = false, long viewId = 0)
        {
            View currentView = GetWriteView();

            if (viewId == 0)
            {
                if (!currentView.IsEmpty)
                {
                    viewId = currentView.ViewId + 1;
                }
                else
                {
                    viewId = 1;
                }
            }

            ReplicatedTableConfigurationStore newConfig = new ReplicatedTableConfigurationStore
            {
                LeaseDuration          = Constants.LeaseDurationInSec,
                Timestamp              = DateTime.UtcNow,
                ReplicaChain           = replicaChain,
                ReadViewHeadIndex      = readViewHeadIndex,
                ConvertXStoreTableMode = convertXStoreTableMode,
                ViewId = viewId
            };

            //If the read view head index is not 0, this means we are introducing 1 or more replicas at the head. For
            //each such replica, update the view id in which it was added to the write view of the chain
            if (readViewHeadIndex != 0)
            {
                for (int i = 0; i < readViewHeadIndex; i++)
                {
                    replicaChain[i].ViewInWhichAddedToChain = viewId;
                }
            }

            Parallel.ForEach(this.configManager.GetBlobs(), blob =>
            {
                ReplicatedTableConfigurationStore configurationStore = null;
                string eTag;

                /*
                 * TODO: (per Parveen Patel <*****@*****.**>)
                 * The below code is incomplete because we are supposed to use eTag to make the changes if the old blob exists.
                 * This is to support multiple clients updating the config, not a high priority scenario but something we should look at.
                 */
                ReplicatedTableReadBlobResult result = CloudBlobHelpers.TryReadBlob(
                    blob,
                    out configurationStore,
                    out eTag,
                    JsonStore <ReplicatedTableConfigurationStore> .Deserialize);
                if (result.Code != ReadBlobCode.Success)
                {
                    //This is the first time we are uploading the config
                    configurationStore = new ReplicatedTableConfigurationStore();
                }

                configurationStore = newConfig;

                CloudBlobHelpers.TryWriteBlob(blob, configurationStore.ToJson());
            });

            this.configManager.Invalidate();
        }
        private void RefreshReadAndWriteViewsFromBlobs(object arg)
        {
            lock (this)
            {
                this.lastRenewedReadView  = new View();
                this.lastRenewedWriteView = new View();

                DateTime refreshStartTime = DateTime.UtcNow;

                Dictionary <long, List <CloudBlockBlob> > viewResult = new Dictionary <long, List <CloudBlockBlob> >();

                foreach (var blob in this.blobs)
                {
                    ReplicatedTableConfigurationStore configurationStore;
                    if (!CloudBlobHelpers.TryReadBlob <ReplicatedTableConfigurationStore>(blob.Value, out configurationStore))
                    {
                        continue;
                    }

                    if (configurationStore.ViewId <= 0)
                    {
                        ReplicatedTableLogger.LogInformational("ViewId={0} is invalid. Must be >= 1. Skipping this blob {1}.",
                                                               configurationStore.ViewId,
                                                               blob.Value.Uri);
                        continue;
                    }

                    List <CloudBlockBlob> viewBlobs;
                    if (!viewResult.TryGetValue(configurationStore.ViewId, out viewBlobs))
                    {
                        viewBlobs = new List <CloudBlockBlob>();
                        viewResult.Add(configurationStore.ViewId, viewBlobs);
                    }

                    viewBlobs.Add(blob.Value);

                    if (viewBlobs.Count >= this.quorumSize)
                    {
                        this.lastRenewedReadView.ViewId = this.lastRenewedWriteView.ViewId = configurationStore.ViewId;
                        for (int i = 0; i < configurationStore.ReplicaChain.Count; i++)
                        {
                            ReplicaInfo      replica     = configurationStore.ReplicaChain[i];
                            CloudTableClient tableClient = GetTableClientForReplica(replica);
                            if (replica != null && tableClient != null)
                            {
                                //Update the write view always
                                this.lastRenewedWriteView.Chain.Add(new Tuple <ReplicaInfo, CloudTableClient>(replica, tableClient));

                                //Update the read view only for replicas part of the view
                                if (i >= configurationStore.ReadViewHeadIndex)
                                {
                                    this.lastRenewedReadView.Chain.Add(new Tuple <ReplicaInfo, CloudTableClient>(replica, tableClient));
                                }

                                //Note the time when the view was updated
                                this.lastViewRefreshTime = refreshStartTime;

                                this.ConvertXStoreTableMode = configurationStore.ConvertXStoreTableMode;
                            }
                        }

                        this.lastRenewedWriteView.ReadHeadIndex = configurationStore.ReadViewHeadIndex;

                        break;
                    }
                }
            }
        }