@@ -338,10 +338,10 @@ def pending_members_from_graphql
338338 def graphql_http_post ( query )
339339 1 . upto ( MAX_GRAPHQL_RETRIES ) do |try_number |
340340 result = graphql_http_post_real ( query )
341- if result [ :code ] < 500
341+ if ! graphql_result_retryable? ( result )
342342 return result
343343 elsif try_number >= MAX_GRAPHQL_RETRIES
344- Entitlements . logger . error "Query still failing after #{ MAX_GRAPHQL_RETRIES } tries. Giving up."
344+ Entitlements . logger . error "Query still failing after #{ MAX_GRAPHQL_RETRIES } tries (last code: #{ result [ :code ] } ) . Giving up."
345345 return result
346346 else
347347 Entitlements . logger . warn "GraphQL failed on try #{ try_number } of #{ MAX_GRAPHQL_RETRIES } . Will retry."
@@ -350,6 +350,34 @@ def graphql_http_post(query)
350350 end
351351 end
352352
353+ # Helper: determine whether a result hash from `graphql_http_post_real` represents
354+ # a transient failure that the retry wrapper will retry. Used by the wrapper itself
355+ # and to decide log severity inside `graphql_http_post_real`.
356+ #
357+ # result - Hash returned by `graphql_http_post_real`.
358+ #
359+ # Returns true if the result is retryable (HTTP 5xx or synthetic 5xx), false otherwise.
360+ Contract ( { code : Integer , data : C ::Or [ nil , Hash ] } ) => C ::Bool
361+ def graphql_result_retryable? ( result )
362+ result [ :code ] >= 500
363+ end
364+
365+ # Helper: log `message` to `Entitlements.logger` at the given severity. Restricts
366+ # the dispatch to a known set of severities so callers can pick a level computed
367+ # at runtime without going through `send`/`public_send`.
368+ #
369+ # severity - Symbol, one of :warn or :error.
370+ # message - String message to log.
371+ #
372+ # Returns nothing.
373+ Contract C ::Or [ :warn , :error ] , String => C ::Any
374+ def log_at_severity ( severity , message )
375+ case severity
376+ when :warn then Entitlements . logger . warn ( message )
377+ when :error then Entitlements . logger . error ( message )
378+ end
379+ end
380+
353381 # Helper method: Do the HTTP POST to the GitHub API for GraphQL.
354382 #
355383 # query - String with the data to be posted.
@@ -370,23 +398,34 @@ def graphql_http_post_real(query)
370398 response = http . request ( request )
371399
372400 if response . code != "200"
373- Entitlements . logger . error "Got HTTP #{ response . code } POSTing to #{ uri } "
374- Entitlements . logger . error response . body
401+ # The retry wrapper retries on 5xx, so log those at WARN to avoid misleading
402+ # the operator with an ERROR for a transient failure that we recover from.
403+ # Terminal non-2xx responses (4xx) stay at ERROR.
404+ severity = response . code . start_with? ( "5" ) ? :warn : :error
405+ log_at_severity ( severity , "Got HTTP #{ response . code } POSTing to #{ uri } " )
406+ log_at_severity ( severity , response . body )
375407 return { code : response . code . to_i , data : { "body" => response . body } }
376408 end
377409
378410 begin
379411 data = JSON . parse ( response . body )
380412 if data . key? ( "errors" )
381- Entitlements . logger . error "Errors reported: #{ data [ 'errors' ] . inspect } "
413+ # Synthesized 500 below triggers a retry, so log at WARN. Note: some GraphQL
414+ # `errors` are permanent (bad query, auth, schema). The retry wrapper's final
415+ # "Giving up" ERROR will surface persistent cases.
416+ Entitlements . logger . warn "Errors reported: #{ data [ 'errors' ] . inspect } "
382417 return { code : 500 , data : }
383418 end
384419 { code : response . code . to_i , data : }
385420 rescue JSON ::ParserError => e
386- Entitlements . logger . error "#{ e . class } #{ e . message } : #{ response . body . inspect } "
421+ # Synthesized 500 below triggers a retry; log at WARN.
422+ Entitlements . logger . warn "#{ e . class } #{ e . message } : #{ response . body . inspect } "
387423 { code : 500 , data : { "body" => response . body } }
388424 end
389425 rescue => e
426+ # Catch-all for any unexpected exception (network blip OR local code bug).
427+ # We retry below via the synthesized 500, but log at ERROR because this
428+ # branch can mask programming errors that operators must see.
390429 Entitlements . logger . error "Caught #{ e . class } POSTing to #{ uri } : #{ e . message } "
391430 { code : 500 , data : nil }
392431 end
0 commit comments