@@ -24,6 +24,7 @@ import (
24
24
25
25
"github.com/cockroachdb/cockroach-go/v2/crdb"
26
26
"github.com/cockroachdb/cockroach/pkg/base"
27
+ "github.com/cockroachdb/cockroach/pkg/jobs"
27
28
"github.com/cockroachdb/cockroach/pkg/jobs/jobspb"
28
29
"github.com/cockroachdb/cockroach/pkg/keys"
29
30
"github.com/cockroachdb/cockroach/pkg/kv"
@@ -49,15 +50,13 @@ import (
49
50
"github.com/cockroachdb/cockroach/pkg/sql/rowenc/keyside"
50
51
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
51
52
"github.com/cockroachdb/cockroach/pkg/sql/sessiondata"
52
- "github.com/cockroachdb/cockroach/pkg/sql/sqltestutils"
53
53
"github.com/cockroachdb/cockroach/pkg/sql/tests"
54
54
"github.com/cockroachdb/cockroach/pkg/sql/types"
55
55
"github.com/cockroachdb/cockroach/pkg/testutils"
56
56
"github.com/cockroachdb/cockroach/pkg/testutils/serverutils"
57
57
"github.com/cockroachdb/cockroach/pkg/testutils/skip"
58
58
"github.com/cockroachdb/cockroach/pkg/testutils/sqlutils"
59
59
"github.com/cockroachdb/cockroach/pkg/testutils/testcluster"
60
- "github.com/cockroachdb/cockroach/pkg/util"
61
60
"github.com/cockroachdb/cockroach/pkg/util/encoding"
62
61
"github.com/cockroachdb/cockroach/pkg/util/hlc"
63
62
"github.com/cockroachdb/cockroach/pkg/util/leaktest"
@@ -981,219 +980,105 @@ func TestTxnObeysTableModificationTime(t *testing.T) {
981
980
defer leaktest .AfterTest (t )()
982
981
983
982
params := createTestServerParams ()
984
- params .ScanMaxIdleTime = time . Millisecond
985
- s , sqlDB , kvDB := serverutils .StartServer (t , params )
983
+ params .Knobs . JobsTestingKnobs = jobs . NewTestingKnobsWithShortIntervals ()
984
+ s , sqlDB , _ := serverutils .StartServer (t , params )
986
985
defer s .Stopper ().Stop (context .Background ())
987
986
988
- // Disable strict GC TTL enforcement because we're going to shove a zero-value
989
- // TTL into the system with AddImmediateGCZoneConfig.
990
- defer sqltestutils .DisableGCTTLStrictEnforcement (t , sqlDB )()
991
-
992
- _ , err := sqlDB .Exec (`SET CLUSTER SETTING sql.gc_job.wait_for_gc.interval = '1s';` )
993
- require .NoError (t , err )
994
-
995
- // Refresh protected timestamp cache immediately to make MVCC GC queue to
996
- // process GC immediately.
997
- _ , err = sqlDB .Exec (`SET CLUSTER SETTING kv.protectedts.poll_interval = '1s';` )
998
- require .NoError (t , err )
999
-
1000
- // This test intentionally relies on uncontended transactions not being pushed
1001
- // in order to verify what it claims to verify. The default closed timestamp
1002
- // interval in 20.1+ is 3s. When run under the race detector, the process can
1003
- // stall for upwards of 3s leading to the write transaction getting pushed.
1004
- //
1005
- // In order to mitigate that push, we increase the target_duration when the
1006
- // test is run under race.
1007
- if util .RaceEnabled || skip .Stress () {
1008
- _ , err := sqlDB .Exec (
1009
- "SET CLUSTER SETTING kv.closed_timestamp.target_duration = '120s'" )
1010
- require .NoError (t , err )
1011
- }
1012
-
1013
- if _ , err := sqlDB .Exec (`
987
+ _ , err := sqlDB .Exec (`
1014
988
CREATE DATABASE t;
1015
989
CREATE TABLE t.kv (k CHAR PRIMARY KEY, v CHAR);
1016
990
INSERT INTO t.kv VALUES ('a', 'b');
1017
- ` ); err != nil {
1018
- t .Fatal (err )
1019
- }
1020
- tableDesc := desctestutils .TestingGetPublicTableDescriptor (kvDB , keys .SystemSQLCodec , "t" , "kv" )
1021
-
1022
- // A read-write transaction that uses the old version of the descriptor.
1023
- txReadWrite , err := sqlDB .Begin ()
1024
- if err != nil {
1025
- t .Fatal (err )
1026
- }
1027
-
1028
- // A read-only transaction that uses the old version of the descriptor.
1029
- txRead , err := sqlDB .Begin ()
1030
- if err != nil {
1031
- t .Fatal (err )
1032
- }
1033
-
1034
- // A write-only transaction that uses the old version of the descriptor.
1035
- txWrite , err := sqlDB .Begin ()
1036
- if err != nil {
1037
- t .Fatal (err )
1038
- }
1039
-
1040
- // Modify the table descriptor.
1041
- if _ , err := sqlDB .Exec (`ALTER TABLE t.kv ADD m CHAR DEFAULT 'z';` ); err != nil {
1042
- t .Fatal (err )
1043
- }
1044
-
1045
- rows , err := txReadWrite .Query (`SELECT * FROM t.kv` )
1046
- if err != nil {
1047
- t .Fatal (err )
1048
- }
991
+ ` )
992
+ require .NoError (t , err )
1049
993
1050
- checkSelectResults := func (rows * gosql.Rows ) {
1051
- defer func () {
1052
- if err := rows .Close (); err != nil {
1053
- t .Fatal (err )
1054
- }
1055
- }()
1056
- for rows .Next () {
1057
- // The transaction is unable to see column m.
994
+ // requireOneRow ensures `res` contains only one row with two
995
+ // columns ("a", "b").
996
+ requireOneRow := func (res * gosql.Rows ) {
997
+ for res .Next () {
1058
998
var k , v , m string
1059
- if err := rows .Scan (& k , & v , & m ); ! testutils .IsError (
1060
- err , "expected 2 destination arguments in Scan, not 3" ,
1061
- ) {
1062
- t .Fatalf ("err = %v" , err )
1063
- }
1064
- err = rows .Scan (& k , & v )
1065
- if err != nil {
1066
- t .Fatal (err )
1067
- }
1068
- if k != "a" || v != "b" {
1069
- t .Fatalf ("didn't find expected row: %s %s" , k , v )
1070
- }
999
+ require .Error (t , res .Scan (& k , & v , & m ))
1000
+ require .NoError (t , res .Scan (& k , & v ))
1001
+ require .Equal (t , "a" , k )
1002
+ require .Equal (t , "b" , v )
1071
1003
}
1004
+ require .NoError (t , res .Close ())
1072
1005
}
1073
1006
1074
- checkSelectResults (rows )
1075
-
1076
- rows , err = txRead .Query (`SELECT * FROM t.kv` )
1077
- if err != nil {
1078
- t .Fatal (err )
1079
- }
1080
-
1081
- checkSelectResults (rows )
1082
-
1083
- // Read-only transaction commits just fine.
1084
- if err := txRead .Commit (); err != nil {
1085
- t .Fatal (err )
1086
- }
1087
-
1088
- // This INSERT will cause the transaction to be pushed,
1089
- // which will be detected when we attempt to Commit() below.
1090
- if _ , err := txReadWrite .Exec (`INSERT INTO t.kv VALUES ('c', 'd');` ); err != nil {
1091
- t .Fatal (err )
1092
- }
1093
-
1094
- checkDeadlineErr := func (err error , t * testing.T ) {
1007
+ // A helper to assert err is a transaction restart error with an error string
1008
+ // that matches the supplied regex.
1009
+ requireRestartTransactionErrWithMsg := func (t * testing.T , err error , re string ) {
1095
1010
var pqe (* pq.Error )
1096
1011
if ! errors .As (err , & pqe ) || pgcode .MakeCode (string (pqe .Code )) != pgcode .SerializationFailure ||
1097
- ! testutils .IsError (err , "RETRY_COMMIT_DEADLINE_EXCEEDED" ) {
1098
- t .Fatalf ("expected deadline exceeded , got: %v" , err )
1012
+ ! testutils .IsError (err , re ) {
1013
+ t .Fatalf ("expected a %v error , got: %v" , re , err )
1099
1014
}
1100
1015
}
1101
1016
1102
- // The transaction read at one timestamp and wrote at another so it
1103
- // has to be restarted because the spans read were modified by the backfill.
1104
- checkDeadlineErr (txReadWrite .Commit (), t )
1105
-
1106
- // This INSERT will cause the transaction to be pushed transparently,
1107
- // which will be detected when we attempt to Commit() below only because
1108
- // a deadline has been set.
1109
- if _ , err := txWrite .Exec (`INSERT INTO t.kv VALUES ('c', 'd');` ); err != nil {
1110
- t .Fatal (err )
1111
- }
1112
-
1113
- checkDeadlineErr (txWrite .Commit (), t )
1114
-
1115
- // Test the deadline exceeded error with a CREATE/DROP INDEX.
1116
- txWrite , err = sqlDB .Begin ()
1117
- if err != nil {
1118
- t .Fatal (err )
1119
- }
1120
- txUpdate , err := sqlDB .Begin ()
1121
- if err != nil {
1122
- t .Fatal (err )
1123
- }
1124
-
1125
- // Modify the table descriptor.
1126
- if _ , err := sqlDB .Exec (`CREATE INDEX foo ON t.kv (v)` ); err != nil {
1127
- t .Fatal (err )
1128
- }
1129
-
1130
- // This INSERT will cause the transaction to be pushed transparently,
1131
- // which will be detected when we attempt to Commit() below only because
1132
- // a deadline has been set.
1133
- if _ , err := txWrite .Exec (`INSERT INTO t.kv VALUES ('c', 'd');` ); err != nil {
1134
- t .Fatal (err )
1017
+ // requireWriteTooOldErr ensures `err` is a WriteTooOldError.
1018
+ requireWriteTooOldErr := func (t * testing.T , err error ) {
1019
+ requireRestartTransactionErrWithMsg (t , err , "WriteTooOldError" )
1135
1020
}
1136
1021
1137
- checkDeadlineErr (txWrite .Commit (), t )
1138
-
1139
- if _ , err := txUpdate .Exec (`UPDATE t.kv SET v = 'c' WHERE k = 'a';` ); err != nil {
1140
- t .Fatal (err )
1022
+ // requireSessionExpiredErr ensures `err` is a liveness session expired error.
1023
+ requireSessionExpiredErr := func (t * testing.T , err error ) {
1024
+ requireRestartTransactionErrWithMsg (t , err , "liveness session expired" )
1141
1025
}
1142
1026
1143
- checkDeadlineErr (txUpdate .Commit (), t )
1144
-
1145
- txWrite , err = sqlDB .Begin ()
1146
- if err != nil {
1147
- t .Fatal (err )
1148
- }
1149
- txRead , err = sqlDB .Begin ()
1150
- if err != nil {
1151
- t .Fatal (err )
1152
- }
1027
+ // A read-write transaction that uses the old version of the descriptor.
1028
+ txReadWrite , err := sqlDB .Begin ()
1029
+ require .NoError (t , err )
1030
+ // A read-only transaction that uses the old version of the descriptor.
1031
+ txRead , err := sqlDB .Begin ()
1032
+ require .NoError (t , err )
1033
+ // A write-only transaction that uses the old version of the descriptor.
1034
+ txWrite , err := sqlDB .Begin ()
1035
+ require .NoError (t , err )
1153
1036
1154
1037
// Modify the table descriptor.
1155
- if _ , err := sqlDB .Exec (`DROP INDEX t.kv@foo` ); err != nil {
1156
- t .Fatal (err )
1157
- }
1158
-
1159
- rows , err = txRead .Query (`SELECT k, v FROM t.kv@foo` )
1160
- if err != nil {
1161
- t .Fatal (err )
1162
- }
1163
- checkSelectResults (rows )
1164
-
1165
- // Uses old descriptor and inserts values into index span which
1166
- // will be cleaned up.
1167
- if _ , err := txWrite .Exec (`INSERT INTO t.kv VALUES ('c', 'd');` ); err != nil {
1168
- t .Fatal (err )
1169
- }
1170
-
1171
- if err := txRead .Commit (); err != nil {
1172
- t .Fatal (err )
1173
- }
1174
- if err := txWrite .Commit (); err != nil {
1175
- t .Fatal (err )
1176
- }
1177
-
1178
- tableSpan := tableDesc .TableSpan (keys .SystemSQLCodec )
1179
-
1180
- // Allow async schema change waiting for GC to complete (when dropping an
1181
- // index) and clear the index keys.
1182
- if _ , err := sqltestutils .AddImmediateGCZoneConfig (sqlDB , tableDesc .GetID ()); err != nil {
1183
- t .Fatal (err )
1184
- }
1185
-
1186
- // Reset closed timestamp so that deleted keys can be GC'ed within the
1187
- // SucceedSoon window.
1188
- if util .RaceEnabled || skip .Stress () {
1189
- _ , err := sqlDB .Exec (
1190
- "SET CLUSTER SETTING kv.closed_timestamp.target_duration = '3s'" )
1191
- require .NoError (t , err )
1192
- }
1193
-
1194
- testutils .SucceedsSoon (t , func () error {
1195
- return tests .CheckKeyCountIncludingTombstonedE (t , s , tableSpan , 2 )
1196
- })
1038
+ _ , err = sqlDB .Exec (`ALTER TABLE t.kv ADD m CHAR DEFAULT 'z';` )
1039
+ require .NoError (t , err )
1040
+ // Wait a short bit so that the SCHEMA CHANGE GC job created by the above ADD COLUMN
1041
+ // gets to run, which will mark the old primary index of `t.kv` as tombstoned.
1042
+ time .Sleep (1 * time .Second )
1043
+
1044
+ // Read-only transaction:
1045
+ // 1). reads uses a historical version of `t.kv` and sees only two columns `k`
1046
+ // and `v`;
1047
+ // 2). it commits just fine;
1048
+ rows , err := txRead .Query (`SELECT * FROM t.kv` )
1049
+ require .NoError (t , err )
1050
+ requireOneRow (rows )
1051
+ require .NoError (t , txRead .Commit ())
1052
+
1053
+ // Read-write transaction:
1054
+ // 1). reads uses a historical version of `t.kv` and sees only two columns `k`
1055
+ // and `v`;
1056
+ // 2). writes to that historical version of `t.kv` attempt to write to the old
1057
+ // primary index, which has previously been written with a higher timestamp
1058
+ // (when the SCHEMA CHANGE GC job lays a range tombstone on it). This is a
1059
+ // write-write conflict, and it will bump up transaction's write_ts, perform a
1060
+ // read refresh which would fail (bc reading from that new and higher
1061
+ // timestamp would return zero row, instead of one), and thus a
1062
+ // TransactionRetryWriteTooOld error will be returned.
1063
+ rows , err = txReadWrite .Query (`SELECT * FROM t.kv` )
1064
+ require .NoError (t , err )
1065
+ requireOneRow (rows )
1066
+ _ , err = txReadWrite .Exec (`INSERT INTO t.kv VALUES ('c', 'd');` )
1067
+ requireWriteTooOldErr (t , err )
1068
+ require .NoError (t , txReadWrite .Rollback ())
1069
+
1070
+ // Write-only transaction:
1071
+ // 1). writes, similarly to the read-write transaction, will trigger a read
1072
+ // refresh and this time it will succeed (bc there is no reads).
1073
+ // 2). commits, however, will update the transaction's deadline, and the
1074
+ // leased descriptor such an "old" transaction is using is the very original
1075
+ // version (v1) of `t.kv` that expires at the modification time of v2 of
1076
+ // `t.kv`. But the bumped commit timestamp is larger than the leased
1077
+ // descriptor's expiration time, leading to a "liveness session expired"
1078
+ // error.
1079
+ _ , err = txWrite .Exec (`INSERT INTO t.kv VALUES ('c', 'd');` )
1080
+ require .NoError (t , err )
1081
+ requireSessionExpiredErr (t , txWrite .Commit ())
1197
1082
}
1198
1083
1199
1084
// Test that a lease on a table descriptor is always acquired on the latest
0 commit comments