Skip to content

Commit caab1c2

Browse files
authored
[Data rearchitecture] Do not propagate error if revision was deleted in reference-counter and liftwing APIs (#6176)
* Add 403 forbidden responses to non transient errors for reference counter API. Usually, this error means that we lack permission to access the revision, possibly because it was deleted or hidden. That is not atransient error, so we don't want to mark the timeslice to be reprocessed. * Add 404 not found responses to non transient errors for reference counter API. This error occurs for some deleted revisions. * Do not return error field in LiftWingAPI response if the revision was deleted. That's not a transient error so keeping retrying the lift wing request doesnt make sense.
1 parent 1fbd302 commit caab1c2

File tree

6 files changed

+74
-16
lines changed

6 files changed

+74
-16
lines changed

lib/lift_wing_api.rb

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,17 +66,13 @@ def get_single_revision_parsed_data(rev_id)
6666
response = lift_wing_server.post(quality_query_url, body)
6767
parsed_response = Oj.load(response.body)
6868
# If the responses contain an error, do not try to calculate wp10 or features.
69-
if parsed_response.key? 'error'
70-
return { 'wp10' => nil, 'features' => nil, 'deleted' => deleted?(parsed_response),
71-
'prediction' => nil, 'error' => parsed_response.dig('error') }
72-
end
69+
return build_error_response parsed_response.dig('error') if parsed_response.key? 'error'
7370
build_successful_response(rev_id, parsed_response)
7471
rescue StandardError => e
7572
tries -= 1
7673
retry unless tries.zero?
7774
@errors << e
78-
return { 'wp10' => nil, 'features' => nil, 'deleted' => false, 'prediction' => nil,
79-
'error' => e }
75+
build_error_response e.to_s
8076
end
8177

8278
class InvalidProjectError < StandardError
@@ -121,6 +117,15 @@ def build_successful_response(rev_id, response)
121117
}
122118
end
123119

120+
def build_error_response(error)
121+
deleted = deleted?(error)
122+
error_response = { 'wp10' => nil, 'features' => nil, 'deleted' => deleted, 'prediction' => nil }
123+
# Add error field only if the revision was not deleted
124+
error_response['error'] = error unless deleted
125+
126+
error_response
127+
end
128+
124129
# TODO: monitor production for errors, understand them, put benign ones here
125130
TYPICAL_ERRORS = [].freeze
126131

@@ -133,9 +138,9 @@ def log_error_batch(rev_ids)
133138
error_count: @errors.count })
134139
end
135140

136-
def deleted?(response)
141+
def deleted?(error)
137142
LiftWingApi::DELETED_REVISION_ERRORS.any? do |revision_error|
138-
response.dig('error').include?(revision_error)
143+
error.include?(revision_error)
139144
end
140145
end
141146
end

lib/reference_counter_api.rb

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,8 @@ def get_number_of_references_from_revision_id(rev_id)
5858
response = toolforge_server.get(references_query_url(rev_id))
5959
parsed_response = Oj.load(response.body)
6060
return { 'num_ref' => parsed_response['num_ref'] } if response.status == 200
61-
# If the response is bad request, then the language and/or the project is not supported.
62-
# Leave the error empty in this case, as it is not a transient error.
63-
return { 'num_ref' => nil } if response.status == 400
61+
# Leave the error empty if it is not a transient error.
62+
return { 'num_ref' => nil } if non_transient_error? response.status
6463
# Log the error and return empty hash
6564
# Sentry.capture_message 'Non-200 response hitting references counter API', level: 'warning',
6665
# extra: { project_code: @project_code, language_code: @language_code, rev_id:,
@@ -89,6 +88,16 @@ def toolforge_server
8988
)
9089
end
9190

91+
BAD_REQUEST = 400
92+
FORBIDDEN = 403
93+
NOT_FOUND = 404
94+
# A bad request response indicates that the language and/or project is not supported.
95+
# A forbidden response likely means we lack permission to access the revision,
96+
# possibly because it was deleted or hidden.
97+
def non_transient_error?(status)
98+
[BAD_REQUEST, FORBIDDEN, NOT_FOUND].include? status
99+
end
100+
92101
TYPICAL_ERRORS = [Faraday::TimeoutError,
93102
Faraday::ConnectionFailed].freeze
94103

spec/lib/importers/revision_score_importer_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@
222222
expect(revisions[2].wp10).to be_nil
223223
expect(revisions[2].features).to eq({})
224224
# TODO: use another field for this. For now we use ithenticate_id as an error field
225-
expect(revisions[2].ithenticate_id).to eq(1)
225+
expect(revisions[2].ithenticate_id).to be_nil
226226
end
227227
end
228228

