Skip to content

Commit

Permalink
feat(storage): export gRPC client constructor (#8509)
Browse files Browse the repository at this point in the history
This publicly exports the gRPC client constructor, making it
possible to call directly rather than access via an environment
variable.

Also adds public docs about the gRPC API which is still in private
preview, as well as basic instructions for DirectPath.

Updates benchmarking script to call the new constructor directly
as well.
  • Loading branch information
tritone committed Aug 31, 2023
1 parent 6fedb83 commit 1a928ae
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 33 deletions.
27 changes: 27 additions & 0 deletions storage/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,33 @@ to add a [custom audit logging] header:
// Use client as usual with the context and the additional headers will be sent.
client.Bucket("my-bucket").Attrs(ctx)
# Experimental gRPC API
This package includes support for the Cloud Storage gRPC API, which is currently
in preview. This implementation uses gRPC rather than the current JSON & XML
APIs to make requests to Cloud Storage. If you would like to try the API,
please contact your GCP account rep for more information. The gRPC API is not
yet generally available, so it may be subject to breaking changes.
To create a client which will use gRPC, use the alternate constructor:
ctx := context.Background()
client, err := storage.NewGRPCClient(ctx)
if err != nil {
// TODO: Handle error.
}
// Use client as usual.
If the application is running within GCP, users may get better performance by
enabling DirectPath (enabling requests to skip some proxy steps). To enable,
set the environment variable `GOOGLE_CLOUD_ENABLE_DIRECT_PATH_XDS=true` and add
the following side-effect imports to your application:
import (
_ "google.golang.org/grpc/balancer/rls"
_ "google.golang.org/grpc/xds/googledirectpath"
)
[Cloud Storage IAM docs]: https://cloud.google.com/storage/docs/access-control/iam
[XML POST Object docs]: https://cloud.google.com/storage/docs/xml-api/post-object
[Cloud Storage retry docs]: https://cloud.google.com/storage/docs/retry-strategy
Expand Down
4 changes: 0 additions & 4 deletions storage/http_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ import (

// httpStorageClient is the HTTP-JSON API implementation of the transport-agnostic
// storageClient interface.
//
// This is an experimental API and not intended for public use.
type httpStorageClient struct {
creds *google.Credentials
hc *http.Client
Expand All @@ -59,8 +57,6 @@ type httpStorageClient struct {

// newHTTPStorageClient initializes a new storageClient that uses the HTTP-JSON
// Storage API.
//
// This is an experimental API and not intended for public use.
func newHTTPStorageClient(ctx context.Context, opts ...storageOption) (storageClient, error) {
s := initSettings(opts...)
o := s.clientOption
Expand Down
4 changes: 2 additions & 2 deletions storage/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,9 +241,9 @@ func testConfigGRPC(ctx context.Context, t *testing.T, opts ...option.ClientOpti
t.Skip("Integration tests skipped in short mode")
}

gc, err := newGRPCClient(ctx, opts...)
gc, err := NewGRPCClient(ctx, opts...)
if err != nil {
t.Fatalf("newHybridClient: %v", err)
t.Fatalf("NewGRPCClient: %v", err)
}

return
Expand Down
13 changes: 1 addition & 12 deletions storage/internal/benchmarks/client_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ import (
"crypto/tls"
"log"
"net/http"
"os"
"sync"
"time"

"cloud.google.com/go/storage"
Expand Down Expand Up @@ -174,9 +172,6 @@ func getClient(ctx context.Context, api benchmarkAPI) *storage.Client {
return nil
}

// mutex on starting a client so that we can set an env variable for GRPC clients
var clientMu sync.Mutex

// Client config
type clientConfig struct {
writeBufferSize, readBufferSize int
Expand Down Expand Up @@ -234,9 +229,7 @@ func initializeHTTPClient(ctx context.Context, config clientConfig) (*storage.Cl
}

// Init client
clientMu.Lock()
client, err := storage.NewClient(ctx, opts...)
clientMu.Unlock()

return client, err
}
Expand All @@ -255,11 +248,7 @@ func initializeGRPCClient(ctx context.Context, config clientConfig) (*storage.Cl
opts = append(opts, option.WithGRPCDialOption(grpc.WithReadBufferSize(config.readBufferSize)))
}

clientMu.Lock()
os.Setenv("STORAGE_USE_GRPC", "true")
client, err := storage.NewClient(ctx, opts...)
os.Unsetenv("STORAGE_USE_GRPC")
clientMu.Unlock()
client, err := storage.NewGRPCClient(ctx, opts...)

return client, err
}
29 changes: 14 additions & 15 deletions storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ type Client struct {
useGRPC bool
}

// NewClient creates a new Google Cloud Storage client.
// NewClient creates a new Google Cloud Storage client using the HTTP transport.
// The default scope is ScopeFullControl. To use a different scope, like
// ScopeReadOnly, use option.WithScopes.
//
Expand All @@ -133,12 +133,6 @@ type Client struct {
// You may configure the client by passing in options from the [google.golang.org/api/option]
// package. You may also use options defined in this package, such as [WithJSONReads].
func NewClient(ctx context.Context, opts ...option.ClientOption) (*Client, error) {
// Use the experimental gRPC client if the env var is set.
// This is an experimental API and not intended for public use.
if withGRPC := os.Getenv("STORAGE_USE_GRPC"); withGRPC != "" {
return newGRPCClient(ctx, opts...)
}

var creds *google.Credentials

// In general, it is recommended to use raw.NewService instead of htransport.NewClient
Expand Down Expand Up @@ -220,11 +214,20 @@ func NewClient(ctx context.Context, opts ...option.ClientOption) (*Client, error
}, nil
}

// newGRPCClient creates a new Storage client that initializes a gRPC-based
// client. Calls that have not been implemented in gRPC will panic.
// NewGRPCClient creates a new Storage client using the gRPC transport and API.
// Client methods which have not been implemented in gRPC will return an error.
// In particular, methods for Cloud Pub/Sub notifications are not supported.
//
// The storage gRPC API is still in preview and not yet publicly available.
// If you would like to use the API, please first contact your GCP account rep to
// request access. The API may be subject to breaking changes.
//
// This is an experimental API and not intended for public use.
func newGRPCClient(ctx context.Context, opts ...option.ClientOption) (*Client, error) {
// Clients should be reused instead of created as needed. The methods of Client
// are safe for concurrent use by multiple goroutines.
//
// You may configure the client by passing in options from the [google.golang.org/api/option]
// package.
func NewGRPCClient(ctx context.Context, opts ...option.ClientOption) (*Client, error) {
opts = append(defaultGRPCOptions(), opts...)
tc, err := newGRPCStorageClient(ctx, withClientOptions(opts...))
if err != nil {
Expand Down Expand Up @@ -2187,8 +2190,6 @@ func toProjectResource(project string) string {

// setConditionProtoField uses protobuf reflection to set named condition field
// to the given condition value if supported on the protobuf message.
//
// This is an experimental API and not intended for public use.
func setConditionProtoField(m protoreflect.Message, f string, v int64) bool {
fields := m.Descriptor().Fields()
if rf := fields.ByName(protoreflect.Name(f)); rf != nil {
Expand All @@ -2201,8 +2202,6 @@ func setConditionProtoField(m protoreflect.Message, f string, v int64) bool {

// applyCondsProto validates and attempts to set the conditions on a protobuf
// message using protobuf reflection.
//
// This is an experimental API and not intended for public use.
func applyCondsProto(method string, gen int64, conds *Conditions, msg proto.Message) error {
rmsg := msg.ProtoReflect()

Expand Down

0 comments on commit 1a928ae

Please sign in to comment.