コード例 #1
0
        /// <inheritdoc/>
        public override Task Initialize(string strProviderName, IStreamQueueMapper queueMapper, TimeSpan siloMaturityPeriod)
        {
            if (queueMapper == null)
            {
                throw new ArgumentNullException("queueMapper");
            }
            var options = this.serviceProvider.GetRequiredService <IOptionsSnapshot <LeaseBasedQueueBalancerOptions> >().Get(strProviderName);

            if (options == null)
            {
                throw new KeyNotFoundException($"No lease base queue balancer options was configured for provider {strProviderName}, nor was a default configured.");
            }
            this.leaseProvider      = this.serviceProvider.GetRequiredService(options.LeaseProviderType) as ILeaseProvider;
            this.leaseLength        = options.LeaseLength;
            this.allQueues          = new ReadOnlyCollection <QueueId>(queueMapper.GetAllQueues().ToList());
            this.siloMaturityPeriod = siloMaturityPeriod;
            NotifyAfterStart().Ignore();
            //make lease renew frequency to be every half of lease time, to avoid renew failing due to timing issues, race condition or clock difference.
            var timerLogger = this.loggerFactory.CreateLogger <AsyncTaskSafeTimer>();

            this.renewLeaseTimer = new AsyncTaskSafeTimer(timerLogger, this.MaintainAndBalanceQueues, null, this.siloMaturityPeriod, this.leaseLength.Divide(2));
            //try to acquire maximum leases every leaseLength
            this.tryAcquireMaximumLeaseTimer = new AsyncTaskSafeTimer(timerLogger, this.AcquireLeaseToMeetMaxResponsibilty, null, this.siloMaturityPeriod, this.leaseLength);
            //Selector default to round robin selector now, but we can make a further change to make selector configurable if needed.  Selector algorithm could
            //be affecting queue balancing stablization time in cluster initializing and auto-scaling
            this.queueSelector = new RoundRobinSelector <QueueId>(this.allQueues);
            return(MaintainAndBalanceQueues(null));
        }
コード例 #2
0
        public DeploymentBasedQueueBalancer(
            ISiloStatusOracle siloStatusOracle,
            IDeploymentConfiguration deploymentConfig,
            IStreamQueueMapper queueMapper,
            bool isFixed)
        {
            if (siloStatusOracle == null)
            {
                throw new ArgumentNullException("siloStatusOracle");
            }
            if (deploymentConfig == null)
            {
                throw new ArgumentNullException("deploymentConfig");
            }
            if (queueMapper == null)
            {
                throw new ArgumentNullException("queueMapper");
            }

            this.siloStatusOracle = siloStatusOracle;
            this.deploymentConfig = deploymentConfig;
            allQueues = queueMapper.GetAllQueues().ToList();
            queueBalanceListeners = new List<IStreamQueueBalanceListener>();
            mySiloName = this.siloStatusOracle.SiloName;
            this.isFixed = isFixed;

            // register for notification of changes to silo status for any silo in the cluster
            this.siloStatusOracle.SubscribeToSiloStatusEvents(this);
        }
コード例 #3
0
        public async Task Initialize(string strProviderName,
                                     IStreamQueueMapper queueMapper)
        {
            this.leaseManagerGrain = this.grainFactory.GetGrain <ILeaseManagerGrain>(strProviderName);
            await this.leaseManagerGrain.SetQueuesAsLeases(queueMapper.GetAllQueues());

            this.id = $"{strProviderName}-{Guid.NewGuid()}";
            await GetInitialLease();
        }
コード例 #4
0
 public override Task Initialize(IStreamQueueMapper queueMapper)
 {
     if (queueMapper == null)
     {
         throw new ArgumentNullException("queueMapper");
     }
     this.allQueues = new ReadOnlyCollection <QueueId>(queueMapper.GetAllQueues().ToList());
     NotifyAfterStart().Ignore();
     return(Task.CompletedTask);
 }
コード例 #5
0
 public override Task Initialize(IStreamQueueMapper queueMapper)
 {
     if (queueMapper == null)
     {
         throw new ArgumentNullException("queueMapper");
     }
     this.allQueues = queueMapper.GetAllQueues().ToList();
     NotifyAfterStart().Ignore();
     return(base.Initialize(queueMapper));
 }
