예제 #1
0
        public async Task KeepFeedingAFollowerAndNeverDreamsOfPower()
        {
            var t        = new CancellationTokenSource();
            var leaderId = Guid.NewGuid();

            _server = new DefaultRaftServer(_sister, _sister, _sister, _maqina.Object, _manijer.Object, _settings);
            _server.Start();

            // to set the term to 1
            await _server.AppendEntriesAsync(new AppendEntriesRequest()
            {
                CurrentTerm       = 1,
                Entries           = new byte[0][],
                LeaderCommitIndex = 20,
                LeaderId          = leaderId,
                PreviousLogIndex  = -1,
                PreviousLogTerm   = 0
            });

            _server.LastHeartBeat     = new AlwaysRecentTimestamp();
            _server.LastHeartBeatSent = new AlwaysRecentTimestamp();

            TheTrace.TraceInformation("OK, now this is before wait...");
            Thread.Sleep(1000);
            TheTrace.TraceInformation("Wait finished.");
            Assert.Equal(1, _server.State.CurrentTerm);
            TheTrace.TraceInformation("Checked Term.");
            Assert.Equal(Role.Follower, _server.Role);
            TheTrace.TraceInformation("Checked Role.");
            t.Cancel();
        }
예제 #2
0
        private void SetupPeerAppendLogJobs(IEnumerable <Peer> peers)
        {
            foreach (var w in _workers.GetWorkers(Queues.PeerAppendLog))
            {
                w.Start();
            }

            _workers.GetWorkers(Queues.ProcessCommandQueue).Single().Start();

            foreach (var p in peers)
            {
                var localP = p;
                var q      = Queues.PeerAppendLog + localP.Address;
                TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] setting up peer append log for queue {q}");
                var todo = PeerAppendLog(localP);

                _workers.Enqueue(q,
                                 new Job(todo,
                                         TheTrace.LogPolicy(_meAsAPeer.ShortName).WaitAndRetryAsync(3, (i) => TimeSpan.FromMilliseconds(i * i * 50)),
                                         TimeSpan.FromMilliseconds(30)));
            }

            // Applying commands received from the clients
            Func <CancellationToken, Task> pcq = ProcessCommandsQueue;

            _workers.Enqueue(Queues.ProcessCommandQueue,
                             new Job(pcq,
                                     TheTrace.LogPolicy(_meAsAPeer.ShortName).RetryForeverAsync(),
                                     _settings.ElectionTimeoutMin.Multiply(0.2)));
        }
예제 #3
0
        public async Task <IEnumerable <Event> > ProcessAsync(Event evnt)
        {
            var shardKeyArrived = evnt.GetBody <ShardRangeArrived>();

            TheTrace.TraceInformation("Got {0}->{1} from {2}", shardKeyArrived.InclusiveStartKey,
                                      shardKeyArrived.InclusiveEndKey, shardKeyArrived.Source.TypeName);

            var account = CloudStorageAccount.Parse(shardKeyArrived.Source.ConnectionString);
            var client  = account.CreateCloudTableClient();
            var table   = client.GetTableReference(shardKeyArrived.Source.DynamicProperties["TableName"].ToString());

            var entities = table.ExecuteQuery(new TableQuery().Where(
                                                  TableQuery.CombineFilters(
                                                      TableQuery.GenerateFilterCondition("PartitionKey", "ge", shardKeyArrived.InclusiveStartKey),
                                                      TableOperators.And,
                                                      TableQuery.GenerateFilterCondition("PartitionKey", "le", shardKeyArrived.InclusiveEndKey))));

            bool hasAnything = false;

            foreach (var entity in entities)
            {
                await _pusher.PushAsync(entity, shardKeyArrived.Source);

                hasAnything = true;
            }

            if (hasAnything)
            {
                await _pusher.FlushAsync();
            }

            return(Enumerable.Empty <Event>());
        }
예제 #4
0
        private static Func <Task> SendCommand(int run)
        {
            return(async() =>
            {
                var r = new Random();
                while (_run == run)
                {
                    try
                    {
                        for (int i = 0; i < r.Next(1, 10); i++)
                        {
                            var payload = BitConverter.GetBytes(r.Next(0, 100000)).Concat(BitConverter.GetBytes(r.Next(0, 100000))).ToArray();
                            await _cluster.ApplyCommandAsync(new StateMachineCommandRequest()
                            {
                                Command = payload
                            });
                        }

                        TheTrace.TraceInformation("Sent commands");
                    }
                    catch (Exception e)
                    {
                        TheTrace.TraceError(e.ToString());
                    }

                    await Task.Delay(r.Next(0, 50));
                }
            });
        }
예제 #5
0
        private void LoadState(Guid?seedId = null)
        {
            using (var tx = _env.BeginReadOnlyTransaction())
            {
                LoadLastTermAndIndex(tx);

                // snapshot
                Snapshot ss;
                if (TryGetLastSnapshot(out ss))
                {
                    LogOffset = ss.LastIncludedIndex + 1;
                }

                // state
                _state = new PersistentState(seedId);
                Bufferable val;
                if (tx.TryGet(_stateDb, StateDbKeys.Id, out val))
                {
                    _state.Id = val;

                    if (tx.TryGet(_stateDb, StateDbKeys.CurrentTerm, out val)) // this should be always TRUE
                    {
                        _state.CurrentTerm = val;
                    }

                    if (tx.TryGet(_stateDb, StateDbKeys.LastVotedFor, out val))
                    {
                        Guid g = val;
                        _state.LastVotedForId = g == Guid.Empty ? (Guid?)null : g;
                    }
                }
            }

            TheTrace.TraceInformation($"Seed Id was {seedId} and now {_state.Id}");
        }
