@@ -3017,36 +3017,29 @@ func getNewGlobal(partInfo *model.PartitionInfo, idx *model.IndexInfo) bool {
3017
3017
return idx .Global
3018
3018
}
3019
3019
3020
- func getReorgPartitionInfo (t * meta.Mutator , job * model.Job , args * model.TablePartitionArgs ) (* model.TableInfo , []string , * model.PartitionInfo , []model. PartitionDefinition , []model. PartitionDefinition , error ) {
3020
+ func getReorgPartitionInfo (t * meta.Mutator , job * model.Job , args * model.TablePartitionArgs ) (* model.TableInfo , []string , * model.PartitionInfo , error ) {
3021
3021
schemaID := job .SchemaID
3022
3022
tblInfo , err := GetTableInfoAndCancelFaultJob (t , job , schemaID )
3023
3023
if err != nil {
3024
- return nil , nil , nil , nil , nil , errors .Trace (err )
3024
+ return nil , nil , nil , errors .Trace (err )
3025
3025
}
3026
3026
partNames , partInfo := args .PartNames , args .PartInfo
3027
- var addingDefs , droppingDefs []model.PartitionDefinition
3028
- if tblInfo .Partition != nil {
3029
- addingDefs = tblInfo .Partition .AddingDefinitions
3030
- droppingDefs = tblInfo .Partition .DroppingDefinitions
3031
- tblInfo .Partition .NewTableID = partInfo .NewTableID
3032
- tblInfo .Partition .DDLType = partInfo .Type
3033
- tblInfo .Partition .DDLExpr = partInfo .Expr
3034
- tblInfo .Partition .DDLColumns = partInfo .Columns
3035
- } else {
3036
- tblInfo .Partition = getPartitionInfoTypeNone ()
3037
- tblInfo .Partition .NewTableID = partInfo .NewTableID
3038
- tblInfo .Partition .Definitions [0 ].ID = tblInfo .ID
3039
- tblInfo .Partition .DDLType = partInfo .Type
3040
- tblInfo .Partition .DDLExpr = partInfo .Expr
3041
- tblInfo .Partition .DDLColumns = partInfo .Columns
3042
- }
3043
- if len (addingDefs ) == 0 {
3044
- addingDefs = []model.PartitionDefinition {}
3045
- }
3046
- if len (droppingDefs ) == 0 {
3047
- droppingDefs = []model.PartitionDefinition {}
3027
+ if job .SchemaState == model .StateNone {
3028
+ if tblInfo .Partition != nil {
3029
+ tblInfo .Partition .NewTableID = partInfo .NewTableID
3030
+ tblInfo .Partition .DDLType = partInfo .Type
3031
+ tblInfo .Partition .DDLExpr = partInfo .Expr
3032
+ tblInfo .Partition .DDLColumns = partInfo .Columns
3033
+ } else {
3034
+ tblInfo .Partition = getPartitionInfoTypeNone ()
3035
+ tblInfo .Partition .NewTableID = partInfo .NewTableID
3036
+ tblInfo .Partition .Definitions [0 ].ID = tblInfo .ID
3037
+ tblInfo .Partition .DDLType = partInfo .Type
3038
+ tblInfo .Partition .DDLExpr = partInfo .Expr
3039
+ tblInfo .Partition .DDLColumns = partInfo .Columns
3040
+ }
3048
3041
}
3049
- return tblInfo , partNames , partInfo , droppingDefs , addingDefs , nil
3042
+ return tblInfo , partNames , partInfo , nil
3050
3043
}
3051
3044
3052
3045
// onReorganizePartition reorganized the partitioning of a table including its indexes.
@@ -3068,8 +3061,9 @@ func getReorgPartitionInfo(t *meta.Mutator, job *model.Job, args *model.TablePar
3068
3061
//
3069
3062
// job.SchemaState goes through the following SchemaState(s):
3070
3063
// StateNone -> StateDeleteOnly -> StateWriteOnly -> StateWriteReorganization
3071
- // -> StateDeleteOrganization -> StatePublic
3064
+ // -> StateDeleteOrganization -> StatePublic -> Done
3072
3065
// There are more details embedded in the implementation, but the high level changes are:
3066
+ //
3073
3067
// StateNone -> StateDeleteOnly:
3074
3068
//
3075
3069
// Various checks and validations.
@@ -3095,13 +3089,20 @@ func getReorgPartitionInfo(t *meta.Mutator, job *model.Job, args *model.TablePar
3095
3089
// and if new unique indexes are added, it also updates them with the rest of data from
3096
3090
// the non-touched partitions.
3097
3091
// For indexes that are to be replaced with new ones (old/new global index),
3098
- // mark the old indexes as StateDeleteReorganization and new ones as StatePublic
3092
+ // mark the old indexes as StateWriteOnly and new ones as StatePublic
3099
3093
// Finally make the table visible with the new partition definitions.
3100
3094
// I.e. in this state clients will read from the old set of partitions,
3101
- // and will read the new set of partitions in StateDeleteReorganization.
3095
+ // and next state will read the new set of partitions in StateDeleteReorganization.
3102
3096
//
3103
3097
// StateDeleteOrganization -> StatePublic:
3104
3098
//
3099
+ // Now we mark all replaced (old) indexes as StateDeleteOnly
3100
+ // in case DeleteRange would be called directly after the DDL,
3101
+ // this way there will be no orphan records inserted after DeleteRanges
3102
+ // has cleaned up the old partitions and old global indexes.
3103
+ //
3104
+ // StatePublic -> Done:
3105
+ //
3105
3106
// Now all heavy lifting is done, and we just need to finalize and drop things, while still doing
3106
3107
// double writes, since previous state sees the old partitions/indexes.
3107
3108
// Remove the old indexes and old partitions from the TableInfo.
@@ -3110,10 +3111,10 @@ func getReorgPartitionInfo(t *meta.Mutator, job *model.Job, args *model.TablePar
3110
3111
// if ALTER TABLE t PARTITION BY/REMOVE PARTITIONING:
3111
3112
// Recreate the table with the new TableID, by DropTableOrView+CreateTableOrView
3112
3113
//
3113
- // StatePublic :
3114
+ // Done :
3114
3115
//
3115
3116
// Everything now looks as it should, no memory of old partitions/indexes,
3116
- // and no more double writing, since the previous state is only reading the new partitions/indexes.
3117
+ // and no more double writing, since the previous state is only using the new partitions/indexes.
3117
3118
//
3118
3119
// Note: Special handling is also required in tables.newPartitionedTable(),
3119
3120
// to get per partition indexes in the right state.
@@ -3133,7 +3134,7 @@ func (w *worker) onReorganizePartition(jobCtx *jobContext, job *model.Job) (ver
3133
3134
return ver , nil
3134
3135
}
3135
3136
3136
- tblInfo , partNames , partInfo , _ , addingDefinitions , err := getReorgPartitionInfo (jobCtx .metaMut , job , args )
3137
+ tblInfo , partNames , partInfo , err := getReorgPartitionInfo (jobCtx .metaMut , job , args )
3137
3138
if err != nil {
3138
3139
return ver , err
3139
3140
}
@@ -3362,7 +3363,7 @@ func (w *worker) onReorganizePartition(jobCtx *jobContext, job *model.Job) (ver
3362
3363
// For available state, the new added partition should wait its replica to
3363
3364
// be finished, otherwise the query to this partition will be blocked.
3364
3365
count := tblInfo .TiFlashReplica .Count
3365
- needRetry , err := checkPartitionReplica (count , addingDefinitions , jobCtx )
3366
+ needRetry , err := checkPartitionReplica (count , tblInfo . Partition . AddingDefinitions , jobCtx )
3366
3367
if err != nil {
3367
3368
return rollbackReorganizePartitionWithErr (jobCtx , job , err )
3368
3369
}
@@ -3376,7 +3377,7 @@ func (w *worker) onReorganizePartition(jobCtx *jobContext, job *model.Job) (ver
3376
3377
3377
3378
// When TiFlash Replica is ready, we must move them into `AvailablePartitionIDs`.
3378
3379
// Since onUpdateFlashReplicaStatus cannot see the partitions yet (not public)
3379
- for _ , d := range addingDefinitions {
3380
+ for _ , d := range tblInfo . Partition . AddingDefinitions {
3380
3381
tblInfo .TiFlashReplica .AvailablePartitionIDs = append (tblInfo .TiFlashReplica .AvailablePartitionIDs , d .ID )
3381
3382
}
3382
3383
}
@@ -3491,6 +3492,37 @@ func (w *worker) onReorganizePartition(jobCtx *jobContext, job *model.Job) (ver
3491
3492
ver , err = updateVersionAndTableInfo (jobCtx , job , tblInfo , true )
3492
3493
3493
3494
case model .StateDeleteReorganization :
3495
+ // Need to have one more state before completing, due to:
3496
+ // - DeleteRanges could possibly start directly after DDL causing
3497
+ // inserts during previous state (DeleteReorg) could insert after the cleanup
3498
+ // leaving data in dropped partitions/indexes that will not be cleaned up again.
3499
+ // - Updates in previous state (DeleteReorg) could have duplicate errors, if the row
3500
+ // was deleted or updated in after finish (so here we need to have DeleteOnly index state!
3501
+ // And we cannot rollback in this state!
3502
+
3503
+ // Stop double writing to the indexes, only do Deletes!
3504
+ // so that previous could do inserts, we do delete and allow second insert for
3505
+ // previous state clients!
3506
+ for _ , index := range tblInfo .Indices {
3507
+ isNew , ok := tblInfo .Partition .DDLChangedIndex [index .ID ]
3508
+ if ! ok || isNew {
3509
+ continue
3510
+ }
3511
+ // Old index, should not be visible any longer,
3512
+ // but needs to be deleted, in case previous state clients inserts.
3513
+ index .State = model .StateDeleteOnly
3514
+ }
3515
+ failpoint .Inject ("reorgPartFail3" , func (val failpoint.Value ) {
3516
+ if val .(bool ) {
3517
+ job .ErrorCount += variable .GetDDLErrorCountLimit () / 2
3518
+ failpoint .Return (ver , errors .New ("Injected error by reorgPartFail3" ))
3519
+ }
3520
+ })
3521
+ job .SchemaState = model .StatePublic
3522
+ tblInfo .Partition .DDLState = job .SchemaState
3523
+ ver , err = updateVersionAndTableInfo (jobCtx , job , tblInfo , true )
3524
+
3525
+ case model .StatePublic :
3494
3526
// Drop the droppingDefinitions and finish the DDL
3495
3527
// This state is needed for the case where client A sees the schema
3496
3528
// with version of StateWriteReorg and would not see updates of
@@ -3515,7 +3547,7 @@ func (w *worker) onReorganizePartition(jobCtx *jobContext, job *model.Job) (ver
3515
3547
3516
3548
var dropIndices []* model.IndexInfo
3517
3549
for _ , indexInfo := range tblInfo .Indices {
3518
- if indexInfo .Unique && indexInfo .State == model .StateWriteOnly {
3550
+ if indexInfo .Unique && indexInfo .State == model .StateDeleteOnly {
3519
3551
// Drop the old unique (possible global) index, see onDropIndex
3520
3552
indexInfo .State = model .StateNone
3521
3553
DropIndexColumnFlag (tblInfo , indexInfo )
@@ -3530,10 +3562,10 @@ func (w *worker) onReorganizePartition(jobCtx *jobContext, job *model.Job) (ver
3530
3562
for _ , indexInfo := range dropIndices {
3531
3563
removeIndexInfo (tblInfo , indexInfo )
3532
3564
}
3533
- failpoint .Inject ("reorgPartFail3 " , func (val failpoint.Value ) {
3565
+ failpoint .Inject ("reorgPartFail4 " , func (val failpoint.Value ) {
3534
3566
if val .(bool ) {
3535
3567
job .ErrorCount += variable .GetDDLErrorCountLimit () / 2
3536
- failpoint .Return (ver , errors .New ("Injected error by reorgPartFail3 " ))
3568
+ failpoint .Return (ver , errors .New ("Injected error by reorgPartFail4 " ))
3537
3569
}
3538
3570
})
3539
3571
var oldTblID int64
@@ -3567,12 +3599,6 @@ func (w *worker) onReorganizePartition(jobCtx *jobContext, job *model.Job) (ver
3567
3599
// ALTER TABLE ... PARTITION BY
3568
3600
tblInfo .Partition .ClearReorgIntermediateInfo ()
3569
3601
}
3570
- failpoint .Inject ("reorgPartFail4" , func (val failpoint.Value ) {
3571
- if val .(bool ) {
3572
- job .ErrorCount += variable .GetDDLErrorCountLimit () / 2
3573
- failpoint .Return (ver , errors .New ("Injected error by reorgPartFail4" ))
3574
- }
3575
- })
3576
3602
err = metaMut .GetAutoIDAccessors (job .SchemaID , tblInfo .ID ).Put (autoIDs )
3577
3603
if err != nil {
3578
3604
return ver , errors .Trace (err )
@@ -3593,6 +3619,7 @@ func (w *worker) onReorganizePartition(jobCtx *jobContext, job *model.Job) (ver
3593
3619
})
3594
3620
args .OldPhysicalTblIDs = physicalTableIDs
3595
3621
args .NewPartitionIDs = newIDs
3622
+ job .SchemaState = model .StateNone
3596
3623
ver , err = updateVersionAndTableInfo (jobCtx , job , tblInfo , true )
3597
3624
if err != nil {
3598
3625
return ver , errors .Trace (err )
0 commit comments