Skip to content

Commit a32503a

Browse files
iHiDclaude
andauthored
Retry transient upstream errors in snippet and LOC generation (#8425)
Rescue 502/503/504 errors from the snippet generator and lines-of-code counter services, requeuing the job up to 3 times with a random delay. After retries are exhausted, the exception propagates to Sidekiq/Sentry so persistent failures are still surfaced. Closes #8396 Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 0fcb2be commit a32503a

4 files changed

Lines changed: 82 additions & 2 deletions

File tree

app/commands/iteration/count_lines_of_code.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ class Iteration::CountLinesOfCode
33

44
queue_as :solution_processing
55

6-
initialize_with :iteration
6+
initialize_with :iteration, retries_count: 0
7+
8+
MAX_RETRIES = 3
79

810
def call
911
# Sometimes the iteration might be deleted
@@ -34,6 +36,10 @@ def call
3436

3537
iteration.update_column(:num_loc, num_loc)
3638
Solution::UpdateNumLoc.(iteration.solution)
39+
rescue RestClient::BadGateway, RestClient::ServiceUnavailable, RestClient::GatewayTimeout
40+
raise if retries_count >= MAX_RETRIES
41+
42+
self.class.defer(iteration, retries_count: retries_count + 1, wait: rand(30..90))
3743
ensure
3844
ToolingJob::DeleteFromEFS.(efs_dir)
3945
end

app/commands/iteration/generate_snippet.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ class Iteration::GenerateSnippet
33

44
queue_as :solution_processing
55

6-
initialize_with :iteration
6+
initialize_with :iteration, retries_count: 0
7+
8+
MAX_RETRIES = 3
79

810
def call
911
# Sometimes the iteration might be deleted
@@ -35,5 +37,9 @@ def call
3537
return if e.message.include?("Invalid Unicode")
3638

3739
raise
40+
rescue RestClient::BadGateway, RestClient::ServiceUnavailable, RestClient::GatewayTimeout
41+
raise if retries_count >= MAX_RETRIES
42+
43+
self.class.defer(iteration, retries_count: retries_count + 1, wait: rand(30..90))
3844
end
3945
end

test/commands/iteration/count_lines_of_code_test.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,31 @@ class Iteration::CountLinesOfCodeTest < ActiveJob::TestCase
9898
assert_equal iteration.num_loc, solution.reload.num_loc
9999
end
100100

101+
test "requeues on bad gateway" do
102+
submission = create :submission
103+
create :submission_file, submission:, content: "Some source code"
104+
iteration = create(:iteration, submission:)
105+
106+
RestClient.stubs(:post).raises(RestClient::BadGateway)
107+
108+
Iteration::CountLinesOfCode.expects(:defer).with do |iter, **kwargs|
109+
iter == iteration && kwargs[:retries_count] == 1 && kwargs[:wait].between?(30, 90)
110+
end
111+
Iteration::CountLinesOfCode.(iteration)
112+
end
113+
114+
test "raises after max retries exhausted" do
115+
submission = create :submission
116+
create :submission_file, submission:, content: "Some source code"
117+
iteration = create(:iteration, submission:)
118+
119+
RestClient.stubs(:post).raises(RestClient::BadGateway)
120+
121+
assert_raises RestClient::BadGateway do
122+
Iteration::CountLinesOfCode.(iteration, retries_count: 3)
123+
end
124+
end
125+
101126
test "solution is not updated if another iteration is published" do
102127
solution = create :concept_solution
103128
submission = create :submission

test/commands/iteration/generate_snippet_test.rb

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,49 @@ class Iteration::GenerateSnippetTest < ActiveJob::TestCase
7777
Iteration::GenerateSnippet.(iteration)
7878
end
7979

80+
test "requeues on bad gateway" do
81+
iteration = create :iteration, submission: @submission
82+
83+
RestClient.stubs(:post).raises(RestClient::BadGateway)
84+
85+
Iteration::GenerateSnippet.expects(:defer).with do |iter, **kwargs|
86+
iter == iteration && kwargs[:retries_count] == 1 && kwargs[:wait].between?(30, 90)
87+
end
88+
Iteration::GenerateSnippet.(iteration)
89+
end
90+
91+
test "requeues on service unavailable" do
92+
iteration = create :iteration, submission: @submission
93+
94+
RestClient.stubs(:post).raises(RestClient::ServiceUnavailable)
95+
96+
Iteration::GenerateSnippet.expects(:defer).with do |iter, **kwargs|
97+
iter == iteration && kwargs[:retries_count] == 1 && kwargs[:wait].between?(30, 90)
98+
end
99+
Iteration::GenerateSnippet.(iteration)
100+
end
101+
102+
test "requeues on gateway timeout" do
103+
iteration = create :iteration, submission: @submission
104+
105+
RestClient.stubs(:post).raises(RestClient::GatewayTimeout)
106+
107+
Iteration::GenerateSnippet.expects(:defer).with do |iter, **kwargs|
108+
iter == iteration && kwargs[:retries_count] == 1 && kwargs[:wait].between?(30, 90)
109+
end
110+
Iteration::GenerateSnippet.(iteration)
111+
end
112+
113+
test "raises after max retries exhausted" do
114+
iteration = create :iteration, submission: @submission
115+
116+
RestClient.stubs(:post).raises(RestClient::BadGateway)
117+
118+
assert_raises RestClient::BadGateway do
119+
Iteration::GenerateSnippet.(iteration, retries_count: 3)
120+
end
121+
end
122+
80123
test "handle long snippets" do
81124
code = "Some source code"
82125
snippet = "Some generated snippet#{"x" * 1500}"

0 commit comments

Comments
 (0)