blob: fdf67e582108bac0bf5d4217fcfddb9df3fdc6bb [file] [log] [blame]
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"context"
"fmt"
"io"
"os"
"path"
"time"
"cloud.google.com/go/storage"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
)
type downloadOpts struct {
client *storage.Client
objectSize int64
bucket string
object string
rangeStart int64
rangeLength int64
downloadToDirectory string
timeout time.Duration
}
func downloadBenchmark(ctx context.Context, dopts downloadOpts) (elapsedTime time.Duration, rerr error) {
var span trace.Span
ctx, span = otel.GetTracerProvider().Tracer(tracerName).Start(ctx, "download")
span.SetAttributes(
attribute.KeyValue{Key: "object_size", Value: attribute.Int64Value(dopts.objectSize)},
attribute.KeyValue{Key: "bucket", Value: attribute.StringValue(dopts.bucket)},
)
defer span.End()
// Set timer
start := time.Now()
// Multiple defer statements execute in LIFO order, so this will be the last
// thing executed. We use named return parameters so that we can set it directly
// and defer the statement so that the time includes typical cleanup steps and
// gets set regardless of errors.
defer func() { elapsedTime = time.Since(start) }()
// Set additional timeout
ctx, cancel := context.WithTimeout(ctx, dopts.timeout)
defer cancel()
o := dopts.client.Bucket(dopts.bucket).Object(dopts.object)
// Create file to download to
f, err := os.Create(path.Join(dopts.downloadToDirectory, o.ObjectName()))
if err != nil {
rerr = fmt.Errorf("os.Create: %w", err)
return
}
defer func() {
closeErr := f.Close()
// if we don't have another error to return, return error for closing file
if rerr == nil {
rerr = closeErr
}
}()
// Get reader from object
objectReader, err := o.NewRangeReader(ctx, dopts.rangeStart, dopts.rangeLength)
if err != nil {
rerr = fmt.Errorf("Object(%q).NewReader: %w", o.ObjectName(), err)
return
}
defer func() {
err := objectReader.Close()
if rerr == nil {
rerr = err
}
}()
// Download
read, err := io.Copy(f, objectReader)
if err != nil {
rerr = fmt.Errorf("io.Copy: %w", err)
return
}
expectedReadBytes := dopts.objectSize
if dopts.rangeLength > 0 {
expectedReadBytes = dopts.rangeLength
}
if read != expectedReadBytes {
rerr = fmt.Errorf("did not read all bytes; read: %d, expected to read: %d", read, dopts.objectSize)
return
}
return
}