@@ -267,80 +267,86 @@ module Log =
267
267
type internal Counter =
268
268
{ mutable rux100: int64 ; mutable count: int64 ; mutable ms: int64 }
269
269
static member Create () = { rux100 = 0 L; count = 0 L; ms = 0 L }
270
- member x.Ingest ( ru , ms ) =
270
+ member x.Ingest ( ms , ru ) =
271
271
Interlocked.Increment(& x.count) |> ignore
272
- Interlocked.Add(& x.rux100, int64 ( ru* 100. )) |> ignore
272
+ Interlocked.Add(& x.rux100, int64 ( ru * 100. )) |> ignore
273
273
Interlocked.Add(& x.ms, ms) |> ignore
274
- let inline private (| RcMs |) ({ interval = i ; ru = ru }: Measurement ) =
275
- ru, int64 i.ElapsedMilliseconds
274
+ type internal Counters () =
275
+ let containers = System.Collections.Concurrent.ConcurrentDictionary< string, Counter>()
276
+ let create ( _name : string ) = Counter.Create()
277
+ member _.Ingest ( container , ms , ru ) = containers.GetOrAdd( container, create) .Ingest( ms, ru)
278
+ member _.Containers = containers.Keys
279
+ member _.TryContainer container = match containers.TryGetValue container with true , t -> Some t | false , _ -> None
280
+ type Epoch () =
281
+ let epoch = System.Diagnostics.Stopwatch.StartNew()
282
+ member val internal Read = Counters() with get, set
283
+ member val internal Write = Counters() with get, set
284
+ member val internal Resync = Counters() with get, set
285
+ member val internal Conflict = Counters() with get, set
286
+ member val internal Prune = Counters() with get, set
287
+ member val internal Delete = Counters() with get, set
288
+ member val internal Trim = Counters() with get, set
289
+ member _.Stop () = epoch.Stop()
290
+ member _.Elapsed = epoch.Elapsed
291
+ let inline private (| BucketMsRu |) ({ container = c ; interval = i ; ru = ru }: Measurement ) =
292
+ c, int64 i.ElapsedMilliseconds, ru
276
293
type LogSink () =
277
- static let epoch = System.Diagnostics.Stopwatch.StartNew()
278
- static member val internal Read = Counter.Create() with get, set
279
- static member val internal Write = Counter.Create() with get, set
280
- static member val internal Resync = Counter.Create() with get, set
281
- static member val internal Conflict = Counter.Create() with get, set
282
- static member val internal Prune = Counter.Create() with get, set
283
- static member val internal Delete = Counter.Create() with get, set
284
- static member val internal Trim = Counter.Create() with get, set
294
+ static let mutable epoch = Epoch()
285
295
static member Restart () =
286
- LogSink.Read <- Counter.Create()
287
- LogSink.Write <- Counter.Create()
288
- LogSink.Resync <- Counter.Create()
289
- LogSink.Conflict <- Counter.Create()
290
- LogSink.Prune <- Counter.Create()
291
- LogSink.Delete <- Counter.Create()
292
- LogSink.Trim <- Counter.Create()
293
- let span = epoch.Elapsed
294
- epoch.Restart()
295
- span
296
+ let fresh = Epoch()
297
+ let outgoing = Interlocked.Exchange(& epoch, fresh)
298
+ outgoing.Stop()
299
+ outgoing
296
300
interface Serilog.Core.ILogEventSink with
297
301
member _.Emit logEvent =
298
302
match logEvent with
299
303
| MetricEvent cm ->
300
304
match cm with
301
- | Op (( Operation.Tip | Operation.Tip404 | Operation.Tip304 | Operation.Query), RcMs m) ->
302
- LogSink .Read.Ingest m
305
+ | Op (( Operation.Tip | Operation.Tip404 | Operation.Tip304 | Operation.Query), BucketMsRu m) ->
306
+ epoch .Read.Ingest m
303
307
| QueryRes (_ direction, _) -> ()
304
- | Op ( Operation.Write, RcMs m) -> LogSink .Write.Ingest m
305
- | Op ( Operation.Conflict, RcMs m) -> LogSink .Conflict.Ingest m
306
- | Op ( Operation.Resync, RcMs m) -> LogSink .Resync.Ingest m
307
- | Op ( Operation.Prune, RcMs m) -> LogSink .Prune.Ingest m
308
+ | Op ( Operation.Write, BucketMsRu m) -> epoch .Write.Ingest m
309
+ | Op ( Operation.Conflict, BucketMsRu m) -> epoch .Conflict.Ingest m
310
+ | Op ( Operation.Resync, BucketMsRu m) -> epoch .Resync.Ingest m
311
+ | Op ( Operation.Prune, BucketMsRu m) -> epoch .Prune.Ingest m
308
312
| PruneRes _ -> ()
309
- | Op ( Operation.Delete, RcMs m) -> LogSink .Delete.Ingest m
310
- | Op ( Operation.Trim, RcMs m) -> LogSink .Trim.Ingest m
313
+ | Op ( Operation.Delete, BucketMsRu m) -> epoch .Delete.Ingest m
314
+ | Op ( Operation.Trim, BucketMsRu m) -> epoch .Trim.Ingest m
311
315
| _ -> ()
312
316
313
317
/// Relies on feeding of metrics from Log through to Stats.LogSink
314
318
/// Use Stats.LogSink.Restart() to reset the start point (and stats) where relevant
315
319
let dump ( log : ILogger ) =
320
+ let res = Stats.LogSink.Restart()
316
321
let stats =
317
- [ " Read" , Stats.LogSink.Read
318
- " Write" , Stats.LogSink.Write
319
- " Resync" , Stats.LogSink.Resync
320
- " Conflict" , Stats.LogSink.Conflict
321
- " Prune" , Stats.LogSink.Prune
322
- " Delete" , Stats.LogSink.Delete
323
- " Trim" , Stats.LogSink.Trim ]
324
- let mutable rows , totalCount , totalRRu , totalWRu , totalMs = 0 , 0 L, 0. , 0. , 0 L
325
- let logActivity name count ru lat =
326
- let aru , ams = ( if count = 0 L then Double.NaN else ru/ float count), ( if count = 0 L then Double.NaN else float lat/ float count)
327
- let rut = name |> function
328
- | " TOTAL" -> " " | " Read" | " Prune" -> totalRRu <- totalRRu + ru; " R"
329
- | _ -> totalWRu <- totalWRu + ru; " W"
330
- log.Information( " {name}: {count:n0}r {ru:n0}{rut:l}RU Average {avgRu:n1}RU {lat:n0}ms" , name, count, ru, rut, aru, ams)
331
- for name, stat in stats do
332
- if stat.count <> 0 L then
333
- let ru = float stat.rux100 / 100.
334
- totalCount <- totalCount + stat.count
335
- totalMs <- totalMs + stat.ms
336
- logActivity name stat.count ru stat.ms
337
- rows <- rows + 1
338
- // Yes, there's a minor race here between the use of the values and the reset
339
- let duration = Stats.LogSink.Restart()
340
- if rows > 1 then logActivity " TOTAL" totalCount ( totalRRu + totalWRu) totalMs
341
- let measures : ( string * ( TimeSpan -> float )) list = [ " s" , _. TotalSeconds(* ; "m", _.TotalMinutes; "h", _.TotalHours*) ]
342
- let logPeriodicRate name count rru wru = log.Information( " {rru:n1}R/{wru:n1}W CU @ {count:n0} rp{unit}" , rru, wru, count, name)
343
- for uom, f in measures do let d = f duration in if d <> 0. then logPeriodicRate uom ( float totalCount/ d |> int64) ( totalRRu/ d) ( totalWRu/ d)
322
+ [| nameof res.Read, res.Read
323
+ nameof res.Write, res.Write
324
+ nameof res.Resync, res.Resync
325
+ nameof res.Conflict, res.Conflict
326
+ nameof res.Prune, res.Prune
327
+ nameof res.Delete, res.Delete
328
+ nameof res.Trim, res.Trim |]
329
+ for container in stats |> Seq.collect ( fun ( _n , stat ) -> stat.Containers) |> Seq.distinct |> Seq.sort do
330
+ let mutable rows , totalCount , totalRRu , totalWRu , totalMs = 0 , 0 L, 0. , 0. , 0 L
331
+ let logActivity name count ru lat =
332
+ let aru , ams = ( if count = 0 L then Double.NaN else ru/ float count), ( if count = 0 L then Double.NaN else float lat/ float count)
333
+ let rut = name |> function
334
+ | " TOTAL" -> " " | nameof res.Read | nameof res.Prune -> totalRRu <- totalRRu + ru; " R"
335
+ | _ -> totalWRu <- totalWRu + ru; " W"
336
+ log.Information( " {container} {name}: {count:n0}r {ru:n0}{rut:l}RU Average {avgRu:n1}RU {lat:n0}ms" , container, name, count, ru, rut, aru, ams)
337
+ for name, stat in stats do
338
+ match stat.TryContainer container with
339
+ | Some stat when stat.count <> 0 L ->
340
+ let ru = float stat.rux100 / 100.
341
+ totalCount <- totalCount + stat.count
342
+ totalMs <- totalMs + stat.ms
343
+ logActivity name stat.count ru stat.ms
344
+ rows <- rows + 1
345
+ | _ -> ()
346
+ if rows > 1 then logActivity " TOTAL" totalCount ( totalRRu + totalWRu) totalMs
347
+ let measures : ( string * ( TimeSpan -> float )) list = [ " s" , _. TotalSeconds(* ; "m", _.TotalMinutes; "h", _.TotalHours*) ]
348
+ let logPeriodicRate name count rru wru = log.Information( " {container} {rru:n1}R/{wru:n1}W RU @ {count:n0} rp{unit}" , container, rru, wru, count, name)
349
+ for uom, f in measures do let d = f res.Elapsed in if d <> 0. then logPeriodicRate uom ( float totalCount/ d |> int64) ( totalRRu/ d) ( totalWRu/ d)
344
350
345
351
[<AutoOpen>]
346
352
module private MicrosoftAzureCosmosWrappers =
0 commit comments