@@ -35,10 +35,9 @@ import (
35
35
36
36
func (w * mergeIndexWorker ) batchCheckTemporaryUniqueKey (
37
37
txn kv.Transaction ,
38
- idxInfo * model.IndexInfo ,
39
38
idxRecords []* temporaryIndexRecord ,
40
39
) error {
41
- if ! idxInfo .Unique {
40
+ if ! w . currentIndex .Unique {
42
41
// non-unique key need no check, just overwrite it,
43
42
// because in most case, backfilling indices is not exists.
44
43
return nil
@@ -55,7 +54,7 @@ func (w *mergeIndexWorker) batchCheckTemporaryUniqueKey(
55
54
err := checkTempIndexKey (txn , idxRecords [i ], val , w .table )
56
55
if err != nil {
57
56
if kv .ErrKeyExists .Equal (err ) {
58
- return driver .ExtractKeyExistsErrFromIndex (key , val , w .table .Meta (), idxInfo .ID )
57
+ return driver .ExtractKeyExistsErrFromIndex (key , val , w .table .Meta (), w . currentIndex .ID )
59
58
}
60
59
return errors .Trace (err )
61
60
}
@@ -128,6 +127,10 @@ type mergeIndexWorker struct {
128
127
tmpIdxRecords []* temporaryIndexRecord
129
128
originIdxKeys []kv.Key
130
129
tmpIdxKeys []kv.Key
130
+
131
+ needValidateKey bool
132
+ currentTempIndexPrefix []byte
133
+ currentIndex * model.IndexInfo
131
134
}
132
135
133
136
func newMergeTempIndexWorker (bfCtx * backfillCtx , t table.PhysicalTable , elements []* meta.Element ) * mergeIndexWorker {
@@ -144,68 +147,99 @@ func newMergeTempIndexWorker(bfCtx *backfillCtx, t table.PhysicalTable, elements
144
147
}
145
148
}
146
149
150
+ func (w * mergeIndexWorker ) validateTaskRange (taskRange * reorgBackfillTask ) (skip bool , err error ) {
151
+ tmpID , err := tablecodec .DecodeIndexID (taskRange .startKey )
152
+ if err != nil {
153
+ return false , err
154
+ }
155
+ startIndexID := tmpID & tablecodec .IndexIDMask
156
+ tmpID , err = tablecodec .DecodeIndexID (taskRange .endKey )
157
+ if err != nil {
158
+ return false , err
159
+ }
160
+ endIndexID := tmpID & tablecodec .IndexIDMask
161
+
162
+ w .needValidateKey = startIndexID != endIndexID
163
+ containsTargetID := false
164
+ for _ , idx := range w .indexes {
165
+ idxInfo := idx .Meta ()
166
+ if idxInfo .ID == startIndexID {
167
+ containsTargetID = true
168
+ w .currentIndex = idxInfo
169
+ break
170
+ }
171
+ if idxInfo .ID == endIndexID {
172
+ containsTargetID = true
173
+ }
174
+ }
175
+ return ! containsTargetID , nil
176
+ }
177
+
147
178
// BackfillData merge temp index data in txn.
148
179
func (w * mergeIndexWorker ) BackfillData (taskRange reorgBackfillTask ) (taskCtx backfillTaskContext , errInTxn error ) {
180
+ skip , err := w .validateTaskRange (& taskRange )
181
+ if skip || err != nil {
182
+ return taskCtx , err
183
+ }
184
+
149
185
oprStartTime := time .Now ()
150
186
ctx := kv .WithInternalSourceAndTaskType (context .Background (), w .jobContext .ddlJobSourceType (), kvutil .ExplicitTypeDDL )
151
- for _ , idx := range w .indexes {
152
- idx := idx // Make linter noloopclosure happy.
153
- errInTxn = kv .RunInNewTxn (ctx , w .sessCtx .GetStore (), true , func (_ context.Context , txn kv.Transaction ) error {
154
- taskCtx .addedCount = 0
155
- taskCtx .scanCount = 0
156
- updateTxnEntrySizeLimitIfNeeded (txn )
157
- txn .SetOption (kv .Priority , taskRange .priority )
158
- if tagger := w .GetCtx ().getResourceGroupTaggerForTopSQL (taskRange .getJobID ()); tagger != nil {
159
- txn .SetOption (kv .ResourceGroupTagger , tagger )
160
- }
161
- txn .SetOption (kv .ResourceGroupName , w .jobContext .resourceGroupName )
162
187
163
- tmpIdxRecords , nextKey , taskDone , err := w .fetchTempIndexVals (txn , idx .Meta (), taskRange )
164
- if err != nil {
165
- return errors .Trace (err )
188
+ errInTxn = kv .RunInNewTxn (ctx , w .sessCtx .GetStore (), true , func (_ context.Context , txn kv.Transaction ) error {
189
+ taskCtx .addedCount = 0
190
+ taskCtx .scanCount = 0
191
+ updateTxnEntrySizeLimitIfNeeded (txn )
192
+ txn .SetOption (kv .Priority , taskRange .priority )
193
+ if tagger := w .GetCtx ().getResourceGroupTaggerForTopSQL (taskRange .getJobID ()); tagger != nil {
194
+ txn .SetOption (kv .ResourceGroupTagger , tagger )
195
+ }
196
+ txn .SetOption (kv .ResourceGroupName , w .jobContext .resourceGroupName )
197
+
198
+ tmpIdxRecords , nextKey , taskDone , err := w .fetchTempIndexVals (txn , taskRange )
199
+ if err != nil {
200
+ return errors .Trace (err )
201
+ }
202
+ taskCtx .nextKey = nextKey
203
+ taskCtx .done = taskDone
204
+
205
+ err = w .batchCheckTemporaryUniqueKey (txn , tmpIdxRecords )
206
+ if err != nil {
207
+ return errors .Trace (err )
208
+ }
209
+
210
+ for i , idxRecord := range tmpIdxRecords {
211
+ taskCtx .scanCount ++
212
+ // The index is already exists, we skip it, no needs to backfill it.
213
+ // The following update, delete, insert on these rows, TiDB can handle it correctly.
214
+ // If all batch are skipped, update first index key to make txn commit to release lock.
215
+ if idxRecord .skip {
216
+ continue
166
217
}
167
- taskCtx .nextKey = nextKey
168
- taskCtx .done = taskDone
169
218
170
- err = w .batchCheckTemporaryUniqueKey (txn , idx .Meta (), tmpIdxRecords )
219
+ // Lock the corresponding row keys so that it doesn't modify the index KVs
220
+ // that are changing by a pessimistic transaction.
221
+ rowKey := tablecodec .EncodeRecordKey (w .table .RecordPrefix (), idxRecord .handle )
222
+ err := txn .LockKeys (context .Background (), new (kv.LockCtx ), rowKey )
171
223
if err != nil {
172
224
return errors .Trace (err )
173
225
}
174
226
175
- for i , idxRecord := range tmpIdxRecords {
176
- taskCtx .scanCount ++
177
- // The index is already exists, we skip it, no needs to backfill it.
178
- // The following update, delete, insert on these rows, TiDB can handle it correctly.
179
- // If all batch are skipped, update first index key to make txn commit to release lock.
180
- if idxRecord .skip {
181
- continue
182
- }
183
-
184
- // Lock the corresponding row keys so that it doesn't modify the index KVs
185
- // that are changing by a pessimistic transaction.
186
- rowKey := tablecodec .EncodeRecordKey (w .table .RecordPrefix (), idxRecord .handle )
187
- err := txn .LockKeys (context .Background (), new (kv.LockCtx ), rowKey )
188
- if err != nil {
189
- return errors .Trace (err )
190
- }
191
-
192
- if idxRecord .delete {
193
- if idxRecord .unique {
194
- err = txn .GetMemBuffer ().DeleteWithFlags (w .originIdxKeys [i ], kv .SetNeedLocked )
195
- } else {
196
- err = txn .GetMemBuffer ().Delete (w .originIdxKeys [i ])
197
- }
227
+ if idxRecord .delete {
228
+ if idxRecord .unique {
229
+ err = txn .GetMemBuffer ().DeleteWithFlags (w .originIdxKeys [i ], kv .SetNeedLocked )
198
230
} else {
199
- err = txn .GetMemBuffer ().Set (w .originIdxKeys [i ], idxRecord .vals )
200
- }
201
- if err != nil {
202
- return err
231
+ err = txn .GetMemBuffer ().Delete (w .originIdxKeys [i ])
203
232
}
204
- taskCtx .addedCount ++
233
+ } else {
234
+ err = txn .GetMemBuffer ().Set (w .originIdxKeys [i ], idxRecord .vals )
205
235
}
206
- return nil
207
- })
208
- }
236
+ if err != nil {
237
+ return err
238
+ }
239
+ taskCtx .addedCount ++
240
+ }
241
+ return nil
242
+ })
209
243
210
244
failpoint .Inject ("mockDMLExecutionMerging" , func (val failpoint.Value ) {
211
245
//nolint:forcetypeassert
@@ -228,9 +262,41 @@ func (w *mergeIndexWorker) GetCtx() *backfillCtx {
228
262
return w .backfillCtx
229
263
}
230
264
265
+ func (w * mergeIndexWorker ) prefixIsChanged (newKey kv.Key ) bool {
266
+ return len (w .currentTempIndexPrefix ) == 0 || ! bytes .HasPrefix (newKey , w .currentTempIndexPrefix )
267
+ }
268
+
269
+ func (w * mergeIndexWorker ) updateCurrentIndexInfo (newIndexKey kv.Key ) (skip bool , err error ) {
270
+ tempIdxID , err := tablecodec .DecodeIndexID (newIndexKey )
271
+ if err != nil {
272
+ return false , err
273
+ }
274
+ idxID := tablecodec .IndexIDMask & tempIdxID
275
+ var curIdx * model.IndexInfo
276
+ for _ , idx := range w .indexes {
277
+ if idx .Meta ().ID == idxID {
278
+ curIdx = idx .Meta ()
279
+ }
280
+ }
281
+ if curIdx == nil {
282
+ // Index IDs are always increasing, but not always continuous:
283
+ // if DDL adds another index between these indexes, it is possible that:
284
+ // multi-schema add index IDs = [1, 2, 4, 5]
285
+ // another index ID = [3]
286
+ // If the new index get rollback, temp index 0xFFxxx03 may have dirty records.
287
+ // We should skip these dirty records.
288
+ return true , nil
289
+ }
290
+ pfx := tablecodec .CutIndexPrefix (newIndexKey )
291
+
292
+ w .currentTempIndexPrefix = kv .Key (pfx ).Clone ()
293
+ w .currentIndex = curIdx
294
+
295
+ return false , nil
296
+ }
297
+
231
298
func (w * mergeIndexWorker ) fetchTempIndexVals (
232
299
txn kv.Transaction ,
233
- indexInfo * model.IndexInfo ,
234
300
taskRange reorgBackfillTask ,
235
301
) ([]* temporaryIndexRecord , kv.Key , bool , error ) {
236
302
startTime := time .Now ()
@@ -254,11 +320,18 @@ func (w *mergeIndexWorker) fetchTempIndexVals(
254
320
return false , nil
255
321
}
256
322
323
+ if w .needValidateKey && w .prefixIsChanged (indexKey ) {
324
+ skip , err := w .updateCurrentIndexInfo (indexKey )
325
+ if err != nil || skip {
326
+ return skip , err
327
+ }
328
+ }
329
+
257
330
tempIdxVal , err := tablecodec .DecodeTempIndexValue (rawValue )
258
331
if err != nil {
259
332
return false , err
260
333
}
261
- tempIdxVal , err = decodeTempIndexHandleFromIndexKV (indexKey , tempIdxVal , len (indexInfo .Columns ))
334
+ tempIdxVal , err = decodeTempIndexHandleFromIndexKV (indexKey , tempIdxVal , len (w . currentIndex .Columns ))
262
335
if err != nil {
263
336
return false , err
264
337
}
0 commit comments