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 }