github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/gcjob_test/gc_job_test.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_test 12 13 import ( 14 "context" 15 "fmt" 16 "strconv" 17 "testing" 18 "time" 19 20 "github.com/cockroachdb/cockroach/pkg/base" 21 "github.com/cockroachdb/cockroach/pkg/jobs" 22 "github.com/cockroachdb/cockroach/pkg/jobs/jobspb" 23 "github.com/cockroachdb/cockroach/pkg/keys" 24 "github.com/cockroachdb/cockroach/pkg/kv" 25 "github.com/cockroachdb/cockroach/pkg/sql/gcjob" 26 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 27 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 28 "github.com/cockroachdb/cockroach/pkg/testutils/jobutils" 29 "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" 30 "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" 31 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 32 "github.com/cockroachdb/cockroach/pkg/util/timeutil" 33 "github.com/stretchr/testify/require" 34 ) 35 36 // TODO(pbardea): Add more testing around the timer calculations. 37 func TestSchemaChangeGCJob(t *testing.T) { 38 defer leaktest.AfterTest(t)() 39 40 defer func(oldAdoptInterval, oldGCInterval time.Duration) { 41 jobs.DefaultAdoptInterval = oldAdoptInterval 42 }(jobs.DefaultAdoptInterval, gcjob.MaxSQLGCInterval) 43 jobs.DefaultAdoptInterval = 100 * time.Millisecond 44 45 type DropItem int 46 const ( 47 INDEX = iota 48 TABLE 49 DATABASE 50 ) 51 52 type TTLTime int 53 const ( 54 PAST = iota // An item was supposed to be GC already. 55 SOON // An item will be GC'd soon. 56 FUTURE // An item should not be GC'd during this test. 57 ) 58 59 for _, dropItem := range []DropItem{INDEX, TABLE, DATABASE} { 60 for _, ttlTime := range []TTLTime{PAST, SOON, FUTURE} { 61 s, db, kvDB := serverutils.StartServer(t, base.TestServerArgs{}) 62 ctx := context.Background() 63 defer s.Stopper().Stop(ctx) 64 sqlDB := sqlutils.MakeSQLRunner(db) 65 66 jobRegistry := s.JobRegistry().(*jobs.Registry) 67 68 sqlDB.Exec(t, "CREATE DATABASE my_db") 69 sqlDB.Exec(t, "USE my_db") 70 sqlDB.Exec(t, "CREATE TABLE my_table (a int primary key, b int, index (b))") 71 sqlDB.Exec(t, "CREATE TABLE my_other_table (a int primary key, b int, index (b))") 72 if ttlTime == SOON { 73 sqlDB.Exec(t, "ALTER TABLE my_table CONFIGURE ZONE USING gc.ttlseconds = 1") 74 sqlDB.Exec(t, "ALTER TABLE my_other_table CONFIGURE ZONE USING gc.ttlseconds = 1") 75 } 76 myDBID := sqlbase.ID(keys.MinUserDescID + 2) 77 myTableID := sqlbase.ID(keys.MinUserDescID + 3) 78 myOtherTableID := sqlbase.ID(keys.MinUserDescID + 4) 79 80 var myTableDesc *sqlbase.TableDescriptor 81 var myOtherTableDesc *sqlbase.TableDescriptor 82 if err := kvDB.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { 83 var err error 84 myTableDesc, err = sqlbase.GetTableDescFromID(ctx, txn, keys.SystemSQLCodec, myTableID) 85 if err != nil { 86 return err 87 } 88 myOtherTableDesc, err = sqlbase.GetTableDescFromID(ctx, txn, keys.SystemSQLCodec, myOtherTableID) 89 return err 90 }); err != nil { 91 t.Fatal(err) 92 } 93 94 // Start the job that drops an index. 95 dropTime := timeutil.Now().UnixNano() 96 if ttlTime == PAST { 97 dropTime = 1 98 } 99 var details jobspb.SchemaChangeGCDetails 100 switch dropItem { 101 case INDEX: 102 details = jobspb.SchemaChangeGCDetails{ 103 Indexes: []jobspb.SchemaChangeGCDetails_DroppedIndex{ 104 { 105 IndexID: sqlbase.IndexID(2), 106 DropTime: dropTime, 107 }, 108 }, 109 ParentID: myTableID, 110 } 111 myTableDesc.Indexes = myTableDesc.Indexes[:0] 112 myTableDesc.GCMutations = append(myTableDesc.GCMutations, sqlbase.TableDescriptor_GCDescriptorMutation{ 113 IndexID: sqlbase.IndexID(2), 114 }) 115 case TABLE: 116 details = jobspb.SchemaChangeGCDetails{ 117 Tables: []jobspb.SchemaChangeGCDetails_DroppedID{ 118 { 119 ID: myTableID, 120 DropTime: dropTime, 121 }, 122 }, 123 } 124 myTableDesc.State = sqlbase.TableDescriptor_DROP 125 myTableDesc.DropTime = dropTime 126 case DATABASE: 127 details = jobspb.SchemaChangeGCDetails{ 128 Tables: []jobspb.SchemaChangeGCDetails_DroppedID{ 129 { 130 ID: myTableID, 131 DropTime: dropTime, 132 }, 133 { 134 ID: myOtherTableID, 135 DropTime: dropTime, 136 }, 137 }, 138 ParentID: myDBID, 139 } 140 myTableDesc.State = sqlbase.TableDescriptor_DROP 141 myTableDesc.DropTime = dropTime 142 myOtherTableDesc.State = sqlbase.TableDescriptor_DROP 143 myOtherTableDesc.DropTime = dropTime 144 } 145 146 if err := kvDB.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { 147 b := txn.NewBatch() 148 descKey := sqlbase.MakeDescMetadataKey(keys.SystemSQLCodec, myTableID) 149 descDesc := sqlbase.WrapDescriptor(myTableDesc) 150 b.Put(descKey, descDesc) 151 descKey2 := sqlbase.MakeDescMetadataKey(keys.SystemSQLCodec, myOtherTableID) 152 descDesc2 := sqlbase.WrapDescriptor(myOtherTableDesc) 153 b.Put(descKey2, descDesc2) 154 return txn.Run(ctx, b) 155 }); err != nil { 156 t.Fatal(err) 157 } 158 159 jobRecord := jobs.Record{ 160 Description: fmt.Sprintf("GC test"), 161 Username: "user", 162 DescriptorIDs: sqlbase.IDs{myTableID}, 163 Details: details, 164 Progress: jobspb.SchemaChangeGCProgress{}, 165 NonCancelable: true, 166 } 167 168 // The job record that will be used to lookup this job. 169 lookupJR := jobs.Record{ 170 Description: fmt.Sprintf("GC test"), 171 Username: "user", 172 DescriptorIDs: sqlbase.IDs{myTableID}, 173 Details: details, 174 } 175 176 resultsCh := make(chan tree.Datums) 177 job, _, err := jobRegistry.CreateAndStartJob(ctx, resultsCh, jobRecord) 178 if err != nil { 179 t.Fatal(err) 180 } 181 182 // Check that the job started. 183 jobIDStr := strconv.Itoa(int(*job.ID())) 184 if err := jobutils.VerifySystemJob(t, sqlDB, 0, jobspb.TypeSchemaChangeGC, jobs.StatusRunning, lookupJR); err != nil { 185 t.Fatal(err) 186 } 187 188 if ttlTime == FUTURE { 189 time.Sleep(500 * time.Millisecond) 190 } else { 191 sqlDB.CheckQueryResultsRetry(t, fmt.Sprintf("SELECT status FROM [SHOW JOBS] WHERE job_id = %s", jobIDStr), [][]string{{"succeeded"}}) 192 if err := jobutils.VerifySystemJob(t, sqlDB, 0, jobspb.TypeSchemaChangeGC, jobs.StatusSucceeded, lookupJR); err != nil { 193 t.Fatal(err) 194 } 195 } 196 197 if err := kvDB.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { 198 var err error 199 myTableDesc, err = sqlbase.GetTableDescFromID(ctx, txn, keys.SystemSQLCodec, myTableID) 200 if ttlTime != FUTURE && (dropItem == TABLE || dropItem == DATABASE) { 201 // We dropped the table, so expect it to not be found. 202 require.EqualError(t, err, "descriptor not found") 203 return nil 204 } 205 myOtherTableDesc, err = sqlbase.GetTableDescFromID(ctx, txn, keys.SystemSQLCodec, myOtherTableID) 206 if ttlTime != FUTURE && dropItem == DATABASE { 207 // We dropped the entire database, so expect none of the tables to be found. 208 require.EqualError(t, err, "descriptor not found") 209 return nil 210 } 211 return err 212 }); err != nil { 213 t.Fatal(err) 214 } 215 216 switch dropItem { 217 case INDEX: 218 if ttlTime == FUTURE { 219 require.Equal(t, 1, len(myTableDesc.GCMutations)) 220 } else { 221 require.Equal(t, 0, len(myTableDesc.GCMutations)) 222 } 223 case TABLE: 224 case DATABASE: 225 // Already handled the case where the TTL was lowered, since we expect 226 // to not find the descriptor. 227 // If the TTL was not lowered, we just expect to have not found an error 228 // when fetching the TTL. 229 } 230 } 231 } 232 }