@@ -233,7 +233,7 @@
233233
expect(revisions[3].wp10).to be_nil
234234
expect(revisions[3].features).to eq({})
235235
# TODO: use another field for this. For now we use ithenticate_id as an error field
236-
expect(revisions[3].ithenticate_id).to eq(1)
236+
expect(revisions[3].ithenticate_id).to be_nil
237237
end
238238
end
239239

spec/lib/lift_wing_api_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@
7777
VCR.use_cassette 'liftwing_api/deleted_revision' do
7878
expect(subject2).to be_a(Hash)
7979
expect(subject2.dig('708326238', 'deleted')).to eq(true)
80-
expect(subject2.dig('708326238', 'error').class).to be(String)
80+
expect(subject2.dig('708326238').key?('error')).to eq(false)
8181
end
8282
end
8383

spec/lib/reference_counter_api_spec.rb

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,34 @@
4141
context 'fails silently' do
4242
before do
4343
stub_wiki_validation
44-
stub_not_supported_wiki_reference_counter_response
4544
end
4645

4746
it 'if response is 400 bad request' do
47+
stub_400_wiki_reference_counter_response
48+
ref_counter_api = described_class.new(not_supported)
49+
response = ref_counter_api.get_number_of_references_from_revision_ids rev_ids
50+
expect(response.dig('5006940', 'num_ref')).to be_nil
51+
expect(response.dig('5006940').key?('error')).to eq(false)
52+
expect(response.dig('5006942', 'num_ref')).to be_nil
53+
expect(response.dig('5006942').key?('error')).to eq(false)
54+
expect(response.dig('5006946', 'num_ref')).to be_nil
55+
expect(response.dig('5006946').key?('error')).to eq(false)
56+
end
57+
58+
it 'if response is 403 forbidden' do
59+
stub_403_wiki_reference_counter_response
60+
ref_counter_api = described_class.new(not_supported)
61+
response = ref_counter_api.get_number_of_references_from_revision_ids rev_ids
62+
expect(response.dig('5006940', 'num_ref')).to be_nil
63+
expect(response.dig('5006940').key?('error')).to eq(false)
64+
expect(response.dig('5006942', 'num_ref')).to be_nil
65+
expect(response.dig('5006942').key?('error')).to eq(false)
66+
expect(response.dig('5006946', 'num_ref')).to be_nil
67+
expect(response.dig('5006946').key?('error')).to eq(false)
68+
end
69+
70+
it 'if response is 404 not found' do
71+
stub_404_wiki_reference_counter_response
4872
ref_counter_api = described_class.new(not_supported)
4973
response = ref_counter_api.get_number_of_references_from_revision_ids rev_ids
5074
expect(response.dig('5006940', 'num_ref')).to be_nil

spec/support/request_helpers.rb

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -736,12 +736,32 @@ def stub_en_wikipedia_reference_counter_reponse
736736
)
737737
end
738738

739-
def stub_not_supported_wiki_reference_counter_response
739+
def stub_400_wiki_reference_counter_response
740740
stub_request(:get, %r{https://reference-counter.toolforge.org/api/v1/references/wikimedia/incubator/\d+})
741741
.to_return(
742742
status: 400,
743743
body: { 'description' => 'Language incubator is not a valid language. ' }.to_json,
744744
headers: { 'Content-Type' => 'application/json' }
745745
)
746746
end
747+
748+
def stub_403_wiki_reference_counter_response
749+
stub_request(:get, %r{https://reference-counter.toolforge.org/api/v1/references/wikimedia/incubator/\d+})
750+
.to_return(
751+
status: 403,
752+
body: { 'description' => "mwapi error: permissiondenied - You don't have permission to view\
753+
deleted text or changes between deleted revisions." }.to_json,
754+
headers: { 'Content-Type' => 'application/json' }
755+
)
756+
end
757+
758+
def stub_404_wiki_reference_counter_response
759+
stub_request(:get, %r{https://reference-counter.toolforge.org/api/v1/references/wikimedia/incubator/\d+})
760+
.to_return(
761+
status: 404,
762+
body: { 'description' => 'rest-nonexistent-revision -\
763+
The specified revision does not exist' }.to_json,
764+
headers: { 'Content-Type' => 'application/json' }
765+
)
766+
end
747767
end

0 commit comments

Comments
 (0)