public async Task WriteEvents <TEntity>(string bucket, Id streamId, Id[] parents, IFullEvent[] events, IDictionary <string, string> commitHeaders) where TEntity : IEntity { Logger.Write(LogLevel.Debug, $"Writing {events.Length} oob events stream [{streamId}] bucket [{bucket}]"); await events.WhenAllAsync(async @event => { var message = new FullMessage { Message = @event.Event, Headers = @event.Descriptor.Headers }; var parentsStr = parents?.Any() ?? false ? parents.Aggregate <Id, string>("", (cur, next) => $"{cur},{next}") : ""; var headers = new Dictionary <string, string>() { [$"{Defaults.PrefixHeader}.EventId"] = @event.EventId.ToString(), [$"{Defaults.PrefixHeader}.EntityType"] = @event.Descriptor.EntityType, [$"{Defaults.PrefixHeader}.Timestamp"] = @event.Descriptor.Timestamp.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture), [$"{Defaults.PrefixHeader}.Version"] = @event.Descriptor.Version.ToString(), [$"{Defaults.PrefixHeader}.Bucket"] = bucket, [$"{Defaults.PrefixHeader}.StreamId"] = streamId, [$"{Defaults.PrefixHeader}.Parents"] = parentsStr }; string id = ""; id = @event.Descriptor.Headers[Defaults.OobHeaderKey]; if (@event.Descriptor.Headers.ContainsKey(Defaults.OobTransientKey) && bool.TryParse(@event.Descriptor.Headers[Defaults.OobTransientKey], out var transient) && !transient) { var stream = _generator(typeof(TEntity), StreamTypes.OOB, $"{id}.{bucket}", streamId, parents); var version = await _store.WriteEvents(stream, new[] { @event }, headers).ConfigureAwait(false); if (@event.Descriptor.Headers.ContainsKey(Defaults.OobDaysToLiveKey) && int.TryParse(@event.Descriptor.Headers[Defaults.OobDaysToLiveKey], out var daysToLive) && daysToLive != -1) { var key = $"{bucket}.{id}.{streamId}.{parentsStr}"; // Uses the dictionary to keep track of daysToLive data its already saved. // If an entity saves the same stream with a new daysToLive the stream metadata needs to be rewritten if (!DaysToLiveKnowns.ContainsKey(key) || DaysToLiveKnowns[key] != daysToLive) { DaysToLiveKnowns[key] = daysToLive; await _store.WriteMetadata(stream, maxAge: TimeSpan.FromDays(daysToLive)).ConfigureAwait(false); } } } else { await _dispatcher.Publish(message, headers).ConfigureAwait(false); } }).ConfigureAwait(false); }
public override async Task Invoke(IIncomingLogicalMessageContext context, Func <Task> next) { var messageId = context.MessageId; var retries = 0; try { RetryRegistry.TryRemove(messageId, out retries); context.Headers[Defaults.Retries] = retries.ToString(); context.Extensions.Set(Defaults.Retries, retries); await next().ConfigureAwait(false); } catch (Exception e) { // Special exception we dont want to retry or reply if (e is BusinessException || context.MessageHandled) { return; } if (retries < _retries || _retries == -1) { Logger.LogEvent((retries > _retries / 2) ? LogLevel.Warn : LogLevel.Info, "Catch", e, "[{MessageId:l}] will retry {Retries}/{MaxRetries}: {ExceptionType} - {ExceptionMessage}", messageId, retries, _retries, e.GetType().Name, e.Message); RetryRegistry.TryAdd(messageId, retries + 1); var message = new FullMessage { Message = context.Message.Instance as Messages.IMessage, Headers = context.Headers }; // todo: Not ideal for this to be here - // but unpacking these headers takes place further down the queue after UOW start // I don't want to start a new unit of work for each message in a bulk message (defeats the purpose) // So need to do something a little special here if (context.Extensions.TryGet(Defaults.BulkHeader, out IFullMessage[] delayedMessages))
private static void Threaded(object state) { var param = (ThreadParam)state; var container = Configuration.Settings.Container; var metrics = container.Resolve <IMetrics>(); var consumer = container.Resolve <IEventStoreConsumer>(); var dispatcher = container.Resolve <IMessageDispatcher>(); try { while (true) { param.Token.ThrowIfCancellationRequested(); var @event = WaitingEvents.Take(param.Token); metrics.Decrement("Events Queued", Unit.Event); try { var message = new FullMessage { Message = @event.Item3.Event, Headers = @event.Item3.Descriptor.Headers }; var headers = new Dictionary <string, string>() { [$"{Defaults.PrefixHeader}.EventId"] = @event.Item3.EventId.ToString(), [$"{Defaults.PrefixHeader}.EventStream"] = @event.Item1, [$"{Defaults.PrefixHeader}.EventPosition"] = @event.Item2.ToString() }; dispatcher.SendLocal(message, headers).ConfigureAwait(false).GetAwaiter().GetResult(); consumer.Acknowledge(@event.Item1, @event.Item2, @event.Item3).ConfigureAwait(false) .GetAwaiter().GetResult(); } catch (System.AggregateException e) { if (e.InnerException is OperationCanceledException) { throw e.InnerException; } // If not a canceled exception, just write to log and continue // we dont want some random unknown exception to kill the whole event loop Logger.ErrorEvent("Exception", e, "From event thread: {ExceptionType} - {ExceptionMessage}", e.GetType().Name, e.Message); } } } catch (Exception e) { if (!(e is OperationCanceledException)) { Logger.ErrorEvent("Died", e, "Event thread closed: {ExceptionType} - {ExceptionMessage}", e.GetType().Name, e.Message); } } }
private static void Threaded(object state) { var param = (ThreadParam)state; var container = Configuration.Settings.Container; var metrics = container.Resolve <IMetrics>(); var consumer = container.Resolve <IEventStoreConsumer>(); var dispatcher = container.Resolve <IMessageDispatcher>(); try { while (true) { param.Token.ThrowIfCancellationRequested(); var @event = param.Queue.Take(param.Token); metrics.Decrement("Events Queued", Unit.Event); try { var message = new FullMessage { Message = @event.Item3.Event, Headers = @event.Item3.Descriptor.Headers }; var headers = new Dictionary <string, string>() { [$"{Defaults.EventPrefixHeader}.EventId"] = @event.Item3.EventId.ToString(), [$"{Defaults.EventPrefixHeader}.EventStream"] = @event.Item1, [$"{Defaults.EventPrefixHeader}.EventPosition"] = @event.Item2.ToString() }; dispatcher.SendLocal(message, headers).ConfigureAwait(false).GetAwaiter().GetResult(); Logger.Write(LogLevel.Debug, () => $"Acknowledge event {@event.Item3.Descriptor.EventId} stream [{@event.Item1}] number {@event.Item2}"); consumer.Acknowledge(@event.Item1, @event.Item2, @event.Item3).ConfigureAwait(false) .GetAwaiter().GetResult(); } catch (System.AggregateException e) { if (e.InnerException is OperationCanceledException) { throw e.InnerException; } // If not a canceled exception, just write to log and continue // we dont want some random unknown exception to kill the whole event loop Logger.Error($"Received exception in main event thread: {e.GetType()}: {e.Message}\n{e.AsString()}"); } } } catch (Exception e) { Logger.Error($"Event subscriber thread terminated due to exception: {e.GetType()}: {e.Message}\n{e.AsString()}"); } }