github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/cmd/roachtest/clock_util.go (about) 1 // Copyright 2018 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 main 12 13 import ( 14 "context" 15 gosql "database/sql" 16 "fmt" 17 "time" 18 ) 19 20 // isAlive returns whether the node queried by db is alive. 21 func isAlive(db *gosql.DB, l *logger) bool { 22 _, err := db.Exec("SHOW DATABASES") 23 if err != nil { 24 l.Printf("isAlive returned err=%v\n", err) 25 } 26 return err == nil 27 } 28 29 // dbUnixEpoch returns the current time in db. 30 func dbUnixEpoch(db *gosql.DB) (float64, error) { 31 var epoch float64 32 if err := db.QueryRow("SELECT now()::DECIMAL").Scan(&epoch); err != nil { 33 return 0, err 34 } 35 return epoch, nil 36 } 37 38 // offsetInjector is used to inject clock offsets in roachtests. 39 type offsetInjector struct { 40 c *cluster 41 deployed bool 42 } 43 44 // deploy installs ntp and downloads / compiles bumptime used to create a clock offset. 45 func (oi *offsetInjector) deploy(ctx context.Context) error { 46 if err := oi.c.RunE(ctx, oi.c.All(), "test -x ./bumptime"); err == nil { 47 oi.deployed = true 48 return nil 49 } 50 51 if err := oi.c.Install(ctx, oi.c.l, oi.c.All(), "ntp"); err != nil { 52 return err 53 } 54 if err := oi.c.Install(ctx, oi.c.l, oi.c.All(), "gcc"); err != nil { 55 return err 56 } 57 if err := oi.c.RunL(ctx, oi.c.l, oi.c.All(), "sudo", "service", "ntp", "stop"); err != nil { 58 return err 59 } 60 if err := oi.c.RunL(ctx, oi.c.l, 61 oi.c.All(), 62 "curl", 63 "-kO", 64 "https://raw.githubusercontent.com/cockroachdb/jepsen/master/cockroachdb/resources/bumptime.c", 65 ); err != nil { 66 return err 67 } 68 if err := oi.c.RunL(ctx, oi.c.l, 69 oi.c.All(), "gcc", "bumptime.c", "-o", "bumptime", "&&", "rm bumptime.c", 70 ); err != nil { 71 return err 72 } 73 oi.deployed = true 74 return nil 75 } 76 77 // offset injects a offset of s into the node with the given nodeID. 78 func (oi *offsetInjector) offset(ctx context.Context, nodeID int, s time.Duration) { 79 if !oi.deployed { 80 oi.c.t.Fatal("Offset injector must be deployed before injecting a clock offset") 81 } 82 83 oi.c.Run( 84 ctx, 85 oi.c.Node(nodeID), 86 fmt.Sprintf("sudo ./bumptime %f", float64(s)/float64(time.Millisecond)), 87 ) 88 } 89 90 // recover force syncs time on the node with the given nodeID to recover 91 // from any offsets. 92 func (oi *offsetInjector) recover(ctx context.Context, nodeID int) { 93 if !oi.deployed { 94 oi.c.t.Fatal("Offset injector must be deployed before recovering from clock offsets") 95 } 96 97 syncCmds := [][]string{ 98 {"sudo", "service", "ntp", "stop"}, 99 {"sudo", "ntpdate", "-u", "time.google.com"}, 100 {"sudo", "service", "ntp", "start"}, 101 } 102 for _, cmd := range syncCmds { 103 oi.c.Run( 104 ctx, 105 oi.c.Node(nodeID), 106 cmd..., 107 ) 108 } 109 } 110 111 // newOffsetInjector creates a offsetInjector which can be used to inject 112 // and recover from clock offsets. 113 func newOffsetInjector(c *cluster) *offsetInjector { 114 return &offsetInjector{c: c} 115 }