コード例 #6
0
        public async Task Initialize(string strProviderName,
                                     IStreamQueueMapper queueMapper,
                                     TimeSpan siloMaturityPeriod,
                                     IProviderConfiguration providerConfig)
        {
            this.leaseManagerGrain = this.grainFactory.GetGrain <ILeaseManagerGrain>(strProviderName);
            await this.leaseManagerGrain.SetQueuesAsLeases(queueMapper.GetAllQueues());

            this.id = $"{strProviderName}-{Guid.NewGuid()}";
            await GetInitialLease();
        }
コード例 #7
0
 public override Task Initialize(string strProviderName,
                                 IStreamQueueMapper queueMapper,
                                 TimeSpan siloMaturityPeriod)
 {
     if (queueMapper == null)
     {
         throw new ArgumentNullException("queueMapper");
     }
     this.allQueues          = new ReadOnlyCollection <QueueId>(queueMapper.GetAllQueues().ToList());
     this.siloMaturityPeriod = siloMaturityPeriod;
     NotifyAfterStart().Ignore();
     return(Task.CompletedTask);
 }
コード例 #8
0
 /// <summary>
 /// Checks to see if deployment configuration has changed, by adding or removing silos.
 /// If so, it updates the list of all silo names and creates a new resource balancer.
 /// This should occure rarely.
 /// </summary>
 /// <param name="newSiloNames">new silo names</param>
 /// <returns>bool, true if list of all silo names has changed</returns>
 private bool UpdateAllSiloNames(IList <string> newSiloNames)
 {
     // Has configured silo names changed
     if (allSiloNames.Count != newSiloNames.Count ||
         !allSiloNames.ListEquals(newSiloNames))
     {
         // record new list of all instance names
         allSiloNames = newSiloNames;
         // rebuild balancer with new list of instance names
         List <QueueId> allQueues = streamQueueMapper.GetAllQueues().ToList();
         resourceBalancer = new BestFitBalancer <string, QueueId>(allSiloNames, allQueues);
         return(true);
     }
     return(false);
 }
コード例 #9
0
        /// <inheritdoc/>
        public override Task Initialize(IStreamQueueMapper queueMapper)
        {
            if (base.Cancellation.IsCancellationRequested)
            {
                throw new InvalidOperationException("Cannot initialize a terminated balancer.");
            }
            if (queueMapper == null)
            {
                throw new ArgumentNullException("queueMapper");
            }
            this.allQueues = new ReadOnlyCollection <QueueId>(queueMapper.GetAllQueues().ToList());

            //Selector default to round robin selector now, but we can make a further change to make selector configurable if needed.  Selector algorithm could
            //be affecting queue balancing stablization time in cluster initializing and auto-scaling
            this.queueSelector = new RoundRobinSelector <QueueId>(this.allQueues, new Random(this.GetHashCode()));
            return(base.Initialize(queueMapper));
        }
コード例 #10
0
        public DeploymentBasedQueueBalancer(
            ISiloStatusOracle siloStatusOracle,
            IDeploymentConfiguration deploymentConfig,
            IStreamQueueMapper queueMapper)
        {
            if (siloStatusOracle == null)
            {
                throw new ArgumentNullException("siloStatusOracle");
            }
            if (deploymentConfig == null)
            {
                throw new ArgumentNullException("deploymentConfig");
            }
            if (queueMapper == null)
            {
                throw new ArgumentNullException("queueMapper");
            }

            this.siloStatusOracle = siloStatusOracle;
            this.deploymentConfig = deploymentConfig;
            streamQueueMapper     = queueMapper;
            queueBalanceListeners = new List <IStreamQueueBalanceListener>();
            mySiloName            = this.siloStatusOracle.SiloName;
            activeSiloNames       = new List <string>();
            allSiloNames          = this.deploymentConfig.GetAllSiloInstanceNames();
            List <QueueId> allQueues = streamQueueMapper.GetAllQueues().ToList();

            resourceBalancer = new BestFitBalancer <string, QueueId>(allSiloNames, allQueues);

            // get silo names for all active silos
            foreach (SiloAddress siloAddress in this.siloStatusOracle.GetApproximateSiloStatuses(true).Keys)
            {
                string siloName;
                if (this.siloStatusOracle.TryGetSiloName(siloAddress, out siloName))
                {
                    activeSiloNames.Add(siloName);
                }
            }

            // register for notification of changes to silo status for any silo in the cluster
            this.siloStatusOracle.SubscribeToSiloStatusEvents(this);
        }