예제 #6
0
        private IEnumerable <DynamicTableEntity> PreprocessEntities(IEnumerable <DynamicTableEntity> entities, ShardKeyArrived shardKeyArrived, string shardKeyTime)
        {
            var minDateTime = DateTimeOffset.MaxValue;
            var n           = 0;

            foreach (var entity in entities)
            {
                var eventDateTimeOffset = entity.GetEventDateTimeOffset();
                var delayInSeconds      = entity.Timestamp.Subtract(eventDateTimeOffset).TotalSeconds;
                if (delayInSeconds >= _shardKeyDelayWarning)
                {
                    TheTrace.TraceWarning(
                        "SHARD_KEY_ACTOR_DELAY_DETECTED => Delay of {0} seconds for {1} in shardKey {2} and time {3}",
                        delayInSeconds, shardKeyArrived.Source.TypeName, shardKeyArrived.ShardKey, shardKeyTime);
                }

                entity.Timestamp = eventDateTimeOffset;
                yield return(entity);

                minDateTime = minDateTime > entity.Timestamp ? entity.Timestamp : minDateTime;
                n++;
            }

            TheTrace.TraceInformation("Gathered {0} records for {1} and ShardKey {2} => {1}_{2} {1}_{3}", n,
                                      shardKeyArrived.Source.TypeName, shardKeyArrived.ShardKey, shardKeyTime);

            if (n > 0)
            {
                _telemetryProvider.WriteTelemetry(
                    "ShardKeyArrivedActor log delay duration",
                    (long)(DateTimeOffset.UtcNow - minDateTime).TotalMilliseconds,
                    shardKeyArrived.Source.TypeName);
            }
        }
예제 #7
0
        public async Task <bool> TryLockAsync(
            LockToken token,
            int tries = 16, // this is NOT retry - it is try
            int retryTimeoutMilliseconds = 15000,
            int timeoutMilliseconds      = 15000)
        {
            if (tries < 1)
            {
                tries = 1;
            }

            var blob = await GetBlobAsync(token.ResourceId);

            for (int i = 0; i < tries; i++)
            {
                try
                {
                    await blob.AcquireLeaseAsync(
                        TimeSpan.FromMilliseconds(timeoutMilliseconds),
                        token.TokenId.ToString("N"));

                    return(true);
                }
                catch (Exception e)
                {
                    TheTrace.TraceInformation("Lock attempt - already locked: {0}", e);
                    // ignore
                }
                await Task.Delay(TimeSpan.FromMilliseconds(retryTimeoutMilliseconds / tries));
            }

            return(false);
        }
예제 #8
0
        public Task <Tuple <IEnumerable <Event>, bool> > TryScheduleAsync(DiagnosticsSource source)
        {
            var key = source.ToTypeKey();

            if (source.IsActive.GetValueOrDefault(true))
            {
                var consume = EventHubConsumer.Consumers.AddOrUpdate(key,
                                                                     new Lazy <EventHubConsumer>(() =>
                {
                    TheTrace.TraceInformation("Just added this eventHub consumer {0}", key);
                    return(new EventHubConsumer(_pusher, source.ToSummary()));
                }),
                                                                     (kk, vv) => vv);

                // to make sure it gets accessed and created if new otherwise system is 'lazy'
                TheTrace.TraceInformation("This is the EventHub thing I was talking about: {0}", consume.Value.Source.TypeName);
            }
            else
            {
                Lazy <EventHubConsumer> consumer = null;

                if (EventHubConsumer.Consumers.TryRemove(key, out consumer))
                {
                    consumer.Value.Dispose();
                    TheTrace.TraceInformation("Just removed this eventHub consumer {0}", key);
                }
            }

            return(Task.FromResult(new Tuple <IEnumerable <Event>, bool>(new Event[0], false)));
        }
예제 #9
0
        public async Task <IEnumerable <Event> > ProcessAsync(Event evnt)
        {
            var shardKeyArrived = evnt.GetBody <ShardKeyArrived>();

            _telemetryProvider.WriteTelemetry(
                "ShardKey receive message delay duration",
                (long)(DateTime.UtcNow - evnt.Timestamp).TotalMilliseconds,
                shardKeyArrived.Source.TypeName);

            await _durationInstrumentor.InstrumentAsync(async() =>
            {
                TheTrace.TraceInformation("Got {0} from {1}", shardKeyArrived.ShardKey,
                                          shardKeyArrived.Source.TypeName);

                var shardKeyQuerier = (string)shardKeyArrived.Source.GetDynamicProperty(ConveyorBeltConstants.ShardKeyQuery);
                var query           = FactoryHelper.Create <IShardKeyQuery>(shardKeyQuerier, typeof(TableStorageShardKeyQuery));
                var entities        = await query.QueryAsync(shardKeyArrived);

                var shardKeyTime = shardKeyArrived.GetDateTimeOffset().ToString("yyyyMMddHHmm");

                await _pusher.PushAll(PreprocessEntities(entities, shardKeyArrived, shardKeyTime), shardKeyArrived.Source).ConfigureAwait(false);
            }).ConfigureAwait(false);

            return(Enumerable.Empty <Event>());
        }
