@@ -9,9 +9,12 @@ import (
9
9
"time"
10
10
11
11
remoteexecution "github.com/bitrise-io/bitrise-build-cache-cli/proto/build/bazel/remote/execution/v2"
12
+ "github.com/bitrise-io/go-utils/retry"
12
13
"github.com/dustin/go-humanize"
13
14
"google.golang.org/genproto/googleapis/bytestream"
15
+ "google.golang.org/grpc/codes"
14
16
"google.golang.org/grpc/metadata"
17
+ "google.golang.org/grpc/status"
15
18
)
16
19
17
20
type PutParams struct {
@@ -26,12 +29,17 @@ type FileDigest struct {
26
29
}
27
30
28
31
func (c * Client ) GetCapabilities (ctx context.Context ) error {
29
- timeoutCtx , cancel := context .WithTimeout (ctx , 10 * time .Second )
32
+ timeoutCtx , cancel := context .WithTimeout (ctx , 5 * time .Second )
30
33
defer cancel ()
31
34
callCtx := metadata .NewOutgoingContext (timeoutCtx , c .getMethodCallMetadata ())
32
35
33
36
_ , err := c .capabilitiesClient .GetCapabilities (callCtx , & remoteexecution.GetCapabilitiesRequest {})
34
37
if err != nil {
38
+ st , ok := status .FromError (err )
39
+ if ok && st .Code () == codes .Unauthenticated {
40
+ return ErrCacheUnauthenticated
41
+ }
42
+
35
43
return fmt .Errorf ("get capabilities: %w" , err )
36
44
}
37
45
@@ -49,6 +57,11 @@ func (c *Client) InitiatePut(ctx context.Context, params PutParams) (io.WriteClo
49
57
50
58
stream , err := c .bitriseKVClient .Put (ctx )
51
59
if err != nil {
60
+ st , ok := status .FromError (err )
61
+ if ok && st .Code () == codes .Unauthenticated {
62
+ return nil , ErrCacheUnauthenticated
63
+ }
64
+
52
65
return nil , fmt .Errorf ("initiate put: %w" , err )
53
66
}
54
67
@@ -75,6 +88,11 @@ func (c *Client) InitiateGet(ctx context.Context, name string) (io.ReadCloser, e
75
88
}
76
89
stream , err := c .bitriseKVClient .Get (ctx , readReq )
77
90
if err != nil {
91
+ st , ok := status .FromError (err )
92
+ if ok && st .Code () == codes .Unauthenticated {
93
+ return nil , ErrCacheUnauthenticated
94
+ }
95
+
78
96
return nil , fmt .Errorf ("initiate get: %w" , err )
79
97
}
80
98
@@ -104,55 +122,86 @@ func (c *Client) Delete(ctx context.Context, name string) error {
104
122
return nil
105
123
}
106
124
107
- func (c * Client ) FindMissing (ctx context.Context , digests []* FileDigest ) ([]* FileDigest , error ) {
108
- var missingBlobs []* FileDigest
109
- blobDigests := convertToBlobDigests (digests )
110
- req := & remoteexecution.FindMissingBlobsRequest {
111
- BlobDigests : blobDigests ,
112
- }
113
- c .logger .Debugf ("Size of FindMissingBlobs request for %d blobs is %s" , len (digests ), humanize .Bytes (uint64 (len (req .String ()))))
114
- gRPCLimitBytes := 4 * 1024 * 1024 // gRPC limit is 4 MiB
115
- if len (req .String ()) > gRPCLimitBytes {
116
- // Chunk up request blobs to fit into gRPC limits
117
- // Calculate the unit size of a blob (in practice can differ to the theoretical sha256(32 bytes) + size(8 bytes) = 40 bytes)
118
- digestUnitSize := float64 (len (req .String ())) / float64 (len (digests ))
119
- maxDigests := int (float64 (gRPCLimitBytes ) / digestUnitSize )
120
- for startIndex := 0 ; startIndex < len (digests ); startIndex += maxDigests {
121
- endIndex := startIndex + maxDigests
122
- if endIndex > len (digests ) {
123
- endIndex = len (digests )
124
- }
125
- req .BlobDigests = blobDigests [startIndex :endIndex ]
126
-
127
- timeoutCtx , cancel := context .WithTimeout (ctx , 20 * time .Second )
128
- callCtx := metadata .NewOutgoingContext (timeoutCtx , c .getMethodCallMetadata ())
129
-
130
- c .logger .Debugf ("Calling FindMissingBlobs for chunk: digests[%d:%d]" , startIndex , endIndex )
131
- resp , err := c .casClient .FindMissingBlobs (callCtx , req )
132
-
133
- cancel ()
134
- if err != nil {
135
- return nil , fmt .Errorf ("find missing blobs[%d:%d]: %w" , startIndex , endIndex , err )
136
- }
137
- missingBlobs = append (missingBlobs , convertToFileDigests (resp .GetMissingBlobDigests ())... )
125
+ func (c * Client ) findMissing (ctx context.Context ,
126
+ req * remoteexecution.FindMissingBlobsRequest ) ([]* FileDigest , error ) {
127
+ var resp * remoteexecution.FindMissingBlobsResponse
128
+ err := retry .Times (3 ).Wait (3 * time .Second ).TryWithAbort (func (attempt uint ) (error , bool ) {
129
+ if attempt > 0 {
130
+ c .logger .Debugf ("Retrying FindMissingBlobs... (attempt %d)" , attempt )
138
131
}
139
- } else {
132
+
140
133
timeoutCtx , cancel := context .WithTimeout (ctx , 20 * time .Second )
141
134
callCtx := metadata .NewOutgoingContext (timeoutCtx , c .getMethodCallMetadata ())
142
135
143
- resp , err := c .casClient .FindMissingBlobs (callCtx , req )
136
+ var err error
137
+ resp , err = c .casClient .FindMissingBlobs (callCtx , req )
144
138
145
139
cancel ()
146
140
147
141
if err != nil {
142
+ c .logger .Errorf ("Error in FindMissingBlobs attempt %d: %s" , attempt , err )
143
+
144
+ st , ok := status .FromError (err )
145
+ if ok && st .Code () == codes .Unauthenticated {
146
+ return ErrCacheUnauthenticated , false
147
+ }
148
+
149
+ return fmt .Errorf ("find missing blobs: %w" , err ), true
150
+ }
151
+
152
+ return nil , false
153
+ })
154
+ if err != nil {
155
+ return nil , fmt .Errorf ("with retries: %w" , err )
156
+ }
157
+
158
+ return convertToFileDigests (resp .GetMissingBlobDigests ()), nil
159
+ }
160
+
161
+ func (c * Client ) findMissingChunked (ctx context.Context ,
162
+ req * remoteexecution.FindMissingBlobsRequest ,
163
+ digests []* FileDigest ,
164
+ blobDigests []* remoteexecution.Digest ,
165
+ gRPCLimitBytes int ) ([]* FileDigest , error ) {
166
+ var missingBlobs []* FileDigest
167
+ // Chunk up request blobs to fit into gRPC limits
168
+ // Calculate the unit size of a blob (in practice can differ to the theoretical sha256(32 bytes) + size(8 bytes) = 40 bytes)
169
+ digestUnitSize := float64 (len (req .String ())) / float64 (len (digests ))
170
+ maxDigests := int (float64 (gRPCLimitBytes ) / digestUnitSize )
171
+ for startIndex := 0 ; startIndex < len (digests ); startIndex += maxDigests {
172
+ endIndex := startIndex + maxDigests
173
+ if endIndex > len (digests ) {
174
+ endIndex = len (digests )
175
+ }
176
+ req .BlobDigests = blobDigests [startIndex :endIndex ]
177
+ c .logger .Debugf ("Calling FindMissingBlobs for chunk: digests[%d:%d]" , startIndex , endIndex )
178
+
179
+ var resp []* FileDigest
180
+ var err error
181
+ if resp , err = c .findMissing (ctx , req ); err != nil {
148
182
return nil , fmt .Errorf ("find missing blobs: %w" , err )
149
183
}
150
- missingBlobs = convertToFileDigests (resp .GetMissingBlobDigests ())
184
+
185
+ missingBlobs = append (missingBlobs , resp ... )
151
186
}
152
187
153
188
return missingBlobs , nil
154
189
}
155
190
191
+ func (c * Client ) FindMissing (ctx context.Context , digests []* FileDigest ) ([]* FileDigest , error ) {
192
+ blobDigests := convertToBlobDigests (digests )
193
+ req := & remoteexecution.FindMissingBlobsRequest {
194
+ BlobDigests : blobDigests ,
195
+ }
196
+ c .logger .Debugf ("Size of FindMissingBlobs request for %d blobs is %s" , len (digests ), humanize .Bytes (uint64 (len (req .String ()))))
197
+ gRPCLimitBytes := 4 * 1024 * 1024 // gRPC limit is 4 MiB
198
+ if len (req .String ()) > gRPCLimitBytes {
199
+ return c .findMissingChunked (ctx , req , digests , blobDigests , gRPCLimitBytes )
200
+ }
201
+
202
+ return c .findMissing (ctx , req )
203
+ }
204
+
156
205
func convertToBlobDigests (digests []* FileDigest ) []* remoteexecution.Digest {
157
206
out := make ([]* remoteexecution.Digest , 0 , len (digests ))
158
207
0 commit comments