Handle out of sync metadata in Redis on PUTs

When the IF-MATCH comparison fails, we check the actual metadata on
the Swift server to be sure.
This commit is contained in:
Garret Alfert 2018-01-05 06:49:42 +01:00
parent 24ae9ad893
commit 18670021b1
2 changed files with 47 additions and 4 deletions

View File

@ -139,10 +139,20 @@ module RemoteStorage
url = url_for_key(user, directory, key) url = url_for_key(user, directory, key)
if required_match = server.env["HTTP_IF_MATCH"] if required_match = server.env["HTTP_IF_MATCH"]
unless required_match.gsub(/^"?W\//, "") == %Q("#{existing_metadata["e"]}") required_match = required_match.gsub(/^"?W\//, "")
unless required_match == %Q("#{existing_metadata["e"]}")
# get actual metadata and compare in case redis metadata became out of sync
head_res = do_head_request(url)
if required_match == %Q("#{head_res.headers[:etag]}")
# log previous size difference that was missed ealier because of redis failure
log_size_difference(user, existing_metadata["s"], head_res.headers[:content_length])
else
server.halt 412, "Precondition Failed" server.halt 412, "Precondition Failed"
end end
end end
end
if server.env["HTTP_IF_NONE_MATCH"] == "*" if server.env["HTTP_IF_NONE_MATCH"] == "*"
server.halt 412, "Precondition Failed" unless existing_metadata.empty? server.halt 412, "Precondition Failed" unless existing_metadata.empty?
end end

View File

@ -284,11 +284,44 @@ describe "App" do
it "fails the request if the header does not match the current ETag" do it "fails the request if the header does not match the current ETag" do
header "If-Match", "someotheretag" header "If-Match", "someotheretag"
head_stub = OpenStruct.new(headers: {
etag: "oldetag",
last_modified: "Fri, 04 Mar 2016 12:20:18 GMT",
content_type: "text/plain",
content_length: 23
})
RestClient.stub :head, head_stub do
put "/phil/food/aguacate", "aye" put "/phil/food/aguacate", "aye"
end
last_response.status.must_equal 412 last_response.status.must_equal 412
last_response.body.must_equal "Precondition Failed" last_response.body.must_equal "Precondition Failed"
end end
it "allows the request if redis metadata became out of sync" do
header "If-Match", "\"existingetag\""
head_stub = OpenStruct.new(headers: {
etag: "existingetag",
last_modified: "Fri, 04 Mar 2016 12:20:18 GMT",
content_type: "text/plain",
content_length: 23
})
put_stub = OpenStruct.new(headers: {
etag: "newetag",
last_modified: "Fri, 04 Mar 2016 12:20:18 GMT"
})
RestClient.stub :head, head_stub do
RestClient.stub :put, put_stub do
put "/phil/food/aguacate", "aye"
end
end
last_response.status.must_equal 200
end
end end
describe "If-None-Match header set to '*'" do describe "If-None-Match header set to '*'" do
@ -307,7 +340,7 @@ describe "App" do
last_response.status.must_equal 201 last_response.status.must_equal 201
end end
it "fails the request if the document already exsits" do it "fails the request if the document already exists" do
put_stub = OpenStruct.new(headers: { put_stub = OpenStruct.new(headers: {
etag: "someetag", etag: "someetag",
last_modified: "Fri, 04 Mar 2016 12:20:18 GMT" last_modified: "Fri, 04 Mar 2016 12:20:18 GMT"