예제 #10
0
        public async Task <IEnumerable <Event> > ProcessAsync(Event evnt)
        {
            var shardKeyArrived = evnt.GetBody <ShardKeyArrived>();

            TheTrace.TraceInformation("Got {0} from {1}", shardKeyArrived.ShardKey,
                                      shardKeyArrived.Source.TypeName);

            var shardKeyQuerier = (string)shardKeyArrived.Source.GetDynamicProperty(ConveyorBeltConstants.ShardKeyQuery);
            var query           = FactoryHelper.Create <IShardKeyQuery>(shardKeyQuerier, typeof(TableStorageShardKeyQuery));
            var entities        = await query.QueryAsync(shardKeyArrived);

            bool hasAnything = false;

            foreach (var entity in entities)
            {
                await _pusher.PushAsync(entity, shardKeyArrived.Source);

                hasAnything = true;
            }

            if (hasAnything)
            {
                await _pusher.FlushAsync();
            }

            return(Enumerable.Empty <Event>());
        }
        public async Task <bool> UpdateMappingAsync(string baseUrl, string indexName, string typeName, string mapping)
        {
            TheTrace.TraceInformation("Adding {0} type to {1} index.", typeName, indexName);

            baseUrl = baseUrl.TrimEnd('/');
            var url      = string.Format(MappingFormat, baseUrl, indexName, typeName);
            var response = await _httpClient.PutAsync(url,
                                                      new StringContent(mapping, Encoding.UTF8, "application/json"));

            var text = await response.Content.ReadAsStringAsync();

            var mappings = _existingIndices[indexName];

            if (response.IsSuccessStatusCode)
            {
                mappings.TryAdd(typeName, null);
                return(true);
            }
            else
            {
                throw new ApplicationException(string.Format("Error {0}: {1}",
                                                             response.StatusCode,
                                                             text));
            }
        }
        private async Task PushbatchAsync()
        {
            if (_stringBuilder.Length == 0)
            {
                return;
            }

            try
            {
                var responseMessage = await _httpClient.PostAsync(_esUrl + "_bulk",
                                                                  new StringContent(_stringBuilder.ToString(),
                                                                                    Encoding.UTF8, "application/json"));

                _stringBuilder.Clear();
                _numberOfRecords = 0;
                responseMessage.EnsureSuccessStatusCode();

                TheTrace.TraceInformation("ConveyorBelt_Pusher: Pushing to {0}", _esUrl);
            }
            catch (Exception e)
            {
                TheTrace.TraceError(e.ToString());
                throw;
            }
        }
예제 #13
0
        public async Task <Tuple <IEnumerable <Event>, bool> > TryScheduleAsync(DiagnosticsSource source)
        {
            // if Stop offset has been reached
            if (!string.IsNullOrEmpty(source.StopOffsetPoint) && source.LastOffsetPoint != null && source.LastOffsetPoint.CompareTo(source.StopOffsetPoint) >= 0)
            {
                return(new Tuple <IEnumerable <Event>, bool>(Enumerable.Empty <Event>(), false));
            }

            var lockToken = new LockToken(source.ToTypeKey());
            int seconds   =
                Convert.ToInt32(_configurationValueProvider.GetValue(ConfigurationKeys.ClusterLockDurationSeconds));

            if (!(await _lockStore.TryLockAsync(lockToken, 2, 1000, seconds * 1000)))
            {
                TheTrace.TraceInformation("I could NOT be master for {0}", source.ToTypeKey());
                return(new Tuple <IEnumerable <Event>, bool>(Enumerable.Empty <Event>(), false));
            }
            try
            {
                var events = await DoSchedule(source);

                return(new Tuple <IEnumerable <Event>, bool>(events, true));
            }
            finally
            {
                Task.Run(() => _lockStore.ReleaseLockAsync(lockToken)).Wait();
            }
        }
예제 #14
0
        public async Task PushAsync(DynamicTableEntity entity, DiagnosticsSourceSummary source)
        {
            if (source.Filter == null)
            {
                source.Filter = string.Empty;
            }

            if (!_filters.ContainsKey(source.Filter))
            {
                _filters.Add(source.Filter, new SimpleFilter(source.Filter));
            }

            if (!_filters[source.Filter].Satisfies(entity))
            {
                return;
            }

            var op = new
            {
                index = new
                {
                    _index = source.IndexName ?? _indexNamer.BuildName(entity.Timestamp, source.DynamicProperties["MappingName"].ToString().ToLowerInvariant()),
                    _type  = source.DynamicProperties["MappingName"].ToString(),
                    _id    = entity.PartitionKey + entity.RowKey
                }
            };

            var doc = new JObject();

            doc.Add("@timestamp", entity.Timestamp);
            doc.Add("PartitionKey", entity.PartitionKey);
            doc.Add("RowKey", entity.RowKey);
            doc.Add("cb_type", source.TypeName);

            foreach (var property in entity.Properties)
            {
                if (property.Key != DiagnosticsSource.CustomAttributesFieldName)
                {
                    doc[property.Key] = JToken.FromObject(property.Value.PropertyAsObject);
                }
            }
            if (entity.Properties.ContainsKey(DiagnosticsSource.CustomAttributesFieldName))
            {
                foreach (var keyValue in GetNameValues(entity.Properties[DiagnosticsSource.CustomAttributesFieldName].StringValue))
                {
                    doc[keyValue.Key] = keyValue.Value;
                }
            }

            _batch.AddDoc(JsonConvert.SerializeObject(op).Replace("\r\n", " "), doc.ToString().Replace("\r\n", " "));

            if (_batch.Count >= _batchSize)
            {
                await PushbatchAsync();

                TheTrace.TraceInformation("ConveyorBelt_Pusher: Pushed records to ElasticSearch for {0}-{1}",
                                          source.PartitionKey,
                                          source.RowKey);
            }
        }
