Skip to content

Commit 7de4a7d

Browse files
authored
feat(storage): support single-shot uploads in gRPC (#8348)
When ChunkSize is zero, use a single shot upload (leaving the stream open between messages) rather than a resumable session. This matches better with what we do in JSON. Also reduce the buffer size to the minimum (256k). Fixes #7798
1 parent e277213 commit 7de4a7d

2 files changed

Lines changed: 38 additions & 18 deletions

File tree

storage/grpc_client.go

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,11 +1044,13 @@ func (c *grpcStorageClient) OpenWriter(params *openWriterParams, opts ...storage
10441044
}
10451045

10461046
// The chunk buffer is full, but there is no end in sight. This
1047-
// means that a resumable upload will need to be used to send
1047+
// means that either:
1048+
// 1. A resumable upload will need to be used to send
10481049
// multiple chunks, until we are done reading data. Start a
10491050
// resumable upload if it has not already been started.
1050-
// Otherwise, all data will be sent over a single gRPC stream.
1051-
if !doneReading && gw.upid == "" {
1051+
// 2. ChunkSize of zero may also have a full buffer, but a resumable
1052+
// session should not be initiated in this case.
1053+
if !doneReading && gw.upid == "" && params.chunkSize != 0 {
10521054
err = gw.startResumableUpload()
10531055
if err != nil {
10541056
err = checkCanceled(err)
@@ -1065,11 +1067,15 @@ func (c *grpcStorageClient) OpenWriter(params *openWriterParams, opts ...storage
10651067
pr.CloseWithError(err)
10661068
return
10671069
}
1070+
10681071
// At this point, the current buffer has been uploaded. For resumable
1069-
// uploads, capture the committed offset here in case the upload was not
1070-
// finalized and another chunk is to be uploaded.
1071-
if gw.upid != "" {
1072+
// uploads and chunkSize = 0, capture the committed offset here in case
1073+
// the upload was not finalized and another chunk is to be uploaded. Call
1074+
// the progress function for resumable uploads only.
1075+
if gw.upid != "" || gw.chunkSize == 0 {
10721076
offset = off
1077+
}
1078+
if gw.upid != "" {
10731079
progress(offset)
10741080
}
10751081

@@ -1485,14 +1491,11 @@ func newGRPCWriter(c *grpcStorageClient, params *openWriterParams, r io.Reader)
14851491
size += googleapi.MinUploadChunkSize - (size % googleapi.MinUploadChunkSize)
14861492
}
14871493

1494+
// A completely bufferless upload is not possible as it is in JSON because
1495+
// the buffer must be provided to the message. However use the minimum size
1496+
// possible in this case.
14881497
if params.chunkSize == 0 {
1489-
// TODO: Should we actually use the minimum of 256 KB here when the user
1490-
// indicates they want minimal memory usage? We cannot do a zero-copy,
1491-
// bufferless upload like HTTP/JSON can.
1492-
// TODO: We need to determine if we can avoid starting a
1493-
// resumable upload when the user *plans* to send more than bufSize but
1494-
// with a bufferless upload.
1495-
size = maxPerMessageWriteSize
1498+
size = googleapi.MinUploadChunkSize
14961499
}
14971500

14981501
return &gRPCWriter{
@@ -1505,6 +1508,7 @@ func newGRPCWriter(c *grpcStorageClient, params *openWriterParams, r io.Reader)
15051508
conds: params.conds,
15061509
encryptionKey: params.encryptionKey,
15071510
sendCRC32C: params.sendCRC32C,
1511+
chunkSize: params.chunkSize,
15081512
}
15091513
}
15101514

@@ -1524,6 +1528,7 @@ type gRPCWriter struct {
15241528
settings *settings
15251529

15261530
sendCRC32C bool
1531+
chunkSize int
15271532

15281533
// The gRPC client-stream used for sending buffers.
15291534
stream storagepb.Storage_WriteObjectClient
@@ -1589,7 +1594,6 @@ func (w *gRPCWriter) uploadBuffer(recvd int, start int64, doneReading bool) (*st
15891594
offset := start
15901595
toWrite := w.buf[:recvd]
15911596
for {
1592-
first := sent == 0
15931597
// This indicates that this is the last message and the remaining
15941598
// data fits in one message.
15951599
belowLimit := recvd-sent <= limit
@@ -1612,10 +1616,10 @@ func (w *gRPCWriter) uploadBuffer(recvd int, start int64, doneReading bool) (*st
16121616
FinishWrite: finishWrite,
16131617
}
16141618

1615-
// Open a new stream and set the first_message field on the request.
1616-
// The first message on the WriteObject stream must either be the
1617-
// Object or the Resumable Upload ID.
1618-
if first {
1619+
// Open a new stream if necessary and set the first_message field on
1620+
// the request. The first message on the WriteObject stream must either
1621+
// be the Object or the Resumable Upload ID.
1622+
if w.stream == nil {
16191623
ctx := gapic.InsertMetadata(w.ctx, metadata.Pairs("x-goog-request-params", fmt.Sprintf("bucket=projects/_/buckets/%s", url.QueryEscape(w.bucket))))
16201624
w.stream, err = w.c.raw.WriteObject(ctx)
16211625
if err != nil {
@@ -1678,6 +1682,13 @@ func (w *gRPCWriter) uploadBuffer(recvd int, start int64, doneReading bool) (*st
16781682
continue
16791683
}
16801684

1685+
// The buffer has been uploaded and there is still more data to be
1686+
// uploaded, but this is not a resumable upload session. Therefore
1687+
// keep the stream open and don't commit yet.
1688+
if !finishWrite && w.chunkSize == 0 {
1689+
return nil, offset, false, nil
1690+
}
1691+
16811692
// Done sending data. Close the stream to "commit" the data sent.
16821693
resp, finalized, err := w.commit()
16831694
// Retriable errors mean we should start over and attempt to

storage/integration_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2393,6 +2393,15 @@ func TestIntegration_WriterChunksize(t *testing.T) {
23932393
if callbacks != test.wantCallbacks {
23942394
t.Errorf("ProgressFunc was called %d times, expected %d", callbacks, test.wantCallbacks)
23952395
}
2396+
2397+
// Confirm all bytes were uploaded.
2398+
attrs, err := obj.Attrs(ctx)
2399+
if err != nil {
2400+
t.Fatalf("obj.Attrs: %v", err)
2401+
}
2402+
if attrs.Size != int64(objSize) {
2403+
t.Errorf("incorrect number of bytes written; got %v, want %v", attrs.Size, objSize)
2404+
}
23962405
})
23972406
}
23982407
})

0 commit comments

Comments
 (0)