diff --git a/src/NLog.Extensions.AzureDataTables/DataTablesTarget.cs b/src/NLog.Extensions.AzureDataTables/DataTablesTarget.cs index 231f054..dbc1ffc 100644 --- a/src/NLog.Extensions.AzureDataTables/DataTablesTarget.cs +++ b/src/NLog.Extensions.AzureDataTables/DataTablesTarget.cs @@ -35,8 +35,8 @@ struct TablePartitionKey : IEquatable public TablePartitionKey(string tableName, string partitionKey) { - TableName = tableName; - PartitionKey = partitionKey; + TableName = tableName ?? string.Empty; + PartitionKey = partitionKey ?? string.Empty; } public bool Equals(TablePartitionKey other) diff --git a/src/NLog.Extensions.AzureEventHub/EventHubTarget.cs b/src/NLog.Extensions.AzureEventHub/EventHubTarget.cs index f2f846c..f87e0cf 100644 --- a/src/NLog.Extensions.AzureEventHub/EventHubTarget.cs +++ b/src/NLog.Extensions.AzureEventHub/EventHubTarget.cs @@ -362,7 +362,7 @@ protected override Task WriteAsyncTask(IList logEvents, Cancellati } } - var partitionBuckets = PartitionKey != null ? SortHelpers.BucketSort(logEvents, _getEventHubPartitionKeyDelegate) : new Dictionary>() { { string.Empty, logEvents } }; + var partitionBuckets = PartitionKey != null ? SortHelpers.BucketSort(logEvents, _getEventHubPartitionKeyDelegate) : new[] { new KeyValuePair>(string.Empty, logEvents) }; IList multipleTasks = partitionBuckets.Count > 1 ? new List(partitionBuckets.Count) : null; foreach (var partitionBucket in partitionBuckets) { diff --git a/src/NLog.Extensions.AzureServiceBus/ServiceBusTarget.cs b/src/NLog.Extensions.AzureServiceBus/ServiceBusTarget.cs index 799965c..aad372e 100644 --- a/src/NLog.Extensions.AzureServiceBus/ServiceBusTarget.cs +++ b/src/NLog.Extensions.AzureServiceBus/ServiceBusTarget.cs @@ -425,7 +425,7 @@ protected override Task WriteAsyncTask(IList logEvents, Cancellati } } - var partitionBuckets = (SessionId != null || PartitionKey != null) ? SortHelpers.BucketSort(logEvents, _getMessagePartitionKeyDelegate) : new Dictionary>() { { string.Empty, logEvents } }; + var partitionBuckets = (SessionId != null || PartitionKey != null) ? SortHelpers.BucketSort(logEvents, _getMessagePartitionKeyDelegate) : new [] { new KeyValuePair>(string.Empty, logEvents) }; IList multipleTasks = partitionBuckets.Count > 1 ? new List(partitionBuckets.Count) : null; foreach (var partitionBucket in partitionBuckets) { diff --git a/src/NLog.Extensions.AzureStorage/SortHelpers.cs b/src/NLog.Extensions.AzureStorage/SortHelpers.cs index 137964c..7ac2b82 100644 --- a/src/NLog.Extensions.AzureStorage/SortHelpers.cs +++ b/src/NLog.Extensions.AzureStorage/SortHelpers.cs @@ -22,24 +22,59 @@ internal static class SortHelpers /// The inputs. /// The key selector. /// - internal static Dictionary> BucketSort(IList inputs, KeySelector keySelector) where TKey : IEquatable + internal static ICollection>> BucketSort(IList inputs, KeySelector keySelector) where TKey : IEquatable { - var retVal = new Dictionary>(); + if (inputs.Count == 0) + return Array.Empty>>(); - for (int i = 0; i < inputs.Count; ++i) + Dictionary> buckets = null; + TKey firstBucketKey = keySelector(inputs[0]); + for (int i = 1; i < inputs.Count; ++i) { - var input = inputs[i]; - var keyValue = keySelector(input); - if (!retVal.TryGetValue(keyValue, out var eventsInBucket)) + TKey keyValue = keySelector(inputs[i]); + if (buckets is null) { - eventsInBucket = new List(inputs.Count - i); - retVal.Add(keyValue, eventsInBucket); + if (!firstBucketKey.Equals(keyValue)) + { + // Multiple buckets needed, allocate full dictionary + buckets = CreateBucketDictionaryWithValue(inputs, i, firstBucketKey, keyValue); + } } + else + { + if (!buckets.TryGetValue(keyValue, out var eventsInBucket)) + { + eventsInBucket = new List(); + buckets.Add(keyValue, eventsInBucket); + } + eventsInBucket.Add(inputs[i]); + } + } - eventsInBucket.Add(input); + if (buckets is null) + { + // All inputs belong to the same bucket + return new[] { new KeyValuePair>(firstBucketKey, inputs) }; + } + else + { + return buckets; + } + } + + private static Dictionary> CreateBucketDictionaryWithValue(IList inputs, int currentIndex, TKey firstBucketKey, TKey nextBucketKey) + { + var buckets = new Dictionary>(); + var firstBucket = new List(Math.Max(currentIndex + 2, 4)); + for (int i = 0; i < currentIndex; i++) + { + firstBucket.Add(inputs[i]); } + buckets[firstBucketKey] = firstBucket; - return retVal; + var nextBucket = new List { inputs[currentIndex] }; + buckets[nextBucketKey] = nextBucket; + return buckets; } } } \ No newline at end of file