public static List <ReplicatedTableReadBlobResult> TryReadAllBlobs <T>(List <CloudBlockBlob> blobs, out List <T> values, out List <string> eTags, Func <string, T> ParseBlobFunc) where T : class { int numberOfBlobs = blobs.Count; T[] valuesArray = new T[numberOfBlobs]; string[] eTagsArray = new string[numberOfBlobs]; ReplicatedTableReadBlobResult[] resultArray = new ReplicatedTableReadBlobResult[numberOfBlobs]; // read from all the blobs in parallel Parallel.For(0, numberOfBlobs, index => { DateTime startTime = DateTime.UtcNow; T currentValue; string currentETag; resultArray[index] = TryReadBlob(blobs[index], out currentValue, out currentETag, ParseBlobFunc); valuesArray[index] = currentValue; eTagsArray[index] = currentETag; ReplicatedTableLogger.LogInformational("TryReadBlob #{0} took {1}", index, DateTime.UtcNow - startTime); }); values = valuesArray.ToList(); eTags = eTagsArray.ToList(); return(resultArray.ToList()); }
public static ReplicatedTableQuorumReadResult TryReadBlobQuorumFast <T>(List <CloudBlockBlob> blobs, out T value, out List <string> eTags, Func <string, T> ParseBlobFunc) where T : class { value = default(T); eTags = null; int numberOfBlobs = blobs.Count; var valuesArray = new List <T>(new T[numberOfBlobs]); var eTagsArray = new List <string>(new string[numberOfBlobs]); var resultArray = new List <ReplicatedTableReadBlobResult>(new ReplicatedTableReadBlobResult[numberOfBlobs]); var cancel = new CancellationTokenSource(); /* * Read async all the blobs in parallel */ Parallel.For(0, numberOfBlobs, async(index) => { valuesArray[index] = default(T); eTagsArray[index] = null; resultArray[index] = new ReplicatedTableReadBlobResult(ReadBlobCode.NullObject, "downloaded not started yet!"); DateTime startTime = DateTime.UtcNow; resultArray[index] = await TryReadBlobAsync( blobs[index], (currentValue, currentETag) => { valuesArray[index] = currentValue; eTagsArray[index] = currentETag; }, ParseBlobFunc, cancel.Token); if (resultArray[index].Code == ReadBlobCode.Success) { ReplicatedTableLogger.LogInformational("TryReadBlobAsync #{0} took {1}", index, DateTime.UtcNow - startTime); } }); /* * Poll for "Quorum" progress ... */ ReplicatedTableQuorumReadResult majority; int quorumIndex; do { Thread.Sleep(Constants.QuorumPollingInMilliSeconds); // "resultArray" may change OOB just after Evaluate Quorum step below. // In such case, we may exit the loop because all blobs are retrieved, while "majority" has a stale value! // Therefore, we have to determine, before Evaluate Quorum step, if we'll exit the loop or no. // If Exist, then "majority" would be final. // If No, then we'll loop to compute the latest, unless we break because we already have Quorum without all blobs. bool allBlobsRetrieved = true; // IMPORTANT: foreach()/LINQ throws when "resultArray" changes (race condition). for (int result = 0; result < resultArray.Count; result++) { if (resultArray[result].Code == ReadBlobCode.NullObject) { allBlobsRetrieved = false; break; } } // Evaluate Quorum ... majority = FindMajority(resultArray.AsReadOnly(), valuesArray.AsReadOnly(), out quorumIndex); if (majority.Code == ReplicatedTableQuorumReadCode.NotFound || majority.Code == ReplicatedTableQuorumReadCode.UpdateInProgress || majority.Code == ReplicatedTableQuorumReadCode.Exception || majority.Code == ReplicatedTableQuorumReadCode.Success) { // Quorum => cancel tasks and exit cancel.Cancel(); break; } //else if (allBlobsRetrieved) { // All blobs 'were' retrieved => exit break; } // keep polling ... } while (true); cancel = null; if (majority.Code == ReplicatedTableQuorumReadCode.Success) { value = valuesArray[quorumIndex]; eTags = eTagsArray; } return(majority); }
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(); }