コード例 #11
0
        public DeploymentBasedQueueBalancer(
            ISiloStatusOracle siloStatusOracle,
            IDeploymentConfiguration deploymentConfig,
            IStreamQueueMapper queueMapper,
            TimeSpan maturityPeriod,
            bool isFixed)
        {
            if (siloStatusOracle == null)
            {
                throw new ArgumentNullException("siloStatusOracle");
            }
            if (deploymentConfig == null)
            {
                throw new ArgumentNullException("deploymentConfig");
            }
            if (queueMapper == null)
            {
                throw new ArgumentNullException("queueMapper");
            }

            this.siloStatusOracle = siloStatusOracle;
            this.deploymentConfig = deploymentConfig;
            allQueues             = new ReadOnlyCollection <QueueId>(queueMapper.GetAllQueues().ToList());
            queueBalanceListeners = new List <IStreamQueueBalanceListener>();
            immatureSilos         = new ConcurrentDictionary <SiloAddress, bool>();
            this.isFixed          = isFixed;
            siloMaturityPeriod    = maturityPeriod;
            isStarting            = true;

            // register for notification of changes to silo status for any silo in the cluster
            this.siloStatusOracle.SubscribeToSiloStatusEvents(this);

            // record all already active silos as already mature.
            // Even if they are not yet, they will be mature by the time I mature myself (after I become !isStarting).
            foreach (var silo in siloStatusOracle.GetApproximateSiloStatuses(true).Keys.Where(s => !s.Equals(siloStatusOracle.SiloAddress)))
            {
                immatureSilos[silo] = false;     // record as mature
            }

            NotifyAfterStart().Ignore();
        }
コード例 #12
0
        public DeploymentBasedQueueBalancer(
            ISiloStatusOracle siloStatusOracle,
            IDeploymentConfiguration deploymentConfig,
            IStreamQueueMapper queueMapper,
            TimeSpan maturityPeriod,
            bool isFixed)
        {
            if (siloStatusOracle == null)
            {
                throw new ArgumentNullException("siloStatusOracle");
            }
            if (deploymentConfig == null)
            {
                throw new ArgumentNullException("deploymentConfig");
            }
            if (queueMapper == null)
            {
                throw new ArgumentNullException("queueMapper");
            }

            this.siloStatusOracle = siloStatusOracle;
            this.deploymentConfig = deploymentConfig;
            allQueues = new ReadOnlyCollection<QueueId>(queueMapper.GetAllQueues().ToList());
            queueBalanceListeners = new List<IStreamQueueBalanceListener>();
            immatureSilos = new ConcurrentDictionary<SiloAddress, bool>();
            this.isFixed = isFixed;
            siloMaturityPeriod = maturityPeriod;
            isStarting = true;

            // register for notification of changes to silo status for any silo in the cluster
            this.siloStatusOracle.SubscribeToSiloStatusEvents(this);

            // record all already active silos as already mature. 
            // Even if they are not yet, they will be mature by the time I mature myself (after I become !isStarting).
            foreach (var silo in siloStatusOracle.GetApproximateSiloStatuses(true).Keys.Where(s => !s.Equals(siloStatusOracle.SiloAddress)))
            {
                immatureSilos[silo] = false;     // record as mature
            }

            NotifyAfterStart().Ignore();
        }
