github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/rpc/clock_offset_test.go (about)

     1  // Copyright 2014 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package rpc
    12  
    13  import (
    14  	"context"
    15  	"fmt"
    16  	"math"
    17  	"strconv"
    18  	"testing"
    19  	"time"
    20  
    21  	"github.com/cockroachdb/cockroach/pkg/testutils"
    22  	"github.com/cockroachdb/cockroach/pkg/util/hlc"
    23  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    24  	"github.com/cockroachdb/cockroach/pkg/util/stop"
    25  )
    26  
    27  const errOffsetGreaterThanMaxOffset = "clock synchronization error: this node is more than .+ away from at least half of the known nodes"
    28  
    29  // TestUpdateOffset tests the three cases that UpdateOffset should or should
    30  // not update the offset for an addr.
    31  func TestUpdateOffset(t *testing.T) {
    32  	defer leaktest.AfterTest(t)()
    33  
    34  	clock := hlc.NewClock(hlc.NewManualClock(123).UnixNano, time.Nanosecond)
    35  	monitor := newRemoteClockMonitor(clock, time.Hour, 0)
    36  
    37  	const key = "addr"
    38  	const latency = 10 * time.Millisecond
    39  
    40  	// Case 1: There is no prior offset for the address.
    41  	offset1 := RemoteOffset{
    42  		Offset:      0,
    43  		Uncertainty: 20,
    44  		MeasuredAt:  monitor.clock.PhysicalTime().Add(-(monitor.offsetTTL + 1)).UnixNano(),
    45  	}
    46  	monitor.UpdateOffset(context.Background(), key, offset1, latency)
    47  	monitor.mu.Lock()
    48  	if o, ok := monitor.mu.offsets[key]; !ok {
    49  		t.Errorf("expected key %s to be set in %v, but it was not", key, monitor.mu.offsets)
    50  	} else if o != offset1 {
    51  		t.Errorf("expected offset %v, instead %v", offset1, o)
    52  	}
    53  	monitor.mu.Unlock()
    54  
    55  	// Case 2: The old offset for addr is stale.
    56  	offset2 := RemoteOffset{
    57  		Offset:      0,
    58  		Uncertainty: 20,
    59  		MeasuredAt:  monitor.clock.PhysicalTime().Add(-(monitor.offsetTTL + 1)).UnixNano(),
    60  	}
    61  	monitor.UpdateOffset(context.Background(), key, offset2, latency)
    62  	monitor.mu.Lock()
    63  	if o, ok := monitor.mu.offsets[key]; !ok {
    64  		t.Errorf("expected key %s to be set in %v, but it was not", key, monitor.mu.offsets)
    65  	} else if o != offset2 {
    66  		t.Errorf("expected offset %v, instead %v", offset2, o)
    67  	}
    68  	monitor.mu.Unlock()
    69  
    70  	// Case 3: The new offset's error is smaller.
    71  	offset3 := RemoteOffset{
    72  		Offset:      0,
    73  		Uncertainty: 10,
    74  		MeasuredAt:  offset2.MeasuredAt + 1,
    75  	}
    76  	monitor.UpdateOffset(context.Background(), key, offset3, latency)
    77  	monitor.mu.Lock()
    78  	if o, ok := monitor.mu.offsets[key]; !ok {
    79  		t.Errorf("expected key %s to be set in %v, but it was not", key, monitor.mu.offsets)
    80  	} else if o != offset3 {
    81  		t.Errorf("expected offset %v, instead %v", offset3, o)
    82  	}
    83  	monitor.mu.Unlock()
    84  
    85  	// Larger error and offset3 is not stale, so no update.
    86  	monitor.UpdateOffset(context.Background(), key, offset2, latency)
    87  	monitor.mu.Lock()
    88  	if o, ok := monitor.mu.offsets[key]; !ok {
    89  		t.Errorf("expected key %s to be set in %v, but it was not", key, monitor.mu.offsets)
    90  	} else if o != offset3 {
    91  		t.Errorf("expected offset %v, instead %v", offset3, o)
    92  	}
    93  	monitor.mu.Unlock()
    94  }
    95  
    96  func TestVerifyClockOffset(t *testing.T) {
    97  	defer leaktest.AfterTest(t)()
    98  
    99  	clock := hlc.NewClock(hlc.NewManualClock(123).UnixNano, 50*time.Nanosecond)
   100  	monitor := newRemoteClockMonitor(clock, time.Hour, 0)
   101  
   102  	for idx, tc := range []struct {
   103  		offsets       []RemoteOffset
   104  		expectedError bool
   105  	}{
   106  		// no error if no offsets.
   107  		{[]RemoteOffset{}, false},
   108  		// no error when a majority of offsets are under the maximum tolerated offset.
   109  		{[]RemoteOffset{{Offset: 20, Uncertainty: 10}, {Offset: 48, Uncertainty: 20}, {Offset: 61, Uncertainty: 25}, {Offset: 91, Uncertainty: 31}}, false},
   110  		// error when less than a majority of offsets are under the maximum tolerated offset.
   111  		{[]RemoteOffset{{Offset: 20, Uncertainty: 10}, {Offset: 58, Uncertainty: 20}, {Offset: 85, Uncertainty: 25}, {Offset: 91, Uncertainty: 31}}, true},
   112  	} {
   113  		monitor.mu.offsets = make(map[string]RemoteOffset)
   114  		for i, offset := range tc.offsets {
   115  			monitor.mu.offsets[strconv.Itoa(i)] = offset
   116  		}
   117  
   118  		if tc.expectedError {
   119  			if err := monitor.VerifyClockOffset(context.Background()); !testutils.IsError(err, errOffsetGreaterThanMaxOffset) {
   120  				t.Errorf("%d: unexpected error %v", idx, err)
   121  			}
   122  		} else {
   123  			if err := monitor.VerifyClockOffset(context.Background()); err != nil {
   124  				t.Errorf("%d: unexpected error %s", idx, err)
   125  			}
   126  		}
   127  	}
   128  }
   129  
   130  // TestIsHealthyOffsetInterval tests if we correctly determine if
   131  // a clusterOffsetInterval is healthy or not i.e. if it indicates that the
   132  // local clock has too great an offset or not.
   133  func TestIsHealthyOffsetInterval(t *testing.T) {
   134  	defer leaktest.AfterTest(t)()
   135  	maxOffset := 10 * time.Nanosecond
   136  
   137  	for i, tc := range []struct {
   138  		offset          RemoteOffset
   139  		expectedHealthy bool
   140  	}{
   141  		{RemoteOffset{}, true},
   142  		{RemoteOffset{Offset: 0, Uncertainty: 5}, true},
   143  		{RemoteOffset{Offset: -15, Uncertainty: 4}, false},
   144  		{RemoteOffset{Offset: 15, Uncertainty: 4}, false},
   145  		{RemoteOffset{Offset: math.MaxInt64, Uncertainty: 0}, false},
   146  	} {
   147  		if isHealthy := tc.offset.isHealthy(context.Background(), maxOffset); tc.expectedHealthy {
   148  			if !isHealthy {
   149  				t.Errorf("%d: expected remote offset %s for maximum offset %s to be healthy", i, tc.offset, maxOffset)
   150  			}
   151  		} else {
   152  			if isHealthy {
   153  				t.Errorf("%d: expected remote offset %s for maximum offset %s to be unhealthy", i, tc.offset, maxOffset)
   154  			}
   155  		}
   156  	}
   157  }
   158  
   159  func TestClockOffsetMetrics(t *testing.T) {
   160  	defer leaktest.AfterTest(t)()
   161  	stopper := stop.NewStopper()
   162  	defer stopper.Stop(context.Background())
   163  
   164  	clock := hlc.NewClock(hlc.NewManualClock(123).UnixNano, 20*time.Nanosecond)
   165  	monitor := newRemoteClockMonitor(clock, time.Hour, 0)
   166  	monitor.mu.offsets = map[string]RemoteOffset{
   167  		"0": {
   168  			Offset:      13,
   169  			Uncertainty: 7,
   170  			MeasuredAt:  6,
   171  		},
   172  	}
   173  
   174  	if err := monitor.VerifyClockOffset(context.Background()); err != nil {
   175  		t.Fatal(err)
   176  	}
   177  
   178  	if a, e := monitor.Metrics().ClockOffsetMeanNanos.Value(), int64(13); a != e {
   179  		t.Errorf("mean %d != expected %d", a, e)
   180  	}
   181  	if a, e := monitor.Metrics().ClockOffsetStdDevNanos.Value(), int64(7); a != e {
   182  		t.Errorf("stdDev %d != expected %d", a, e)
   183  	}
   184  }
   185  
   186  // TestLatencies tests the tracking of round-trip latency between nodes.
   187  func TestLatencies(t *testing.T) {
   188  	defer leaktest.AfterTest(t)()
   189  
   190  	clock := hlc.NewClock(hlc.NewManualClock(123).UnixNano, time.Nanosecond)
   191  	monitor := newRemoteClockMonitor(clock, time.Hour, 0)
   192  
   193  	// All test cases have to have at least 11 measurement values in order for
   194  	// the exponentially-weighted moving average to work properly. See the
   195  	// comment on the WARMUP_SAMPLES const in the ewma package for details.
   196  	const emptyKey = "no measurements"
   197  	for i := 0; i < 11; i++ {
   198  		monitor.UpdateOffset(context.Background(), emptyKey, RemoteOffset{}, 0)
   199  	}
   200  	if l, ok := monitor.mu.latenciesNanos[emptyKey]; ok {
   201  		t.Errorf("expected no latency measurement for %q, got %v", emptyKey, l.Value())
   202  	}
   203  
   204  	testCases := []struct {
   205  		measurements []time.Duration
   206  		expectedAvg  time.Duration
   207  	}{
   208  		{[]time.Duration{10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, 10},
   209  		{[]time.Duration{10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0}, 10},
   210  		{[]time.Duration{0, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, 10},
   211  		{[]time.Duration{10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99}, 18},
   212  		{[]time.Duration{99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 10}, 90},
   213  		{[]time.Duration{10, 10, 10, 10, 10, 10, 99, 99, 99, 99, 99}, 50},
   214  		{[]time.Duration{99, 99, 99, 99, 99, 99, 10, 10, 10, 10, 10}, 58},
   215  		{[]time.Duration{10, 10, 10, 10, 10, 99, 99, 99, 99, 99, 99}, 58},
   216  		{[]time.Duration{99, 99, 99, 99, 99, 10, 10, 10, 10, 10, 10}, 50},
   217  	}
   218  	for _, tc := range testCases {
   219  		key := fmt.Sprintf("%v", tc.measurements)
   220  		for _, measurement := range tc.measurements {
   221  			monitor.UpdateOffset(context.Background(), key, RemoteOffset{}, measurement)
   222  		}
   223  		if val, ok := monitor.Latency(key); !ok || val != tc.expectedAvg {
   224  			t.Errorf("%q: expected latency %d, got %d", key, tc.expectedAvg, val)
   225  		}
   226  	}
   227  }