예제 #15
0
        private async Task KeepExtendingLeaseAsync(Func <Task> extendLeaseAsync, TimeSpan howLong, CancellationToken cancellationToken, string resource)
        {
            await EnsureExists();

            var thisLong = new TimeSpan(2 * howLong.Ticks / 3); // RATM: how long? This long, what you reap is what you sew!
            await Task.Delay(thisLong, cancellationToken);

            while (true)
            {
                try
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        break;
                    }

                    await extendLeaseAsync();

                    TheTrace.TraceInformation("Extended the lifetime of the lease for {0}...", resource);
                    await Task.Delay(thisLong, cancellationToken);
                }
                catch (Exception exception)
                {
                    if (!cancellationToken.IsCancellationRequested) // it is OK if cancellation requested, it would have been cancellation
                    {
                        TheTrace.TraceError(exception.ToString());
                    }
                    break;
                }
            }
        }
        protected override Task <IEnumerable <Event> > DoSchedule(DiagnosticsSource source)
        {
            if (source.LastOffsetPoint == null)
            {
                source.LastOffsetPoint = DateTimeOffset.UtcNow.AddDays(-1).DropSecondAndMilliseconds().ToString("O");
            }

            var lastOffset   = DateTimeOffset.Parse(source.LastOffsetPoint);
            var events       = new List <Event>();
            var graceMinutes = source.GracePeriodMinutes ?? 3;

            var now           = DateTimeOffset.UtcNow.DropSecondAndMilliseconds();
            var newLastOffset = lastOffset;
            int n             = 1; // start from a minute after

            while (now >= lastOffset.Add(TimeSpan.FromMinutes(graceMinutes + n)))
            {
                newLastOffset = lastOffset.Add(TimeSpan.FromMinutes(n))
                                .DropSecondAndMilliseconds(); // just to be sure
                var shardKeys = GetShardKeys(newLastOffset);
                events.AddRange(shardKeys.Select(shardKey => new Event(new ShardKeyArrived {
                    Source = source.ToSummary(), ShardKey = shardKey
                })));
                if (source.MaxItemsInAScheduleRun.HasValue && n >= source.MaxItemsInAScheduleRun)
                {
                    break;
                }
                n++;
                TheTrace.TraceInformation("Scheduling {0} for minute {1} and shardkey {2} => {0}_{1} AND {0}_{2}", source.ToTypeKey(), newLastOffset.ToString("yyyyMMddHHmm"), shardKeys.First());
            }

            source.LastOffsetPoint = newLastOffset.ToString("O");
            return(Task.FromResult((IEnumerable <Event>)events));
        }
예제 #17
0
        public async Task <IEnumerable <Event> > ProcessAsync(Event evnt)
        {
            var blobFileArrived = evnt.GetBody <BlobFileArrived>();

            _telemetryProvider.WriteTelemetry(
                "BlobFileActor receive message delay duration",
                (long)(DateTime.UtcNow - evnt.Timestamp).TotalMilliseconds,
                blobFileArrived.Source.TypeName);

            await _durationInstrumentor.InstrumentAsync(async() =>
            {
                TheTrace.TraceInformation("Got {0} from {1}", blobFileArrived.BlobId,
                                          blobFileArrived.Source.TypeName);
                //var account = CloudStorageAccount.Parse(blobFileArrived.Source.ConnectionString);
                CloudStorageAccount account;
                if (!String.IsNullOrWhiteSpace(blobFileArrived.Source.AccountSasKey))
                {
                    // Create new storage credentials using the SAS token.
                    var accountSas = new StorageCredentials(blobFileArrived.Source.AccountSasKey);
                    // Use these credentials and the account name to create a Blob service client.
                    account = new CloudStorageAccount(accountSas, blobFileArrived.Source.AccountName, "", useHttps: true);
                }
                else
                {
                    account = CloudStorageAccount.Parse(blobFileArrived.Source.ConnectionString);
                }
                var client    = account.CreateCloudBlobClient();
                var container = client.GetContainerReference(blobFileArrived.Source.DynamicProperties["ContainerName"].ToString());
                var uri       = new Uri(blobFileArrived.BlobId);
                var id        = string.Join("", uri.Segments.Skip(2));
                var blob      = container.GetBlockBlobReference(id);
                if (!blob.Exists())
                {
                    throw new InvalidOperationException("Blob does not exist: " + id);
                }
                var stream = new MemoryStream();
                await blob.DownloadToStreamAsync(stream);
                var parser      = FactoryHelper.Create <IParser>(blobFileArrived.Source.DynamicProperties["Parser"].ToString(), typeof(IisLogParser));
                var hasAnything = false;
                var minDateTime = DateTimeOffset.UtcNow;

                var records = parser.Parse(() => stream, blob.Uri, blobFileArrived.Source, new ParseCursor(blobFileArrived.Position ?? 0)
                {
                    EndPosition = blobFileArrived.EndPosition ?? 0
                });
                var seenPages = await _pusher.PushAll(records, blobFileArrived.Source).ConfigureAwait(false);
                hasAnything   = seenPages > 0;

                if (hasAnything)
                {
                    _telemetryProvider.WriteTelemetry(
                        "BlobFileActor message processing duration",
                        (long)(DateTimeOffset.UtcNow - minDateTime).TotalMilliseconds,
                        blobFileArrived.Source.TypeName);
                }
            }, blobFileArrived.Source.TypeName);

            return(Enumerable.Empty <Event>());
        }
