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); } }
public static bool TryReadBlob <T>(CloudBlockBlob blob, out T configurationStore) where T : class { configurationStore = default(T); try { string content = blob.DownloadText(); if (content == Constants.ConfigurationStoreUpdatingText) { return(false); } string blobContent = content; configurationStore = JsonStore <T> .Deserialize(blobContent); return(true); } catch (Exception e) { ReplicatedTableLogger.LogError("Error reading blob: {0}. Exception: {1}", blob.Uri, e.Message); } return(false); }
public static View InitFromConfigVer2(string name, ReplicatedTableConfigurationStore configurationStore, Action <ReplicaInfo> SetConnectionString) { View view = new View(name); if (configurationStore != null) { view.ViewId = configurationStore.ViewId; foreach (ReplicaInfo replica in configurationStore.GetCurrentReplicaChain()) { SetConnectionString(replica); CloudTableClient tableClient = ReplicatedTableConfigurationManager.GetTableClientForReplica(replica); if (tableClient == null) { // All replicas MUST exist or View is not relevant view.Chain.Clear(); ReplicatedTableLogger.LogError("ViewName={0} could not load replica ({1})", name, replica); break; } view.Chain.Add(new Tuple <ReplicaInfo, CloudTableClient>(replica, tableClient)); } // Infered: first readable replica view.ReadHeadIndex = view.Chain.FindIndex(tuple => tuple.Item1.IsReadable()); } return(view); }
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)); } }
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); }
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; }
public static async Task <ReplicatedTableReadBlobResult> TryReadBlobAsync <T>(CloudBlockBlob blob, Action <T, string> callback, Func <string, T> ParseBlobFunc, CancellationToken ct) where T : class { try { BlobRequestOptions options = new BlobRequestOptions() { ServerTimeout = TimeSpan.FromSeconds(5), MaximumExecutionTime = TimeSpan.FromSeconds(30) }; string content = await blob.DownloadTextAsync(null, null, options, null, ct); if (content == Constants.ConfigurationStoreUpdatingText) { return(new ReplicatedTableReadBlobResult(ReadBlobCode.UpdateInProgress, "Blob update in progress ...")); } // ParseBlobFunc != null T configuration = ParseBlobFunc(content); string eTag = blob.Properties.ETag; // callback != null callback(configuration, eTag); return(new ReplicatedTableReadBlobResult(ReadBlobCode.Success, "")); } catch (StorageException e) { var msg = string.Format("Error reading blob: {0}. StorageException: {1}", blob.Uri, e.Message); ReplicatedTableLogger.LogError(msg); if (e.RequestInformation != null && e.RequestInformation.HttpStatusCode == (int)HttpStatusCode.NotFound) { return(new ReplicatedTableReadBlobResult(ReadBlobCode.NotFound, msg)); } return(new ReplicatedTableReadBlobResult(ReadBlobCode.StorageException, msg)); } catch (OperationCanceledException) { var msg = string.Format("TryReadBlobAsync cancelled ({0})", blob.Uri); ReplicatedTableLogger.LogInformational(msg); return(new ReplicatedTableReadBlobResult(ReadBlobCode.Exception, msg)); } catch (Exception e) { var msg = string.Format("Error reading blob: {0}. Exception: {1}", blob.Uri, e.Message); ReplicatedTableLogger.LogError(msg); return(new ReplicatedTableReadBlobResult(ReadBlobCode.Exception, msg)); } }
/* * 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); }
public static View InitFromConfigVer1(string name, ReplicatedTableConfigurationStore configurationStore, Action <ReplicaInfo> SetConnectionString) { View view = new View(name); if (configurationStore != null) { view.ViewId = configurationStore.ViewId; view.ReadHeadIndex = configurationStore.ReadViewHeadIndex; view.RefreshTime = DateTime.UtcNow; foreach (ReplicaInfo replica in configurationStore.ReplicaChain) { if (replica == null) { continue; } SetConnectionString(replica); CloudTableClient tableClient = ReplicatedTableConfigurationManager.GetTableClientForReplica(replica); if (tableClient == null) { // All replicas MUST exist or View is not relevant view.Chain.Clear(); ReplicatedTableLogger.LogError("ViewName={0} could not load replica ({1})", name, replica); break; } view.Chain.Add(new Tuple <ReplicaInfo, CloudTableClient>(replica, tableClient)); } // If not configured use Tail. Chain must be defined at this point, so don't move this code up! view.ReadTailIndex = configurationStore.ReadViewTailIndex; if (view.ReadTailIndex < 0) { view.ReadTailIndex = view.TailIndex; } if (!view.IsEmpty) { ReplicaInfo head = view.GetReplicaInfo(0); head.Status = ReplicaStatus.WriteOnly; if (view.IsStable) { head.Status = ReplicaStatus.ReadWrite; } } } return(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); } // - 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); } }
internal protected void MoveReplicaToHeadAndSetViewToReadOnly(string viewName, string storageAccountName) { // Assert (storageAccountName != null) int matchIndex = ReplicaChain.FindIndex(r => r.StorageAccountName == storageAccountName); if (matchIndex == -1) { return; } // - Ensure its status is *None* ReplicaInfo candidateReplica = ReplicaChain[matchIndex]; var oldStatus = candidateReplica.Status; candidateReplica.Status = ReplicaStatus.None; // First check it will be possible to modify the sequence foreach (ReplicaInfo replica in GetCurrentReplicaChain()) { // Change is not possible if (replica.Status == ReplicaStatus.WriteOnly) { // Restore previous status candidateReplica.Status = oldStatus; var msg = string.Format("View:\'{0}\' : can't set a WriteOnly replica to ReadOnly !!!", viewName); ReplicatedTableLogger.LogError(msg); throw new Exception(msg); } } // Do the change ... // - Move it to the front of the chain ReplicaChain.RemoveAt(matchIndex); ReplicaChain.Insert(0, candidateReplica); // Set all active replicas to *ReadOnly* foreach (ReplicaInfo replica in GetCurrentReplicaChain()) { replica.Status = ReplicaStatus.ReadOnly; } // Update view id ViewId++; // Reset 'ReadViewTailIndex' => user has to set it again ResetReadViewTailIndex(); }
public View GetTableView(string tableName) { ReplicatedTableConfiguredTable config; if (IsConfiguredTable(tableName, out config)) { return(GetView(config.ViewName)); } var msg = string.Format("Table={0}: is not configured!", tableName); ReplicatedTableLogger.LogError(msg); throw new Exception(msg); }
public bool ConvertToRTable(string tableName) { ReplicatedTableConfiguredTable config; if (IsConfiguredTable(tableName, out config)) { return(config.ConvertToRTable); } var msg = string.Format("Table={0}: is not configured!", tableName); ReplicatedTableLogger.LogError(msg); throw new Exception(msg); }
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); }
public static ReplicatedTableReadBlobResult TryReadBlob <T>(CloudBlockBlob blob, out T configuration, out string eTag, Func <string, T> ParseBlobFunc) where T : class { configuration = default(T); eTag = null; try { BlobRequestOptions options = new BlobRequestOptions() { ServerTimeout = TimeSpan.FromSeconds(5), MaximumExecutionTime = TimeSpan.FromSeconds(30) }; string content = blob.DownloadText(null, null, options, null); if (content == Constants.ConfigurationStoreUpdatingText) { return(new ReplicatedTableReadBlobResult(ReadBlobCode.UpdateInProgress, "Blob update in progress ...")); } configuration = ParseBlobFunc(content); eTag = blob.Properties.ETag; return(new ReplicatedTableReadBlobResult(ReadBlobCode.Success, "")); } catch (StorageException e) { var msg = string.Format("Error reading blob: {0}. StorageException: {1}", blob.Uri, e.Message); ReplicatedTableLogger.LogError(msg); if (e.RequestInformation != null && e.RequestInformation.HttpStatusCode == (int)HttpStatusCode.NotFound) { return(new ReplicatedTableReadBlobResult(ReadBlobCode.NotFound, msg)); } return(new ReplicatedTableReadBlobResult(ReadBlobCode.StorageException, msg)); } catch (Exception e) { var msg = string.Format("Error reading blob: {0}. Exception: {1}", blob.Uri, e.Message); ReplicatedTableLogger.LogError(msg); return(new ReplicatedTableReadBlobResult(ReadBlobCode.Exception, msg)); } }
public static void TryWriteBlob(CloudBlockBlob blob, string content) { try { //Step 1: Delete the current configuration blob.UploadText(Constants.ConfigurationStoreUpdatingText); //Step 2: Wait for L + CF to make sure no pending transaction working on old views Thread.Sleep(TimeSpan.FromSeconds(Constants.LeaseDurationInSec + Constants.ClockFactorInSec)); //Step 3: Update new config blob.UploadText(content); } catch (StorageException e) { ReplicatedTableLogger.LogError("Updating the blob: {0} failed. Exception: {1}", blob, e.Message); } }
/* * 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); }
public static bool TryCreateCloudTableClient(string storageAccountConnectionString, out CloudTableClient cloudTableClient) { cloudTableClient = null; try { cloudTableClient = CloudStorageAccount.Parse(storageAccountConnectionString).CreateCloudTableClient(); return(true); } catch (Exception e) { ReplicatedTableLogger.LogError("Error creating cloud table client: Connection string {0}. Exception: {1}", storageAccountConnectionString, e.Message); } return(false); }
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); }
/// <summary> /// Convert SecureString to String /// </summary> /// <param name="secureText"></param> /// <returns></returns> public static string ToString(SecureString secureText) { IntPtr unmanagedPtr = IntPtr.Zero; try { unmanagedPtr = Marshal.SecureStringToGlobalAllocUnicode(secureText); return(Marshal.PtrToStringUni(unmanagedPtr)); } catch (Exception ex) { ReplicatedTableLogger.LogError("Exception converting from SecureString : {0}", ex); } finally { Marshal.ZeroFreeGlobalAllocUnicode(unmanagedPtr); } return(null); }
public static ReplicatedTableWriteBlobResult TryWriteBlob(CloudBlockBlob blob, string content, string eTag) { try { AccessCondition condition = string.IsNullOrEmpty(eTag) ? AccessCondition.GenerateEmptyCondition() : AccessCondition.GenerateIfMatchCondition(eTag); blob.UploadText(content, accessCondition: condition); return(new ReplicatedTableWriteBlobResult(true, "")); } catch (StorageException e) { var msg = string.Format("Updating the blob: {0} failed. Exception: {1}", blob, e.Message); ReplicatedTableLogger.LogError(msg); return(new ReplicatedTableWriteBlobResult(false, msg)); } }
public static bool TryCreateCloudTableClient(SecureString connectionString, out CloudTableClient cloudTableClient) { cloudTableClient = null; try { string decryptConnectionString = SecureStringHelper.ToString(connectionString); cloudTableClient = CloudStorageAccount.Parse(decryptConnectionString).CreateCloudTableClient(); return(true); } catch (Exception e) { ReplicatedTableLogger.LogError( "Error creating cloud table client: Connection string {0}. Exception: {1}", "********", e.Message); } return(false); }
public static View InitFromConfigVer2(string name, ReplicatedTableConfigurationStore configurationStore, Action <ReplicaInfo> SetConnectionString) { View view = new View(name); if (configurationStore != null) { view.ViewId = configurationStore.ViewId; view.RefreshTime = DateTime.UtcNow; foreach (ReplicaInfo replica in configurationStore.GetCurrentReplicaChain()) { SetConnectionString(replica); CloudTableClient tableClient = ReplicatedTableConfigurationManager.GetTableClientForReplica(replica); if (tableClient == null) { // All replicas MUST exist or View is not relevant view.Chain.Clear(); ReplicatedTableLogger.LogError("ViewName={0} could not load replica ({1})", name, replica); break; } view.Chain.Add(new Tuple <ReplicaInfo, CloudTableClient>(replica, tableClient)); } // Infered: first readable replica view.ReadHeadIndex = view.Chain.FindIndex(tuple => tuple.Item1.IsReadable()); // If not configured use Tail. Chain must be defined at this point, so don't move this code up! view.ReadTailIndex = configurationStore.ReadViewTailIndex; if (view.ReadTailIndex < 0) { view.ReadTailIndex = view.TailIndex; } } return(view); }
public bool IsTableViewStable(string tableName, string viewToUse = null) { ReplicatedTableConfiguredTable config; if (IsConfiguredTable(tableName, out config)) { // Use table default view if (string.IsNullOrEmpty(viewToUse)) { viewToUse = config.ViewName; } if (config.IsViewReferenced(viewToUse)) { return(IsViewStable(viewToUse)); } } var msg = string.Format("Table={0}: is not configured or ViewToUse={1} is not referenced!", tableName, viewToUse); ReplicatedTableLogger.LogError(msg); throw new Exception(msg); }
internal protected void MoveReplicaToHeadAndSetViewToReadOnly(string viewName, string storageAccountName) { // Assert (storageAccountName != null) int matchIndex = ReplicaChain.FindIndex(r => r.StorageAccountName == storageAccountName); if (matchIndex == -1) { return; } // - Ensure its status is *None* ReplicaInfo candidateReplica = ReplicaChain[matchIndex]; candidateReplica.Status = ReplicaStatus.None; // - Move it to the front of the chain ReplicaChain.RemoveAt(matchIndex); ReplicaChain.Insert(0, candidateReplica); // Set all active replicas to *ReadOnly* foreach (ReplicaInfo replica in GetCurrentReplicaChain()) { if (replica.Status == ReplicaStatus.WriteOnly) { var msg = string.Format("View:\'{0}\' : can't set a WriteOnly replica to ReadOnly !!!", viewName); ReplicatedTableLogger.LogError(msg); throw new Exception(msg); } replica.Status = ReplicaStatus.ReadOnly; } // Update view id ViewId++; }
private void SaveConfigAndRefreshItsIdAndValidateIsLoaded(ReplicatedTableConfiguration configuration, string iteration, bool validateConfigIsLoaded = true) { string msg = ""; ReplicatedTableQuorumWriteResult writeResult = UpdateConfigurationInternal(configuration, true); if (writeResult.Code != ReplicatedTableQuorumWriteCode.Success) { msg = string.Format("{0} : Failed to update configuration, \n{1}", iteration, writeResult.ToString()); ReplicatedTableLogger.LogError(msg); throw new Exception(msg); } // Update config with new Id configuration.Id = new Guid(writeResult.Message); // do we need to validate if the config is loaded by the config manager ? if (!validateConfigIsLoaded) { return; } // - Confirm the new config is the current loaded into the RTable config manager if (configuration.Id == this.configManager.GetCurrentRunningConfigId()) { return; } msg = string.Format("{0} : ConfigId({1}) != currently running configurationId({2})", iteration, configuration.Id, this.configManager.GetCurrentRunningConfigId()); ReplicatedTableLogger.LogError(msg); throw new Exception(msg); }
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 ! **/ }
/// <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 }); }
/// <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); }