< Summary

Information
Class: AmbientServices.AmbientBottleneckAccessor
Assembly: AmbientServices
File(s): /home/runner/work/AmbientServices/AmbientServices/AmbientServices/Services/BottleneckDetector.cs
Tag: 332_35464845198
Line coverage
99%
Covered lines: 101
Uncovered lines: 1
Coverable lines: 102
Total lines: 405
Line coverage: 99%
Branch coverage
100%
Covered branches: 72
Total branches: 72
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_AccessEndStopwatchTimestamp()100%210%
get_AccessBegin()100%11100%
get_AccessEnd()100%22100%
get_AccessCount()100%11100%
get_LimitUsed()100%11100%
get_Utilization()100%11100%
get_AccessDurationStopwatchTicks()100%22100%
.ctor(...)100%44100%
.ctor(...)100%44100%
.ctor(...)100%44100%
SetUsage(...)100%22100%
AddUsage(...)100%22100%
UpdateUtilization()100%11100%
ComputeUtilization(...)100%1212100%
Dispose()100%44100%
Split(...)100%66100%
Combine(...)100%22100%
Combine(...)100%44100%
CompareTo(...)100%66100%
Equals(...)100%88100%
GetHashCode()100%11100%
op_Equality(...)100%22100%
op_Inequality(...)100%11100%
op_LessThan(...)100%22100%
op_LessThanOrEqual(...)100%22100%
op_GreaterThan(...)100%22100%
op_GreaterThanOrEqual(...)100%22100%

File(s)

/home/runner/work/AmbientServices/AmbientServices/AmbientServices/Services/BottleneckDetector.cs

