Skip to content

Commit 0844204

Browse files
author
MB Burch
committed
Add check for rate limiting and debugging logs
1 parent 1d0e190 commit 0844204

File tree

2 files changed

+83
-3
lines changed

2 files changed

+83
-3
lines changed

pkg/linear/client.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -971,9 +971,36 @@ func (c *Client) doRequest(ctx context.Context, body interface{}, res interface{
971971
}
972972

973973
resp, err := c.httpClient.Do(req, doOptions...)
974+
975+
// Add logging for troubleshooting
976+
l := ctxzap.Extract(ctx)
977+
l.Info("doRequest completed",
978+
zap.Error(err),
979+
zap.Int("status_code", func() int {
980+
if resp != nil {
981+
return resp.StatusCode
982+
}
983+
return 0
984+
}()),
985+
zap.Any("rate_limit_data", rlData),
986+
zap.Bool("gql_err_is_rate_limited", gqlErr.IsRateLimited()))
987+
974988
// Linear returns 400 when rate limited, so change it to a retryable error
975-
if err != nil && resp != nil && resp.StatusCode == http.StatusBadRequest {
989+
if err != nil && resp != nil && (resp.StatusCode == http.StatusBadRequest || gqlErr.IsRateLimited()) {
990+
l.Info("rate limiting detected", zap.Int("status_code", resp.StatusCode))
991+
992+
rlData.Status = v2.RateLimitDescription_STATUS_OVERLIMIT
976993
return resp, rlData, uhttp.WrapErrorsWithRateLimitInfo(codes.Unavailable, resp, err)
977994
}
995+
996+
// Ensure error is wrapped with rate limit info when using WithRatelimitData
997+
if err != nil && resp != nil {
998+
l.Info("wrapping error with rate limit info",
999+
zap.Int("status_code", resp.StatusCode),
1000+
zap.Error(err))
1001+
return resp, rlData, uhttp.WrapErrorsWithRateLimitInfo(codes.Unavailable, resp, err)
1002+
}
1003+
1004+
l.Info("returning without rate limit wrapping")
9781005
return resp, rlData, err
9791006
}

pkg/linear/models.go

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
package linear
22

3-
import "time"
3+
import (
4+
"context"
5+
"fmt"
6+
"strings"
7+
"time"
8+
9+
"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"
10+
"go.uber.org/zap"
11+
)
412

513
type PageInfo struct {
614
EndCursor string `json:"endCursor"`
@@ -93,7 +101,8 @@ type Project struct {
93101
type GraphQLError struct {
94102
Error string `json:"error"`
95103
Errors []struct {
96-
Message string `json:"message"`
104+
Message string `json:"message"`
105+
Extensions map[string]interface{} `json:"extensions,omitempty"`
97106
} `json:"errors"`
98107
}
99108

@@ -107,6 +116,50 @@ func (e *GraphQLError) Message() string {
107116
return e.Errors[0].Message
108117
}
109118

119+
// IsRateLimited checks if the error contains Linear's RATELIMITED error code.
120+
func (e *GraphQLError) IsRateLimited() bool {
121+
// Get logger from context if available, otherwise use a default logger
122+
ctx := context.Background()
123+
l := ctxzap.Extract(ctx)
124+
125+
for _, err := range e.Errors {
126+
// Log the error message and extensions for debugging
127+
l.Info("checking GraphQL error for rate limiting",
128+
zap.String("message", err.Message),
129+
zap.Any("extensions", err.Extensions))
130+
131+
if extensions, ok := err.Extensions["code"]; ok {
132+
l.Info("found 'code' extension",
133+
zap.Any("code_value", extensions),
134+
zap.String("code_type", fmt.Sprintf("%T", extensions)))
135+
136+
if code, ok := extensions.(string); ok && code == "RATELIMITED" {
137+
l.Info("rate limited detected via code extension",
138+
zap.String("code", code))
139+
return true
140+
} else {
141+
l.Info("code extension is not 'RATELIMITED'",
142+
zap.Any("got_value", extensions),
143+
zap.String("got_type", fmt.Sprintf("%T", extensions)))
144+
}
145+
} else {
146+
l.Info("no 'code' extension found")
147+
}
148+
149+
// Also check the message for backward compatibility
150+
if strings.Contains(strings.ToLower(err.Message), "ratelimited") {
151+
l.Info("rate limited detected via message",
152+
zap.String("message", err.Message))
153+
return true
154+
} else {
155+
l.Info("message does not contain 'ratelimited'",
156+
zap.String("message", err.Message))
157+
}
158+
}
159+
l.Info("no rate limiting detected in any GraphQL errors")
160+
return false
161+
}
162+
110163
type ViewerPermissions struct {
111164
Guest bool `json:"guest"`
112165
Admin bool `json:"admin"`

0 commit comments

Comments
 (0)