github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ccl/changefeedccl/cdctest/validator_test.go (about) 1 // Copyright 2018 The Cockroach Authors. 2 // 3 // Licensed as a CockroachDB Enterprise file under the Cockroach Community 4 // License (the "License"); you may not use this file except in compliance with 5 // the License. You may obtain a copy of the License at 6 // 7 // https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt 8 9 package cdctest 10 11 import ( 12 "context" 13 "fmt" 14 "reflect" 15 "testing" 16 17 "github.com/cockroachdb/cockroach/pkg/base" 18 "github.com/cockroachdb/cockroach/pkg/sql" 19 "github.com/cockroachdb/cockroach/pkg/testutils" 20 "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" 21 "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" 22 "github.com/cockroachdb/cockroach/pkg/util/hlc" 23 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 24 "github.com/stretchr/testify/require" 25 ) 26 27 func ts(i int64) hlc.Timestamp { 28 return hlc.Timestamp{WallTime: i} 29 } 30 31 func noteRow(t *testing.T, v Validator, partition, key, value string, updated hlc.Timestamp) { 32 t.Helper() 33 if err := v.NoteRow(partition, key, value, updated); err != nil { 34 t.Fatal(err) 35 } 36 } 37 38 func noteResolved(t *testing.T, v Validator, partition string, resolved hlc.Timestamp) { 39 t.Helper() 40 if err := v.NoteResolved(partition, resolved); err != nil { 41 t.Fatal(err) 42 } 43 } 44 45 func assertValidatorFailures(t *testing.T, v Validator, expected ...string) { 46 t.Helper() 47 if f := v.Failures(); !reflect.DeepEqual(f, expected) { 48 t.Errorf(`got %v expected %v`, f, expected) 49 } 50 } 51 52 func TestOrderValidator(t *testing.T) { 53 defer leaktest.AfterTest(t)() 54 const ignored = `ignored` 55 56 t.Run(`empty`, func(t *testing.T) { 57 v := NewOrderValidator(`t1`) 58 if f := v.Failures(); f != nil { 59 t.Fatalf("got %v expected %v", f, nil) 60 } 61 }) 62 t.Run(`dupe okay`, func(t *testing.T) { 63 v := NewOrderValidator(`t1`) 64 noteRow(t, v, `p1`, `k1`, ignored, ts(1)) 65 noteRow(t, v, `p1`, `k1`, ignored, ts(2)) 66 noteRow(t, v, `p1`, `k1`, ignored, ts(1)) 67 assertValidatorFailures(t, v) 68 }) 69 t.Run(`key on two partitions`, func(t *testing.T) { 70 v := NewOrderValidator(`t1`) 71 noteRow(t, v, `p1`, `k1`, ignored, ts(2)) 72 noteRow(t, v, `p2`, `k1`, ignored, ts(1)) 73 assertValidatorFailures(t, v, 74 `key [k1] received on two partitions: p1 and p2`, 75 ) 76 }) 77 t.Run(`new key with lower timestamp`, func(t *testing.T) { 78 v := NewOrderValidator(`t1`) 79 noteRow(t, v, `p1`, `k1`, ignored, ts(2)) 80 noteRow(t, v, `p1`, `k1`, ignored, ts(1)) 81 assertValidatorFailures(t, v, 82 `topic t1 partition p1: saw new row timestamp 1.0000000000 after 2.0000000000 was seen`, 83 ) 84 }) 85 t.Run(`new key after resolved`, func(t *testing.T) { 86 v := NewOrderValidator(`t1`) 87 noteResolved(t, v, `p2`, ts(3)) 88 // Okay because p2 saw the resolved timestamp but p1 didn't. 89 noteRow(t, v, `p1`, `k1`, ignored, ts(1)) 90 noteResolved(t, v, `p1`, ts(3)) 91 // This one is not okay. 92 noteRow(t, v, `p1`, `k1`, ignored, ts(2)) 93 // Still okay because we've seen it before. 94 noteRow(t, v, `p1`, `k1`, ignored, ts(1)) 95 assertValidatorFailures(t, v, 96 `topic t1 partition p1`+ 97 `: saw new row timestamp 2.0000000000 after 3.0000000000 was resolved`, 98 ) 99 }) 100 } 101 102 func TestBeforeAfterValidator(t *testing.T) { 103 defer leaktest.AfterTest(t)() 104 105 ctx := context.Background() 106 s, sqlDBRaw, _ := serverutils.StartServer(t, base.TestServerArgs{UseDatabase: "d"}) 107 defer s.Stopper().Stop(ctx) 108 sqlDB := sqlutils.MakeSQLRunner(sqlDBRaw) 109 sqlDB.Exec(t, `CREATE DATABASE d`) 110 sqlDB.Exec(t, `CREATE TABLE foo (k INT PRIMARY KEY, v INT)`) 111 112 tsRaw := make([]string, 6) 113 sqlDB.QueryRow(t, `SELECT cluster_logical_timestamp()`).Scan(&tsRaw[0]) 114 sqlDB.QueryRow(t, 115 `UPSERT INTO foo VALUES (1, 1) RETURNING cluster_logical_timestamp()`, 116 ).Scan(&tsRaw[1]) 117 sqlDB.QueryRow(t, 118 `UPSERT INTO foo VALUES (1, 2), (2, 2) RETURNING cluster_logical_timestamp()`, 119 ).Scan(&tsRaw[2]) 120 sqlDB.QueryRow(t, 121 `UPSERT INTO foo VALUES (1, 3) RETURNING cluster_logical_timestamp()`, 122 ).Scan(&tsRaw[3]) 123 sqlDB.QueryRow(t, 124 `DELETE FROM foo WHERE k = 1 RETURNING cluster_logical_timestamp()`, 125 ).Scan(&tsRaw[4]) 126 sqlDB.QueryRow(t, `SELECT cluster_logical_timestamp()`).Scan(&tsRaw[5]) 127 ts := make([]hlc.Timestamp, len(tsRaw)) 128 for i := range tsRaw { 129 var err error 130 ts[i], err = sql.ParseHLC(tsRaw[i]) 131 if err != nil { 132 t.Fatal(err) 133 } 134 } 135 136 t.Run(`empty`, func(t *testing.T) { 137 v, err := NewBeforeAfterValidator(sqlDBRaw, `foo`) 138 require.NoError(t, err) 139 assertValidatorFailures(t, v) 140 }) 141 t.Run(`during initial`, func(t *testing.T) { 142 v, err := NewBeforeAfterValidator(sqlDBRaw, `foo`) 143 require.NoError(t, err) 144 // "before" is ignored if missing. 145 noteRow(t, v, `p`, `[1]`, `{"after": {"k":1,"v":1}}`, ts[1]) 146 noteRow(t, v, `p`, `[1]`, `{"after": {"k":1,"v":2}}`, ts[2]) 147 // However, if provided, it is validated. 148 noteRow(t, v, `p`, `[1]`, `{"after": {"k":1,"v":2}, "before": {"k":1,"v":1}}`, ts[2]) 149 assertValidatorFailures(t, v) 150 noteRow(t, v, `p`, `[1]`, `{"after": {"k":1,"v":3}, "before": {"k":1,"v":3}}`, ts[3]) 151 assertValidatorFailures(t, v, 152 `"before" field did not agree with row at `+ts[3].Prev().AsOfSystemTime()+ 153 `: SELECT count(*) = 1 FROM foo AS OF SYSTEM TIME '`+ts[3].Prev().AsOfSystemTime()+ 154 `' WHERE k = $1 AND v = $2 [1 3]`) 155 }) 156 t.Run(`missing before`, func(t *testing.T) { 157 v, err := NewBeforeAfterValidator(sqlDBRaw, `foo`) 158 require.NoError(t, err) 159 noteResolved(t, v, `p`, ts[0]) 160 // "before" should have been provided. 161 noteRow(t, v, `p`, `[1]`, `{"after": {"k":1,"v":2}}`, ts[2]) 162 assertValidatorFailures(t, v, 163 `"before" field did not agree with row at `+ts[2].Prev().AsOfSystemTime()+ 164 `: SELECT count(*) = 0 FROM foo AS OF SYSTEM TIME '`+ts[2].Prev().AsOfSystemTime()+ 165 `' WHERE k = $1 [1]`) 166 }) 167 t.Run(`incorrect before`, func(t *testing.T) { 168 v, err := NewBeforeAfterValidator(sqlDBRaw, `foo`) 169 require.NoError(t, err) 170 noteResolved(t, v, `p`, ts[0]) 171 // "before" provided with wrong value. 172 noteRow(t, v, `p`, `[1]`, `{"after": {"k":1,"v":3}, "before": {"k":5,"v":10}}`, ts[3]) 173 assertValidatorFailures(t, v, 174 `"before" field did not agree with row at `+ts[3].Prev().AsOfSystemTime()+ 175 `: SELECT count(*) = 1 FROM foo AS OF SYSTEM TIME '`+ts[3].Prev().AsOfSystemTime()+ 176 `' WHERE k = $1 AND v = $2 [5 10]`) 177 }) 178 t.Run(`unnecessary before`, func(t *testing.T) { 179 v, err := NewBeforeAfterValidator(sqlDBRaw, `foo`) 180 require.NoError(t, err) 181 noteResolved(t, v, `p`, ts[0]) 182 // "before" provided but should not have been. 183 noteRow(t, v, `p`, `[1]`, `{"after": {"k":1,"v":1}, "before": {"k":1,"v":1}}`, ts[1]) 184 assertValidatorFailures(t, v, 185 `"before" field did not agree with row at `+ts[1].Prev().AsOfSystemTime()+ 186 `: SELECT count(*) = 1 FROM foo AS OF SYSTEM TIME '`+ts[1].Prev().AsOfSystemTime()+ 187 `' WHERE k = $1 AND v = $2 [1 1]`) 188 }) 189 t.Run(`missing after`, func(t *testing.T) { 190 v, err := NewBeforeAfterValidator(sqlDBRaw, `foo`) 191 require.NoError(t, err) 192 noteResolved(t, v, `p`, ts[0]) 193 // "after" should have been provided. 194 noteRow(t, v, `p`, `[1]`, `{"before": {"k":1,"v":1}}`, ts[2]) 195 assertValidatorFailures(t, v, 196 `"after" field did not agree with row at `+ts[2].AsOfSystemTime()+ 197 `: SELECT count(*) = 0 FROM foo AS OF SYSTEM TIME '`+ts[2].AsOfSystemTime()+ 198 `' WHERE k = $1 [1]`) 199 }) 200 t.Run(`incorrect after`, func(t *testing.T) { 201 v, err := NewBeforeAfterValidator(sqlDBRaw, `foo`) 202 require.NoError(t, err) 203 noteResolved(t, v, `p`, ts[0]) 204 // "after" provided with wrong value. 205 noteRow(t, v, `p`, `[1]`, `{"after": {"k":1,"v":5}, "before": {"k":1,"v":2}}`, ts[3]) 206 assertValidatorFailures(t, v, 207 `"after" field did not agree with row at `+ts[3].AsOfSystemTime()+ 208 `: SELECT count(*) = 1 FROM foo AS OF SYSTEM TIME '`+ts[3].AsOfSystemTime()+ 209 `' WHERE k = $1 AND v = $2 [1 5]`) 210 }) 211 t.Run(`unnecessary after`, func(t *testing.T) { 212 v, err := NewBeforeAfterValidator(sqlDBRaw, `foo`) 213 require.NoError(t, err) 214 noteResolved(t, v, `p`, ts[0]) 215 // "after" provided but should not have been. 216 noteRow(t, v, `p`, `[1]`, `{"after": {"k":1,"v":3}, "before": {"k":1,"v":3}}`, ts[4]) 217 assertValidatorFailures(t, v, 218 `"after" field did not agree with row at `+ts[4].AsOfSystemTime()+ 219 `: SELECT count(*) = 1 FROM foo AS OF SYSTEM TIME '`+ts[4].AsOfSystemTime()+ 220 `' WHERE k = $1 AND v = $2 [1 3]`) 221 }) 222 t.Run(`incorrect before and after`, func(t *testing.T) { 223 v, err := NewBeforeAfterValidator(sqlDBRaw, `foo`) 224 require.NoError(t, err) 225 noteResolved(t, v, `p`, ts[0]) 226 // "before" and "after" both provided with wrong value. 227 noteRow(t, v, `p`, `[1]`, `{"after": {"k":1,"v":5}, "before": {"k":1,"v":4}}`, ts[3]) 228 assertValidatorFailures(t, v, 229 `"after" field did not agree with row at `+ts[3].AsOfSystemTime()+ 230 `: SELECT count(*) = 1 FROM foo AS OF SYSTEM TIME '`+ts[3].AsOfSystemTime()+ 231 `' WHERE k = $1 AND v = $2 [1 5]`, 232 `"before" field did not agree with row at `+ts[3].Prev().AsOfSystemTime()+ 233 `: SELECT count(*) = 1 FROM foo AS OF SYSTEM TIME '`+ts[3].Prev().AsOfSystemTime()+ 234 `' WHERE k = $1 AND v = $2 [1 4]`) 235 }) 236 t.Run(`correct`, func(t *testing.T) { 237 v, err := NewBeforeAfterValidator(sqlDBRaw, `foo`) 238 require.NoError(t, err) 239 noteResolved(t, v, `p`, ts[0]) 240 noteRow(t, v, `p`, `[1]`, `{}`, ts[0]) 241 noteRow(t, v, `p`, `[1]`, `{"after": {"k":1,"v":1}}`, ts[1]) 242 noteRow(t, v, `p`, `[1]`, `{"after": {"k":1,"v":1}, "before": null}`, ts[1]) 243 noteRow(t, v, `p`, `[1]`, `{"after": {"k":1,"v":2}, "before": {"k":1,"v":1}}`, ts[2]) 244 noteRow(t, v, `p`, `[1]`, `{"after": {"k":1,"v":3}, "before": {"k":1,"v":2}}`, ts[3]) 245 noteRow(t, v, `p`, `[1]`, `{ "before": {"k":1,"v":3}}`, ts[4]) 246 noteRow(t, v, `p`, `[1]`, `{"after": null, "before": {"k":1,"v":3}}`, ts[4]) 247 noteRow(t, v, `p`, `[2]`, `{}`, ts[1]) 248 noteRow(t, v, `p`, `[2]`, `{"after": {"k":2,"v":2}}`, ts[2]) 249 noteRow(t, v, `p`, `[2]`, `{"after": {"k":2,"v":2}, "before": null}`, ts[2]) 250 assertValidatorFailures(t, v) 251 }) 252 } 253 254 func TestFingerprintValidator(t *testing.T) { 255 defer leaktest.AfterTest(t)() 256 const ignored = `ignored` 257 258 ctx := context.Background() 259 s, sqlDBRaw, _ := serverutils.StartServer(t, base.TestServerArgs{UseDatabase: "d"}) 260 defer s.Stopper().Stop(ctx) 261 sqlDB := sqlutils.MakeSQLRunner(sqlDBRaw) 262 sqlDB.Exec(t, `CREATE DATABASE d`) 263 sqlDB.Exec(t, `CREATE TABLE foo (k INT PRIMARY KEY, v INT)`) 264 265 tsRaw := make([]string, 6) 266 sqlDB.QueryRow(t, `SELECT cluster_logical_timestamp()`).Scan(&tsRaw[0]) 267 sqlDB.QueryRow(t, 268 `UPSERT INTO foo VALUES (1, 1) RETURNING cluster_logical_timestamp()`, 269 ).Scan(&tsRaw[1]) 270 sqlDB.QueryRow(t, 271 `UPSERT INTO foo VALUES (1, 2), (2, 2) RETURNING cluster_logical_timestamp()`, 272 ).Scan(&tsRaw[2]) 273 sqlDB.QueryRow(t, 274 `UPSERT INTO foo VALUES (1, 3) RETURNING cluster_logical_timestamp()`, 275 ).Scan(&tsRaw[3]) 276 sqlDB.QueryRow(t, 277 `DELETE FROM foo WHERE k = 1 RETURNING cluster_logical_timestamp()`, 278 ).Scan(&tsRaw[4]) 279 sqlDB.QueryRow(t, `SELECT cluster_logical_timestamp()`).Scan(&tsRaw[5]) 280 ts := make([]hlc.Timestamp, len(tsRaw)) 281 for i := range tsRaw { 282 var err error 283 ts[i], err = sql.ParseHLC(tsRaw[i]) 284 if err != nil { 285 t.Fatal(err) 286 } 287 } 288 289 createTableStmt := func(tableName string) string { 290 return fmt.Sprintf(`CREATE TABLE %s (k INT PRIMARY KEY, v INT)`, tableName) 291 } 292 testColumns := 0 293 294 t.Run(`empty`, func(t *testing.T) { 295 sqlDB.Exec(t, createTableStmt(`empty`)) 296 v, err := NewFingerprintValidator(sqlDBRaw, `foo`, `empty`, []string{`p`}, testColumns) 297 require.NoError(t, err) 298 noteResolved(t, v, `p`, ts[0]) 299 assertValidatorFailures(t, v) 300 }) 301 t.Run(`wrong data`, func(t *testing.T) { 302 sqlDB.Exec(t, createTableStmt(`wrong_data`)) 303 v, err := NewFingerprintValidator(sqlDBRaw, `foo`, `wrong_data`, []string{`p`}, testColumns) 304 require.NoError(t, err) 305 noteRow(t, v, ignored, `[1]`, `{"after": {"k":1,"v":10}}`, ts[1]) 306 noteResolved(t, v, `p`, ts[1]) 307 assertValidatorFailures(t, v, 308 `fingerprints did not match at `+ts[1].AsOfSystemTime()+ 309 `: 590700560494856539 vs -2774220564100127343`, 310 ) 311 }) 312 t.Run(`all resolved`, func(t *testing.T) { 313 sqlDB.Exec(t, createTableStmt(`all_resolved`)) 314 v, err := NewFingerprintValidator(sqlDBRaw, `foo`, `all_resolved`, []string{`p`}, testColumns) 315 require.NoError(t, err) 316 if err := v.NoteResolved(`p`, ts[0]); err != nil { 317 t.Fatal(err) 318 } 319 noteRow(t, v, ignored, `[1]`, `{"after": {"k":1,"v":1}}`, ts[1]) 320 noteResolved(t, v, `p`, ts[1]) 321 noteRow(t, v, ignored, `[1]`, `{"after": {"k":1,"v":2}}`, ts[2]) 322 noteRow(t, v, ignored, `[2]`, `{"after": {"k":2,"v":2}}`, ts[2]) 323 noteResolved(t, v, `p`, ts[2]) 324 noteRow(t, v, ignored, `[1]`, `{"after": {"k":1,"v":3}}`, ts[3]) 325 noteResolved(t, v, `p`, ts[3]) 326 noteRow(t, v, ignored, `[1]`, `{"after": null}`, ts[4]) 327 noteResolved(t, v, `p`, ts[4]) 328 noteResolved(t, v, `p`, ts[5]) 329 assertValidatorFailures(t, v) 330 }) 331 t.Run(`rows unsorted`, func(t *testing.T) { 332 sqlDB.Exec(t, createTableStmt(`rows_unsorted`)) 333 v, err := NewFingerprintValidator(sqlDBRaw, `foo`, `rows_unsorted`, []string{`p`}, testColumns) 334 require.NoError(t, err) 335 noteRow(t, v, ignored, `[1]`, `{"after": {"k":1,"v":3}}`, ts[3]) 336 noteRow(t, v, ignored, `[1]`, `{"after": {"k":1,"v":2}}`, ts[2]) 337 noteRow(t, v, ignored, `[1]`, `{"after": {"k":1,"v":1}}`, ts[1]) 338 noteRow(t, v, ignored, `[1]`, `{"after": null}`, ts[4]) 339 noteRow(t, v, ignored, `[2]`, `{"after": {"k":2,"v":2}}`, ts[2]) 340 noteResolved(t, v, `p`, ts[5]) 341 assertValidatorFailures(t, v) 342 }) 343 t.Run(`missed initial`, func(t *testing.T) { 344 sqlDB.Exec(t, createTableStmt(`missed_initial`)) 345 v, err := NewFingerprintValidator(sqlDBRaw, `foo`, `missed_initial`, []string{`p`}, testColumns) 346 require.NoError(t, err) 347 noteResolved(t, v, `p`, ts[0]) 348 // Intentionally missing {"k":1,"v":1} at ts[1]. 349 // Insert a fake row since we don't fingerprint earlier than the first seen row. 350 noteRow(t, v, ignored, `[2]`, `{"after": {"k":2,"v":2}}`, ts[2].Prev()) 351 noteRow(t, v, ignored, `[1]`, `{"after": {"k":1,"v":2}}`, ts[2]) 352 noteRow(t, v, ignored, `[2]`, `{"after": {"k":2,"v":2}}`, ts[2]) 353 noteResolved(t, v, `p`, ts[2].Prev()) 354 assertValidatorFailures(t, v, 355 `fingerprints did not match at `+ts[2].Prev().AsOfSystemTime()+ 356 `: 590700560494856539 vs 590699460983228293`, 357 ) 358 }) 359 t.Run(`missed middle`, func(t *testing.T) { 360 sqlDB.Exec(t, createTableStmt(`missed_middle`)) 361 v, err := NewFingerprintValidator(sqlDBRaw, `foo`, `missed_middle`, []string{`p`}, testColumns) 362 require.NoError(t, err) 363 noteResolved(t, v, `p`, ts[0]) 364 noteRow(t, v, ignored, `[1]`, `{"after": {"k":1,"v":1}}`, ts[1]) 365 // Intentionally missing {"k":1,"v":2} at ts[2]. 366 noteRow(t, v, ignored, `[2]`, `{"after": {"k":2,"v":2}}`, ts[2]) 367 noteResolved(t, v, `p`, ts[2]) 368 noteRow(t, v, ignored, `[1]`, `{"after": {"k":1,"v":3}}`, ts[3]) 369 noteResolved(t, v, `p`, ts[3]) 370 assertValidatorFailures(t, v, 371 `fingerprints did not match at `+ts[2].AsOfSystemTime()+ 372 `: 1099511631581 vs 1099511631582`, 373 `fingerprints did not match at `+ts[3].Prev().AsOfSystemTime()+ 374 `: 1099511631581 vs 1099511631582`, 375 ) 376 }) 377 t.Run(`missed end`, func(t *testing.T) { 378 sqlDB.Exec(t, createTableStmt(`missed_end`)) 379 v, err := NewFingerprintValidator(sqlDBRaw, `foo`, `missed_end`, []string{`p`}, testColumns) 380 require.NoError(t, err) 381 noteResolved(t, v, `p`, ts[0]) 382 noteRow(t, v, ignored, `[1]`, `{"after": {"k":1,"v":1}}`, ts[1]) 383 noteRow(t, v, ignored, `[1]`, `{"after": {"k":1,"v":2}}`, ts[2]) 384 noteRow(t, v, ignored, `[2]`, `{"after": {"k":2,"v":2}}`, ts[2]) 385 // Intentionally missing {"k":1,"v":3} at ts[3]. 386 noteResolved(t, v, `p`, ts[3]) 387 assertValidatorFailures(t, v, 388 `fingerprints did not match at `+ts[3].AsOfSystemTime()+ 389 `: 1099511631580 vs 1099511631581`, 390 ) 391 }) 392 t.Run(`initial scan`, func(t *testing.T) { 393 sqlDB.Exec(t, createTableStmt(`initial_scan`)) 394 v, err := NewFingerprintValidator(sqlDBRaw, `foo`, `initial_scan`, []string{`p`}, testColumns) 395 require.NoError(t, err) 396 noteRow(t, v, ignored, `[1]`, `{"after": {"k":1,"v":3}}`, ts[3]) 397 noteRow(t, v, ignored, `[2]`, `{"after": {"k":2,"v":2}}`, ts[3]) 398 noteResolved(t, v, `p`, ts[3]) 399 assertValidatorFailures(t, v) 400 }) 401 t.Run(`unknown partition`, func(t *testing.T) { 402 sqlDB.Exec(t, createTableStmt(`unknown_partition`)) 403 v, err := NewFingerprintValidator(sqlDBRaw, `foo`, `unknown_partition`, []string{`p`}, testColumns) 404 require.NoError(t, err) 405 if err := v.NoteResolved(`nope`, ts[1]); !testutils.IsError(err, `unknown partition`) { 406 t.Fatalf(`expected "unknown partition" error got: %+v`, err) 407 } 408 }) 409 t.Run(`resolved unsorted`, func(t *testing.T) { 410 sqlDB.Exec(t, createTableStmt(`resolved_unsorted`)) 411 v, err := NewFingerprintValidator(sqlDBRaw, `foo`, `resolved_unsorted`, []string{`p`}, testColumns) 412 require.NoError(t, err) 413 noteRow(t, v, ignored, `[1]`, `{"after": {"k":1,"v":1}}`, ts[1]) 414 noteResolved(t, v, `p`, ts[1]) 415 noteResolved(t, v, `p`, ts[1]) 416 noteResolved(t, v, `p`, ts[0]) 417 assertValidatorFailures(t, v) 418 }) 419 t.Run(`two partitions`, func(t *testing.T) { 420 sqlDB.Exec(t, createTableStmt(`two_partitions`)) 421 v, err := NewFingerprintValidator(sqlDBRaw, `foo`, `two_partitions`, []string{`p0`, `p1`}, testColumns) 422 require.NoError(t, err) 423 noteRow(t, v, ignored, `[1]`, `{"after": {"k":1,"v":1}}`, ts[1]) 424 noteRow(t, v, ignored, `[1]`, `{"after": {"k":1,"v":2}}`, ts[2]) 425 // Intentionally missing {"k":2,"v":2}. 426 noteResolved(t, v, `p0`, ts[2]) 427 noteResolved(t, v, `p0`, ts[4]) 428 // p1 has not been closed, so no failures yet. 429 assertValidatorFailures(t, v) 430 noteResolved(t, v, `p1`, ts[2]) 431 assertValidatorFailures(t, v, 432 `fingerprints did not match at `+ts[2].AsOfSystemTime()+ 433 `: 1099511631581 vs 590700560494856536`, 434 ) 435 }) 436 } 437 438 func TestValidators(t *testing.T) { 439 defer leaktest.AfterTest(t)() 440 const ignored = `ignored` 441 442 t.Run(`empty`, func(t *testing.T) { 443 v := Validators{ 444 NewOrderValidator(`t1`), 445 NewOrderValidator(`t2`), 446 } 447 if f := v.Failures(); f != nil { 448 t.Fatalf("got %v expected %v", f, nil) 449 } 450 }) 451 t.Run(`failures`, func(t *testing.T) { 452 v := Validators{ 453 NewOrderValidator(`t1`), 454 NewOrderValidator(`t2`), 455 } 456 noteResolved(t, v, `p1`, ts(2)) 457 noteRow(t, v, `p1`, `k1`, ignored, ts(1)) 458 assertValidatorFailures(t, v, 459 `topic t1 partition p1`+ 460 `: saw new row timestamp 1.0000000000 after 2.0000000000 was resolved`, 461 `topic t2 partition p1`+ 462 `: saw new row timestamp 1.0000000000 after 2.0000000000 was resolved`, 463 ) 464 }) 465 }