Skip to content

Commit 12a03cf

Browse files
dnerdyfsouzaStevenACoffman
authored
Stream object downloads instead of buffering objects in memory (#829)
* Stream object downloads instead of buffering object in memory Fixes #828. This PR makes it so objects in the FS backed are streamed from disk instead of being loaded into memory. This drastically reduces memory usage when working with large files, especially when performing small concurrent range reads on a large file. A StreamingObject type has been added to both the fakestorage and API packages. This type contains a io.ReadSeekCloser that can be used to access object data. This change sets the stage for fixing #669 and #397. #669 isn't fixed because it will require re-working of how resumable uploads are stored (though other upload types have been made streaming in this PR). Also, fixing #397 would require changing how initial objects are specified when starting the server, which with this approach would require changing the public API. Regarding the public API: the server methods for working with objects have been changed to use StreamingObject instead of Object. This is a breaking change. It would be possible to make streaming backward compatible by adding some metadata and methods to Object (to distinguish between streaming and buffered objects), but I went with this approach because it made identifying the code that needed to be updated easier; the compiler pointed out the locations to update instead of needing to track down runtime and test errors. An additional improvement, which is compatible with the backward compatible approach, would be to add an OpenForReading method on Object so callers that are currently responsible for closing objects also explicitly open objects (which would make the responsibility clearer). Anyway, I wanted to get this initial approach up for feedback before putting in additional work. * Fix typo in xattr_windows.go * Fix staticcheck lint errors. * Attempt to fix renaming over open files on Windows * Add build directive to rename_unix.go * Close objects when listing objects and in tests * Use compatOpenForWritingAllowingRename for temp file * Close temp file * Move the Windows "temporary delete path" to the temp dir * Close initial objects * Bring back original non-streaming public APIs * Apply suggestions from code review Applying it myself so we can ship this. I'll run `go fix` after merging. Co-authored-by: Steve Coffman <[email protected]> Co-authored-by: fsouza <[email protected]> Co-authored-by: Steve Coffman <[email protected]>
1 parent 9b1abb7 commit 12a03cf

19 files changed

+653
-219
lines changed

fakestorage/object.go

+200-75
Large diffs are not rendered by default.

fakestorage/server.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -183,13 +183,13 @@ func NewServerWithOptions(options Options) (*Server, error) {
183183
}
184184

185185
func newServer(options Options) (*Server, error) {
186-
backendObjects := toBackendObjects(options.InitialObjects)
186+
backendObjects := bufferedObjectsToBackendObjects(options.InitialObjects)
187187
var backendStorage backend.Storage
188188
var err error
189189
if options.StorageRoot != "" {
190190
backendStorage, err = backend.NewStorageFS(backendObjects, options.StorageRoot)
191191
} else {
192-
backendStorage = backend.NewStorageMemory(backendObjects)
192+
backendStorage, err = backend.NewStorageMemory(backendObjects)
193193
}
194194
if err != nil {
195195
return nil, err

fakestorage/server_test.go

+7-4
Original file line numberDiff line numberDiff line change
@@ -895,15 +895,13 @@ func TestServerBatchRequest(t *testing.T) {
895895
type fakeEventFields struct {
896896
BucketName string
897897
Name string
898-
Content []byte
899898
Metadata map[string]string
900899
}
901900

902901
func fakeEventFieldsFromObject(obj Object) fakeEventFields {
903902
return fakeEventFields{
904903
BucketName: obj.BucketName,
905904
Name: obj.Name,
906-
Content: obj.Content,
907905
Metadata: obj.Metadata,
908906
}
909907
}
@@ -923,9 +921,14 @@ type fakeEventManager struct {
923921
events []fakeEvent
924922
}
925923

926-
func (m *fakeEventManager) Trigger(o *backend.Object, eventType notification.EventType, extraEventAttr map[string]string) {
924+
func (m *fakeEventManager) Trigger(o *backend.StreamingObject, eventType notification.EventType, extraEventAttr map[string]string) {
925+
streamingObject := fromBackendObjects([]backend.StreamingObject{*o})[0]
926+
bufferedObject, err := streamingObject.BufferedObject()
927+
if err != nil {
928+
panic(err)
929+
}
927930
m.events = append(m.events, fakeEvent{
928-
obj: fakeEventFieldsFromObject(fromBackendObjects([]backend.Object{*o})[0]),
931+
obj: fakeEventFieldsFromObject(bufferedObject),
929932
eventType: eventType,
930933
})
931934
}

fakestorage/upload.go

+41-41
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
package fakestorage
66

77
import (
8+
"bytes"
89
"crypto/rand"
910
"encoding/json"
11+
"errors"
1012
"fmt"
1113
"io"
1214
"mime"
@@ -122,29 +124,22 @@ func (s *Server) insertFormObject(r *http.Request) xmlResponse {
122124
if err != nil {
123125
return xmlResponse{errorMessage: err.Error()}
124126
}
125-
data, err := io.ReadAll(infile)
126-
if err != nil {
127-
return xmlResponse{errorMessage: err.Error()}
128-
}
129-
md5Hash := checksum.EncodedMd5Hash(data)
130-
obj := Object{
127+
obj := StreamingObject{
131128
ObjectAttrs: ObjectAttrs{
132129
BucketName: bucketName,
133130
Name: name,
134131
ContentType: contentType,
135132
ContentEncoding: contentEncoding,
136-
Crc32c: checksum.EncodedCrc32cChecksum(data),
137-
Md5Hash: md5Hash,
138-
Etag: fmt.Sprintf("%q", md5Hash),
139133
ACL: getObjectACL(predefinedACL),
140134
Metadata: metaData,
141135
},
142-
Content: data,
136+
Content: infile,
143137
}
144-
_, err = s.createObject(obj)
138+
obj, err = s.createObject(obj)
145139
if err != nil {
146140
return xmlResponse{errorMessage: err.Error()}
147141
}
142+
defer obj.Close()
148143
return xmlResponse{status: http.StatusNoContent}
149144
}
150145

@@ -186,7 +181,10 @@ func (s *Server) checkUploadPreconditions(r *http.Request, bucketName string, ob
186181
errorMessage: err.Error(),
187182
}
188183
}
189-
_, err = s.backend.GetObjectWithGeneration(bucketName, objectName, gen)
184+
obj, err := s.backend.GetObjectWithGeneration(bucketName, objectName, gen)
185+
// Calling Close before checking err is okay on objects, and the return
186+
// path below is complicated.
187+
defer obj.Close() //lint:ignore SA5001 // see above
190188
if gen == 0 {
191189
if err != nil {
192190
return &jsonResponse{
@@ -216,31 +214,32 @@ func (s *Server) simpleUpload(bucketName string, r *http.Request) jsonResponse {
216214
errorMessage: "name is required for simple uploads",
217215
}
218216
}
219-
data, err := io.ReadAll(r.Body)
220-
if err != nil {
221-
return jsonResponse{errorMessage: err.Error()}
222-
}
223-
md5Hash := checksum.EncodedMd5Hash(data)
224-
obj := Object{
217+
obj := StreamingObject{
225218
ObjectAttrs: ObjectAttrs{
226219
BucketName: bucketName,
227220
Name: name,
228221
ContentType: r.Header.Get(contentTypeHeader),
229222
ContentEncoding: contentEncoding,
230-
Crc32c: checksum.EncodedCrc32cChecksum(data),
231-
Md5Hash: md5Hash,
232-
Etag: fmt.Sprintf("%q", md5Hash),
233223
ACL: getObjectACL(predefinedACL),
234224
},
235-
Content: data,
225+
Content: notImplementedSeeker{r.Body},
236226
}
237-
obj, err = s.createObject(obj)
227+
obj, err := s.createObject(obj)
238228
if err != nil {
239229
return errToJsonResponse(err)
240230
}
231+
obj.Close()
241232
return jsonResponse{data: newObjectResponse(obj.ObjectAttrs)}
242233
}
243234

235+
type notImplementedSeeker struct {
236+
io.ReadCloser
237+
}
238+
239+
func (s notImplementedSeeker) Seek(offset int64, whence int) (int64, error) {
240+
return 0, errors.New("not implemented")
241+
}
242+
244243
func (s *Server) signedUpload(bucketName string, r *http.Request) jsonResponse {
245244
defer r.Body.Close()
246245
name := mux.Vars(r)["objectName"]
@@ -260,29 +259,22 @@ func (s *Server) signedUpload(bucketName string, r *http.Request) jsonResponse {
260259
}
261260
}
262261

263-
data, err := io.ReadAll(r.Body)
264-
if err != nil {
265-
return jsonResponse{errorMessage: err.Error()}
266-
}
267-
md5Hash := checksum.EncodedMd5Hash(data)
268-
obj := Object{
262+
obj := StreamingObject{
269263
ObjectAttrs: ObjectAttrs{
270264
BucketName: bucketName,
271265
Name: name,
272266
ContentType: r.Header.Get(contentTypeHeader),
273267
ContentEncoding: contentEncoding,
274-
Crc32c: checksum.EncodedCrc32cChecksum(data),
275-
Md5Hash: md5Hash,
276-
Etag: fmt.Sprintf("%q", md5Hash),
277268
ACL: getObjectACL(predefinedACL),
278269
Metadata: metaData,
279270
},
280-
Content: data,
271+
Content: notImplementedSeeker{r.Body},
281272
}
282-
obj, err = s.createObject(obj)
273+
obj, err := s.createObject(obj)
283274
if err != nil {
284275
return errToJsonResponse(err)
285276
}
277+
obj.Close()
286278
return jsonResponse{data: newObjectResponse(obj.ObjectAttrs)}
287279
}
288280

@@ -319,6 +311,9 @@ func (s *Server) multipartUpload(bucketName string, r *http.Request) jsonRespons
319311
)
320312
var contentType string
321313
reader := multipart.NewReader(r.Body, params["boundary"])
314+
315+
var partReaders []io.Reader
316+
322317
part, err := reader.NextPart()
323318
for ; err == nil; part, err = reader.NextPart() {
324319
if metadata == nil {
@@ -327,6 +322,7 @@ func (s *Server) multipartUpload(bucketName string, r *http.Request) jsonRespons
327322
} else {
328323
contentType = part.Header.Get(contentTypeHeader)
329324
content, err = loadContent(part)
325+
partReaders = append(partReaders, bytes.NewReader(content))
330326
}
331327
if err != nil {
332328
break
@@ -346,25 +342,22 @@ func (s *Server) multipartUpload(bucketName string, r *http.Request) jsonRespons
346342
return *resp
347343
}
348344

349-
md5Hash := checksum.EncodedMd5Hash(content)
350-
obj := Object{
345+
obj := StreamingObject{
351346
ObjectAttrs: ObjectAttrs{
352347
BucketName: bucketName,
353348
Name: objName,
354349
ContentType: contentType,
355350
ContentEncoding: metadata.ContentEncoding,
356-
Crc32c: checksum.EncodedCrc32cChecksum(content),
357-
Md5Hash: md5Hash,
358-
Etag: fmt.Sprintf("%q", md5Hash),
359351
ACL: getObjectACL(predefinedACL),
360352
Metadata: metadata.Metadata,
361353
},
362-
Content: content,
354+
Content: notImplementedSeeker{io.NopCloser(io.MultiReader(partReaders...))},
363355
}
364356
obj, err = s.createObject(obj)
365357
if err != nil {
366358
return errToJsonResponse(err)
367359
}
360+
defer obj.Close()
368361
return jsonResponse{data: newObjectResponse(obj.ObjectAttrs)}
369362
}
370363

@@ -451,6 +444,8 @@ func (s *Server) uploadFileContent(r *http.Request) jsonResponse {
451444
return jsonResponse{status: http.StatusNotFound}
452445
}
453446
obj := rawObj.(Object)
447+
// TODO: stream upload file content to and from disk (when using the FS
448+
// backend, at least) instead of loading the entire content into memory.
454449
content, err := loadContent(r.Body)
455450
if err != nil {
456451
return jsonResponse{errorMessage: err.Error()}
@@ -480,7 +475,12 @@ func (s *Server) uploadFileContent(r *http.Request) jsonResponse {
480475
}
481476
if commit {
482477
s.uploads.Delete(uploadID)
483-
obj, err = s.createObject(obj)
478+
streamingObject, err := s.createObject(obj.StreamingObject())
479+
if err != nil {
480+
return errToJsonResponse(err)
481+
}
482+
defer streamingObject.Close()
483+
obj, err = streamingObject.BufferedObject()
484484
if err != nil {
485485
return errToJsonResponse(err)
486486
}

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ require (
1717
cloud.google.com/go v0.104.0 // indirect
1818
cloud.google.com/go/compute v1.7.0 // indirect
1919
cloud.google.com/go/iam v0.3.0 // indirect
20+
github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 // indirect
2021
github.com/davecgh/go-spew v1.1.1 // indirect
2122
github.com/felixge/httpsnoop v1.0.2 // indirect
2223
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect

go.sum

+15
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+Y
2626
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
2727
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
2828
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
29+
cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U=
2930
cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
3031
cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=
32+
cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU=
3133
cloud.google.com/go v0.104.0 h1:gSmWO7DY1vOm0MVU6DNXM11BWHHsTUmsC5cv1fuW5X8=
3234
cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA=
3335
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
@@ -45,9 +47,11 @@ cloud.google.com/go/compute v1.7.0 h1:v/k9Eueb8aAJ0vZuxKMrgm6kPhCLZU9HxFU+AFDs9U
4547
cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
4648
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
4749
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
50+
cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c=
4851
cloud.google.com/go/iam v0.3.0 h1:exkAomrVUuzx9kWFI1wm3KI0uoDeUFPB4kKGzx6x+Gc=
4952
cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
5053
cloud.google.com/go/kms v1.4.0 h1:iElbfoE61VeLhnZcGOltqL8HIly8Nhbe5t6JlH9GXjo=
54+
cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA=
5155
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
5256
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
5357
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
@@ -60,12 +64,15 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
6064
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
6165
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
6266
cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
67+
cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc=
6368
cloud.google.com/go/storage v1.26.0 h1:lYAGjknyDJirSzfwUlkv4Nsnj7od7foxQNH/fqZqles=
6469
cloud.google.com/go/storage v1.26.0/go.mod h1:mk/N7YwIKEWyTvXAWQCIeiCTdLoRH6Pd5xmSnolQLTI=
6570
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
6671
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
6772
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
6873
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
74+
github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 h1:iW0a5ljuFxkLGPNem5Ui+KBjFJzKg4Fv2fnxe4dvzpM=
75+
github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5/go.mod h1:Y2QMoi1vgtOIfc+6DhrMOGkLoGzqSV2rKp4Sm+opsyA=
6976
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
7077
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
7178
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
@@ -321,6 +328,7 @@ golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su
321328
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
322329
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
323330
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
331+
golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
324332
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ=
325333
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
326334
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -343,6 +351,7 @@ golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j
343351
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
344352
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
345353
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
354+
golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
346355
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 h1:2o1E+E8TpNLklK9nHiPiK1uzIYrIHt+cQx3ynCwq9V8=
347356
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
348357
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -417,6 +426,7 @@ golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBc
417426
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
418427
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
419428
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
429+
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
420430
golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
421431
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
422432
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -435,6 +445,7 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
435445
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
436446
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
437447
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
448+
golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
438449
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
439450
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
440451
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -533,6 +544,8 @@ google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69
533544
google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
534545
google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
535546
google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
547+
google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g=
548+
google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
536549
google.golang.org/api v0.94.0 h1:KtKM9ru3nzQioV1HLlUf1cR7vMYJIpgls5VhAYQXIwA=
537550
google.golang.org/api v0.94.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI=
538551
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
@@ -621,7 +634,9 @@ google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP
621634
google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
622635
google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
623636
google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
637+
google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
624638
google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
639+
google.golang.org/genproto v0.0.0-20220810155839-1856144b1d9c/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
625640
google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc h1:Nf+EdcTLHR8qDNN/KfkQL0u0ssxt9OhbaWCl5C0ucEI=
626641
google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
627642
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=

0 commit comments

Comments
 (0)