diff --git a/.github/workflows/install-dependencies/action.yml b/.github/workflows/install-dependencies/action.yml index 80fea7da5..cc3e300fe 100644 --- a/.github/workflows/install-dependencies/action.yml +++ b/.github/workflows/install-dependencies/action.yml @@ -19,7 +19,7 @@ runs: - name: Rustup if: "${{ inputs.rust == 'true' }}" shell: bash - run: rustup default 1.67.1 + run: rustup default 1.69.0 # - name: Cargo make # if: "${{ inputs.rust == 'true' }}" diff --git a/src/Elastic.Apm/Model/Transaction.cs b/src/Elastic.Apm/Model/Transaction.cs index 7421091ea..659f24c6b 100644 --- a/src/Elastic.Apm/Model/Transaction.cs +++ b/src/Elastic.Apm/Model/Transaction.cs @@ -357,7 +357,7 @@ private void CheckAndCaptureBaggage() /// /// Internal dictionary to keep track of and look up dropped span stats. /// - private Dictionary _droppedSpanStatsMap; + private ConcurrentDictionary _droppedSpanStatsMap; private bool _isEnded; @@ -552,38 +552,35 @@ private Activity StartActivity(bool shouldRestartTrace) return activity; } + private readonly object _lock = new(); internal void UpdateDroppedSpanStats(string serviceTargetType, string serviceTargetName, string destinationServiceResource, Outcome outcome, double duration ) { + //lock the lazy initialization of the dictionary if (_droppedSpanStatsMap == null) { - _droppedSpanStatsMap = new Dictionary - { - { - new DroppedSpanStatsKey(serviceTargetType, serviceTargetName, outcome), - new DroppedSpanStats(serviceTargetType, serviceTargetName, destinationServiceResource, outcome, duration) - } - }; + lock (_lock) + _droppedSpanStatsMap ??= new ConcurrentDictionary(); } - else + + lock (_lock) { if (_droppedSpanStatsMap.Count >= 128) return; - - if (_droppedSpanStatsMap.TryGetValue(new DroppedSpanStatsKey(serviceTargetType, serviceTargetName, outcome), out var item)) - { - item.Duration ??= - new DroppedSpanStats.DroppedSpanDuration { Sum = new DroppedSpanStats.DroppedSpanDuration.DroppedSpanDurationSum() }; - - item.Duration.Count++; - item.Duration.Sum.UsRaw += duration; - } - else - { - _droppedSpanStatsMap.Add(new DroppedSpanStatsKey(serviceTargetType, serviceTargetName, outcome), - new DroppedSpanStats(serviceTargetType, serviceTargetName, destinationServiceResource, outcome, duration)); - } + //AddOrUpdate callbacks can run multiple times so still wrapping this in a lock + var key = new DroppedSpanStatsKey(serviceTargetType, serviceTargetName, outcome); + _droppedSpanStatsMap.AddOrUpdate(key, + _ => new DroppedSpanStats(serviceTargetType, serviceTargetName, destinationServiceResource, outcome, duration), + (_, stats) => + { + stats.Duration ??= + new DroppedSpanStats.DroppedSpanDuration { Sum = new DroppedSpanStats.DroppedSpanDuration.DroppedSpanDurationSum() }; + + stats.Duration.Count++; + stats.Duration.Sum.UsRaw += duration; + return stats; + }); } }