@@ -8,10 +8,12 @@ package rpc
8
8
import (
9
9
"context"
10
10
"math"
11
+ "sort"
11
12
"time"
12
13
13
14
"github.com/VividCortex/ewma"
14
15
"github.com/cockroachdb/cockroach/pkg/roachpb"
16
+ "github.com/cockroachdb/cockroach/pkg/util/buildutil"
15
17
"github.com/cockroachdb/cockroach/pkg/util/hlc"
16
18
"github.com/cockroachdb/cockroach/pkg/util/log"
17
19
"github.com/cockroachdb/cockroach/pkg/util/metric"
@@ -340,7 +342,7 @@ func (r *RemoteClockMonitor) VerifyClockOffset(ctx context.Context) error {
340
342
341
343
now := r .clock .Now ()
342
344
healthyOffsetCount := 0
343
-
345
+ sum := float64 ( 0 )
344
346
offsets , numClocks := func () (stats.Float64Data , int ) {
345
347
r .mu .Lock ()
346
348
defer r .mu .Unlock ()
@@ -351,31 +353,24 @@ func (r *RemoteClockMonitor) VerifyClockOffset(ctx context.Context) error {
351
353
delete (r .mu .offsets , id )
352
354
continue
353
355
}
354
- offs = append (offs , float64 (offset .Offset + offset .Uncertainty ))
355
- offs = append (offs , float64 (offset .Offset - offset .Uncertainty ))
356
+ off1 := float64 (offset .Offset + offset .Uncertainty )
357
+ off2 := float64 (offset .Offset - offset .Uncertainty )
358
+ sum += off1 + off2
359
+ offs = append (offs , off1 , off2 )
356
360
if offset .isHealthy (ctx , r .toleratedOffset ) {
357
361
healthyOffsetCount ++
358
362
}
359
363
}
360
364
return offs , len (r .mu .offsets )
361
365
}()
362
366
363
- mean , err := offsets .Mean ()
364
- if err != nil && ! errors .Is (err , stats .EmptyInput ) {
365
- return err
366
- }
367
- stdDev , err := offsets .StandardDeviation ()
368
- if err != nil && ! errors .Is (err , stats .EmptyInput ) {
369
- return err
370
- }
371
- median , err := offsets .Median ()
372
- if err != nil && ! errors .Is (err , stats .EmptyInput ) {
373
- return err
374
- }
375
- medianAbsoluteDeviation , err := offsets .MedianAbsoluteDeviation ()
376
- if err != nil && ! errors .Is (err , stats .EmptyInput ) {
377
- return err
378
- }
367
+ sort .Float64s (offsets )
368
+
369
+ mean := sum / float64 (len (offsets ))
370
+ stdDev := StandardDeviationPopulationKnownMean (offsets , mean )
371
+ median := MedianSortedInput (offsets )
372
+ medianAbsoluteDeviation := MedianAbsoluteDeviationPopulationSortedInput (offsets )
373
+
379
374
r .metrics .ClockOffsetMeanNanos .Update (int64 (mean ))
380
375
r .metrics .ClockOffsetStdDevNanos .Update (int64 (stdDev ))
381
376
r .metrics .ClockOffsetMedianNanos .Update (int64 (median ))
@@ -458,3 +453,99 @@ func updateClockOffsetTracking(
458
453
remoteClocks .UpdateOffset (ctx , nodeID , offset , pingDuration )
459
454
return pingDuration , offset , remoteClocks .VerifyClockOffset (ctx )
460
455
}
456
+
457
+ // The following statistics functions are re-implementations of similar
458
+ // functions provided by github.com/montanaflynn/stats. Those original functions
459
+ // were originally offered under:
460
+ //
461
+ // The MIT License (MIT)
462
+ //
463
+ // Copyright (c) 2014-2023 Montana Flynn (https://montanaflynn.com)
464
+ //
465
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
466
+ // of this software and associated documentation files (the "Software"), to deal
467
+ // in the Software without restriction, including without limitation the rights
468
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
469
+ // copies of the Software, and to permit persons to whom the Software is
470
+ // furnished to do so, subject to the following conditions:
471
+ //
472
+ // The above copyright notice and this permission notice shall be included in all
473
+ // copies or substantial portions of the Software.
474
+ //
475
+
476
+ // StandardDeviationPopulationKnownMean calculates the standard deviation
477
+ // assuming the input is the population and that the given mean is the mean of
478
+ // the input.
479
+ func StandardDeviationPopulationKnownMean (input stats.Float64Data , mean float64 ) float64 {
480
+ if input .Len () == 0 {
481
+ return math .NaN ()
482
+ }
483
+ return math .Sqrt (PopulationVarianceKnownMean (input , mean ))
484
+ }
485
+
486
+ // PopulationVarianceKnownMean calculates the variance assuming the input is the
487
+ // population and that the given mean is the mean of the input.
488
+ func PopulationVarianceKnownMean (input stats.Float64Data , mean float64 ) float64 {
489
+ if input .Len () == 0 {
490
+ return math .NaN ()
491
+ }
492
+ variance := float64 (0 )
493
+ for _ , n := range input {
494
+ diff := n - mean
495
+ variance += diff * diff
496
+ }
497
+ return variance / float64 (input .Len ())
498
+ }
499
+
500
+ // MedianSortedInput calculates the median of the input, assuming it is already
501
+ // sorted.
502
+ func MedianSortedInput (sortedInput stats.Float64Data ) float64 {
503
+ if buildutil .CrdbTestBuild {
504
+ if ! sort .IsSorted (sortedInput ) {
505
+ panic ("MedianSortedInput expects sorted input" )
506
+ }
507
+ }
508
+
509
+ l := len (sortedInput )
510
+ if l == 0 {
511
+ return math .NaN ()
512
+ } else if l % 2 == 0 {
513
+ return (sortedInput [(l / 2 )- 1 ] + sortedInput [(l / 2 )]) / 2.0
514
+ } else {
515
+ return sortedInput [l / 2 ]
516
+ }
517
+ }
518
+
519
+ // MedianAbsoluteDeviationPopulationSortedInput calculates the median absolute
520
+ // deviation from a pre-sorted population.
521
+ func MedianAbsoluteDeviationPopulationSortedInput (sortedInput stats.Float64Data ) float64 {
522
+ switch sortedInput .Len () {
523
+ case 0 :
524
+ return math .NaN ()
525
+ case 1 :
526
+ return 0
527
+ }
528
+
529
+ m := MedianSortedInput (sortedInput )
530
+ a := sortedInput
531
+
532
+ // Peal off the largest difference on either end until we reach the midpoint(s).
533
+ last := 0.0
534
+ for len (a ) > (len (sortedInput ) / 2 ) {
535
+ leftDiff := m - a [0 ]
536
+ rightDiff := a [len (a )- 1 ] - m
537
+ if leftDiff >= rightDiff {
538
+ last = leftDiff
539
+ a = a [1 :]
540
+ } else {
541
+ last = rightDiff
542
+ a = a [:len (a )- 1 ]
543
+ }
544
+ }
545
+
546
+ if len (sortedInput )% 2 == 1 {
547
+ return last
548
+ } else {
549
+ return (max (m - a [0 ], a [len (a )- 1 ]- m ) + last ) * 0.5
550
+ }
551
+ }
0 commit comments