public async Task Publish(IMultiClusterGossipData data)
        {
            logger.Debug("-Publish data:{0}", data);
            // this is (almost) always called with just one item in data to be written back
            // so we are o.k. with doing individual tasks for each storage read and write

            var tasks = new List <Task>();

            if (data.Configuration != null)
            {
                Func <Task> publishconfig = async() => {
                    var configInStorage = await tableManager.ReadConfigurationEntryAsync();
                    await DiffAndWriteBackConfigAsync(data.Configuration, configInStorage);
                };
                tasks.Add(publishconfig());
            }
            foreach (var gateway in data.Gateways.Values)
            {
                Func <Task> publishgatewayinfo = async() => {
                    var gatewayInfoInStorage = await tableManager.ReadGatewayEntryAsync(gateway);
                    await DiffAndWriteBackGatewayInfoAsync(gateway, gatewayInfoInStorage);
                };
                tasks.Add(publishgatewayinfo());
            }
            await Task.WhenAll(tasks);
        }
Exemple #2
0
        private void PublishMyStatusToNewDestinations(IMultiClusterGossipData delta)
        {
            // for quicker convergence, we publish active local status information
            // immediately when we learn about a new destination

            GatewayEntry myEntry;

            // don't do this if we are not an active gateway
            if (!localData.Current.Gateways.TryGetValue(this.Silo, out myEntry) ||
                myEntry.Status != GatewayStatus.Active)
            {
                return;
            }

            foreach (var gateway in delta.Gateways.Values)
            {
                var gossipworker = (gateway.ClusterId == this.clusterId) ?
                                   GetSiloWorker(gateway.SiloAddress) : GetClusterWorker(gateway.ClusterId);

                var destinationCluster = gateway.ClusterId;

                if (!gossipworker.KnowsMe)
                {
                    gossipworker.Publish(new MultiClusterData(myEntry));
                }
            }
        }
        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;
            }
        }
        public IMultiClusterGossipData ApplyDataAndNotify(IMultiClusterGossipData data)
        {
            if (data.IsEmpty)
            {
                return(data);
            }

            MultiClusterData delta;
            MultiClusterData prev = this.localData;

            this.localData = prev.Merge(data, out delta);

            if (logger.IsEnabled(LogLevel.Trace))
            {
                logger.Trace("ApplyDataAndNotify: delta {0}", delta);
            }

            if (delta.IsEmpty)
            {
                return(delta);
            }

            if (delta.Gateways.Count > 0)
            {
                // some gateways have changed
                ComputeAvailableGatewaysPerCluster();
            }

            if (delta.Configuration != null)
            {
                // notify configuration listeners of change
                List <IMultiClusterConfigurationListener> listenersToNotify;
                lock (confListeners)
                {
                    // make a copy under the lock
                    listenersToNotify = confListeners.ToList();
                }

                foreach (var listener in listenersToNotify)
                {
                    try
                    {
                        listener.OnMultiClusterConfigurationChange(delta.Configuration);
                    }
                    catch (Exception exc)
                    {
                        logger.Error(ErrorCode.MultiClusterNetwork_LocalSubscriberException,
                                     String.Format("IMultiClusterConfigurationListener {0} threw exception processing configuration {1}",
                                                   listener, delta.Configuration), exc);
                    }
                }
            }


            return(delta);
        }
Exemple #5
0
            public void Publish(IMultiClusterGossipData data)
            {
                // add the data to the data waiting to be published
                toPublish = toPublish.Merge(data);

                if (oracle.logger.IsEnabled(LogLevel.Debug))
                {
                    LogQueuedPublish(toPublish);
                }

                Notify();
            }
        /// <summary>
        ///  incorporate source, producing new result, and report delta.
        ///  Ignores expired entries in source, and removes expired entries from this.
        /// </summary>
        /// <param name="source">The source data to apply to the data in this object</param>
        /// <param name="delta">A delta of what changes were actually applied, used for change listeners</param>
        /// <returns>The updated data</returns>
        public MultiClusterData Merge(IMultiClusterGossipData source, out MultiClusterData delta)
        {
            //--  configuration
            var sourceConf = source.Configuration;
            var thisConf   = this.Configuration;
            MultiClusterConfiguration resultConf;
            MultiClusterConfiguration deltaConf = null;

            if (MultiClusterConfiguration.OlderThan(thisConf, sourceConf))
            {
                resultConf = sourceConf;
                deltaConf  = sourceConf;
            }
            else
            {
                resultConf = thisConf;
            }

            //--  gateways
            var sourceList = source.Gateways;
            var thisList   = this.Gateways;
            var resultList = new Dictionary <SiloAddress, GatewayEntry>();
            var deltaList  = new Dictionary <SiloAddress, GatewayEntry>();

            foreach (var key in sourceList.Keys.Union(thisList.Keys).Distinct())
            {
                GatewayEntry thisEntry;
                GatewayEntry sourceEntry;
                thisList.TryGetValue(key, out thisEntry);
                sourceList.TryGetValue(key, out sourceEntry);

                if (sourceEntry != null && !sourceEntry.Expired &&
                    (thisEntry == null || thisEntry.HeartbeatTimestamp < sourceEntry.HeartbeatTimestamp))
                {
                    resultList.Add(key, sourceEntry);
                    deltaList.Add(key, sourceEntry);
                }
                else if (thisEntry != null)
                {
                    if (!thisEntry.Expired)
                    {
                        resultList.Add(key, thisEntry);
                    }
                    else
                    {
                        deltaList.Add(key, thisEntry);
                    }
                }
            }

            delta = new MultiClusterData(deltaList, deltaConf);
            return(new MultiClusterData(resultList, resultConf));
        }