예제 #18
0
        private async Task Candidacy(CancellationToken c)
        {
            while (_role == Role.Candidate)
            {
                var forMe     = 1; // vote for yourself
                var againstMe = 0;

                var peers     = _peerManager.GetPeers().ToArray();
                var concensus = (peers.Length / 2) + 1;
                var proxies   = peers.Select(x => _peerManager.GetProxy(x.Address));
                var retry     = TheTrace.LogPolicy(_meAsAPeer.ShortName).WaitAndRetryAsync(3, (i) => TimeSpan.FromMilliseconds(i * i * 30));
                var policy    = Policy.TimeoutAsync(_settings.CandidacyTimeout).WrapAsync(retry);
                var request   = new RequestVoteRequest()
                {
                    CandidateId  = State.Id,
                    CurrentTerm  = State.CurrentTerm,
                    LastLogIndex = _logPersister.LastIndex,
                    LastLogTerm  = _logPersister.LastEntryTerm
                };

                var all = await Task.WhenAll(proxies.Select(p => policy.ExecuteAndCaptureAsync(() => p.RequestVoteAsync(request))));

                var maxTerm = 0L;
                foreach (var r in all)
                {
                    if (r.Outcome == OutcomeType.Successful)
                    {
                        if (r.Result.CurrentTerm > maxTerm)
                        {
                            maxTerm = r.Result.CurrentTerm;
                        }

                        if (r.Result != null && r.Result.VoteGranted)
                        {
                            forMe++;
                        }
                        else
                        {
                            againstMe++;
                        }
                    }
                }

                if (againstMe >= concensus)
                {
                    TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] Result of the candidacy for term {State.CurrentTerm}. I got rejected with {againstMe} votes :/");
                    BecomeFollower(maxTerm);
                }
                else if (forMe >= concensus)
                {
                    TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] Result of the candidacy for term {State.CurrentTerm}. I got elected with {forMe} votes! :)");
                    BecomeLeader();
                }
                else
                {
                    TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] Result of the candidacy for term {State.CurrentTerm}. Non-conclusive with {forMe} for me and {againstMe} against me.");
                }
            }
        }
예제 #19
0
 public void Dispose()
 {
     TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] Disposing server.");
     _workers.Stop();
     TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] Disposing server. Workers stopped.");
     _logPersister.Dispose();
     TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] Disposing server. Log Persister stopped.");
 }
예제 #20
0
        protected override Task <IEnumerable <Event> > DoSchedule(DiagnosticsSource source)
        {
            TheTrace.TraceInformation("IisBlobScheduler - Starting scheduling");
            //var account = CloudStorageAccount.Parse(source.ConnectionString);
            CloudStorageAccount account;

            if (!String.IsNullOrWhiteSpace(source.AccountSasKey))
            {
                // Create new storage credentials using the SAS token.
                var accountSas = new StorageCredentials(source.AccountSasKey);
                // Use these credentials and the account name to create a Blob service client.
                account = new CloudStorageAccount(accountSas, source.AccountName, "", useHttps: true);
            }
            else
            {
                account = CloudStorageAccount.Parse(source.ConnectionString);
            }
            var client   = account.CreateCloudBlobClient();
            var blobPath = source.GetProperty <string>("BlobPath");

            TheTrace.TraceInformation("IisBlobScheduler - pathformat: {0}", blobPath);
            blobPath = blobPath.TrimEnd('/') + "/"; // ensure path ends with /
            var offset = FileOffset.Parse(source.LastOffsetPoint);

            if (offset == null)
            {
                throw new InvalidOperationException("FileOffset failed parsing: => " + source.LastOffsetPoint);
            }

            DateTimeOffset maxOffset = offset.TimeOffset;
            FileOffset     newOffset = null;
            var            events    = new List <Event>();

            foreach (var blob in client.ListBlobs(blobPath).Where(itm => itm is CloudBlockBlob)
                     .Cast <CloudBlockBlob>().OrderBy(x => x.Properties.LastModified))
            {
                if (blob.Properties.LastModified > offset.TimeOffset)
                {
                    var filename = blob.Uri.ToString();
                    newOffset = new FileOffset(filename, blob.Properties.LastModified ?? DateTimeOffset.UtcNow, 0);
                    TheTrace.TraceInformation("IisBlobScheduler - found {0}", blob.Uri);

                    events.Add(new Event(new BlobFileArrived()
                    {
                        Source      = source.ToSummary(),
                        BlobId      = filename,
                        Position    = 0,
                        EndPosition = blob.Properties.Length
                    }));

                    TheTrace.TraceInformation("Created BlobFileArrived for file: {0}", filename);
                }
            }

            source.LastOffsetPoint = newOffset == null?offset.ToString() : newOffset.ToString();

            return(Task.FromResult((IEnumerable <Event>)events));
        }
예제 #21
0
 private void BecomeFollower(long term)
 {
     State.LastVotedForId = null;
     DestroyPeerAppendLogJobs();
     _lastHeartbeat.Set(); // important not to become candidate again at least for another timeout
     TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] About to set term from {State.CurrentTerm} to {term}");
     State.CurrentTerm   = term;
     OnRoleChanged(_role = Role.Follower);
 }
