github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/execution/ingestion/uploader/gcp_uploader.go (about)

     1  package uploader
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"cloud.google.com/go/storage"
     8  	"github.com/hashicorp/go-multierror"
     9  	"github.com/rs/zerolog"
    10  
    11  	"github.com/onflow/flow-go/engine/execution"
    12  )
    13  
    14  type GCPBucketUploader struct {
    15  	log    zerolog.Logger
    16  	bucket *storage.BucketHandle
    17  	ctx    context.Context
    18  }
    19  
    20  func NewGCPBucketUploader(ctx context.Context, bucketName string, log zerolog.Logger) (*GCPBucketUploader, error) {
    21  
    22  	// no need to close the client according to documentation
    23  	// https://pkg.go.dev/cloud.google.com/go/storage#Client.Close
    24  	client, err := storage.NewClient(ctx)
    25  	if err != nil {
    26  		return nil, fmt.Errorf("cannot create GCP Bucket client: %w", err)
    27  	}
    28  	bucket := client.Bucket(bucketName)
    29  
    30  	// try accessing buckets to validate settings
    31  	_, err = bucket.Attrs(ctx)
    32  	if err != nil {
    33  		return nil, fmt.Errorf("error while listing bucket attributes: %w", err)
    34  	}
    35  
    36  	return &GCPBucketUploader{
    37  		bucket: bucket,
    38  		log:    log.With().Str("subcomponent", "gcp_bucket_uploader").Logger(),
    39  		ctx:    ctx,
    40  	}, nil
    41  }
    42  
    43  // Upload uploads the computation result to the configured GCP bucket.
    44  // All errors returned from this function can be considered benign.
    45  func (u *GCPBucketUploader) Upload(computationResult *execution.ComputationResult) error {
    46  	var errs *multierror.Error
    47  
    48  	objectName := GCPBlockDataObjectName(computationResult)
    49  	object := u.bucket.Object(objectName)
    50  
    51  	writer := object.NewWriter(u.ctx)
    52  
    53  	// serialize and write computation result to upload stream
    54  	err := WriteComputationResultsTo(computationResult, writer)
    55  
    56  	if err != nil {
    57  		errs = multierror.Append(errs, fmt.Errorf("error while writing computation result to GCP object: %w", err))
    58  		// fall through because we always want to close the writer
    59  	}
    60  
    61  	// flush and close the stream
    62  	err = writer.Close()
    63  
    64  	// this occasionally fails with HTTP 5xx errors due to flakiness on the network/GCP API
    65  	if err != nil {
    66  		errs = multierror.Append(errs, fmt.Errorf("error while closing GCP object: %w", err))
    67  	}
    68  
    69  	return errs.ErrorOrNil()
    70  }
    71  
    72  func GCPBlockDataObjectName(computationResult *execution.ComputationResult) string {
    73  	return fmt.Sprintf("%s.cbor", computationResult.ExecutableBlock.ID().String())
    74  }