github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/tests/monotonic_insert_test.go (about) 1 // Copyright 2016 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 tests_test 12 13 import ( 14 "bytes" 15 "context" 16 gosql "database/sql" 17 "fmt" 18 "math/rand" 19 "sort" 20 "sync/atomic" 21 "testing" 22 "time" 23 24 "github.com/cockroachdb/cockroach-go/crdb" 25 "github.com/cockroachdb/cockroach/pkg/base" 26 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/concurrency" 27 "github.com/cockroachdb/cockroach/pkg/sql" 28 "github.com/cockroachdb/cockroach/pkg/sql/sessiondata" 29 "github.com/cockroachdb/cockroach/pkg/testutils/testcluster" 30 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 31 "github.com/cockroachdb/cockroach/pkg/util/log" 32 ) 33 34 type mtRow struct { 35 val int64 36 sts string 37 node, tb int64 38 } 39 40 type mtRows []mtRow 41 42 func (r mtRows) Len() int { 43 return len(r) 44 } 45 46 func (r mtRows) Less(i, j int) bool { 47 if r[i].val == r[j].val { 48 return r[i].sts < r[j].sts 49 } 50 return r[i].val < r[j].val 51 } 52 53 func (r mtRows) Swap(i, j int) { 54 r[i], r[j] = r[j], r[i] 55 } 56 57 func (r mtRows) String() string { 58 var buf bytes.Buffer 59 for i, row := range r { 60 prefix := "ok" 61 if i > 0 && r.Less(i, i-1) { 62 prefix = "!!" 63 } 64 fmt.Fprintf(&buf, "%s %+v\n", prefix, row) 65 } 66 return buf.String() 67 } 68 69 type mtClient struct { 70 *gosql.DB 71 ID int 72 } 73 74 // TestMonotonicInserts replicates the 'monotonic' test from the Jepsen 75 // CockroachDB test suite: 76 // https://github.com/jepsen-io/jepsen/blob/master/cockroachdb/src/jepsen/cockroach/monotonic.clj 77 func TestMonotonicInserts(t *testing.T) { 78 defer leaktest.AfterTest(t)() 79 s := log.Scope(t) 80 defer s.Close(t) 81 82 for _, distSQLMode := range []sessiondata.DistSQLExecMode{ 83 sessiondata.DistSQLOff, sessiondata.DistSQLOn, 84 } { 85 t.Run(fmt.Sprintf("distsql=%s", distSQLMode), func(t *testing.T) { 86 testMonotonicInserts(t, distSQLMode) 87 }) 88 } 89 } 90 91 func testMonotonicInserts(t *testing.T, distSQLMode sessiondata.DistSQLExecMode) { 92 defer leaktest.AfterTest(t)() 93 94 if testing.Short() { 95 t.Skip("short flag") 96 } 97 ctx := context.Background() 98 tc := testcluster.StartTestCluster( 99 t, 3, 100 base.TestClusterArgs{ 101 ReplicationMode: base.ReplicationAuto, 102 ServerArgs: base.TestServerArgs{}, 103 }, 104 ) 105 defer tc.Stopper().Stop(ctx) 106 107 for _, server := range tc.Servers { 108 st := server.ClusterSettings() 109 st.Manual.Store(true) 110 sql.DistSQLClusterExecMode.Override(&st.SV, int64(distSQLMode)) 111 // Let transactions push immediately to detect deadlocks. The test creates a 112 // large amount of contention and dependency cycles, and could take a long 113 // time to complete without this. 114 concurrency.LockTableDeadlockDetectionPushDelay.Override(&st.SV, 0) 115 } 116 117 var clients []mtClient 118 for i := range tc.Conns { 119 clients = append(clients, mtClient{ID: i, DB: tc.Conns[i]}) 120 } 121 // We will insert into this table by selecting MAX(val) and increasing by 122 // one and expect that val and sts (the commit timestamp) are both 123 // simultaneously increasing. 124 if _, err := clients[0].Exec(` 125 CREATE DATABASE mono; 126 CREATE TABLE IF NOT EXISTS mono.mono (val INT, sts STRING, node INT, tb INT); 127 INSERT INTO mono.mono VALUES(-1, '0', -1, -1)`); err != nil { 128 t.Fatal(err) 129 } 130 131 var idGen uint64 132 133 invoke := func(client mtClient) { 134 logPrefix := fmt.Sprintf("%03d.%03d: ", atomic.AddUint64(&idGen, 1), client.ID) 135 l := func(msg string, args ...interface{}) { 136 log.Infof(ctx, logPrefix+msg /* nolint:fmtsafe */, args...) 137 } 138 l("begin") 139 defer l("done") 140 141 var exRow, insRow mtRow 142 var attempt int 143 if err := crdb.ExecuteTx(ctx, client.DB, nil, func(tx *gosql.Tx) error { 144 attempt++ 145 l("attempt %d", attempt) 146 if err := tx.QueryRow(`SELECT cluster_logical_timestamp()`).Scan( 147 &insRow.sts, 148 ); err != nil { 149 l(err.Error()) 150 return err 151 } 152 153 l("read max val") 154 if err := tx.QueryRow(`SELECT max(val) AS m FROM mono.mono`).Scan( 155 &exRow.val, 156 ); err != nil { 157 l(err.Error()) 158 return err 159 } 160 161 l("read max row for val=%d", exRow.val) 162 if err := tx.QueryRow(`SELECT sts, node, tb FROM mono.mono WHERE val = $1`, 163 exRow.val, 164 ).Scan( 165 &exRow.sts, &exRow.node, &exRow.tb, 166 ); err != nil { 167 l(err.Error()) 168 return err 169 } 170 171 l("insert") 172 if err := tx.QueryRow(` 173 INSERT INTO mono.mono (val, sts, node, tb) VALUES($1, $2, $3, $4) 174 RETURNING val, sts, node, tb`, 175 exRow.val+1, insRow.sts, client.ID, 0, 176 ).Scan( 177 &insRow.val, &insRow.sts, &insRow.node, &insRow.tb, 178 ); err != nil { 179 l(err.Error()) 180 return err 181 } 182 l("commit") 183 return nil 184 }); err != nil { 185 t.Errorf("%T: %v", err, err) 186 } 187 } 188 189 verify := func() { 190 client := clients[0] 191 var numDistinct int 192 if err := client.QueryRow("SELECT count(DISTINCT(val)) FROM mono.mono").Scan( 193 &numDistinct, 194 ); err != nil { 195 t.Fatal(err) 196 } 197 rows, err := client.Query("SELECT val, sts, node, tb FROM mono.mono ORDER BY val ASC, sts ASC") 198 if err != nil { 199 t.Fatal(err) 200 } 201 var results mtRows 202 for rows.Next() { 203 var row mtRow 204 if err := rows.Scan(&row.val, &row.sts, &row.node, &row.tb); err != nil { 205 t.Fatal(err) 206 } 207 results = append(results, row) 208 } 209 210 if !sort.IsSorted(results) { 211 t.Errorf("results are not sorted:\n%s", results) 212 } 213 214 if numDistinct != len(results) { 215 t.Errorf("'val' column is not unique: %d results, but %d distinct:\n%s", 216 len(results), numDistinct, results) 217 } 218 } 219 220 sem := make(chan struct{}, 2*len(tc.Conns)) 221 timer := time.After(5 * time.Second) 222 223 defer verify() 224 defer func() { 225 // Now that consuming has stopped, fill up the semaphore (i.e. wait for 226 // still-running goroutines to stop) 227 for i := 0; i < cap(sem); i++ { 228 sem <- struct{}{} 229 } 230 }() 231 232 for { 233 select { 234 case sem <- struct{}{}: 235 case <-tc.Stopper().ShouldStop(): 236 return 237 case <-timer: 238 return 239 } 240 go func(client mtClient) { 241 invoke(client) 242 <-sem 243 }(clients[rand.Intn(len(clients))]) 244 } 245 }