Skip to content

Commit 23133c1

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

File tree

2 files changed

+83
-3
lines changed

2 files changed

+83
-3
lines changed

pkg/linear/client.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -971,9 +971,37 @@ 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+
isRateLimited := gqlErr.IsRateLimited(ctx)
978+
l.Info("doRequest completed",
979+
zap.Error(err),
980+
zap.Int("status_code", func() int {
981+
if resp != nil {
982+
return resp.StatusCode
983+
}
984+
return 0
985+
}()),
986+
zap.Any("rate_limit_data", rlData),
987+
zap.Bool("gql_err_is_rate_limited", isRateLimited))
988+
974989
// Linear returns 400 when rate limited, so change it to a retryable error
975-
if err != nil && resp != nil && resp.StatusCode == http.StatusBadRequest {
990+
if err != nil && resp != nil && (resp.StatusCode == http.StatusBadRequest || isRateLimited) {
991+
l.Info("rate limiting detected", zap.Int("status_code", resp.StatusCode))
992+
993+
rlData.Status = v2.RateLimitDescription_STATUS_OVERLIMIT
976994
return resp, rlData, uhttp.WrapErrorsWithRateLimitInfo(codes.Unavailable, resp, err)
977995
}
996+
997+
// Ensure error is wrapped with rate limit info when using WithRatelimitData
998+
if err != nil && resp != nil {
999+
l.Info("wrapping error with rate limit info",
1000+
zap.Int("status_code", resp.StatusCode),
1001+
zap.Error(err))
1002+
return resp, rlData, uhttp.WrapErrorsWithRateLimitInfo(codes.Unavailable, resp, err)
1003+
}
1004+
1005+
l.Info("returning without rate limit wrapping")
9781006
return resp, rlData, err
9791007
}

pkg/linear/models.go

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

0 commit comments

Comments
 (0)