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  }