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 }