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;
        }
        /// <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);
        }
        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;
                    }
                }
            }
        }