github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ccl/changefeedccl/rowfetcher_cache.go (about)

     1  // Copyright 2018 The Cockroach Authors.
     2  //
     3  // Licensed as a CockroachDB Enterprise file under the Cockroach Community
     4  // License (the "License"); you may not use this file except in compliance with
     5  // the License. You may obtain a copy of the License at
     6  //
     7  //     https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt
     8  
     9  package changefeedccl
    10  
    11  import (
    12  	"context"
    13  
    14  	"github.com/cockroachdb/cockroach/pkg/keys"
    15  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    16  	"github.com/cockroachdb/cockroach/pkg/sql/catalog/lease"
    17  	"github.com/cockroachdb/cockroach/pkg/sql/row"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    19  	"github.com/cockroachdb/cockroach/pkg/util"
    20  	"github.com/cockroachdb/cockroach/pkg/util/encoding"
    21  	"github.com/cockroachdb/cockroach/pkg/util/hlc"
    22  )
    23  
    24  // rowFetcherCache maintains a cache of single table RowFetchers. Given a key
    25  // with an mvcc timestamp, it retrieves the correct TableDescriptor for that key
    26  // and returns a Fetcher initialized with that table. This Fetcher's
    27  // StartScanFrom can be used to turn that key (or all the keys making up the
    28  // column families of one row) into a row.
    29  type rowFetcherCache struct {
    30  	codec    keys.SQLCodec
    31  	leaseMgr *lease.Manager
    32  	fetchers map[idVersion]*row.Fetcher
    33  
    34  	a sqlbase.DatumAlloc
    35  }
    36  
    37  type idVersion struct {
    38  	id      sqlbase.ID
    39  	version sqlbase.DescriptorVersion
    40  }
    41  
    42  func newRowFetcherCache(codec keys.SQLCodec, leaseMgr *lease.Manager) *rowFetcherCache {
    43  	return &rowFetcherCache{
    44  		codec:    codec,
    45  		leaseMgr: leaseMgr,
    46  		fetchers: make(map[idVersion]*row.Fetcher),
    47  	}
    48  }
    49  
    50  func (c *rowFetcherCache) TableDescForKey(
    51  	ctx context.Context, key roachpb.Key, ts hlc.Timestamp,
    52  ) (*sqlbase.ImmutableTableDescriptor, error) {
    53  	var tableDesc *sqlbase.ImmutableTableDescriptor
    54  	key, err := c.codec.StripTenantPrefix(key)
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  	for skippedCols := 0; ; {
    59  		remaining, tableID, _, err := sqlbase.DecodePartialTableIDIndexID(key)
    60  		if err != nil {
    61  			return nil, err
    62  		}
    63  		// No caching of these are attempted, since the lease manager does its
    64  		// own caching.
    65  		tableDesc, _, err = c.leaseMgr.Acquire(ctx, ts, tableID)
    66  		if err != nil {
    67  			// Manager can return all kinds of errors during chaos, but based on
    68  			// its usage, none of them should ever be terminal.
    69  			return nil, MarkRetryableError(err)
    70  		}
    71  		// Immediately release the lease, since we only need it for the exact
    72  		// timestamp requested.
    73  		if err := c.leaseMgr.Release(tableDesc); err != nil {
    74  			return nil, err
    75  		}
    76  
    77  		// Skip over the column data.
    78  		for ; skippedCols < len(tableDesc.PrimaryIndex.ColumnIDs); skippedCols++ {
    79  			l, err := encoding.PeekLength(remaining)
    80  			if err != nil {
    81  				return nil, err
    82  			}
    83  			remaining = remaining[l:]
    84  		}
    85  		var interleaved bool
    86  		remaining, interleaved = encoding.DecodeIfInterleavedSentinel(remaining)
    87  		if !interleaved {
    88  			break
    89  		}
    90  		key = remaining
    91  	}
    92  
    93  	return tableDesc, nil
    94  }
    95  
    96  func (c *rowFetcherCache) RowFetcherForTableDesc(
    97  	tableDesc *sqlbase.ImmutableTableDescriptor,
    98  ) (*row.Fetcher, error) {
    99  	idVer := idVersion{id: tableDesc.ID, version: tableDesc.Version}
   100  	if rf, ok := c.fetchers[idVer]; ok {
   101  		return rf, nil
   102  	}
   103  	// TODO(dan): Allow for decoding a subset of the columns.
   104  	colIdxMap := make(map[sqlbase.ColumnID]int)
   105  	var valNeededForCol util.FastIntSet
   106  	for colIdx := range tableDesc.Columns {
   107  		colIdxMap[tableDesc.Columns[colIdx].ID] = colIdx
   108  		valNeededForCol.Add(colIdx)
   109  	}
   110  
   111  	var rf row.Fetcher
   112  	if err := rf.Init(
   113  		c.codec,
   114  		false, /* reverse */
   115  		sqlbase.ScanLockingStrength_FOR_NONE,
   116  		false, /* returnRangeInfo */
   117  		false, /* isCheck */
   118  		&c.a,
   119  		row.FetcherTableArgs{
   120  			Spans:            tableDesc.AllIndexSpans(c.codec),
   121  			Desc:             tableDesc,
   122  			Index:            &tableDesc.PrimaryIndex,
   123  			ColIdxMap:        colIdxMap,
   124  			IsSecondaryIndex: false,
   125  			Cols:             tableDesc.Columns,
   126  			ValNeededForCol:  valNeededForCol,
   127  		},
   128  	); err != nil {
   129  		return nil, err
   130  	}
   131  	// TODO(dan): Bound the size of the cache. Resolved notifications will let
   132  	// us evict anything for timestamps entirely before the notification. Then
   133  	// probably an LRU just in case?
   134  	c.fetchers[idVer] = &rf
   135  	return &rf, nil
   136  }