예제 #22
0
        public async Task <IEnumerable <Event> > ProcessAsync(Event evnt)
        {
            var shardKeyArrived = evnt.GetBody <ShardKeyArrived>();

            _telemetryProvider.WriteTelemetry(
                "ShardKey receive message delay duration",
                (long)(DateTime.UtcNow - evnt.Timestamp).TotalMilliseconds,
                shardKeyArrived.Source.TypeName);

            await _durationInstrumentor.InstrumentAsync(async() =>
            {
                TheTrace.TraceInformation("Got {0} from {1}", shardKeyArrived.ShardKey,
                                          shardKeyArrived.Source.TypeName);

                var shardKeyQuerier = (string)shardKeyArrived.Source.GetDynamicProperty(ConveyorBeltConstants.ShardKeyQuery);
                var query           = FactoryHelper.Create <IShardKeyQuery>(shardKeyQuerier, typeof(TableStorageShardKeyQuery));
                var entities        = await query.QueryAsync(shardKeyArrived);

                var minDateTime  = DateTimeOffset.MaxValue;
                var hasAnything  = false;
                int n            = 0;
                var shardKeyTime = shardKeyArrived.GetDateTimeOffset().ToString("yyyyMMddHHmm");

                foreach (var entity in entities)
                {
                    var eventDateTimeOffset = entity.GetEventDateTimeOffset();
                    var delayInSeconds      = entity.Timestamp.Subtract(eventDateTimeOffset).TotalSeconds;
                    if (delayInSeconds >= _shardKeyDelayWarning)
                    {
                        TheTrace.TraceWarning("SHARD_KEY_ACTOR_DELAY_DETECTED => Delay of {0} seconds for {1} in shardKey {2} and time {3}",
                                              delayInSeconds, shardKeyArrived.Source.TypeName, shardKeyArrived.ShardKey, shardKeyTime);
                    }

                    entity.Timestamp = eventDateTimeOffset;
                    await _pusher.PushAsync(entity, shardKeyArrived.Source);
                    hasAnything = true;
                    minDateTime = minDateTime > entity.Timestamp ? entity.Timestamp : minDateTime;
                    n++;
                }

                TheTrace.TraceInformation("Gathered {0} records for {1} and ShardKey {2} => {1}_{2} {1}_{3}", n,
                                          shardKeyArrived.Source.TypeName, shardKeyArrived.ShardKey, shardKeyTime);

                if (hasAnything)
                {
                    await _pusher.FlushAsync();

                    _telemetryProvider.WriteTelemetry(
                        "ShardKeyArrivedActor log delay duration",
                        (long)(DateTimeOffset.UtcNow - minDateTime).TotalMilliseconds,
                        shardKeyArrived.Source.TypeName);
                }
            });

            return(Enumerable.Empty <Event>());
        }
예제 #23
0
 private void BecomeCandidate()
 {
     TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] BecomeCandidate start");
     State.IncrementTerm();
     State.LastVotedForId = State.Id;
     _leaderAddress       = null;
     DestroyPeerAppendLogJobs();
     OnRoleChanged(_role = Role.Candidate);
     TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] BecomeCandidate end");
 }
        public async Task <bool> CreateIndexIfNotExistsAsync(string baseUrl, string indexName, string jsonCommand = "")
        {
            if (_existingIndices.ContainsKey(indexName))
            {
                return(false);
            }

            baseUrl = baseUrl.TrimEnd('/');
            string searchUrl = string.Format(IndexSearchFormat, baseUrl, indexName);

            TheTrace.TraceInformation("Just wanna check if this index exists: {0}. URL: {1}", indexName, searchUrl);
            var getResponse = await _httpClient.GetAsync(searchUrl);

            var getText = await getResponse.Content.ReadAsStringAsync();

            if (getResponse.IsSuccessStatusCode)
            {
                _existingIndices.TryAdd(indexName, new ConcurrentDictionary <string, string>());
                return(false);
            }

            if (getResponse.StatusCode == HttpStatusCode.NotFound)
            {
                TheTrace.TraceInformation("It sent Back this {0} and text => {1}", (int)getResponse.StatusCode, getText);

                var url         = string.Format(IndexFormat, baseUrl, indexName);
                var putResponse = await _httpClient.PutAsync(url, new StringContent(jsonCommand, Encoding.UTF8, "application/json"));

                var putText = "[NO CONTENT]";

                if (putResponse.Content != null)
                {
                    putText = await putResponse.Content.ReadAsStringAsync();
                }

                if (putResponse.IsSuccessStatusCode || (putResponse.StatusCode == HttpStatusCode.BadRequest && putText.Contains("already exists")))
                {
                    _existingIndices.TryAdd(indexName, new ConcurrentDictionary <string, string>());
                    return(true);
                }
                else
                {
                    throw new ApplicationException(string.Format("Error in PUT {0}: {1}",
                                                                 putResponse.StatusCode,
                                                                 putText));
                }
            }
            else
            {
                TheTrace.TraceInformation("It sent Back this {0}", (int)getResponse.StatusCode);
                throw new ApplicationException(string.Format("Error in GET: {0}: {1}",
                                                             getResponse.StatusCode,
                                                             getText));
            }
        }
