< Summary

Information
Class: IceRpc.Locator.Internal.LogServerAddressCacheDecorator
Assembly: IceRpc.Locator
File(s): /home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc.Locator/Internal/ServerAddressCache.cs
Tag: 275_13775359185
Line coverage
0%
Covered lines: 0
Uncovered lines: 21
Coverable lines: 21
Total lines: 169
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 2
Branch coverage: 0%
Method coverage
0%
Covered methods: 0
Total methods: 4
Method coverage: 0%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
Remove(...)100%210%
Set(...)100%210%
TryGetValue(...)0%620%
.ctor(...)100%210%

File(s)

/home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc.Locator/Internal/ServerAddressCache.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3using Microsoft.Extensions.Logging;
 4using System.Collections.Concurrent;
 5using System.Diagnostics;
 6
 7namespace IceRpc.Locator.Internal;
 8
 9/// <summary>Provides extension methods for <see cref="ILogger" />. They are used by <see
 10/// cref="LogServerAddressCacheDecorator"/>.
 11/// </summary>
 12internal static partial class ServerAddressCacheLoggerExtensions
 13{
 14    [LoggerMessage(
 15        EventId = (int)LocationEventId.FoundEntry,
 16        EventName = nameof(LocationEventId.FoundEntry),
 17        Level = LogLevel.Trace,
 18        Message = "Found {LocationKind} '{Location}' = '{ServiceAddress}' in cache")]
 19    internal static partial void LogFoundEntry(
 20        this ILogger logger,
 21        string locationKind,
 22        Location location,
 23        ServiceAddress serviceAddress);
 24
 25    [LoggerMessage(
 26        EventId = (int)LocationEventId.SetEntry,
 27        EventName = nameof(LocationEventId.SetEntry),
 28        Level = LogLevel.Trace,
 29        Message = "Set {LocationKind} '{Location}' = '{ServiceAddress}' in cache")]
 30    internal static partial void LogSetEntry(
 31        this ILogger logger,
 32        string locationKind,
 33        Location location,
 34        ServiceAddress serviceAddress);
 35
 36    [LoggerMessage(
 37        EventId = (int)LocationEventId.RemovedEntry,
 38        EventName = nameof(LocationEventId.RemovedEntry),
 39        Level = LogLevel.Trace,
 40        Message = "Removed {LocationKind} '{Location}' from cache")]
 41    internal static partial void LogRemovedEntry(
 42        this ILogger logger,
 43        string locationKind,
 44        Location location);
 45}
 46
 47/// <summary>A server address cache maintains a dictionary of location to server address(es), where the server
 48/// addresses are held by a dummy service address. It also keeps track of the insertion time of each entry. It's
 49/// consumed by <see cref="LocationResolver" />.</summary>
 50internal interface IServerAddressCache
 51{
 52    void Remove(Location location);
 53
 54    void Set(Location location, ServiceAddress serviceAddress);
 55
 56    bool TryGetValue(Location location, out (TimeSpan InsertionTime, ServiceAddress ServiceAddress) value);
 57}
 58
 59/// <summary>The main implementation for <see cref="IServerAddressCache" />.</summary>
 60internal sealed class ServerAddressCache : IServerAddressCache
 61{
 62    private readonly ConcurrentDictionary<Location, (TimeSpan InsertionTime, ServiceAddress ServiceAddress, LinkedListNo
 63
 64    // The keys in _cache. The first entries correspond to the most recently added cache entries.
 65    private readonly LinkedList<Location> _cacheKeys = new();
 66
 67    private readonly int _maxCacheSize;
 68
 69    // _mutex protects _cacheKeys and updates to _cache
 70    private readonly object _mutex = new();
 71
 72    public void Remove(Location location)
 73    {
 74        lock (_mutex)
 75        {
 76            if (_cache.TryRemove(
 77                location,
 78                out (TimeSpan InsertionTime, ServiceAddress ServiceAddress, LinkedListNode<Location> Node) entry))
 79            {
 80                _cacheKeys.Remove(entry.Node);
 81            }
 82        }
 83    }
 84
 85    public void Set(Location location, ServiceAddress serviceAddress)
 86    {
 87        lock (_mutex)
 88        {
 89            Remove(location); // remove existing cache entry if present
 90
 91            _cache[location] =
 92                (TimeSpan.FromMilliseconds(Environment.TickCount64), serviceAddress, _cacheKeys.AddFirst(location));
 93
 94            if (_cacheKeys.Count == _maxCacheSize + 1)
 95            {
 96                // drop last (oldest) entry
 97                Remove(_cacheKeys.Last!.Value);
 98
 99                Debug.Assert(_cacheKeys.Count == _maxCacheSize); // removed the last entry
 100            }
 101        }
 102    }
 103
 104    public bool TryGetValue(Location location, out (TimeSpan InsertionTime, ServiceAddress ServiceAddress) value)
 105    {
 106        // no mutex lock: _cache is a concurrent dictionary and it's ok if it's updated while we read it
 107
 108        if (_cache.TryGetValue(
 109            location,
 110            out (TimeSpan InsertionTime, ServiceAddress ServiceAddress, LinkedListNode<Location> Node) entry))
 111        {
 112            value.InsertionTime = entry.InsertionTime;
 113            value.ServiceAddress = entry.ServiceAddress;
 114            return true;
 115        }
 116        else
 117        {
 118            value = default;
 119            return false;
 120        }
 121    }
 122
 123    internal ServerAddressCache(int maxCacheSize)
 124    {
 125        Debug.Assert(maxCacheSize > 0);
 126        _maxCacheSize = maxCacheSize;
 127        _cache = new(concurrencyLevel: 1, capacity: _maxCacheSize + 1);
 128    }
 129}
 130
 131/// <summary>A decorator that adds event source logging to a server address cache.</summary>
 132internal class LogServerAddressCacheDecorator : IServerAddressCache
 133{
 134    private readonly IServerAddressCache _decoratee;
 135    private readonly ILogger _logger;
 136
 137    public void Remove(Location location)
 0138    {
 0139        _decoratee.Remove(location);
 0140        _logger.LogRemovedEntry(location.Kind, location);
 0141    }
 142
 143    public void Set(Location location, ServiceAddress serviceAddress)
 0144    {
 0145        _decoratee.Set(location, serviceAddress);
 0146        _logger.LogSetEntry(location.Kind, location, serviceAddress);
 0147    }
 148
 149    public bool TryGetValue(
 150        Location location,
 151        out (TimeSpan InsertionTime, ServiceAddress ServiceAddress) value)
 0152    {
 0153        if (_decoratee.TryGetValue(location, out value))
 0154        {
 0155            _logger.LogFoundEntry(location.Kind, location, value.ServiceAddress);
 0156            return true;
 157        }
 158        else
 0159        {
 0160            return false;
 161        }
 0162    }
 163
 0164    internal LogServerAddressCacheDecorator(IServerAddressCache decoratee, ILogger logger)
 0165    {
 0166        _decoratee = decoratee;
 0167        _logger = logger;
 0168    }
 169}