Пример #1
0
        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));
        }
Пример #2
0
        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));
        }
Пример #3
0
        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;
            }
        }
Пример #4
0
        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));
        }
Пример #5
0
        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));
        }
Пример #6
0
        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));
        }
Пример #7
0
        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));
        }
Пример #8
0
 /// <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));
 }
Пример #9
0
 internal Task <bool> TryDeleteGatewayEntryAsync(GossipTableEntry row, string eTag)
 {
     return(TryDeleteTableEntryAsync(row, eTag));
 }
Пример #10
0
 // 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;
 }
Пример #11
0
 internal Task<bool> TryDeleteGatewayEntryAsync(GossipTableEntry row, string eTag)
 {
     return TryDeleteTableEntryAsync(row, eTag);
 }
Пример #12
0
        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);
        }
Пример #13
0
 // 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);
 }
Пример #14
0
        // 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);
        }
Пример #15
0
        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));
        }
Пример #16
0
        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));
        }
Пример #17
0
        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));
        }
Пример #18
0
 /// <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);
 }
Пример #19
0
        // 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;
        }