예제 #25
0
        public async Task <bool> TryLockAsync(
            LockToken token,
            int tries = 16, // this is NOT retry - it is try
            int retryTimeoutMilliseconds  = 15000,
            int timeoutMilliseconds       = 15000,
            int aquireTimeoutMilliseconds = 15000)
        {
            await EnsureExists();

            if (tries < 1)
            {
                tries = 1;
            }

            var blob = await GetBlobAsync(token.ResourceId);

            for (var i = 0; i < tries; i++)
            {
                try
                {
                    using (var cancellationTokenSource = new CancellationTokenSource(aquireTimeoutMilliseconds))
                    {
                        await blob.AcquireLeaseAsync(
                            TimeSpan.FromMilliseconds(Math.Min(MaxLockPossibleMilliseconds, timeoutMilliseconds)),
                            token.TokenId.ToString("N"), null, null, null, cancellationTokenSource.Token);
                    }

                    if (timeoutMilliseconds > MaxLockPossibleMilliseconds)
                    {
                        KeepExtendingLeaseAsync(async() =>
                        {
                            await blob.AcquireLeaseAsync(
                                TimeSpan.FromMilliseconds(MaxLockPossibleMilliseconds),
                                token.TokenId.ToString("N"));
                        }, TimeSpan.FromMilliseconds(MaxLockPossibleMilliseconds), token.RenewCancellation.Token, token.ResourceId);  // DO NOT WAIT THIS!!!
                    }

                    return(true);
                }
                catch (Exception e)
                {
                    TheTrace.TraceInformation("Lock attempt - already locked: {0}", e);
                    // ignore
                }

                if (i < tries - 1)
                {
                    await Task.Delay(TimeSpan.FromMilliseconds(retryTimeoutMilliseconds / tries));
                }
            }

            return(false);
        }
예제 #26
0
        private async Task <bool> Process(CancellationToken cancellationToken)
        {
            var result = await _queueOperator.NextAsync(
                new QueueName(_actorDescriptor.SourceQueueName));

            var cancellationTokenSource = new CancellationTokenSource();

            if (result.IsSuccessful)
            {
                TheTrace.TraceInformation("Receieved a message. Id: {0} Queue: {1} ", result.PollingResult.Id, _actorDescriptor.SourceQueueName);
                var actor = (IProcessorActor)_serviceLocator.GetService(_actorDescriptor.ActorType);
                try
                {
                    // this is NOT supposed to be awaited upon!!
                    _queueOperator.KeepExtendingLeaseAsync(result.PollingResult, TimeSpan.FromSeconds(30),
                                                           cancellationTokenSource.Token).SafeObserve();

                    // Would have been great to make this fixed memory foot-print with real iterable vs. list
                    var events = (await actor.ProcessAsync(result.PollingResult)).ToArray(); // the enumerable has to be turned into a list anyway. it does further on
                    var groups = events.GroupBy(x => x.QueueName);

                    foreach (var gr in groups)
                    {
                        await _queueOperator.PushBatchAsync(gr);

                        TryDisposeMessages(gr);
                    }

                    cancellationTokenSource.Cancel();
                    await _queueOperator.CommitAsync(result.PollingResult);

                    TheTrace.TraceInformation("Processing succeeded. Id: {0} Queue: {1} ", result.PollingResult.Id, _actorDescriptor.SourceQueueName);
                }
                catch (Exception exception)
                {
                    TheTrace.TraceInformation("Processing failed. Id: {0} Queue: {1} ", result.PollingResult.Id, _actorDescriptor.SourceQueueName);
                    TheTrace.TraceError(exception.ToString());
                    cancellationTokenSource.Cancel();
                    _queueOperator.AbandonAsync(result.PollingResult).SafeObserve().Wait();
                }
                finally
                {
                    if (result.IsSuccessful)
                    {
                        TryDisposeMessage(result.PollingResult);
                    }
                    _serviceLocator.ReleaseService(actor);
                }
            }

            return(result.IsSuccessful);
        }
예제 #27
0
        private void SetupPool()
        {
            var names = new List <string>();

            foreach (var p in _peerManager.GetPeers())
            {
                names.Add(Queues.PeerAppendLog + p.Address);
            }

            names.Add(Queues.LogCommit);
            names.Add(Queues.HeartBeatReceiveAndCandidacy);
            names.Add(Queues.HeartBeatSend);
            names.Add(Queues.ProcessCommandQueue);
            names.Add(Queues.CreateSnapshot);

            _workers = new WorkerPool(names.ToArray());
            _workers.Start();

            // LogCommit
            Func <CancellationToken, Task> logCommit = LogCommit;

            _workers.Enqueue(Queues.LogCommit,
                             new Job(logCommit,
                                     TheTrace.LogPolicy(_meAsAPeer.ShortName).RetryForeverAsync(),
                                     _settings.ElectionTimeoutMin.Multiply(0.2)));

            // receiving heartbeat
            Func <CancellationToken, Task> hbr = HeartBeatReceive;

            _workers.Enqueue(Queues.HeartBeatReceiveAndCandidacy,
                             new Job(hbr,
                                     TheTrace.LogPolicy(_meAsAPeer.ShortName).RetryForeverAsync(),
                                     _settings.ElectionTimeoutMin.Multiply(0.2)));

            // sending heartbeat
            Func <CancellationToken, Task> hbs = HeartBeatSend;

            _workers.Enqueue(Queues.HeartBeatSend,
                             new Job(hbs,
                                     TheTrace.LogPolicy(_meAsAPeer.ShortName).RetryForeverAsync(),
                                     _settings.ElectionTimeoutMin.Multiply(0.2)));

            // Applying commands received from the clients
            Func <CancellationToken, Task> cs = CreateSnapshot;

            _workers.Enqueue(Queues.CreateSnapshot,
                             new Job(cs,
                                     TheTrace.LogPolicy(_meAsAPeer.ShortName).WaitAndRetryAsync(2, (i) => TimeSpan.FromMilliseconds(i * i * 50)),
                                     _settings.ElectionTimeoutMin.Multiply(0.2)));

            TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] Setup finished.");
        }
