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  }