#LineLine coverage
 1using AmbientServices.Utilities;
 2using System;
 3
 4namespace AmbientServices;
 5
 6/// <summary>
 7/// An interface that callers implement to receive bottleneck exit notifications.
 8/// </summary>
 9/// <remarks>
 10/// <pitch>The push side of bottleneck detection: implement this to receive every bottleneck access as it completes and 
 11/// <pledge>
 12/// <see cref="BottleneckExited"/> is called once per access, after the access has ended, with the <see cref="AmbientBot
 13/// Calls may arrive concurrently from any call context or thread; implementations must be thread-safe unless the regist
 14/// </pledge>
 15/// </remarks>
 16public interface IAmbientBottleneckExitNotificationSink
 17{
 18    /// <summary>
 19    /// Notification that the bottleneck was exited.
 20    /// </summary>
 21    /// <param name="bottleneckAccessor">The <see cref="AmbientBottleneckAccessor"/> representing the access.</param>
 22    void BottleneckExited(AmbientBottleneckAccessor bottleneckAccessor);
 23}
 24/// <summary>
 25/// An interface that callers implement to receive bottleneck entry notifications.
 26/// </summary>
 27/// <remarks>
 28/// <pitch>The optional companion to <see cref="IAmbientBottleneckExitNotificationSink"/> for sinks that need to see acc
 29/// <pledge>
 30/// <see cref="BottleneckEntered"/> is called once per access, when the access begins; the delivered <see cref="AmbientB
 31/// There is no separate registration for this interface: the detector notifies entries only to registered exit sinks th
 32/// Calls may arrive concurrently from any call context or thread; implementations must be thread-safe.
 33/// </pledge>
 34/// </remarks>
 35public interface IAmbientBottleneckEnterNotificationSink
 36{
 37    /// <summary>
 38    /// Notification that the bottleneck was entered.
 39    /// </summary>
 40    /// <param name="bottleneckAccessor">The <see cref="AmbientBottleneckAccessor"/> representing the access.</param>
 41    void BottleneckEntered(AmbientBottleneckAccessor bottleneckAccessor);
 42}
 43/// <summary>
 44/// An interface that abstracts an ambient bottleneck detector.
 45/// </summary>
 46/// <remarks>
 47/// Each time a bottleneck is used, the caller informs this interface, telling it when the bottleneck is entered and exi
 48/// A survey that ranks the bottlenecks by how close to (or how much over) their limits they are may then be accessed.
 49/// <pitch>
 50/// Cheap, always-on measurement of how close the system is to maxing out — saturation of known scalability limits (API 
 51/// It measures only what callers explicitly bracket, and only against limits the caller declares — it does not discover
 52/// </pitch>
 53/// <pledge>
 54/// <see cref="EnterBottleneck"/> begins an access to the specified bottleneck and returns the accessor whose disposal e
 55/// Registered <see cref="IAmbientBottleneckExitNotificationSink"/> instances are notified once per access when it ends;
 56/// </pledge>
 57/// <priority>
 58/// 1. Always-on affordability over finding bottlenecks by itself: it measures only what callers explicitly bracket, and
 59/// 2. Accumulating nothing here over sink simplicity: raw accesses are broadcast and every aggregation, windowing, and 
 60/// </priority>
 61/// </remarks>
 62public interface IAmbientBottleneckDetector
 63{
 64    /// <summary>
 65    /// Enters the specified bottleneck.
 66    /// </summary>
 67    /// <param name="bottleneck">The <see cref="AmbientBottleneck"/> for the bottleneck being accessed.</param>
 68    /// <returns>An <see cref="AmbientBottleneckAccessor"/> that should be disposed when exiting the bottleneck.</return
 69    AmbientBottleneckAccessor EnterBottleneck(AmbientBottleneck bottleneck);
 70    /// <summary>
 71    /// Registers an access notification sink with this ambient bottleneck detector.
 72    /// </summary>
 73    /// <param name="sink">An <see cref="IAmbientBottleneckExitNotificationSink"/> that will receive notifications when 
 74    /// <returns>true if the registration was successful, false if the specified sink was already registered.</returns>
 75    bool RegisterAccessNotificationSink(IAmbientBottleneckExitNotificationSink sink);
 76    /// <summary>
 77    /// Deregisters an access notification sink with this ambient bottleneck detector.
 78    /// </summary>
 79    /// <param name="sink">The previously-registered <see cref="IAmbientBottleneckExitNotificationSink"/> that received 
 80    /// <returns>true if the deregistration was successful, false if the specified sink was not registered.</returns>
 81    bool DeregisterAccessNotificationSink(IAmbientBottleneckExitNotificationSink sink);
 82}
 83/// <summary>
 84/// An enumeration of possible bottleneck types.
 85/// </summary>
 86public enum AmbientBottleneckUtilizationAlgorithm
 87{
 88    /// <summary>
 89    /// Utilization will always be zero, so this type of bottleneck will never rank above any bottlenecks with a non-zer
 90    /// </summary>
 91    Zero,
 92    /// <summary>
 93    /// Utilization will be computed as (<see cref="AmbientBottleneckAccessor.LimitUsed"/> / <see cref="AmbientBottlenec
 94    /// </summary>
 95    Linear,
 96    /// <summary>
 97    /// There are no known fixed limits for this bottleneck, but it should be tracked anyway.
 98    /// Utilization will be computed as 1.0 - 1.0 / (1.0 + Linear Result * 2.0).
 99    /// This formula starts at zero, hits one half when the limit used hits half the limit (relative to the limit window
 100    /// </summary>
 101    ExponentialLimitApproach,
 102}
 103/// <summary>
 104/// A class that tracks a single access to a bottleneck.
 105/// </summary>
 106/// <remarks>
 107/// The access may or may not be in-progress.
 108/// The access counts the same whether the operation being performed succeeds or not because the contention will be the 
 109/// <pitch>The record of usage against one bottleneck — a single access (dispose it to end the access) or an aggregate o
 110/// <pledge>
 111/// For a bottleneck marked <see cref="AmbientBottleneck.Automatic"/>, usage accrues as elapsed stopwatch time and is fi
 112/// <see cref="Utilization"/> is recomputed only at construction, on each usage report, and at disposal; instances sort 
 113/// Property reads and usage updates are thread-safe; disposal ends the access exactly once and notifies the owning dete
 114/// </pledge>
 115/// <plan>
 116/// Mutable state (end timestamp, access count, limit used, utilization) lives in fields updated with <see cref="System.
 117/// Utilization derives from the bottleneck's <see cref="AmbientBottleneckUtilizationAlgorithm"/>: Linear compares the r
 118/// The internal <c>Split</c>/<c>Combine</c> operations exist for windowed surveys: Split clips an access at a window bo
 119/// </plan>
 120/// </remarks>
 121public sealed class AmbientBottleneckAccessor : IComparable<AmbientBottleneckAccessor>, IDisposable
 122{
 123    private readonly BasicAmbientBottleneckDetector _owner;
 124    private long _accessEndStopwatchTimestamp;  // interlocked, long.MaxValue until the access finishes
 125    private long _accessCount;                  // interlocked, zero until the access finishes
 126    private double _limitUsed;                  // interlocked, zero until set by Dispose, SetUsage or AddUsage
 127    private double _utilization;                // interlocked, zero until set by Dispose, SetUsage or AddUsage
 128
 129    internal long AccessBeginStopwatchTimestamp { get; }
 0130    internal long AccessEndStopwatchTimestamp => _accessEndStopwatchTimestamp;
 131    /// <summary>
 132    /// Gets the <see cref="AmbientBottleneck"/> for the bottleneck.
 133    /// </summary>
 134    public AmbientBottleneck Bottleneck { get; }
 135    /// <summary>
 136    /// Gets the beginning of the time range for the access.
 137    /// </summary>
 2138    public DateTime AccessBegin => new(TimeSpanUtilities.StopwatchTimestampToDateTime(AccessBeginStopwatchTimestamp));
 139    /// <summary>
 140    /// Gets the end of the time range for the access, if the access is finished.
 141    /// </summary>
 2142    public DateTime? AccessEnd => (_accessEndStopwatchTimestamp >= long.MaxValue) ? null : new DateTime(TimeSpanUtilitie
 143    /// <summary>
 144    /// Gets the number of times the bottleneck was accessed.  This is only statistical and is not used to compute <see 
 145    /// </summary>
 2146    public long AccessCount => _accessCount;
 147    /// <summary>
 148    /// Gets the amount of the limit which was used.  Note that this is in units of <see cref="System.Diagnostics.Stopwa
 149    /// </summary>
 2150    public double LimitUsed => _limitUsed;
 151    /// <summary>
 152    /// Gets the utilization factor, usually between 0.0 and 1.0, where 1.0 indicates that the limit was just reached, a
 153    /// </summary>
 154    /// <remarks>
 155    /// This computed value is used to sort the seriousness of the usage, with larger values indicating more of a proble
 156    /// The value is only updated when constructed, when <see cref="SetUsage"/> or <see cref="AddUsage"/> is called, or 
 157    /// </remarks>
 2158    public double Utilization => _utilization;
 159    /// <summary>
 160    /// Gets number of stopwatch ticks between the beginning and end of the access.
 161    /// </summary>
 2162    public long AccessDurationStopwatchTicks => ((_accessEndStopwatchTimestamp >= long.MaxValue) ? AmbientClock.Ticks : 
 163
 164    /// <summary>
 165    /// Constructs a single-access AmbientBottleneckAccessRecord for the specified bottleneck with access starting at th
 166    /// </summary>
 2167    internal AmbientBottleneckAccessor(BasicAmbientBottleneckDetector owner, AmbientBottleneck bottleneck, long accessBe
 168    {
 2169        _owner = owner ?? throw new ArgumentNullException(nameof(owner));
 2170        Bottleneck = bottleneck ?? throw new ArgumentNullException(nameof(bottleneck));
 2171        AccessBeginStopwatchTimestamp = accessBeginStopwatchTimestamp;
 2172        _accessEndStopwatchTimestamp = long.MaxValue;
 2173    }
 174    /// <summary>
 175    /// Constructs an AmbientBottleneckAccessRecord from the completely specified property values.
 176    /// </summary>
 2177    internal AmbientBottleneckAccessor(BasicAmbientBottleneckDetector owner, AmbientBottleneck bottleneck, long accessBe
 178    {
 2179        _owner = owner ?? throw new ArgumentNullException(nameof(owner));
 2180        Bottleneck = bottleneck ?? throw new ArgumentNullException(nameof(bottleneck));
 2181        AccessBeginStopwatchTimestamp = accessBeginStopwatchTimestamp;
 2182        _accessEndStopwatchTimestamp = accessEndStopwatchTimestamp;
 2183        _accessCount = accessCount;
 2184        _limitUsed = limitUsed;
 2185        UpdateUtilization();
 2186    }
 187    /// <summary>
 188    /// Constructs a AmbientBottleneckAccessRecord from the specified property values.
 189    /// </summary>
 190    /// <param name="owner">The <see cref="BasicAmbientBottleneckDetector"/> that owns this access.</param>
 191    /// <param name="bottleneck">The <see cref="AmbientBottleneck"/> being accessed.</param>
 192    /// <param name="accessBegin">The <see cref="DateTime"/> indicating the beginning of the access (presumably now, or 
 2193    internal AmbientBottleneckAccessor(BasicAmbientBottleneckDetector owner, AmbientBottleneck bottleneck, DateTime acce
 194    {
 2195        _owner = owner ?? throw new ArgumentNullException(nameof(owner));
 2196        Bottleneck = bottleneck ?? throw new ArgumentNullException(nameof(bottleneck));
 2197        AccessBeginStopwatchTimestamp = TimeSpanUtilities.DateTimeToStopwatchTimestamp(accessBegin.Ticks);
 2198        _accessEndStopwatchTimestamp = long.MaxValue;
 2199    }
 200
 201    /// <summary>
 202    /// Sets the access count and limit used.
 203    /// </summary>
 204    /// <param name="accessCount">The number of access counts.</param>
 205    /// <param name="limitUsed">The amount towards the associated limit that has been used.</param>
 206    public void SetUsage(long accessCount, double limitUsed)
 207    {
 2208        if (Bottleneck.Automatic) throw new InvalidOperationException("SetUsage cannot be used on Automatic bottlenecks!
 2209        System.Threading.Interlocked.Exchange(ref _accessCount, accessCount);
 2210        System.Threading.Interlocked.Exchange(ref _limitUsed, limitUsed);
 2211        UpdateUtilization();
 2212    }
 213
 214    /// <summary>
 215    /// Adds to the access count and limit used.
 216    /// </summary>
 217    /// <param name="additionalAccessCount">The additional number of access counts.</param>
 218    /// <param name="additionalLimitUsed">The additional amount towards the associated limit that has been used.</param>
 219    public void AddUsage(long additionalAccessCount, double additionalLimitUsed)
 220    {
 2221        if (Bottleneck.Automatic) throw new InvalidOperationException("SetUsage cannot be used on Automatic bottlenecks!
 2222        System.Threading.Interlocked.Add(ref _accessCount, additionalAccessCount);
 2223        InterlockedUtilities.TryOptimisticAdd(ref _limitUsed, additionalLimitUsed);
 2224        UpdateUtilization();
 2225    }
 226
 227    private void UpdateUtilization()
 228    {
 2229        System.Threading.Interlocked.Exchange(ref _utilization, ComputeUtilization(Bottleneck.UtilizationAlgorithm, _acc
 2230    }
 231
 232    private static double ComputeUtilization(AmbientBottleneckUtilizationAlgorithm limitType, long totalStopwatchTicks, 
 233    {
 2234        if (limit == null || Math.Abs(limit.Value) < 1) limit = 1.0;
 2235        double limitPeriodStopwatchTicks = (limitPeriod == null) ? 1.0 : TimeSpanUtilities.TimeSpanTicksToStopwatchTicks
 2236        if (totalStopwatchTicks < 1) totalStopwatchTicks = 1;
 2237        double result = limitType switch {
 2238            AmbientBottleneckUtilizationAlgorithm.Linear => (1.0 * limitUsed / totalStopwatchTicks) / (1.0 * limit.Value
 2239            AmbientBottleneckUtilizationAlgorithm.ExponentialLimitApproach => 1.0 - 1.0 / (2.0 * limitUsed / totalStopwa
 2240            _ => 0.0,
 2241        };
 2242        return result;
 243    }
 244
 245    /// <summary>
 246    /// Disposes of this instance, indicating that the access of the bottleneck has completed.
 247    /// </summary>
 248    public void Dispose()
 249    {
 2250        System.Threading.Interlocked.Exchange(ref _accessEndStopwatchTimestamp, AmbientClock.Ticks);
 2251        if (Bottleneck.Automatic && _accessCount == 0)
 252        {
 2253            System.Threading.Interlocked.Exchange(ref _accessCount, 1);
 2254            System.Threading.Interlocked.Exchange(ref _limitUsed, _accessEndStopwatchTimestamp - AccessBeginStopwatchTim
 255        }
 2256        UpdateUtilization();
 2257        _owner.LeaveBottleneck(this);
 2258        GC.SuppressFinalize(this);
 2259    }
 260
 261    internal (AmbientBottleneckAccessor, AmbientBottleneckAccessor?) Split(long oldWindowBeginStopwatchTicks, long split
 262    {
 263        long accessCount;
 264        double limitUsedSoFar;
 2265        if (Bottleneck.Automatic)
 266        {
 2267            accessCount = _accessCount;
 2268            limitUsedSoFar = ((_accessEndStopwatchTimestamp >= long.MaxValue) ? splitStopwatchTicks : _accessEndStopwatc
 2269                - AccessBeginStopwatchTimestamp - windowStartLimitUsage;
 270        }
 271        else
 272        {
 2273            accessCount = _accessCount - windowStartAccessCount;
 2274            limitUsedSoFar = _limitUsed - windowStartLimitUsage;
 275        }
 276        // the old part will be the usage that has occurred since the last window started and will be clipped to the old
 2277        AmbientBottleneckAccessor oldPart = new( _owner, Bottleneck,
 2278            Math.Max(AccessBeginStopwatchTimestamp, oldWindowBeginStopwatchTicks), splitStopwatchTicks,
 2279            accessCount, limitUsedSoFar);
 280        // is this entry *not* still open?  // the entry is *not* still open, but it could have span multiple windows ea
 2281        if (_accessEndStopwatchTimestamp < long.MaxValue) return (oldPart, null);
 282        // else the new part will exist, and it will use the fork time as the start
 2283        AmbientBottleneckAccessor newPart = new(_owner, Bottleneck, splitStopwatchTicks, splitStopwatchTicks, 0, 0);
 2284        return (oldPart, newPart);
 285    }
 286    internal AmbientBottleneckAccessor Combine(AmbientBottleneckAccessor that)
 287    {
 2288        if (Bottleneck != that.Bottleneck) throw new ArgumentException("The bottlenecks must be the same in order to com
 2289        return Combine(that.AccessBeginStopwatchTimestamp, that._accessEndStopwatchTimestamp, that._accessCount, that._l
 290    }
 291    internal AmbientBottleneckAccessor Combine(long accessBeginTicks, long accessEndTicks, long accessCount, double limi
 292    {
 2293        long beginTicks = Math.Min(AccessBeginStopwatchTimestamp, accessBeginTicks);
 2294        long endTicks = Math.Max(_accessEndStopwatchTimestamp, accessEndTicks);
 2295        double sumLimitUsed = _limitUsed + limitUsed;
 296        // if the access hasn't finished, compute everything as if it finished right now
 2297        long totalStopwatchTicks = (endTicks >= long.MaxValue) ? (AmbientClock.Ticks - beginTicks) : (endTicks - beginTi
 2298        if (totalStopwatchTicks == 0) totalStopwatchTicks = 1;    // make sure total ticks is not zero to avoid a divide
 2299        double limitProportionUsed = // if the usage is equal to the limit and the range is equal to the period, 1.0 sho
 2300            ComputeUtilization(Bottleneck.UtilizationAlgorithm, totalStopwatchTicks, sumLimitUsed, Bottleneck.Limit, Bot
 2301        return new AmbientBottleneckAccessor(_owner, Bottleneck, beginTicks)
 2302        {
 2303            _accessEndStopwatchTimestamp = endTicks,
 2304            _accessCount = accessCount,
 2305            _limitUsed = limitUsed,
 2306            _utilization = limitProportionUsed
 2307        };
 308    }
 309
 310    /// <summary>
 311    /// Compares this AmbientBottleneckAccessRecord to another one to see which one has used more towards any set limit.
 312    /// </summary>
 313    /// <param name="other">The other AmbientBottleneckAccessRecord.</param>
 314    /// <returns>&gt;0 if this one has used more than <paramref name="other"/>, &lt;0 if this one has used less than <pa
 315    public int CompareTo(AmbientBottleneckAccessor? other)
 316    {
 2317        if (other is null) return 1;
 2318        int diff = _utilization.CompareTo(other._utilization);
 2319        if (diff != 0) return diff;
 2320        diff = _limitUsed.CompareTo(other._limitUsed);
 2321        if (diff != 0) return diff;
 2322        return AccessCount.CompareTo(other.AccessCount);
 323    }
 324    /// <summary>
 325    /// Gets whether or not the specified object is logically equivalent to this one.
 326    /// </summary>
 327    /// <param name="obj">The object to compare to.</param>
 328    /// <returns>true if the objects are logically equivalent, false if they are not.</returns>
 329    public override bool Equals(object? obj)
 330    {
 2331        if (ReferenceEquals(this, obj)) return true;
 2332        if (obj is not AmbientBottleneckAccessor that) return false;
 2333        return Bottleneck.Equals(that.Bottleneck) && AccessBeginStopwatchTimestamp.Equals(that.AccessBeginStopwatchTimes
 334    }
 335    /// <summary>
 336    /// Gets a 32-bit hash code for the value of this object.
 337    /// </summary>
 338    /// <returns>A 32-bit hash code for the value of this object.</returns>
 339    public override int GetHashCode()
 340    {
 2341        return Bottleneck.GetHashCode() ^ AccessBeginStopwatchTimestamp.GetHashCode() ^ _accessEndStopwatchTimestamp.Get
 342    }
 343
 344    /// <summary>
 345    /// Checks to see if two AmbientBottleneckAccessRecord are logically equal.
 346    /// </summary>
 347    /// <param name="left">The left AmbientBottleneckAccessRecord.</param>
 348    /// <param name="right">The right AmbientBottleneckAccessRecord.</param>
 349    /// <returns>true if the AmbientBottleneckAccessRecords are logically equal, false if they are not.</returns>
 350    public static bool operator ==(AmbientBottleneckAccessor? left, AmbientBottleneckAccessor? right)
 351    {
 2352        if (left is null) return right is null;
 2353        return left.Equals(right);
 354    }
 355    /// <summary>
 356    /// Checks to see if two AmbientBottleneckAccessRecord are logically not equal.
 357    /// </summary>
 358    /// <param name="left">The left AmbientBottleneckAccessRecord.</param>
 359    /// <param name="right">The right AmbientBottleneckAccessRecord.</param>
 360    /// <returns>true if the AmbientBottleneckAccessRecords are logically not equal, false if they are logically equal.<
 361    public static bool operator !=(AmbientBottleneckAccessor? left, AmbientBottleneckAccessor? right)
 362    {
 2363        return !(left == right);
 364    }
 365    /// <summary>
 366    /// Checks to see if a AmbientBottleneckAccessRecord is less than another one.
 367    /// </summary>
 368    /// <param name="left">The left AmbientBottleneckAccessRecord.</param>
 369    /// <param name="right">The right AmbientBottleneckAccessRecord.</param>
 370    /// <returns>true if the <paramref name="left"/> is less than <paramref name="right"/>, false if not.</returns>
 371    public static bool operator <(AmbientBottleneckAccessor? left, AmbientBottleneckAccessor? right)
 372    {
 2373        return left is null ? right is not null : left.CompareTo(right) < 0;
 374    }
 375    /// <summary>
 376    /// Checks to see if a AmbientBottleneckAccessRecord is less than or equal to another one.
 377    /// </summary>
 378    /// <param name="left">The left AmbientBottleneckAccessRecord.</param>
 379    /// <param name="right">The right AmbientBottleneckAccessRecord.</param>
 380    /// <returns>true if the <paramref name="left"/> is less than or equal to <paramref name="right"/>, false if not.</r
 381    public static bool operator <=(AmbientBottleneckAccessor? left, AmbientBottleneckAccessor? right)
 382    {
 2383        return left is null || left.CompareTo(right) <= 0;
 384    }
 385    /// <summary>
 386    /// Checks to see if a AmbientBottleneckAccessRecord is greater than another one.
 387    /// </summary>
 388    /// <param name="left">The left AmbientBottleneckAccessRecord.</param>
 389    /// <param name="right">The right AmbientBottleneckAccessRecord.</param>
 390    /// <returns>true if the <paramref name="left"/> is greater than <paramref name="right"/>, false if not.</returns>
 391    public static bool operator >(AmbientBottleneckAccessor? left, AmbientBottleneckAccessor? right)
 392    {
 2393        return left is not null && left.CompareTo(right) > 0;
 394    }
 395    /// <summary>
 396    /// Checks to see if a AmbientBottleneckAccessRecord is greater than or equal to another one.
 397    /// </summary>
 398    /// <param name="left">The left AmbientBottleneckAccessRecord.</param>
 399    /// <param name="right">The right AmbientBottleneckAccessRecord.</param>
 400    /// <returns>true if the <paramref name="left"/> is greater than or equal to <paramref name="right"/>, false if not.
 401    public static bool operator >=(AmbientBottleneckAccessor? left, AmbientBottleneckAccessor? right)
 402    {
 2403        return left is null ? right is null : left.CompareTo(right) >= 0;
 404    }
 405}

Methods/Properties

get_AccessEndStopwatchTimestamp()
get_AccessBegin()
get_AccessEnd()
get_AccessCount()
get_LimitUsed()
get_Utilization()
get_AccessDurationStopwatchTicks()
.ctor(AmbientServices.BasicAmbientBottleneckDetector, AmbientServices.AmbientBottleneck, long)
.ctor(AmbientServices.BasicAmbientBottleneckDetector, AmbientServices.AmbientBottleneck, long, long, long, double)
.ctor(AmbientServices.BasicAmbientBottleneckDetector, AmbientServices.AmbientBottleneck, System.DateTime)
SetUsage(long, double)
AddUsage(long, double)
UpdateUtilization()
ComputeUtilization(AmbientServices.AmbientBottleneckUtilizationAlgorithm, long, double, System.Nullable<double>, System.Nullable<System.TimeSpan>)
Dispose()
Split(long, long, long, double)
Combine(AmbientServices.AmbientBottleneckAccessor)
Combine(long, long, long, double)
CompareTo(AmbientServices.AmbientBottleneckAccessor)
Equals(object)
GetHashCode()
op_Equality(AmbientServices.AmbientBottleneckAccessor, AmbientServices.AmbientBottleneckAccessor)
op_Inequality(AmbientServices.AmbientBottleneckAccessor, AmbientServices.AmbientBottleneckAccessor)
op_LessThan(AmbientServices.AmbientBottleneckAccessor, AmbientServices.AmbientBottleneckAccessor)
op_LessThanOrEqual(AmbientServices.AmbientBottleneckAccessor, AmbientServices.AmbientBottleneckAccessor)
op_GreaterThan(AmbientServices.AmbientBottleneckAccessor, AmbientServices.AmbientBottleneckAccessor)
op_GreaterThanOrEqual(AmbientServices.AmbientBottleneckAccessor, AmbientServices.AmbientBottleneckAccessor)