예제 #28
0
        /// <inheritdoc />
        public Task <RequestVoteResponse> RequestVoteAsync(RequestVoteRequest request)
        {
            var peers    = _peerManager.GetPeers();
            var peer     = peers.Where(x => x.Id == request.CandidateId).FirstOrDefault();
            var peerName = peer?.ShortName ?? request.CandidateId.ToString();

            lock (State)
            {
                if (request.CurrentTerm > State.CurrentTerm)
                {
                    BecomeFollower(request.CurrentTerm);
                }

                // Reply false if term < currentTerm
                if (State.CurrentTerm > request.CurrentTerm)
                {
                    TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] Rejecting vote of {peerName} due to backward term");
                    return(Task.FromResult(new RequestVoteResponse()
                    {
                        CurrentTerm = State.CurrentTerm,
                        VoteGranted = false
                    }));
                }


                // If votedFor is null or candidateId, and candidate’s log is at least as up-to-date as receiver’s log, grant vote(§5.2, §5.4)
                if (!State.LastVotedForId.HasValue && _logPersister.LastIndex <= request.LastLogIndex)
                {
                    State.LastVotedForId = request.CandidateId;

                    // If election timeout elapses without receiving AppendEntries RPC from current leader OR GRANTING VOTE TO CANDIDATE: convert to candidate
                    _lastHeartbeat.Set();

                    TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] Voting for {peerName} for term {request.CurrentTerm}");
                    return(Task.FromResult(new RequestVoteResponse()
                    {
                        CurrentTerm = State.CurrentTerm,
                        VoteGranted = true
                    }));
                }

                TheTrace.TraceInformation($"[{_meAsAPeer.ShortName}] Rejecting vote of {peerName} for term {request.CurrentTerm} as it did not fulfil");

                // assume the rest we send back no
                return(Task.FromResult(new RequestVoteResponse()
                {
                    CurrentTerm = State.CurrentTerm,
                    VoteGranted = false
                }));
            }
        }
예제 #29
0
        private async Task <bool> Process(CancellationToken cancellationToken)
        {
            var result = await _queueOperator.NextAsync(
                new QueueName(_actorDescriptor.SourceQueueName));

            var cancellationTokenSource = new CancellationTokenSource();

            if (result.IsSuccessful)
            {
                TheTrace.TraceInformation("Receieved a message. Id: {0} Queue: {1} ", result.PollingResult.Id, _actorDescriptor.SourceQueueName);
                var actor = (IProcessorActor)_serviceLocator.GetService(_actorDescriptor.ActorType);
                try
                {
                    _queueOperator.KeepExtendingLeaseAsync(result.PollingResult, TimeSpan.FromSeconds(30),
                                                           cancellationTokenSource.Token).SafeObserve();

                    var events = (await actor.ProcessAsync(result.PollingResult)).ToArray();
                    cancellationTokenSource.Cancel();

                    await _queueOperator.CommitAsync(result.PollingResult);

                    var groups = events.GroupBy(x => x.QueueName);

                    foreach (var gr in groups)
                    {
                        await _queueOperator.PushBatchAsync(gr);

                        TryDisposeMessages(gr);
                    }

                    TheTrace.TraceInformation("Processing succeeded. Id: {0} Queue: {1} ", result.PollingResult.Id, _actorDescriptor.SourceQueueName);
                }
                catch (Exception exception)
                {
                    TheTrace.TraceInformation("Processing failed. Id: {0} Queue: {1} ", result.PollingResult.Id, _actorDescriptor.SourceQueueName);
                    TheTrace.TraceError(exception.ToString());
                    cancellationTokenSource.Cancel();
                    _queueOperator.AbandonAsync(result.PollingResult).SafeObserve().Wait();
                }
                finally
                {
                    if (result.IsSuccessful)
                    {
                        TryDisposeMessage(result.PollingResult);
                    }
                    _serviceLocator.ReleaseService(actor);
                }
            }

            return(result.IsSuccessful);
        }
예제 #30
0
        public async Task <IEnumerable <Event> > ProcessAsync(Event evnt)
        {
            var blobFileArrived = evnt.GetBody <BlobFileArrived>();

            TheTrace.TraceInformation("Got {0} from {1}", blobFileArrived.BlobId,
                                      blobFileArrived.Source.TypeName);
            var account   = CloudStorageAccount.Parse(blobFileArrived.Source.ConnectionString);
            var client    = account.CreateCloudBlobClient();
            var container = client.GetContainerReference(blobFileArrived.Source.DynamicProperties["ContainerName"].ToString());
            var uri       = new Uri(blobFileArrived.BlobId);
            var id        = string.Join("", uri.Segments.Skip(2));
            var blob      = container.GetBlockBlobReference(id);

            if (!blob.Exists())
            {
                throw new InvalidOperationException("Blob does not exist: " + id);
            }
            var stream = new MemoryStream();
            await blob.DownloadToStreamAsync(stream);

            var parserTypeName = blobFileArrived.Source.DynamicProperties["Parser"].ToString();

            if (parserTypeName == null)
            {
                throw new NullReferenceException("parserTypeName");
            }

            var parserType = Assembly.GetExecutingAssembly().GetType(parserTypeName) ?? Type.GetType(parserTypeName);

            if (parserType == null)
            {
                throw new InvalidOperationException("Parser type was null: " + parserTypeName);
            }
            var  parser      = (IParser)Activator.CreateInstance(parserType);
            bool hasAnything = false;

            foreach (var entity in parser.Parse(stream, blob.Uri))
            {
                await _pusher.PushAsync(entity, blobFileArrived.Source);

                hasAnything = true;
            }

            if (hasAnything)
            {
                await _pusher.FlushAsync();
            }

            return(Enumerable.Empty <Event>());
        }