コード例 #13
0
        public DeploymentBasedQueueBalancer(
            ISiloStatusOracle siloStatusOracle,
            IDeploymentConfiguration deploymentConfig,
            IStreamQueueMapper queueMapper)
        {
            if (siloStatusOracle == null)
            {
                throw new ArgumentNullException("siloStatusOracle");
            }
            if (deploymentConfig == null)
            {
                throw new ArgumentNullException("deploymentConfig");
            }
            if (queueMapper == null)
            {
                throw new ArgumentNullException("queueMapper");
            }

            this.siloStatusOracle = siloStatusOracle;
            this.deploymentConfig = deploymentConfig;
            streamQueueMapper = queueMapper;
            queueBalanceListeners = new List<IStreamQueueBalanceListener>();
            mySiloName = this.siloStatusOracle.SiloName;
            activeSiloNames = new List<string>();
            allSiloNames = this.deploymentConfig.GetAllSiloInstanceNames();
            List<QueueId> allQueues = streamQueueMapper.GetAllQueues().ToList();
            resourceBalancer = new BestFitBalancer<string, QueueId>(allSiloNames, allQueues);

            // get silo names for all active silos
            foreach (SiloAddress siloAddress in this.siloStatusOracle.GetApproximateSiloStatuses(true).Keys)
            {
                string siloName;
                if (this.siloStatusOracle.TryGetSiloName(siloAddress, out siloName))
                {
                    activeSiloNames.Add(siloName);
                }
            }

            // register for notification of changes to silo status for any silo in the cluster
            this.siloStatusOracle.SubscribeToSiloStatusEvents(this);
        }
コード例 #14
0
        /// <inheritdoc/>
        public override Task Initialize(IStreamQueueMapper queueMapper)
        {
            if (queueMapper == null)
            {
                throw new ArgumentNullException("queueMapper");
            }
            this.allQueues = new ReadOnlyCollection <QueueId>(queueMapper.GetAllQueues().ToList());
            if (this.allQueues.Count == 0)
            {
                return(Task.CompletedTask);
            }
            this.leaseProvider = this.serviceProvider.GetRequiredService(options.LeaseProviderType) as ILeaseProvider;
            NotifyAfterStart().Ignore();
            //make lease renew frequency to be every half of lease time, to avoid renew failing due to timing issues, race condition or clock difference.
            ITimerRegistry timerRegistry = this.serviceProvider.GetRequiredService <ITimerRegistry>();

            this.renewLeaseTimer = timerRegistry.RegisterTimer(null, this.MaintainAndBalanceQueues, null, this.options.SiloMaturityPeriod, this.options.LeaseLength.Divide(2));
            //try to acquire maximum leases every leaseLength
            this.tryAcquireMaximumLeaseTimer = timerRegistry.RegisterTimer(null, this.AcquireLeaseToMeetMaxResponsibility, null, this.options.SiloMaturityPeriod, this.options.SiloMaturityPeriod);
            //Selector default to round robin selector now, but we can make a further change to make selector configurable if needed.  Selector algorithm could
            //be affecting queue balancing stablization time in cluster initializing and auto-scaling
            this.queueSelector = new RoundRobinSelector <QueueId>(this.allQueues);
            return(MaintainAndBalanceQueues(null));
        }
