github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/tablewriter_delete.go (about)

     1  // Copyright 2018 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 sql
    12  
    13  import (
    14  	"context"
    15  	"fmt"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/kv"
    18  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/row"
    20  	"github.com/cockroachdb/cockroach/pkg/sql/rowcontainer"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    22  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    23  	"github.com/cockroachdb/cockroach/pkg/util"
    24  	"github.com/cockroachdb/cockroach/pkg/util/log"
    25  	"github.com/cockroachdb/errors"
    26  )
    27  
    28  // tableDeleter handles writing kvs and forming table rows for deletes.
    29  type tableDeleter struct {
    30  	tableWriterBase
    31  
    32  	rd    row.Deleter
    33  	alloc *sqlbase.DatumAlloc
    34  }
    35  
    36  var _ tableWriter = &tableDeleter{}
    37  
    38  // desc is part of the tableWriter interface.
    39  func (*tableDeleter) desc() string { return "deleter" }
    40  
    41  // walkExprs is part of the tableWriter interface.
    42  func (td *tableDeleter) walkExprs(_ func(desc string, index int, expr tree.TypedExpr)) {}
    43  
    44  // init is part of the tableWriter interface.
    45  func (td *tableDeleter) init(_ context.Context, txn *kv.Txn, _ *tree.EvalContext) error {
    46  	td.tableWriterBase.init(txn)
    47  	return nil
    48  }
    49  
    50  // flushAndStartNewBatch is part of the tableWriter interface.
    51  func (td *tableDeleter) flushAndStartNewBatch(ctx context.Context) error {
    52  	return td.tableWriterBase.flushAndStartNewBatch(ctx, td.rd.Helper.TableDesc)
    53  }
    54  
    55  // finalize is part of the tableWriter interface.
    56  func (td *tableDeleter) finalize(ctx context.Context, _ bool) (*rowcontainer.RowContainer, error) {
    57  	return nil, td.tableWriterBase.finalize(ctx, td.rd.Helper.TableDesc)
    58  }
    59  
    60  // atBatchEnd is part of the tableWriter interface.
    61  func (td *tableDeleter) atBatchEnd(_ context.Context, _ bool) error { return nil }
    62  
    63  // row is part of the tableWriter interface.
    64  // TODO(mgartner): Pass ignoreIndexes to DeleteRow and do not delete index
    65  // entries for indexes in the set.
    66  func (td *tableDeleter) row(
    67  	ctx context.Context, values tree.Datums, ignoreIndexes util.FastIntSet, traceKV bool,
    68  ) error {
    69  	td.batchSize++
    70  	return td.rd.DeleteRow(ctx, td.b, values, row.CheckFKs, traceKV)
    71  }
    72  
    73  // deleteAllRows runs the kv operations necessary to delete all sql rows in the
    74  // table passed at construction. This may require a scan.
    75  //
    76  // resume is the resume-span which should be used for the table deletion when
    77  // the table deletion is chunked. The first call to this method should use a
    78  // zero resume-span. After a chunk is deleted a new resume-span is returned.
    79  //
    80  // limit is a limit on either the number of keys or table-rows (for
    81  // interleaved tables) deleted in the operation.
    82  func (td *tableDeleter) deleteAllRows(
    83  	ctx context.Context, resume roachpb.Span, limit int64, traceKV bool,
    84  ) (roachpb.Span, error) {
    85  	if td.rd.Helper.TableDesc.IsInterleaved() {
    86  		log.VEvent(ctx, 2, "delete forced to scan: table is interleaved")
    87  		return td.deleteAllRowsScan(ctx, resume, limit, traceKV)
    88  	}
    89  	// TODO(pbardea): Is this ever called anymore?
    90  	return td.deleteAllRowsFast(ctx, resume, limit, traceKV)
    91  }
    92  
    93  // deleteAllRowsFast uses the DelRange KV request to delete data quickly,
    94  // relative to deleteAllRowsScan.
    95  //
    96  // Note that this method leaves a RocksDB deletion tombstone on every key in the
    97  // table, resulting in substantial write amplification. When possible, the
    98  // schema changer avoids using a tableDeleter entirely in favor of the
    99  // ClearRange KV request, which uses RocksDB range deletion tombstones to avoid
   100  // write amplification.
   101  func (td *tableDeleter) deleteAllRowsFast(
   102  	ctx context.Context, resume roachpb.Span, limit int64, traceKV bool,
   103  ) (roachpb.Span, error) {
   104  	if resume.Key == nil {
   105  		tablePrefix := td.rd.Helper.Codec.TablePrefix(uint32(td.rd.Helper.TableDesc.ID))
   106  		// Delete rows and indexes starting with the table's prefix.
   107  		resume = roachpb.Span{
   108  			Key:    tablePrefix,
   109  			EndKey: tablePrefix.PrefixEnd(),
   110  		}
   111  	}
   112  
   113  	log.VEventf(ctx, 2, "DelRange %s - %s", resume.Key, resume.EndKey)
   114  	td.b.DelRange(resume.Key, resume.EndKey, false /* returnKeys */)
   115  	td.b.Header.MaxSpanRequestKeys = limit
   116  	if _, err := td.finalize(ctx, traceKV); err != nil {
   117  		return resume, err
   118  	}
   119  	if l := len(td.b.Results); l != 1 {
   120  		panic(fmt.Sprintf("%d results returned", l))
   121  	}
   122  	return td.b.Results[0].ResumeSpanAsValue(), nil
   123  }
   124  
   125  func (td *tableDeleter) deleteAllRowsScan(
   126  	ctx context.Context, resume roachpb.Span, limit int64, traceKV bool,
   127  ) (roachpb.Span, error) {
   128  	if resume.Key == nil {
   129  		resume = td.rd.Helper.TableDesc.PrimaryIndexSpan(td.rd.Helper.Codec)
   130  	}
   131  
   132  	var valNeededForCol util.FastIntSet
   133  	for _, idx := range td.rd.FetchColIDtoRowIndex {
   134  		valNeededForCol.Add(idx)
   135  	}
   136  
   137  	var rf row.Fetcher
   138  	tableArgs := row.FetcherTableArgs{
   139  		Desc:            td.rd.Helper.TableDesc,
   140  		Index:           &td.rd.Helper.TableDesc.PrimaryIndex,
   141  		ColIdxMap:       td.rd.FetchColIDtoRowIndex,
   142  		Cols:            td.rd.FetchCols,
   143  		ValNeededForCol: valNeededForCol,
   144  	}
   145  	if err := rf.Init(
   146  		td.rd.Helper.Codec,
   147  		false, /* reverse */
   148  		// TODO(nvanbenschoten): it might make sense to use a FOR_UPDATE locking
   149  		// strength here. Consider hooking this in to the same knob that will
   150  		// control whether we perform locking implicitly during DELETEs.
   151  		sqlbase.ScanLockingStrength_FOR_NONE,
   152  		false, /* returnRangeInfo */
   153  		false, /* isCheck */
   154  		td.alloc,
   155  		tableArgs,
   156  	); err != nil {
   157  		return resume, err
   158  	}
   159  	if err := rf.StartScan(ctx, td.txn, roachpb.Spans{resume}, true /* limit batches */, 0, traceKV); err != nil {
   160  		return resume, err
   161  	}
   162  
   163  	for i := int64(0); i < limit; i++ {
   164  		datums, _, _, err := rf.NextRowDecoded(ctx)
   165  		if err != nil {
   166  			return resume, err
   167  		}
   168  		if datums == nil {
   169  			// Done deleting all rows.
   170  			resume = roachpb.Span{}
   171  			break
   172  		}
   173  		// TODO(mgartner): Add partial index IDs to ignoreIndexes that we should
   174  		// not delete entries from.
   175  		var ignoreIndexes util.FastIntSet
   176  		if err = td.row(ctx, datums, ignoreIndexes, traceKV); err != nil {
   177  			return resume, err
   178  		}
   179  	}
   180  	if resume.Key != nil {
   181  		// Update the resume start key for the next iteration.
   182  		resume.Key = rf.Key()
   183  	}
   184  	_, err := td.finalize(ctx, traceKV)
   185  	return resume, err
   186  }
   187  
   188  // deleteIndex runs the kv operations necessary to delete all kv entries in the
   189  // given index. This may require a scan.
   190  //
   191  // resume is the resume-span which should be used for the index deletion
   192  // when the index deletion is chunked. The first call to this method should
   193  // use a zero resume-span. After a chunk of the index is deleted a new resume-
   194  // span is returned.
   195  //
   196  // limit is a limit on the number of index entries deleted in the operation.
   197  func (td *tableDeleter) deleteIndex(
   198  	ctx context.Context, idx *sqlbase.IndexDescriptor, resume roachpb.Span, limit int64, traceKV bool,
   199  ) (roachpb.Span, error) {
   200  	if idx.IsInterleaved() {
   201  		if log.V(2) {
   202  			log.Info(ctx, "delete forced to scan: table is interleaved")
   203  		}
   204  		return td.deleteIndexScan(ctx, idx, resume, limit, traceKV)
   205  	}
   206  	return td.deleteIndexFast(ctx, idx, resume, limit, traceKV)
   207  }
   208  
   209  func (td *tableDeleter) deleteIndexFast(
   210  	ctx context.Context, idx *sqlbase.IndexDescriptor, resume roachpb.Span, limit int64, traceKV bool,
   211  ) (roachpb.Span, error) {
   212  	if resume.Key == nil {
   213  		resume = td.rd.Helper.TableDesc.IndexSpan(td.rd.Helper.Codec, idx.ID)
   214  	}
   215  
   216  	if traceKV {
   217  		log.VEventf(ctx, 2, "DelRange %s - %s", resume.Key, resume.EndKey)
   218  	}
   219  	td.b.DelRange(resume.Key, resume.EndKey, false /* returnKeys */)
   220  	td.b.Header.MaxSpanRequestKeys = limit
   221  	if _, err := td.finalize(ctx, traceKV); err != nil {
   222  		return resume, err
   223  	}
   224  	if l := len(td.b.Results); l != 1 {
   225  		panic(fmt.Sprintf("%d results returned, expected 1", l))
   226  	}
   227  	return td.b.Results[0].ResumeSpanAsValue(), nil
   228  }
   229  
   230  func (td *tableDeleter) clearIndex(ctx context.Context, idx *sqlbase.IndexDescriptor) error {
   231  	if idx.IsInterleaved() {
   232  		return errors.Errorf("unexpected interleaved index %d", idx.ID)
   233  	}
   234  
   235  	sp := td.rd.Helper.TableDesc.IndexSpan(td.rd.Helper.Codec, idx.ID)
   236  
   237  	// ClearRange cannot be run in a transaction, so create a
   238  	// non-transactional batch to send the request.
   239  	b := &kv.Batch{}
   240  	b.AddRawRequest(&roachpb.ClearRangeRequest{
   241  		RequestHeader: roachpb.RequestHeader{
   242  			Key:    sp.Key,
   243  			EndKey: sp.EndKey,
   244  		},
   245  	})
   246  	return td.txn.DB().Run(ctx, b)
   247  }
   248  
   249  func (td *tableDeleter) deleteIndexScan(
   250  	ctx context.Context, idx *sqlbase.IndexDescriptor, resume roachpb.Span, limit int64, traceKV bool,
   251  ) (roachpb.Span, error) {
   252  	if resume.Key == nil {
   253  		resume = td.rd.Helper.TableDesc.PrimaryIndexSpan(td.rd.Helper.Codec)
   254  	}
   255  
   256  	var valNeededForCol util.FastIntSet
   257  	for _, idx := range td.rd.FetchColIDtoRowIndex {
   258  		valNeededForCol.Add(idx)
   259  	}
   260  
   261  	var rf row.Fetcher
   262  	tableArgs := row.FetcherTableArgs{
   263  		Desc:            td.rd.Helper.TableDesc,
   264  		Index:           &td.rd.Helper.TableDesc.PrimaryIndex,
   265  		ColIdxMap:       td.rd.FetchColIDtoRowIndex,
   266  		Cols:            td.rd.FetchCols,
   267  		ValNeededForCol: valNeededForCol,
   268  	}
   269  	if err := rf.Init(
   270  		td.rd.Helper.Codec,
   271  		false, /* reverse */
   272  		// TODO(nvanbenschoten): it might make sense to use a FOR_UPDATE locking
   273  		// strength here. Consider hooking this in to the same knob that will
   274  		// control whether we perform locking implicitly during DELETEs.
   275  		sqlbase.ScanLockingStrength_FOR_NONE,
   276  		false, /* returnRangeInfo */
   277  		false, /* isCheck */
   278  		td.alloc,
   279  		tableArgs,
   280  	); err != nil {
   281  		return resume, err
   282  	}
   283  	if err := rf.StartScan(ctx, td.txn, roachpb.Spans{resume}, true /* limit batches */, 0, traceKV); err != nil {
   284  		return resume, err
   285  	}
   286  
   287  	for i := int64(0); i < limit; i++ {
   288  		datums, _, _, err := rf.NextRowDecoded(ctx)
   289  		if err != nil {
   290  			return resume, err
   291  		}
   292  		if datums == nil {
   293  			// Done deleting all rows.
   294  			resume = roachpb.Span{}
   295  			break
   296  		}
   297  		if err := td.rd.DeleteIndexRow(ctx, td.b, idx, datums, traceKV); err != nil {
   298  			return resume, err
   299  		}
   300  	}
   301  	if resume.Key != nil {
   302  		// Update the resume start key for the next iteration.
   303  		resume.Key = rf.Key()
   304  	}
   305  	_, err := td.finalize(ctx, traceKV)
   306  	return resume, err
   307  }
   308  
   309  func (td *tableDeleter) tableDesc() *sqlbase.ImmutableTableDescriptor {
   310  	return td.rd.Helper.TableDesc
   311  }
   312  
   313  func (td *tableDeleter) close(_ context.Context) {}