Exemple #7
0
        // called by remote nodes' full background gossip
        public Task <IMultiClusterGossipData> Synchronize(IMultiClusterGossipData gossipData)
        {
            logger.Debug("--- Synchronize: gossip {0}", gossipData);

            var data = (MultiClusterData)gossipData;

            var delta = this.localData.ApplyDataAndNotify(data);

            PublishMyStatusToNewDestinations(delta);

            logger.Debug("--- Synchronize: done, answer={0}", delta);

            return(Task.FromResult((IMultiClusterGossipData)delta));
        }
Exemple #8
0
        // called by remote nodes that publish changes
        public Task Publish(IMultiClusterGossipData gossipData, bool forwardLocally)
        {
            logger.Verbose("--- Publish: receive {0} data {1}", forwardLocally ? "remote" : "local", gossipData);

            var data = (MultiClusterData)gossipData;

            var delta = localData.ApplyDataAndNotify(data);

            // forward changes to all local silos
            if (forwardLocally)
            {
                foreach (var activeSilo in this.GetApproximateOtherActiveSilos())
                    GetSiloWorker(activeSilo).Publish(delta);
            }

            PublishMyStatusToNewDestinations(delta);

            logger.Verbose("--- Publish: done");

            return Task.CompletedTask;
        }
        /// <summary>
        ///  merge source into this object, and return result.
        ///  Ignores expired entries in source, and removes expired entries from this.
        /// </summary>
        /// <param name="source">The source data to apply to the data in this object</param>
        /// <returns>The updated data</returns>
        public MultiClusterData Merge(IMultiClusterGossipData source)
        {
            MultiClusterData ignore;

            return(Merge(source, out ignore));
        }
Exemple #10
0
        public IMultiClusterGossipData ApplyDataAndNotify(IMultiClusterGossipData data)
        {
            if (data.IsEmpty)
            {
                return(data);
            }

            MultiClusterData delta;
            MultiClusterData prev = this.localData;

            this.localData = prev.Merge(data, out delta);

            if (logger.IsEnabled(LogLevel.Trace))
            {
                logger.Trace("ApplyDataAndNotify: delta {0}", delta);
            }

            if (delta.IsEmpty)
            {
                return(delta);
            }

            if (delta.Gateways.Count > 0)
            {
                // some gateways have changed
                ComputeAvailableGatewaysPerCluster();
            }

            if (delta.Configuration != null)
            {
                // notify configuration listeners of change

                List <GrainReference> listenersToNotify;
                lock (confListeners)
                {
                    // make a copy under the lock
                    listenersToNotify = confListeners.ToList();
                }

                foreach (var listener in listenersToNotify)
                {
                    try
                    {
                        if (logger.IsEnabled(LogLevel.Trace))
                        {
                            logger.Trace("-NotificationWork: notify IProtocolParticipant {0} of configuration {1}", listener, delta.Configuration);
                        }

                        // enqueue conf change event as grain call
                        var g = this.grainFactory.Cast <ILogConsistencyProtocolParticipant>(listener);
                        g.OnMultiClusterConfigurationChange(delta.Configuration).Ignore();
                    }
                    catch (Exception exc)
                    {
                        logger.Error(ErrorCode.MultiClusterNetwork_LocalSubscriberException,
                                     String.Format("IProtocolParticipant {0} threw exception processing configuration {1}",
                                                   listener, delta.Configuration), exc);
                    }
                }
            }


            return(delta);
        }