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  }