github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/gcjob/gc_job_utils.go (about) 1 // Copyright 2020 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package gcjob 12 13 import ( 14 "context" 15 16 "github.com/cockroachdb/cockroach/pkg/jobs" 17 "github.com/cockroachdb/cockroach/pkg/jobs/jobspb" 18 "github.com/cockroachdb/cockroach/pkg/kv" 19 "github.com/cockroachdb/cockroach/pkg/sql" 20 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 21 "github.com/cockroachdb/cockroach/pkg/util/log" 22 "github.com/cockroachdb/errors" 23 ) 24 25 // markTableGCed updates the job payload details to indicate that the specified 26 // table was GC'd. 27 func markTableGCed( 28 ctx context.Context, tableID sqlbase.ID, progress *jobspb.SchemaChangeGCProgress, 29 ) { 30 for i := range progress.Tables { 31 tableProgress := &progress.Tables[i] 32 if tableProgress.ID == tableID { 33 tableProgress.Status = jobspb.SchemaChangeGCProgress_DELETED 34 if log.V(2) { 35 log.Infof(ctx, "determined table %d is GC'd", tableID) 36 } 37 } 38 } 39 } 40 41 // markIndexGCed marks the index as GC'd. 42 func markIndexGCed( 43 ctx context.Context, 44 garbageCollectedIndexID sqlbase.IndexID, 45 progress *jobspb.SchemaChangeGCProgress, 46 ) { 47 // Update the job details to remove the dropped indexes. 48 for i := range progress.Indexes { 49 indexToUpdate := &progress.Indexes[i] 50 if indexToUpdate.IndexID == garbageCollectedIndexID { 51 indexToUpdate.Status = jobspb.SchemaChangeGCProgress_DELETED 52 log.Infof(ctx, "marked index %d as GC'd", garbageCollectedIndexID) 53 } 54 } 55 } 56 57 // initDetailsAndProgress sets up the job progress if not already populated and 58 // validates that the job details is properly formatted. 59 func initDetailsAndProgress( 60 ctx context.Context, execCfg *sql.ExecutorConfig, jobID int64, 61 ) (*jobspb.SchemaChangeGCDetails, *jobspb.SchemaChangeGCProgress, error) { 62 var details jobspb.SchemaChangeGCDetails 63 var progress *jobspb.SchemaChangeGCProgress 64 var job *jobs.Job 65 if err := execCfg.DB.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { 66 var err error 67 job, err = execCfg.JobRegistry.LoadJobWithTxn(ctx, jobID, txn) 68 if err != nil { 69 return err 70 } 71 details = job.Details().(jobspb.SchemaChangeGCDetails) 72 jobProgress := job.Progress() 73 progress = jobProgress.GetSchemaChangeGC() 74 return nil 75 }); err != nil { 76 return nil, nil, err 77 } 78 if err := validateDetails(&details); err != nil { 79 return nil, nil, err 80 } 81 if err := initializeProgress(ctx, execCfg, jobID, &details, progress); err != nil { 82 return nil, nil, err 83 } 84 return &details, progress, nil 85 } 86 87 // initializeProgress converts the details provided into a progress payload that 88 // will be updated as the elements that need to be GC'd get processed. 89 func initializeProgress( 90 ctx context.Context, 91 execCfg *sql.ExecutorConfig, 92 jobID int64, 93 details *jobspb.SchemaChangeGCDetails, 94 progress *jobspb.SchemaChangeGCProgress, 95 ) error { 96 if len(progress.Tables) != len(details.Tables) || len(progress.Indexes) != len(details.Indexes) { 97 for _, table := range details.Tables { 98 progress.Tables = append(progress.Tables, jobspb.SchemaChangeGCProgress_TableProgress{ID: table.ID}) 99 } 100 for _, index := range details.Indexes { 101 progress.Indexes = append(progress.Indexes, jobspb.SchemaChangeGCProgress_IndexProgress{IndexID: index.IndexID}) 102 } 103 104 // Write out new progress. 105 if err := execCfg.DB.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { 106 job, err := execCfg.JobRegistry.LoadJobWithTxn(ctx, jobID, txn) 107 if err != nil { 108 return err 109 } 110 return job.SetProgress(ctx, *progress) 111 }); err != nil { 112 return err 113 } 114 } 115 return nil 116 } 117 118 // Check if we are done GC'ing everything. 119 func isDoneGC(progress *jobspb.SchemaChangeGCProgress) bool { 120 for _, index := range progress.Indexes { 121 if index.Status != jobspb.SchemaChangeGCProgress_DELETED { 122 return false 123 } 124 } 125 for _, table := range progress.Tables { 126 if table.Status != jobspb.SchemaChangeGCProgress_DELETED { 127 return false 128 } 129 } 130 131 return true 132 } 133 134 // getAllTablesWaitingForGC returns a slice with all of the table IDs which have 135 // note yet been been GC'd. This is used to determine which tables' statuses 136 // need to be updated. 137 func getAllTablesWaitingForGC( 138 details *jobspb.SchemaChangeGCDetails, progress *jobspb.SchemaChangeGCProgress, 139 ) []sqlbase.ID { 140 allRemainingTableIDs := make([]sqlbase.ID, 0, len(progress.Tables)) 141 if len(details.Indexes) > 0 { 142 allRemainingTableIDs = append(allRemainingTableIDs, details.ParentID) 143 } 144 for _, table := range progress.Tables { 145 if table.Status != jobspb.SchemaChangeGCProgress_DELETED { 146 allRemainingTableIDs = append(allRemainingTableIDs, table.ID) 147 } 148 } 149 150 return allRemainingTableIDs 151 } 152 153 // validateDetails ensures that the job details payload follows the structure 154 // described in the comment for SchemaChangeGCDetails. 155 func validateDetails(details *jobspb.SchemaChangeGCDetails) error { 156 if len(details.Indexes) > 0 { 157 if details.ParentID == sqlbase.InvalidID { 158 return errors.Errorf("must provide a parentID when dropping an index") 159 } 160 } 161 return nil 162 } 163 164 // persistProgress sets the current state of the progress back on the job. 165 func persistProgress( 166 ctx context.Context, 167 execCfg *sql.ExecutorConfig, 168 jobID int64, 169 progress *jobspb.SchemaChangeGCProgress, 170 ) { 171 if err := execCfg.DB.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { 172 job, err := execCfg.JobRegistry.LoadJobWithTxn(ctx, jobID, txn) 173 if err != nil { 174 return err 175 } 176 if err := job.SetProgress(ctx, *progress); err != nil { 177 return err 178 } 179 log.Infof(ctx, "updated progress payload: %+v", progress) 180 return nil 181 }); err != nil { 182 log.Warningf(ctx, "failed to update job's progress payload err: %+v", err) 183 } 184 } 185 186 // getDropTimes returns the data stored in details as a map for convenience. 187 func getDropTimes( 188 details *jobspb.SchemaChangeGCDetails, 189 ) (map[sqlbase.ID]int64, map[sqlbase.IndexID]int64) { 190 tableDropTimes := make(map[sqlbase.ID]int64) 191 for _, table := range details.Tables { 192 tableDropTimes[table.ID] = table.DropTime 193 } 194 indexDropTimes := make(map[sqlbase.IndexID]int64) 195 for _, index := range details.Indexes { 196 indexDropTimes[index.IndexID] = index.DropTime 197 } 198 return tableDropTimes, indexDropTimes 199 }