コード例 #15
0
        private async Task SendAndReceiveFromQueueAdapter(IQueueAdapterFactory adapterFactory, IProviderConfiguration config)
        {
            IQueueAdapter adapter = await adapterFactory.CreateAdapter();

            IQueueAdapterCache cache = adapterFactory.GetQueueAdapterCache();

            // Create receiver per queue
            IStreamQueueMapper mapper = adapterFactory.GetStreamQueueMapper();
            Dictionary <QueueId, IQueueAdapterReceiver> receivers = mapper.GetAllQueues().ToDictionary(queueId => queueId, adapter.CreateReceiver);
            Dictionary <QueueId, IQueueCache>           caches    = mapper.GetAllQueues().ToDictionary(queueId => queueId, cache.CreateQueueCache);

            await Task.WhenAll(receivers.Values.Select(receiver => receiver.Initialize(TimeSpan.FromSeconds(5))));

            // test using 2 streams
            Guid streamId1 = Guid.NewGuid();
            Guid streamId2 = Guid.NewGuid();

            int receivedBatches = 0;
            var streamsPerQueue = new ConcurrentDictionary <QueueId, HashSet <IStreamIdentity> >();

            // reader threads (at most 2 active queues because only two streams)
            var work = new List <Task>();

            foreach (KeyValuePair <QueueId, IQueueAdapterReceiver> receiverKvp in receivers)
            {
                QueueId queueId  = receiverKvp.Key;
                var     receiver = receiverKvp.Value;
                var     qCache   = caches[queueId];
                Task    task     = Task.Factory.StartNew(() =>
                {
                    while (receivedBatches < NumBatches)
                    {
                        var messages = receiver.GetQueueMessagesAsync(CloudQueueMessage.MaxNumberOfMessagesToPeek).Result.ToArray();
                        if (!messages.Any())
                        {
                            continue;
                        }
                        foreach (AzureQueueBatchContainer message in messages.Cast <AzureQueueBatchContainer>())
                        {
                            streamsPerQueue.AddOrUpdate(queueId,
                                                        id => new HashSet <IStreamIdentity> {
                                new StreamIdentity(message.StreamGuid, message.StreamGuid.ToString())
                            },
                                                        (id, set) =>
                            {
                                set.Add(new StreamIdentity(message.StreamGuid, message.StreamGuid.ToString()));
                                return(set);
                            });
                            output.WriteLine("Queue {0} received message on stream {1}", queueId,
                                             message.StreamGuid);
                            Assert.Equal(NumMessagesPerBatch / 2, message.GetEvents <int>().Count());    // "Half the events were ints"
                            Assert.Equal(NumMessagesPerBatch / 2, message.GetEvents <string>().Count()); // "Half the events were strings"
                        }
                        Interlocked.Add(ref receivedBatches, messages.Length);
                        qCache.AddToCache(messages);
                    }
                });
                work.Add(task);
            }

            // send events
            List <object> events = CreateEvents(NumMessagesPerBatch);

            work.Add(Task.Factory.StartNew(() => Enumerable.Range(0, NumBatches)
                                           .Select(i => i % 2 == 0 ? streamId1 : streamId2)
                                           .ToList()
                                           .ForEach(streamId =>
                                                    adapter.QueueMessageBatchAsync(streamId, streamId.ToString(),
                                                                                   events.Take(NumMessagesPerBatch).ToArray(), null, RequestContext.Export()).Wait())));
            await Task.WhenAll(work);

            // Make sure we got back everything we sent
            Assert.Equal(NumBatches, receivedBatches);

            // check to see if all the events are in the cache and we can enumerate through them
            StreamSequenceToken firstInCache = new EventSequenceToken(0);

            foreach (KeyValuePair <QueueId, HashSet <IStreamIdentity> > kvp in streamsPerQueue)
            {
                var receiver = receivers[kvp.Key];
                var qCache   = caches[kvp.Key];

                foreach (IStreamIdentity streamGuid in kvp.Value)
                {
                    // read all messages in cache for stream
                    IQueueCacheCursor cursor         = qCache.GetCacheCursor(streamGuid, firstInCache);
                    int messageCount                 = 0;
                    StreamSequenceToken tenthInCache = null;
                    StreamSequenceToken lastToken    = firstInCache;
                    while (cursor.MoveNext())
                    {
                        Exception ex;
                        messageCount++;
                        IBatchContainer batch = cursor.GetCurrent(out ex);
                        output.WriteLine("Token: {0}", batch.SequenceToken);
                        Assert.True(batch.SequenceToken.CompareTo(lastToken) >= 0, $"order check for event {messageCount}");
                        lastToken = batch.SequenceToken;
                        if (messageCount == 10)
                        {
                            tenthInCache = batch.SequenceToken;
                        }
                    }
                    output.WriteLine("On Queue {0} we received a total of {1} message on stream {2}", kvp.Key, messageCount, streamGuid);
                    Assert.Equal(NumBatches / 2, messageCount);
                    Assert.NotNull(tenthInCache);

                    // read all messages from the 10th
                    cursor       = qCache.GetCacheCursor(streamGuid, tenthInCache);
                    messageCount = 0;
                    while (cursor.MoveNext())
                    {
                        messageCount++;
                    }
                    output.WriteLine("On Queue {0} we received a total of {1} message on stream {2}", kvp.Key, messageCount, streamGuid);
                    const int expected = NumBatches / 2 - 10 + 1; // all except the first 10, including the 10th (10 + 1)
                    Assert.Equal(expected, messageCount);
                }
            }
        }
コード例 #16
0
 public async Task Initialize(IStreamQueueMapper queueMapper)
 {
     await this.leaseManagerGrain.SetQueuesAsLeases(queueMapper.GetAllQueues());
     await GetInitialLease();
 }