@@ -22,32 +22,60 @@ package sql
22
22
23
23
import (
24
24
"context"
25
+ "time"
25
26
26
27
"github.com/cockroachdb/cockroach/pkg/roachpb"
28
+ "github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
29
+ "github.com/cockroachdb/cockroach/pkg/storage/engine/enginepb"
27
30
// We dot-import fsm to use common names such as fsm.True/False. State machine
28
31
// implementations using that library are weird beasts intimately inter-twined
29
32
// with that package; therefor this file should stay as small as possible.
30
33
. "github.com/cockroachdb/cockroach/pkg/util/fsm"
34
+ "github.com/cockroachdb/cockroach/pkg/util/hlc"
31
35
)
32
36
33
37
/// States.
34
38
35
39
type stateNoTxn struct {}
40
+
41
+ func (stateNoTxn ) String () string {
42
+ return "NoTxn"
43
+ }
44
+
36
45
type stateOpen struct {
37
46
ImplicitTxn Bool
38
47
// RetryIntent, if set, means the user declared the intention to retry the txn
39
48
// in case of retriable errors by running a SAVEPOINT cockroach_restart. The
40
49
// txn will enter a RestartWait state in case of such errors.
41
50
RetryIntent Bool
42
51
}
52
+
53
+ func (stateOpen ) String () string {
54
+ return "Open"
55
+ }
56
+
43
57
type stateAborted struct {
44
58
// RetryIntent carries over the setting from stateOpen, in case we move back
45
59
// to Open.
46
60
RetryIntent Bool
47
61
}
62
+
63
+ func (stateAborted ) String () string {
64
+ return "Aborted"
65
+ }
66
+
48
67
type stateRestartWait struct {}
68
+
69
+ func (stateRestartWait ) String () string {
70
+ return "RestartWait"
71
+ }
72
+
49
73
type stateCommitWait struct {}
50
74
75
+ func (stateCommitWait ) String () string {
76
+ return "CommitWait"
77
+ }
78
+
51
79
func (stateNoTxn ) State () {}
52
80
func (stateOpen ) State () {}
53
81
func (stateAborted ) State () {}
@@ -60,13 +88,51 @@ type eventTxnStart struct {
60
88
ImplicitTxn Bool
61
89
}
62
90
type eventTxnStartPayload struct {
63
- tranCtx transitionCtx
91
+ iso enginepb.IsolationType
92
+ pri roachpb.UserPriority
93
+ // txnSQLTimestamp is the timestamp that statements executed in the
94
+ // transaction that is started by this event will report for now(),
95
+ // current_timestamp(), transaction_timestamp().
96
+ txnSQLTimestamp time.Time
97
+ tranCtx transitionCtx
98
+ readOnly tree.ReadWriteMode
99
+ }
100
+
101
+ func makeEventTxnStartPayload (
102
+ iso enginepb.IsolationType ,
103
+ pri roachpb.UserPriority ,
104
+ txnSQLTimestamp time.Time ,
105
+ tranCtx transitionCtx ,
106
+ readOnly tree.ReadWriteMode ,
107
+ ) eventTxnStartPayload {
108
+ return eventTxnStartPayload {
109
+ iso : iso ,
110
+ pri : pri ,
111
+ readOnly : readOnly ,
112
+ txnSQLTimestamp : txnSQLTimestamp ,
113
+ tranCtx : tranCtx ,
114
+ }
64
115
}
65
116
66
117
// eventRetryIntentSet is generated in the Open state when a SAVEPOINT
67
118
// cockroach_restart is seen.
68
119
type eventRetryIntentSet struct {}
69
120
type eventTxnFinish struct {}
121
+
122
+ // eventTxnFinishPayload represents the payload for eventTxnFinish.
123
+ type eventTxnFinishPayload struct {
124
+ // commit is set if the transaction committed, false if it was aborted.
125
+ commit bool
126
+ }
127
+
128
+ // toEvent turns the eventTxnFinishPayload into a txnEvent.
129
+ func (e eventTxnFinishPayload ) toEvent () txnEvent {
130
+ if e .commit {
131
+ return txnCommit
132
+ }
133
+ return txnAborted
134
+ }
135
+
70
136
type eventTxnRestart struct {}
71
137
72
138
type eventNonRetriableErr struct {
@@ -148,7 +214,7 @@ var TxnStateTransitions = Compile(Pattern{
148
214
Action : func (args Args ) error {
149
215
return args .Extended .(* txnState2 ).noTxnToOpen (
150
216
args .Ctx , args .Event .(eventTxnStart ),
151
- args .Payload .(eventTxnStartPayload ). tranCtx )
217
+ args .Payload .(eventTxnStartPayload ))
152
218
},
153
219
},
154
220
},
@@ -161,7 +227,8 @@ var TxnStateTransitions = Compile(Pattern{
161
227
Action : func (args Args ) error {
162
228
ts := args .Extended .(* txnState2 )
163
229
ts .finishSQLTxn (args .Ctx )
164
- ts .setAdvanceInfo (advanceInfo {code : advanceOne , flush : true })
230
+ ts .setAdvanceInfo (
231
+ advanceOne , flush , noRewind , args .Payload .(eventTxnFinishPayload ).toEvent ())
165
232
return nil
166
233
},
167
234
},
@@ -190,10 +257,10 @@ var TxnStateTransitions = Compile(Pattern{
190
257
Action : func (args Args ) error {
191
258
// The caller will call rewCap.rewindAndUnlock().
192
259
args .Extended .(* txnState2 ).setAdvanceInfo (
193
- advanceInfo {
194
- code : rewind ,
195
- rewCap : args .Payload .(eventRetriableErrPayload ).rewCap ,
196
- flush : false } )
260
+ rewind ,
261
+ noFlush ,
262
+ args .Payload .(eventRetriableErrPayload ).rewCap ,
263
+ txnRestart )
197
264
return nil
198
265
},
199
266
},
@@ -216,7 +283,7 @@ var TxnStateTransitions = Compile(Pattern{
216
283
Action : func (args Args ) error {
217
284
ts := args .Extended .(* txnState2 )
218
285
ts .mu .txn .CleanupOnError (ts .Ctx , args .Payload .(payloadWithError ).errorCause ())
219
- ts .setAdvanceInfo (advanceInfo { code : skipQueryStr , flush : true } )
286
+ ts .setAdvanceInfo (skipBatch , flush , noRewind , txnAborted )
220
287
return nil
221
288
},
222
289
},
@@ -230,7 +297,7 @@ var TxnStateTransitions = Compile(Pattern{
230
297
Action : func (args Args ) error {
231
298
ts := args .Extended .(* txnState2 )
232
299
ts .mu .txn .CleanupOnError (ts .Ctx , args .Payload .(payloadWithError ).errorCause ())
233
- ts .setAdvanceInfo (advanceInfo { code : skipQueryStr , flush : true } )
300
+ ts .setAdvanceInfo (skipBatch , flush , noRewind , txnAborted )
234
301
return nil
235
302
},
236
303
},
@@ -241,7 +308,7 @@ var TxnStateTransitions = Compile(Pattern{
241
308
Action : func (args Args ) error {
242
309
// We flush after setting the retry intent; we know what statement
243
310
// caused this event and we don't need to rewind past it.
244
- args .Extended .(* txnState2 ).setAdvanceInfo (advanceInfo { code : advanceOne , flush : true } )
311
+ args .Extended .(* txnState2 ).setAdvanceInfo (advanceOne , flush , noRewind , noEvent )
245
312
return nil
246
313
},
247
314
},
@@ -252,15 +319,36 @@ var TxnStateTransitions = Compile(Pattern{
252
319
Action : func (args Args ) error {
253
320
// Note: Preparing the KV txn for restart has already happened by this
254
321
// point.
255
- args .Extended .(* txnState2 ).setAdvanceInfo (advanceInfo { code : skipQueryStr , flush : true } )
322
+ args .Extended .(* txnState2 ).setAdvanceInfo (skipBatch , flush , noRewind , txnRestart )
256
323
return nil
257
324
},
258
325
},
259
326
eventTxnReleased {}: {
260
327
Description : "RELEASE SAVEPOINT cockroach_restart" ,
261
328
Next : stateCommitWait {},
262
329
Action : func (args Args ) error {
263
- args .Extended .(* txnState2 ).setAdvanceInfo (advanceInfo {code : advanceOne , flush : true })
330
+ args .Extended .(* txnState2 ).setAdvanceInfo (advanceOne , flush , noRewind , txnCommit )
331
+ return nil
332
+ },
333
+ },
334
+ // ROLLBACK TO SAVEPOINT
335
+ eventTxnRestart {}: {
336
+ Description : "ROLLBACK TO SAVEPOINT cockroach_restart" ,
337
+ Next : stateOpen {ImplicitTxn : False , RetryIntent : True },
338
+ Action : func (args Args ) error {
339
+ state := args .Extended .(* txnState2 )
340
+ // If commands have already been sent through the transaction, restart
341
+ // the client txn's proto to increment the epoch.
342
+ if state .mu .txn .GetTxnCoordMeta ().CommandCount > 0 {
343
+ // NOTE: We don't bump the txn timestamp on this restart. If this
344
+ // we generally supported savepoints and one would issue a rollback to
345
+ // a regular savepoint, clearly we couldn't bump the timestamp. But in
346
+ // the special case of the cockroach_restart savepoint, it's not clear
347
+ // to me what a user's expectation might be.
348
+ state .mu .txn .Proto ().Restart (
349
+ 0 /* userPriority */ , 0 /* upgradePriority */ , hlc.Timestamp {})
350
+ }
351
+ args .Extended .(* txnState2 ).setAdvanceInfo (advanceOne , flush , noRewind , noEvent )
264
352
return nil
265
353
},
266
354
},
@@ -277,7 +365,8 @@ var TxnStateTransitions = Compile(Pattern{
277
365
Action : func (args Args ) error {
278
366
ts := args .Extended .(* txnState2 )
279
367
ts .finishSQLTxn (ts .Ctx )
280
- ts .setAdvanceInfo (advanceInfo {code : advanceOne , flush : true })
368
+ ts .setAdvanceInfo (
369
+ advanceOne , flush , noRewind , args .Payload .(eventTxnFinishPayload ).toEvent ())
281
370
return nil
282
371
},
283
372
},
@@ -293,13 +382,15 @@ var TxnStateTransitions = Compile(Pattern{
293
382
ts := args .Extended .(* txnState2 )
294
383
ts .finishSQLTxn (args .Ctx )
295
384
385
+ payload := args .Payload .(eventTxnStartPayload )
386
+
296
387
ts .resetForNewSQLTxn (
297
388
args .Ctx ,
298
389
explicitTxn ,
299
- ts . sqlTimestamp , ts . isolation , ts . priority ,
390
+ payload . txnSQLTimestamp , payload . iso , payload . pri , payload . readOnly ,
300
391
args .Payload .(eventTxnStartPayload ).tranCtx ,
301
392
)
302
- ts .setAdvanceInfo (advanceInfo { code : advanceOne , flush : true } )
393
+ ts .setAdvanceInfo (advanceOne , flush , noRewind , noEvent )
303
394
return nil
304
395
},
305
396
},
@@ -313,7 +404,8 @@ var TxnStateTransitions = Compile(Pattern{
313
404
Action : func (args Args ) error {
314
405
ts := args .Extended .(* txnState2 )
315
406
ts .finishSQLTxn (args .Ctx )
316
- ts .setAdvanceInfo (advanceInfo {code : advanceOne , flush : true })
407
+ ts .setAdvanceInfo (
408
+ advanceOne , flush , noRewind , args .Payload .(eventTxnFinishPayload ).toEvent ())
317
409
return nil
318
410
},
319
411
},
@@ -322,7 +414,7 @@ var TxnStateTransitions = Compile(Pattern{
322
414
Description : "ROLLBACK TO SAVEPOINT cockroach_restart" ,
323
415
Next : stateOpen {ImplicitTxn : False , RetryIntent : True },
324
416
Action : func (args Args ) error {
325
- args .Extended .(* txnState2 ).setAdvanceInfo (advanceInfo { code : advanceOne , flush : true } )
417
+ args .Extended .(* txnState2 ).setAdvanceInfo (advanceOne , flush , noRewind , noEvent )
326
418
return nil
327
419
},
328
420
},
@@ -332,7 +424,7 @@ var TxnStateTransitions = Compile(Pattern{
332
424
ts := args .Extended .(* txnState2 )
333
425
ts .mu .txn .CleanupOnError (ts .Ctx , args .Payload .(eventNonRetriableErrPayload ).err )
334
426
ts .mu .txn = nil
335
- ts .setAdvanceInfo (advanceInfo { code : skipQueryStr , flush : true } )
427
+ ts .setAdvanceInfo (skipBatch , flush , noRewind , noEvent )
336
428
return nil
337
429
},
338
430
},
@@ -345,7 +437,8 @@ var TxnStateTransitions = Compile(Pattern{
345
437
Action : func (args Args ) error {
346
438
ts := args .Extended .(* txnState2 )
347
439
ts .finishSQLTxn (args .Ctx )
348
- ts .setAdvanceInfo (advanceInfo {code : advanceOne , flush : true })
440
+ ts .setAdvanceInfo (
441
+ advanceOne , flush , noRewind , args .Payload .(eventTxnFinishPayload ).toEvent ())
349
442
return nil
350
443
},
351
444
},
@@ -355,7 +448,7 @@ var TxnStateTransitions = Compile(Pattern{
355
448
Description : "any other statement" ,
356
449
Next : stateAborted {RetryIntent : True },
357
450
Action : func (args Args ) error {
358
- args .Extended .(* txnState2 ).setAdvanceInfo (advanceInfo { code : skipQueryStr , flush : true } )
451
+ args .Extended .(* txnState2 ).setAdvanceInfo (skipBatch , flush , noRewind , noEvent )
359
452
return nil
360
453
},
361
454
},
@@ -367,27 +460,32 @@ func cleanupAndFinish(args Args) error {
367
460
ts := args .Extended .(* txnState2 )
368
461
ts .mu .txn .CleanupOnError (ts .Ctx , args .Payload .(payloadWithError ).errorCause ())
369
462
ts .finishSQLTxn (args .Ctx )
370
- ts .setAdvanceInfo (advanceInfo { code : skipQueryStr , flush : true } )
463
+ ts .setAdvanceInfo (skipBatch , flush , noRewind , txnAborted )
371
464
return nil
372
465
}
373
466
374
467
// noTxnToOpen implements the side effects of starting a txn. It also calls
375
- // setAdvanceInfo(), telling the execution to stayInPlace .
468
+ // setAdvanceInfo().
376
469
func (ts * txnState2 ) noTxnToOpen (
377
- connCtx context.Context , ev eventTxnStart , tranCtx transitionCtx ,
470
+ connCtx context.Context , ev eventTxnStart , payload eventTxnStartPayload ,
378
471
) error {
379
472
txnTyp := explicitTxn
473
+ advCode := advanceOne
380
474
if ev .ImplicitTxn .Get () {
381
475
txnTyp = implicitTxn
476
+ // For an implicit txn, we want the statement that produced the event to be
477
+ // executed again (this time in state Open).
478
+ advCode = stayInPlace
382
479
}
383
480
384
481
ts .resetForNewSQLTxn (
385
482
connCtx ,
386
483
txnTyp ,
387
- tranCtx .clock .PhysicalTime (), /* sqlTimestamp */
388
- tranCtx .defaultIsolationLevel ,
389
- roachpb .NormalUserPriority ,
390
- tranCtx ,
484
+ payload .txnSQLTimestamp ,
485
+ payload .iso ,
486
+ payload .pri ,
487
+ payload .readOnly ,
488
+ payload .tranCtx ,
391
489
)
392
490
// When starting a transaction from the NoTxn state, we don't advance the
393
491
// cursor - we want the same statement to be executed again. Note that this is
@@ -399,6 +497,6 @@ func (ts *txnState2) noTxnToOpen(
399
497
// flushed (since we're not in a txn), and we shouldn't have generated any
400
498
// results for the statement that generated this event (which is why we can
401
499
// stayInPlace here).
402
- ts .setAdvanceInfo (advanceInfo { code : stayInPlace , flush : true } )
500
+ ts .setAdvanceInfo (advCode , flush , noRewind , noEvent )
403
501
return nil
404
502
}
0 commit comments