internal async Task <Tuple <GossipTableEntry, Dictionary <SiloAddress, GossipTableEntry> > > ReadAllEntriesAsync() { var queryResults = await storage.ReadAllTableEntriesForPartitionAsync(this.GlobalServiceId).ConfigureAwait(false); // organize the returned storage entries by what they represent GossipTableEntry configInStorage = null; var gatewayInfoInStorage = new Dictionary <SiloAddress, GossipTableEntry>(); foreach (var x in queryResults) { var tableEntry = x.Item1; if (tableEntry.RowKey.Equals(GossipTableEntry.CONFIGURATION_ROW)) { configInStorage = tableEntry; } else { try { tableEntry.ParseSiloAddressFromRowKey(); gatewayInfoInStorage.Add(tableEntry.SiloAddress, tableEntry); } catch (Exception exc) { logger.Error( (int)TableStorageErrorCode.AzureTable_61, string.Format("Intermediate error parsing GossipTableEntry: {0}. Ignoring this entry.", tableEntry), exc); } } } return(new Tuple <GossipTableEntry, Dictionary <SiloAddress, GossipTableEntry> >(configInStorage, gatewayInfoInStorage)); }
internal async Task <bool> TryUpdateGatewayEntryAsync(GatewayEntry entry, GossipTableEntry row, string eTag) { row.Status = entry.Status.ToString(); row.GossipTimestamp = entry.HeartbeatTimestamp; return(await TryUpdateTableEntryAsync(row, eTag).ConfigureAwait(false)); }
public async Task <IMultiClusterGossipData> Synchronize(IMultiClusterGossipData pushed) { logger.Debug("-Synchronize pushed:{0}", pushed); try { // read the entire table from storage var entriesFromStorage = await tableManager.ReadAllEntriesAsync(); var configInStorage = entriesFromStorage.Item1; var gatewayInfoInStorage = entriesFromStorage.Item2; // diff and write back configuration var configDeltaTask = DiffAndWriteBackConfigAsync(pushed.Configuration, configInStorage); // diff and write back gateway info for each gateway appearing locally or in storage var gatewayDeltaTasks = new List <Task <GatewayEntry> >(); var allAddresses = gatewayInfoInStorage.Keys.Union(pushed.Gateways.Keys); foreach (var address in allAddresses) { GatewayEntry pushedInfo = null; pushed.Gateways.TryGetValue(address, out pushedInfo); GossipTableEntry infoInStorage = null; gatewayInfoInStorage.TryGetValue(address, out infoInStorage); gatewayDeltaTasks.Add(DiffAndWriteBackGatewayInfoAsync(pushedInfo, infoInStorage)); } // wait for all the writeback tasks to complete // these are not batched because we want them to fail individually on e-tag conflicts, not all await configDeltaTask; await Task.WhenAll(gatewayDeltaTasks); // assemble delta pieces var gw = new Dictionary <SiloAddress, GatewayEntry>(); foreach (var t in gatewayDeltaTasks) { var d = t.Result; if (d != null) { gw.Add(d.SiloAddress, d); } } var delta = new MultiClusterData(gw, configDeltaTask.Result); logger.Debug("-Synchronize pulled delta:{0}", delta); return(delta); } catch (Exception e) { logger.Info("-Synchronize encountered exception {0}", e); throw e; } }
internal async Task <bool> TryCreateGatewayEntryAsync(GatewayEntry entry) { var row = new GossipTableEntry() { PartitionKey = GlobalServiceId, RowKey = GossipTableEntry.ConstructRowKey(entry.SiloAddress, entry.ClusterId), Status = entry.Status.ToString(), GossipTimestamp = entry.HeartbeatTimestamp }; return(await TryCreateTableEntryAsync(row).ConfigureAwait(false)); }
internal async Task <bool> TryCreateConfigurationEntryAsync(MultiClusterConfiguration configuration) { if (configuration == null) { throw new ArgumentNullException("configuration"); } var entry = new GossipTableEntry { PartitionKey = GlobalServiceId, RowKey = GossipTableEntry.CONFIGURATION_ROW, GossipTimestamp = configuration.AdminTimestamp, Clusters = string.Join(GossipTableEntry.ClustersListSeparator, configuration.Clusters), Comment = configuration.Comment ?? "" }; return(await TryCreateTableEntryAsync(entry).ConfigureAwait(false)); }
internal async Task<bool> TryUpdateConfigurationEntryAsync(MultiClusterConfiguration configuration, GossipTableEntry entry, string eTag) { if (configuration == null) throw new ArgumentNullException("configuration"); entry.GossipTimestamp = configuration.AdminTimestamp; entry.Clusters = string.Join(GossipTableEntry.ClustersListSeparator, configuration.Clusters); entry.Comment = configuration.Comment ?? ""; return (await TryUpdateTableEntryAsync(entry, eTag).ConfigureAwait(false)); }
internal async Task<bool> TryCreateConfigurationEntryAsync(MultiClusterConfiguration configuration) { if (configuration == null) throw new ArgumentNullException("configuration"); var entry = new GossipTableEntry { PartitionKey = GlobalServiceId, RowKey = GossipTableEntry.CONFIGURATION_ROW, GossipTimestamp = configuration.AdminTimestamp, Clusters = string.Join(GossipTableEntry.ClustersListSeparator, configuration.Clusters), Comment = configuration.Comment ?? "" }; return (await TryCreateTableEntryAsync(entry).ConfigureAwait(false)); }
/// <summary> /// Try once to delete a data entry in the Azure table. Returns false if there is a conflict. /// </summary> private async Task <bool> TryDeleteTableEntryAsync(GossipTableEntry data, string etag, [CallerMemberName] string operation = null) { return(await TryOperation(() => storage.DeleteTableEntryAsync(data, etag), operation)); }
internal Task <bool> TryDeleteGatewayEntryAsync(GossipTableEntry row, string eTag) { return(TryDeleteTableEntryAsync(row, eTag)); }
// compare gatewayInfo with gatewayInfoInStorage, and // - write gatewayInfo to storage if it is newer (or do nothing on etag conflict) // - remove expired gateway info from storage // - return gatewayInfoInStorage if it is newer internal async Task<GatewayEntry> DiffAndWriteBackGatewayInfoAsync(GatewayEntry gatewayInfo, GossipTableEntry gatewayInfoInStorage) { if ((gatewayInfo != null && !gatewayInfo.Expired) && (gatewayInfoInStorage == null || gatewayInfoInStorage.GossipTimestamp < gatewayInfo.HeartbeatTimestamp)) { // push the more recent gateway info to storage if (gatewayInfoInStorage == null) { await tableManager.TryCreateGatewayEntryAsync(gatewayInfo); } else { await tableManager.TryUpdateGatewayEntryAsync(gatewayInfo, gatewayInfoInStorage, gatewayInfoInStorage.ETag); } } else if (gatewayInfoInStorage != null && (gatewayInfo == null || gatewayInfo.HeartbeatTimestamp < gatewayInfoInStorage.GossipTimestamp)) { var fromstorage = gatewayInfoInStorage.ToGatewayEntry(); if (fromstorage.Expired) { // remove gateway info from storage await tableManager.TryDeleteGatewayEntryAsync(gatewayInfoInStorage, gatewayInfoInStorage.ETag); } else { // pull the more recent info from storage return fromstorage; } } return null; }
internal Task<bool> TryDeleteGatewayEntryAsync(GossipTableEntry row, string eTag) { return TryDeleteTableEntryAsync(row, eTag); }
internal async Task <GossipTableEntry> ReadGatewayEntryAsync(GatewayEntry gateway) { var result = await storage.ReadSingleTableEntryAsync(this.GlobalServiceId, GossipTableEntry.ConstructRowKey(gateway.SiloAddress, gateway.ClusterId)).ConfigureAwait(false); if (result != null) { var tableEntry = result.Item1; try { tableEntry.ParseSiloAddressFromRowKey(); return(tableEntry); } catch (Exception exc) { logger.Error( (int)TableStorageErrorCode.AzureTable_61, string.Format("Intermediate error parsing GossipTableEntry: {0}. Ignoring this entry.", tableEntry), exc); } } return(null); }
// compare gatewayInfo with gatewayInfoInStorage, and // - write gatewayInfo to storage if it is newer (or do nothing on etag conflict) // - remove expired gateway info from storage // - return gatewayInfoInStorage if it is newer internal async Task <GatewayEntry> DiffAndWriteBackGatewayInfoAsync(GatewayEntry gatewayInfo, GossipTableEntry gatewayInfoInStorage) { if ((gatewayInfo != null && !gatewayInfo.Expired) && (gatewayInfoInStorage == null || gatewayInfoInStorage.GossipTimestamp < gatewayInfo.HeartbeatTimestamp)) { // push the more recent gateway info to storage if (gatewayInfoInStorage == null) { await tableManager.TryCreateGatewayEntryAsync(gatewayInfo); } else { await tableManager.TryUpdateGatewayEntryAsync(gatewayInfo, gatewayInfoInStorage, gatewayInfoInStorage.ETag); } } else if (gatewayInfoInStorage != null && (gatewayInfo == null || gatewayInfo.HeartbeatTimestamp < gatewayInfoInStorage.GossipTimestamp)) { var fromstorage = gatewayInfoInStorage.ToGatewayEntry(); if (fromstorage.Expired) { // remove gateway info from storage await tableManager.TryDeleteGatewayEntryAsync(gatewayInfoInStorage, gatewayInfoInStorage.ETag); } else { // pull the more recent info from storage return(fromstorage); } } return(null); }
// compare config with configInStorage, and // - write config to storage if it is newer (or do nothing on etag conflict) // - return config from store if it is newer internal async Task <MultiClusterConfiguration> DiffAndWriteBackConfigAsync(MultiClusterConfiguration config, GossipTableEntry configInStorage) { // interpret empty admin timestamp by taking the azure table timestamp instead // this allows an admin to inject a configuration by editing table directly if (configInStorage != null && configInStorage.GossipTimestamp == default(DateTime)) { configInStorage.GossipTimestamp = configInStorage.Timestamp.UtcDateTime; } if (config != null && (configInStorage == null || configInStorage.GossipTimestamp < config.AdminTimestamp)) { // push the more recent configuration to storage if (configInStorage == null) { await tableManager.TryCreateConfigurationEntryAsync(config); } else { await tableManager.TryUpdateConfigurationEntryAsync(config, configInStorage, configInStorage.ETag); } } else if (configInStorage != null && (config == null || config.AdminTimestamp < configInStorage.GossipTimestamp)) { // pull the more recent configuration from storage return(configInStorage.ToConfiguration()); } return(null); }
internal async Task<bool> TryCreateGatewayEntryAsync(GatewayEntry entry) { var row = new GossipTableEntry() { PartitionKey = GlobalServiceId, RowKey = GossipTableEntry.ConstructRowKey(entry.SiloAddress, entry.ClusterId), Status = entry.Status.ToString(), GossipTimestamp = entry.HeartbeatTimestamp }; return (await TryCreateTableEntryAsync(row).ConfigureAwait(false)); }
internal async Task<bool> TryUpdateGatewayEntryAsync(GatewayEntry entry, GossipTableEntry row, string eTag) { row.Status = entry.Status.ToString(); row.GossipTimestamp = entry.HeartbeatTimestamp; return (await TryUpdateTableEntryAsync(row, eTag).ConfigureAwait(false)); }
internal async Task <bool> TryUpdateConfigurationEntryAsync(MultiClusterConfiguration configuration, GossipTableEntry entry, string eTag) { if (configuration == null) { throw new ArgumentNullException("configuration"); } entry.GossipTimestamp = configuration.AdminTimestamp; entry.Clusters = string.Join(GossipTableEntry.ClustersListSeparator, configuration.Clusters); entry.Comment = configuration.Comment ?? ""; return(await TryUpdateTableEntryAsync(entry, eTag).ConfigureAwait(false)); }
/// <summary> /// Try once to delete a data entry in the Azure table. Returns false if there is a conflict. /// </summary> private async Task<bool> TryDeleteTableEntryAsync(GossipTableEntry data, string etag, [CallerMemberName]string operation = null) { return await TryOperation(() => storage.DeleteTableEntryAsync(data, etag), operation); }
// compare config with configInStorage, and // - write config to storage if it is newer (or do nothing on etag conflict) // - return config from store if it is newer internal async Task<MultiClusterConfiguration> DiffAndWriteBackConfigAsync(MultiClusterConfiguration config, GossipTableEntry configInStorage) { // interpret empty admin timestamp by taking the azure table timestamp instead // this allows an admin to inject a configuration by editing table directly if (configInStorage != null && configInStorage.GossipTimestamp == default(DateTime)) configInStorage.GossipTimestamp = configInStorage.Timestamp.UtcDateTime; if (config != null && (configInStorage == null || configInStorage.GossipTimestamp < config.AdminTimestamp)) { // push the more recent configuration to storage if (configInStorage == null) await tableManager.TryCreateConfigurationEntryAsync(config); else await tableManager.TryUpdateConfigurationEntryAsync(config, configInStorage, configInStorage.ETag); } else if (configInStorage != null && (config == null || config.AdminTimestamp < configInStorage.GossipTimestamp)) { // pull the more recent configuration from storage return configInStorage.ToConfiguration(); } return null; }