Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
7192705
Avoid leaking a MeterListener per Cache in DEBUG builds (#19995)
NatElkins Jul 8, 2026
3ebcc74
Bump FCSMinorVersion to 13 (keep main above 10.0.4xx servicing 43.12.…
T-Gro Jul 9, 2026
87ad51b
Update dependencies from https://github.com/dotnet/msbuild build 2026…
dotnet-maestro[bot] Jul 9, 2026
d11945a
Update dependencies from https://github.com/dotnet/roslyn build 20260…
dotnet-maestro[bot] Jul 9, 2026
60d315a
Add ResetCompilerGeneratedNameState to compiler-generated name genera…
NatElkins Jul 9, 2026
5928e91
Update dependencies from https://github.com/dotnet/msbuild build 2026…
dotnet-maestro[bot] Jul 10, 2026
4eefd05
[main] Update dependencies from dotnet/msbuild (#20055)
dotnet-maestro[bot] Jul 21, 2026
2cd254e
[main] Update dependencies from dotnet/roslyn (#20052)
dotnet-maestro[bot] Jul 21, 2026
9fc230a
Localized file check-in by OneLocBuild Task: Build definition ID 499:…
dotnet-bot Jul 21, 2026
e729d97
[main] Update dependencies from dotnet/arcade (#20054)
dotnet-maestro[bot] Jul 22, 2026
69fca7f
Tests/source context: support multiple carets (#20077)
auduchinok Jul 22, 2026
2f07589
Move VS language-service logic tests to FSharp.Compiler.Service.Tests…
T-Gro Jul 23, 2026
24d731a
Add Roslyn-format EnC CustomDebugInformation codec and portable PDB m…
NatElkins Jul 24, 2026
2e838bb
Add stable synthesized-name replay infrastructure for hot reload (#20…
NatElkins Jul 24, 2026
4a2749e
Fix #19457: lift CE constructs from plain let RHS in computation expr…
T-Gro Jul 24, 2026
1d8dc39
Fix attribute resolution in recursive module/namespace scopes (#19744)
T-Gro Jul 24, 2026
1dc395a
Correct StructLayout size emission for data-less struct unions (#19759)
Copilot Jul 24, 2026
8c0e444
Report FS3888 for generic attribute type abbreviations instead of FS0…
T-Gro Jul 26, 2026
fd6ed49
Move to .NET 11 (SDK, Arcade, product TargetFramework) (#20080)
T-Gro Jul 28, 2026
0c94e4e
Support NotNullIfNotNullAttribute (#19977)
kerams Jul 28, 2026
5dfbf7f
Move SDL/TSA validation to 1ES templates after Arcade 11 upgrade (#20…
abonie Jul 29, 2026
17cb503
Compiled ToStrings under -reflectionfree for DUs and Records (#19976)
charlesroddie Jul 30, 2026
c00299f
Run ilverify via the tool manifest instead of a hard-coded cache path…
abonie Jul 30, 2026
f4b785f
Record spreads (#18927)
brianrourkeboll Aug 1, 2026
d3403ca
Implement interpolated strings via String.Concat (#19971)
charlesroddie Aug 1, 2026
f5c88eb
[main] Source code updates from dotnet/dotnet (#20058)
dotnet-maestro[bot] Aug 3, 2026
647548b
Secure release-note checks for fork pull requests (#20081)
NatElkins Aug 3, 2026
536800c
Update test project to net11 (#20104)
abonie Aug 3, 2026
a6b69ce
Update dependencies from https://github.com/dotnet/arcade build 20260…
dotnet-maestro[bot] Aug 4, 2026
3cc7755
Move to Roslyn's unified ExternalAccess library (#20099)
JoeRobich Aug 4, 2026
ea778bb
LexFilter: drop non-strict mode (#20106)
auduchinok Aug 4, 2026
36ce348
Enable Central Package Management with transitive pinning (#20084)
T-Gro Aug 4, 2026
d89529c
Implement direct delegates (#19993)
kerams Aug 4, 2026
7e2af62
Merge remote-tracking branch 'origin/main' into darc-release/10.0.4xx…
invalid-email-address Aug 4, 2026
92a3700
Fix check_release_notes 403 by restoring pull-requests: write and mak…
T-Gro Aug 4, 2026
8ed0ce1
Restore NuGetRepack UsingTask workaround for Arcade 10 in Microsoft.F…
invalid-email-address Aug 4, 2026
4d65a64
Merge remote-tracking branch 'origin/main' into darc-release/10.0.4xx…
invalid-email-address Aug 4, 2026
3bc2155
Update dependencies from https://github.com/dotnet/arcade build 20260…
dotnet-maestro[bot] Aug 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Avoid leaking a MeterListener per Cache in DEBUG builds (#19995)
* Avoid leaking a MeterListener per Cache in DEBUG builds

In DEBUG builds, every Cache instance created a CacheMetrics.CacheMetricsListener,
which starts a System.Diagnostics.Metrics.MeterListener registered in the
process-global metrics registry. These were never disposed, so they accumulated for
the lifetime of the process. Because every cache hit/miss/add publishes a measurement
to all registered listeners, the per-operation cost grew linearly with the number of
leaked listeners, so workloads that create many caches (for example repeated
ParseAndCheckProject / per-file checks) slowed down steadily.

Track the per-cache totals used by DebugDisplay directly, incrementing a small Stats
object alongside the existing global Meter counters, instead of via a per-cache
MeterListener. No listener is created, so nothing leaks, and DebugDisplay still works.
The now-unused CacheMetrics.Hit/Miss/Add/Update/Eviction/EvictionFail helpers are
replaced by a single recordMetric helper.

* Address review: drop per-cache CacheMetricsListener and cacheId tag

- Remove the CacheMetrics.CacheMetricsListener type. Its only per-cache use
  was the #if DEBUG debugListener each Cache created and never disposed, which
  was the leak this PR set out to fix. (majocha)
- Drop the per-instance cacheId tag (and nextCacheId). Measurements now carry
  only the cache name, shrinking the payload published to any connected
  exporter and removing the per-instance filtering that was cacheId's only
  purpose. (majocha)
- DebugDisplay and the cache tests read the existing name-aggregated stats via
  CacheMetrics.getTotalsByName / getRatioByName, populated by the single
  process-wide ListenToAll listener. No per-cache listener is created and no
  per-operation cost is added in any configuration, so there is no DEBUG-only
  overhead left to gate behind a separate directive. (T-Gro)
- Overload-cache tests enable ListenToAll and snapshot totals before/after to
  stay scoped to their own compilation; FSharpChecker
  .CreateOverloadCacheMetricsListener is removed.

* Update public SurfaceArea baseline after removing CacheMetricsListener

CacheMetricsListener was a public type, so dropping it changes the recorded
public surface. Remove its 10 entries from
FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl; the SurfaceArea test
now passes. Also note the single-listener assumption the cache metric tests
rely on.

* Apply fantomas formatting to Caches.fs

* Document why OverloadCacheTests is not parallelizable (global cache metrics state)

---------

Co-authored-by: Tomas Grosup <Tomas.Grosup@gmail.com>
  • Loading branch information
NatElkins and T-Gro authored Jul 8, 2026
commit 7192705edeb685a7594d5f6114c63f2596fadf1c
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/11.0.100.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
### Fixed

* Stop leaking a `System.Diagnostics.Metrics.MeterListener` per `Cache` in DEBUG builds. Each cache created a `CacheMetrics.CacheMetricsListener` (which starts a `MeterListener` registered in the process-global metrics registry) and never disposed it, so listeners accumulated for the lifetime of the process. Because every cache hit/miss/add published to all registered listeners, the per-operation cost grew linearly with the number of leaked listeners, so repeated checks (and Debug FCS test runs) slowed down over time. The per-cache `CacheMetricsListener` and the per-instance `cacheId` tag are removed; `DebugDisplay` and tests now read the existing name-aggregated stats populated by the single `ListenToAll` listener, so no per-cache listener is created and no per-operation cost is added. ([PR #19995](https://github.com/dotnet/fsharp/pull/19995))
* Fix state machine lowering dropping the side-effectful receiver of an unused unit-typed member access (e.g. inside `task { (effectful()).UnitProp }`). ([Issue #13099](https://github.com/dotnet/fsharp/issues/13099), [PR #19885](https://github.com/dotnet/fsharp/pull/19885))
* `--deterministic` Release builds now produce byte-identical `FSharp.Compiler.Service.dll` under `--parallelcompilation+` and `--parallelcompilation-`, so it is restored to the determinism gate (now also checked sequential-vs-parallel). Code generation runs the same deferred per-file drain in both modes, with type/member/field emit-order keys and generated names derived from the file being emitted rather than thread-scheduling order. ([Issue #19928](https://github.com/dotnet/fsharp/issues/19928), [PR #19929](https://github.com/dotnet/fsharp/pull/19929))
* Fix `[<CompiledName>]` silently producing duplicate IL entries (FS0192/FS2014) when applied to a multi-value let-binding (e.g. `let a, b = 1, 2`); now emits FS0755 at type-check time. ([Issue #6131](https://github.com/dotnet/fsharp/issues/6131), [PR #19924](https://github.com/dotnet/fsharp/pull/19924))
Expand Down
3 changes: 0 additions & 3 deletions src/Compiler/Service/service.fs
Original file line number Diff line number Diff line change
Expand Up @@ -623,9 +623,6 @@ type FSharpChecker

static member Instance = globalInstance.Force()

static member internal CreateOverloadCacheMetricsListener() =
new CacheMetrics.CacheMetricsListener("overloadResolutionCache")

member internal _.FrameworkImportsCache = backgroundCompiler.FrameworkImportsCache

/// Tokenize a single line, returning token information and a tokenization state represented by an integer
Expand Down
3 changes: 0 additions & 3 deletions src/Compiler/Service/service.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -506,9 +506,6 @@ type public FSharpChecker =
[<Obsolete("Please create an instance of FSharpChecker using FSharpChecker.Create")>]
static member Instance: FSharpChecker

/// Creates a listener for overload resolution cache metrics, aggregating across all compilations.
static member internal CreateOverloadCacheMetricsListener: unit -> CacheMetrics.CacheMetricsListener

member internal FrameworkImportsCache: FrameworkImportsCache
member internal ReferenceResolver: LegacyReferenceResolver

Expand Down
66 changes: 10 additions & 56 deletions src/Compiler/Utilities/Caches.fs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,12 @@ module CacheMetrics =
let creations = Meter.CreateCounter<int64>("creations", "count")
let disposals = Meter.CreateCounter<int64>("disposals", "count")

let mutable private nextCacheId = 0

let mkTags (name: string) =
let cacheId = Interlocked.Increment &nextCacheId
// Avoid TagList(ReadOnlySpan<...>) to support net472 runtime
// Only the cache name is tagged: a per-instance id would be published on every measurement,
// inflating the tag payload sent to any connected exporter for no in-process benefit.
let mutable tags = TagList()
tags.Add("name", box name)
tags.Add("cacheId", box cacheId)
tags

let Add (tags: inref<TagList>) = adds.Add(1L, &tags)
Expand Down Expand Up @@ -78,6 +76,10 @@ module CacheMetrics =
let getStatsByName name =
statsByName.GetOrAdd(name, fun _ -> Stats())

let getTotalsByName name = (getStatsByName name).GetTotals()

let getRatioByName name = (getStatsByName name).Ratio

let ListenToAll () =
let listener = new MeterListener()

Expand Down Expand Up @@ -123,50 +125,6 @@ module CacheMetrics =
Console.WriteLine(StatsToString())
}

[<Sealed>]
type CacheMetricsListener(cacheTags: TagList, ?nameOnlyFilter: string) =

let stats = Stats()
let listener = new MeterListener()

do
for instrument in allCounters do
listener.EnableMeasurementEvents instrument

listener.SetMeasurementEventCallback(fun instrument v tags _ ->
let shouldIncrement =
match nameOnlyFilter with
| Some filterName ->
match tags[0].Value with
| :? string as name when name = filterName -> true
| _ -> false
| None -> tags[0] = cacheTags[0] && tags[1] = cacheTags[1]

if shouldIncrement then
stats.Incr instrument.Name v)

listener.Start()

/// Creates a listener that aggregates metrics across all cache instances with the given name.
new(cacheName: string) = new CacheMetricsListener(TagList(), nameOnlyFilter = cacheName)

interface IDisposable with
member _.Dispose() = listener.Dispose()

/// Gets the current totals for each metric type.
member _.GetTotals() = stats.GetTotals()

/// Gets the current hit ratio (hits / (hits + misses)).
member _.Ratio = stats.Ratio

/// Gets the total number of cache hits.
member _.Hits = stats.GetTotals().[hits.Name]

/// Gets the total number of cache misses.
member _.Misses = stats.GetTotals().[misses.Name]

override _.ToString() = stats.ToString()

[<RequireQualifiedAccess>]
type EvictionMode =
| NoEviction
Expand Down Expand Up @@ -361,10 +319,6 @@ type Cache<'Key, 'Value when 'Key: not null> internal (options: CacheOptions<'Ke

post, dispose

#if DEBUG
let debugListener = new CacheMetrics.CacheMetricsListener(tags)
#endif

do CacheMetrics.Created &tags

member val Evicted = evicted.Publish
Expand Down Expand Up @@ -430,9 +384,6 @@ type Cache<'Key, 'Value when 'Key: not null> internal (options: CacheOptions<'Ke
CacheMetrics.Update &tags
post (EvictionQueueMessage.Update result)

member _.CreateMetricsListener() =
new CacheMetrics.CacheMetricsListener(tags)

member _.Dispose() =
if Interlocked.Exchange(&disposed, 1) = 0 then
disposeEvictionProcessor ()
Expand All @@ -447,5 +398,8 @@ type Cache<'Key, 'Value when 'Key: not null> internal (options: CacheOptions<'Ke
override this.Finalize() = this.Dispose()

#if DEBUG
member _.DebugDisplay() = debugListener.ToString()
// Shows the totals aggregated for this cache's name. Populated only while a metrics listener
// (CacheMetrics.ListenToAll, e.g. under --times or the editor's metrics view) is running.
member _.DebugDisplay() =
(CacheMetrics.getStatsByName name).ToString()
#endif
25 changes: 8 additions & 17 deletions src/Compiler/Utilities/Caches.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,18 @@ module CacheMetrics =
/// Global telemetry Meter for all caches. Exposed for testing purposes.
/// Set FSHARP_OTEL_EXPORT environment variable to enable OpenTelemetry export to external collectors in tests.
val Meter: Meter

/// Current metric totals aggregated across all cache instances with the given name.
/// Totals only accumulate while a listener from ListenToAll is running.
val internal getTotalsByName: name: string -> Map<string, int64>

/// Current hit ratio (hits / (hits + misses)) aggregated across all cache instances with the given name.
val internal getRatioByName: name: string -> float

val internal ListenToAll: unit -> IDisposable
val internal StatsToString: unit -> string
val internal CaptureStatsAndWriteToConsole: unit -> IDisposable

/// A listener that captures cache metrics, matching by cache name or exact cache tags.
[<Sealed>]
type CacheMetricsListener =
/// Creates a listener that aggregates metrics across all cache instances with the given name.
new: cacheName: string -> CacheMetricsListener
/// Gets the current totals for each metric type.
member GetTotals: unit -> Map<string, int64>
/// Gets the current hit ratio (hits / (hits + misses)).
member Ratio: float
/// Gets the total number of cache hits.
member Hits: int64
/// Gets the total number of cache misses.
member Misses: int64
interface IDisposable

[<RequireQualifiedAccess; NoComparison>]
type internal EvictionMode =
/// Do not evict items, cache is effectively a ConcurrentDictionary.
Expand Down Expand Up @@ -74,5 +67,3 @@ type internal Cache<'Key, 'Value when 'Key: not null> =
member Evicted: IEvent<unit>
/// For testing only.
member EvictionFailed: IEvent<unit>
/// For testing only. Creates a local telemetry listener for this cache instance.
member CreateMetricsListener: unit -> CacheMetrics.CacheMetricsListener
53 changes: 30 additions & 23 deletions tests/FSharp.Compiler.ComponentTests/CompilerService/Caches.fs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ let shouldNeverTimeout = 200_000

let defaultStructural() = CacheOptions.getDefault HashIdentity.Structural

// Metrics assertions below read absolute per-name totals via CacheMetrics.getTotalsByName. Those totals
// are aggregated process-globally while a CacheMetrics.ListenToAll() listener is running. This works
// because each test uses a unique cache name and this module is the only ListenToAll caller in the
// assembly, so nothing else increments those names. A second concurrently-active listener would
// double-count every measurement, so keep it that way.

[<Fact>]
let ``Create and dispose many`` () =
let caches =
Expand All @@ -28,8 +34,8 @@ let ``Create and dispose many`` () =
[<Fact>]
let ``Basic add and retrieve`` () =
let name = "Basic_add_and_retrieve"
use _ = CacheMetrics.ListenToAll()
use cache = new Cache<string, int>(defaultStructural(), name = name)
use metricsListener = cache.CreateMetricsListener()

cache.TryAdd("key1", 1) |> shouldBeTrue
cache.TryAdd("key2", 2) |> shouldBeTrue
Expand All @@ -45,14 +51,14 @@ let ``Basic add and retrieve`` () =
cache.TryGetValue("key3", &value) |> shouldBeFalse

// Metrics assertions
let totals = metricsListener.GetTotals()
let totals = CacheMetrics.getTotalsByName name
totals.["adds"] |> shouldEqual 2L

[<Fact>]
let ``Eviction of least recently used`` () =
let name = "Eviction_of_least_recently_used"
use _ = CacheMetrics.ListenToAll()
use cache = new Cache<string, int>({ defaultStructural() with TotalCapacity = 2; HeadroomPercentage = 0 }, name = name)
use metricsListener = cache.CreateMetricsListener()

cache.TryAdd("key1", 1) |> shouldBeTrue
cache.TryAdd("key2", 2) |> shouldBeTrue
Expand All @@ -76,7 +82,7 @@ let ``Eviction of least recently used`` () =
value |> shouldEqual 3

// Metrics assertions
let totals = metricsListener.GetTotals()
let totals = CacheMetrics.getTotalsByName name
totals.["adds"] |> shouldEqual 3L

[<Fact>]
Expand All @@ -85,14 +91,14 @@ let ``Stress test evictions`` () =
let iterations = 10_000
let name = "Stress test evictions"

use _ = CacheMetrics.ListenToAll()
use cache = new Cache<string, int>({ defaultStructural() with TotalCapacity = cacheSize; HeadroomPercentage = 0 }, name = name)
use metricsListener = cache.CreateMetricsListener()

let evictionsCompleted = new TaskCompletionSource<unit>()
let expectedEvictions = iterations - cacheSize

cache.Evicted.Add <| fun () ->
if metricsListener.GetTotals().["evictions"] = expectedEvictions then
if (CacheMetrics.getTotalsByName name).["evictions"] = expectedEvictions then
evictionsCompleted.SetResult()

cache.EvictionFailed.Add <| fun _ ->
Expand All @@ -114,13 +120,14 @@ let ``Stress test evictions`` () =
value |> shouldEqual iterations

// Metrics assertions
let totals = metricsListener.GetTotals()
let totals = CacheMetrics.getTotalsByName name
totals.["adds"] |> shouldEqual (int64 iterations)

[<Fact>]
let ``Metrics can be retrieved`` () =
use cache = new Cache<string, int>({ defaultStructural() with TotalCapacity = 2; HeadroomPercentage = 0 }, name = "test_metrics")
use metricsListener = cache.CreateMetricsListener()
let name = "test_metrics"
use _ = CacheMetrics.ListenToAll()
use cache = new Cache<string, int>({ defaultStructural() with TotalCapacity = 2; HeadroomPercentage = 0 }, name = name)

cache.TryAdd("key1", 1) |> shouldBeTrue
cache.TryAdd("key2", 2) |> shouldBeTrue
Expand All @@ -135,17 +142,17 @@ let ``Metrics can be retrieved`` () =
cache.TryAdd("key3", 3) |> shouldBeTrue
evictionCompleted.Task.Wait shouldNeverTimeout |> shouldBeTrue

let totals = metricsListener.GetTotals()
let totals = CacheMetrics.getTotalsByName name

metricsListener.Ratio |> shouldEqual 1.0
CacheMetrics.getRatioByName name |> shouldEqual 1.0
totals.["evictions"] |> shouldEqual 1L
totals.["adds"] |> shouldEqual 3L

[<Fact>]
let ``GetOrAdd basic usage`` () =
let cacheName = "GetOrAdd_basic_usage"
use _ = CacheMetrics.ListenToAll()
use cache = new Cache<string, int>(defaultStructural(), name = cacheName)
use metricsListener = cache.CreateMetricsListener()
let mutable factoryCalls = 0
let factory k = factoryCalls <- factoryCalls + 1; String.length k
let v1 = cache.GetOrAdd("abc", factory)
Expand All @@ -157,17 +164,17 @@ let ``GetOrAdd basic usage`` () =
v3 |> shouldEqual 4
factoryCalls |> shouldEqual 2
// Metrics assertions
let totals = metricsListener.GetTotals()
let totals = CacheMetrics.getTotalsByName cacheName
totals.["hits"] |> shouldEqual 1L
totals.["misses"] |> shouldEqual 2L
metricsListener.Ratio |> shouldEqual (1.0/3.0)
CacheMetrics.getRatioByName cacheName |> shouldEqual (1.0/3.0)
totals.["adds"] |> shouldEqual 2L

[<Fact>]
let ``AddOrUpdate basic usage`` () =
let cacheName = "AddOrUpdate_basic_usage"
use _ = CacheMetrics.ListenToAll()
use cache = new Cache<string, int>(defaultStructural(), name = cacheName)
use metricsListener = cache.CreateMetricsListener()
cache.AddOrUpdate("x", 1)
let mutable value = 0
cache.TryGetValue("x", &value) |> shouldBeTrue
Expand All @@ -179,10 +186,10 @@ let ``AddOrUpdate basic usage`` () =
cache.TryGetValue("y", &value) |> shouldBeTrue
value |> shouldEqual 99
// Metrics assertions
let totals = metricsListener.GetTotals()
let totals = CacheMetrics.getTotalsByName cacheName
totals.["hits"] |> shouldEqual 3L // 3 cache hits
totals.["misses"] |> shouldEqual 0L // 0 cache misses
metricsListener.Ratio |> shouldEqual 1.0
CacheMetrics.getRatioByName cacheName |> shouldEqual 1.0
totals.["adds"] |> shouldEqual 2L // "x" and "y" added
totals.["updates"] |> shouldEqual 1L // "x" updated

Expand All @@ -191,8 +198,8 @@ type BoxedKey = BoxedKey of int * int
[<Fact>]
let ``GetOrAdd with reference identity`` () =
let cacheName = "GetOrAdd_with_Reference"
use _ = CacheMetrics.ListenToAll()
use cache = new Cache<BoxedKey, int>(CacheOptions.getReferenceIdentity(), cacheName)
use metricsListener = cache.CreateMetricsListener()
let t1 = BoxedKey (1, 2)
let t2 = BoxedKey (1, 2)
let t3 = BoxedKey (1, 2)
Expand All @@ -219,17 +226,17 @@ let ``GetOrAdd with reference identity`` () =
v1'' |> shouldEqual v1'
v2'' |> shouldEqual v2'
// Metrics assertions
let totals = metricsListener.GetTotals()
let totals = CacheMetrics.getTotalsByName cacheName
totals.["hits"] |> shouldEqual 4L
totals.["misses"] |> shouldEqual 3L
metricsListener.Ratio |> shouldEqual (4.0 / 7.0)
CacheMetrics.getRatioByName cacheName |> shouldEqual (4.0 / 7.0)
totals.["adds"] |> shouldEqual 2L

[<Fact>]
let ``AddOrUpdate with reference identity`` () =
let cacheName = "AddOrUpdate_with_Reference"
use _ = CacheMetrics.ListenToAll()
use cache = new Cache<obj, int>(CacheOptions.getReferenceIdentity(), name = cacheName)
use metricsListener = cache.CreateMetricsListener()
let t1 = box (3, 4)
let t2 = box (3, 4)
cache.AddOrUpdate(t1, 7)
Expand All @@ -248,9 +255,9 @@ let ``AddOrUpdate with reference identity`` () =
cache.TryGetValue(t1, &value1Updated) |> shouldBeTrue
value1Updated |> shouldEqual 9
// Metrics assertions
let totals = metricsListener.GetTotals()
let totals = CacheMetrics.getTotalsByName cacheName
totals.["hits"] |> shouldEqual 3L // 3 cache hits
totals.["misses"] |> shouldEqual 0L // 0 cache misses
metricsListener.Ratio |> shouldEqual 1.0
CacheMetrics.getRatioByName cacheName |> shouldEqual 1.0
totals.["adds"] |> shouldEqual 2L // t1 and t2 added
totals.["updates"] |> shouldEqual 1L // t1 updated once
Original file line number Diff line number Diff line change
Expand Up @@ -2035,16 +2035,6 @@ FSharp.Compiler.AbstractIL.ILBinaryReader: FSharp.Compiler.AbstractIL.ILBinaryRe
FSharp.Compiler.AbstractIL.ILBinaryReader: FSharp.Compiler.AbstractIL.ILBinaryReader+MetadataOnlyFlag
FSharp.Compiler.AbstractIL.ILBinaryReader: FSharp.Compiler.AbstractIL.ILBinaryReader+ReduceMemoryFlag
FSharp.Compiler.AbstractIL.ILBinaryReader: FSharp.Compiler.AbstractIL.ILBinaryReader+Shim
FSharp.Compiler.Caches.CacheMetrics+CacheMetricsListener: Double Ratio
FSharp.Compiler.Caches.CacheMetrics+CacheMetricsListener: Double get_Ratio()
FSharp.Compiler.Caches.CacheMetrics+CacheMetricsListener: Int64 Hits
FSharp.Compiler.Caches.CacheMetrics+CacheMetricsListener: Int64 Misses
FSharp.Compiler.Caches.CacheMetrics+CacheMetricsListener: Int64 get_Hits()
FSharp.Compiler.Caches.CacheMetrics+CacheMetricsListener: Int64 get_Misses()
FSharp.Compiler.Caches.CacheMetrics+CacheMetricsListener: Microsoft.FSharp.Collections.FSharpMap`2[System.String,System.Int64] GetTotals()
FSharp.Compiler.Caches.CacheMetrics+CacheMetricsListener: System.String ToString()
FSharp.Compiler.Caches.CacheMetrics+CacheMetricsListener: Void .ctor(System.String)
FSharp.Compiler.Caches.CacheMetrics: FSharp.Compiler.Caches.CacheMetrics+CacheMetricsListener
FSharp.Compiler.Caches.CacheMetrics: System.Diagnostics.Metrics.Meter Meter
FSharp.Compiler.Caches.CacheMetrics: System.Diagnostics.Metrics.Meter get_Meter()
FSharp.Compiler.Cancellable: Boolean HasCancellationToken
Expand Down
Loading
Loading