github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ccl/storageccl/import.go (about) 1 // Copyright 2017 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 storageccl 10 11 import ( 12 "bytes" 13 "context" 14 "fmt" 15 "io/ioutil" 16 17 "github.com/cockroachdb/cockroach/pkg/base" 18 "github.com/cockroachdb/cockroach/pkg/kv/bulk" 19 "github.com/cockroachdb/cockroach/pkg/kv/kvserver" 20 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/batcheval" 21 "github.com/cockroachdb/cockroach/pkg/roachpb" 22 "github.com/cockroachdb/cockroach/pkg/settings" 23 "github.com/cockroachdb/cockroach/pkg/settings/cluster" 24 "github.com/cockroachdb/cockroach/pkg/storage" 25 "github.com/cockroachdb/cockroach/pkg/util/hlc" 26 "github.com/cockroachdb/cockroach/pkg/util/humanizeutil" 27 "github.com/cockroachdb/cockroach/pkg/util/log" 28 "github.com/cockroachdb/cockroach/pkg/util/retry" 29 "github.com/cockroachdb/errors" 30 ) 31 32 var importBatchSize = func() *settings.ByteSizeSetting { 33 s := settings.RegisterByteSizeSetting( 34 "kv.bulk_ingest.batch_size", 35 "the maximum size of the payload in an AddSSTable request", 36 16<<20, 37 ) 38 return s 39 }() 40 41 var importPKAdderBufferSize = func() *settings.ByteSizeSetting { 42 s := settings.RegisterByteSizeSetting( 43 "kv.bulk_ingest.pk_buffer_size", 44 "the initial size of the BulkAdder buffer handling primary index imports", 45 32<<20, 46 ) 47 return s 48 }() 49 50 var importPKAdderMaxBufferSize = func() *settings.ByteSizeSetting { 51 s := settings.RegisterByteSizeSetting( 52 "kv.bulk_ingest.max_pk_buffer_size", 53 "the maximum size of the BulkAdder buffer handling primary index imports", 54 128<<20, 55 ) 56 return s 57 }() 58 59 var importIndexAdderBufferSize = func() *settings.ByteSizeSetting { 60 s := settings.RegisterByteSizeSetting( 61 "kv.bulk_ingest.index_buffer_size", 62 "the initial size of the BulkAdder buffer handling secondary index imports", 63 32<<20, 64 ) 65 return s 66 }() 67 68 var importIndexAdderMaxBufferSize = func() *settings.ByteSizeSetting { 69 s := settings.RegisterByteSizeSetting( 70 "kv.bulk_ingest.max_index_buffer_size", 71 "the maximum size of the BulkAdder buffer handling secondary index imports", 72 512<<20, 73 ) 74 return s 75 }() 76 77 var importBufferIncrementSize = func() *settings.ByteSizeSetting { 78 s := settings.RegisterByteSizeSetting( 79 "kv.bulk_ingest.buffer_increment", 80 "the size by which the BulkAdder attempts to grow its buffer before flushing", 81 32<<20, 82 ) 83 return s 84 }() 85 86 // commandMetadataEstimate is an estimate of how much metadata Raft will add to 87 // an AddSSTable command. It is intentionally a vast overestimate to avoid 88 // embedding intricate knowledge of the Raft encoding scheme here. 89 const commandMetadataEstimate = 1 << 20 // 1 MB 90 91 func init() { 92 kvserver.SetImportCmd(evalImport) 93 94 // Ensure that the user cannot set the maximum raft command size so low that 95 // more than half of an Import or AddSSTable command will be taken up by Raft 96 // metadata. 97 if commandMetadataEstimate > kvserver.MaxCommandSizeFloor/2 { 98 panic(fmt.Sprintf("raft command size floor (%s) is too small for import commands", 99 humanizeutil.IBytes(kvserver.MaxCommandSizeFloor))) 100 } 101 } 102 103 // MaxImportBatchSize determines the maximum size of the payload in an 104 // AddSSTable request. It uses the ImportBatchSize setting directly unless the 105 // specified value would exceed the maximum Raft command size, in which case it 106 // returns the maximum batch size that will fit within a Raft command. 107 func MaxImportBatchSize(st *cluster.Settings) int64 { 108 desiredSize := importBatchSize.Get(&st.SV) 109 maxCommandSize := kvserver.MaxCommandSize.Get(&st.SV) 110 if desiredSize+commandMetadataEstimate > maxCommandSize { 111 return maxCommandSize - commandMetadataEstimate 112 } 113 return desiredSize 114 } 115 116 // ImportBufferConfigSizes determines the minimum, maximum and step size for the 117 // BulkAdder buffer used in import. 118 func ImportBufferConfigSizes(st *cluster.Settings, isPKAdder bool) (int64, func() int64, int64) { 119 if isPKAdder { 120 return importPKAdderBufferSize.Get(&st.SV), 121 func() int64 { return importPKAdderMaxBufferSize.Get(&st.SV) }, 122 importBufferIncrementSize.Get(&st.SV) 123 } 124 return importIndexAdderBufferSize.Get(&st.SV), 125 func() int64 { return importIndexAdderMaxBufferSize.Get(&st.SV) }, 126 importBufferIncrementSize.Get(&st.SV) 127 } 128 129 // evalImport bulk loads key/value entries. 130 func evalImport(ctx context.Context, cArgs batcheval.CommandArgs) (*roachpb.ImportResponse, error) { 131 args := cArgs.Args.(*roachpb.ImportRequest) 132 db := cArgs.EvalCtx.DB() 133 // args.Rekeys could be using table descriptors from either the old or new 134 // foreign key representation on the table descriptor, but this is fine 135 // because foreign keys don't matter for the key rewriter. 136 kr, err := MakeKeyRewriterFromRekeys(args.Rekeys) 137 if err != nil { 138 return nil, errors.Wrap(err, "make key rewriter") 139 } 140 if err := cArgs.EvalCtx.GetLimiters().ConcurrentImportRequests.Begin(ctx); err != nil { 141 return nil, err 142 } 143 defer cArgs.EvalCtx.GetLimiters().ConcurrentImportRequests.Finish() 144 145 var iters []storage.SimpleIterator 146 for _, file := range args.Files { 147 log.VEventf(ctx, 2, "import file %s %s", file.Path, args.Key) 148 149 dir, err := cArgs.EvalCtx.GetExternalStorage(ctx, file.Dir) 150 if err != nil { 151 return nil, err 152 } 153 defer func() { 154 if err := dir.Close(); err != nil { 155 log.Warningf(ctx, "close export storage failed %v", err) 156 } 157 }() 158 159 const maxAttempts = 3 160 var fileContents []byte 161 if err := retry.WithMaxAttempts(ctx, base.DefaultRetryOptions(), maxAttempts, func() error { 162 f, err := dir.ReadFile(ctx, file.Path) 163 if err != nil { 164 return err 165 } 166 defer f.Close() 167 fileContents, err = ioutil.ReadAll(f) 168 return err 169 }); err != nil { 170 return nil, errors.Wrapf(err, "fetching %q", file.Path) 171 } 172 dataSize := int64(len(fileContents)) 173 log.Eventf(ctx, "fetched file (%s)", humanizeutil.IBytes(dataSize)) 174 175 if args.Encryption != nil { 176 fileContents, err = DecryptFile(fileContents, args.Encryption.Key) 177 if err != nil { 178 return nil, err 179 } 180 } 181 182 if len(file.Sha512) > 0 { 183 checksum, err := SHA512ChecksumData(fileContents) 184 if err != nil { 185 return nil, err 186 } 187 if !bytes.Equal(checksum, file.Sha512) { 188 return nil, errors.Errorf("checksum mismatch for %s", file.Path) 189 } 190 } 191 192 iter, err := storage.NewMemSSTIterator(fileContents, false) 193 if err != nil { 194 return nil, err 195 } 196 197 defer iter.Close() 198 iters = append(iters, iter) 199 } 200 201 batcher, err := bulk.MakeSSTBatcher(ctx, db, cArgs.EvalCtx.ClusterSettings(), func() int64 { return MaxImportBatchSize(cArgs.EvalCtx.ClusterSettings()) }) 202 if err != nil { 203 return nil, err 204 } 205 defer batcher.Close() 206 207 startKeyMVCC, endKeyMVCC := storage.MVCCKey{Key: args.DataSpan.Key}, storage.MVCCKey{Key: args.DataSpan.EndKey} 208 iter := storage.MakeMultiIterator(iters) 209 defer iter.Close() 210 var keyScratch, valueScratch []byte 211 212 for iter.SeekGE(startKeyMVCC); ; { 213 ok, err := iter.Valid() 214 if err != nil { 215 return nil, err 216 } 217 if !ok { 218 break 219 } 220 221 if args.EndTime != (hlc.Timestamp{}) { 222 // TODO(dan): If we have to skip past a lot of versions to find the 223 // latest one before args.EndTime, then this could be slow. 224 if args.EndTime.Less(iter.UnsafeKey().Timestamp) { 225 iter.Next() 226 continue 227 } 228 } 229 230 if !ok || !iter.UnsafeKey().Less(endKeyMVCC) { 231 break 232 } 233 if len(iter.UnsafeValue()) == 0 { 234 // Value is deleted. 235 iter.NextKey() 236 continue 237 } 238 239 keyScratch = append(keyScratch[:0], iter.UnsafeKey().Key...) 240 valueScratch = append(valueScratch[:0], iter.UnsafeValue()...) 241 key := storage.MVCCKey{Key: keyScratch, Timestamp: iter.UnsafeKey().Timestamp} 242 value := roachpb.Value{RawBytes: valueScratch} 243 iter.NextKey() 244 245 key.Key, ok, err = kr.RewriteKey(key.Key, false /* isFromSpan */) 246 if err != nil { 247 return nil, err 248 } 249 if !ok { 250 // If the key rewriter didn't match this key, it's not data for the 251 // table(s) we're interested in. 252 if log.V(3) { 253 log.Infof(ctx, "skipping %s %s", key.Key, value.PrettyPrint()) 254 } 255 continue 256 } 257 258 // Rewriting the key means the checksum needs to be updated. 259 value.ClearChecksum() 260 value.InitChecksum(key.Key) 261 262 if log.V(3) { 263 log.Infof(ctx, "Put %s -> %s", key.Key, value.PrettyPrint()) 264 } 265 if err := batcher.AddMVCCKey(ctx, key, value.RawBytes); err != nil { 266 return nil, errors.Wrapf(err, "adding to batch: %s -> %s", key, value.PrettyPrint()) 267 } 268 } 269 // Flush out the last batch. 270 if err := batcher.Flush(ctx); err != nil { 271 return nil, err 272 } 273 log.Event(ctx, "done") 274 return &roachpb.ImportResponse{Imported: batcher.GetSummary()}, nil 275 }