Do collision detection via Redis metadata

This commit is contained in:
Garret Alfert 2016-01-28 19:06:24 +01:00
parent 4e7c8f68bb
commit 4ca67c7ea9
2 changed files with 92 additions and 0 deletions

View File

@ -289,6 +289,58 @@ module RemoteStorage
end end
def has_name_collision?(user, directory, key) def has_name_collision?(user, directory, key)
if settings.use_redis_dir_listing
has_name_collision_via_redis?(user, directory, key)
else
has_name_collision_via_swift?(user, directory, key)
end
end
def has_name_collision_via_redis?(user, directory, key)
lua_script = <<-EOF
local user = ARGV[1]
local directory = ARGV[2]
local key = ARGV[3]
-- build table with parent directories from remaining arguments
local parent_dir_count = #ARGV - 3
local parent_directories = {}
for i = 4, 4 + parent_dir_count do
table.insert(parent_directories, ARGV[i])
end
-- check for existing directory with the same name as the document
local redis_key = "rs_meta:"..user..":"
if directory == "" then
redis_key = redis_key..key.."/"
else
redis_key = redis_key..directory.."/"..key.."/"
end
if redis.call("hget", redis_key, "etag") then
return true
end
for index, dir in pairs(parent_directories) do
if redis.call("hget", "rs_meta:"..user..":"..dir.."/", "etag") then
-- the directory already exists, no need to do further checks
return false
else
-- check for existing document with same name as directory
if redis.call("hget", "rs_meta:"..user..":"..dir, "etag") then
return true
end
end
end
return false
EOF
parent_directories = parent_directories_for(directory)
redis.eval(lua_script, nil, [user, directory, key, *parent_directories])
end
def has_name_collision_via_swift?(user, directory, key)
# check for existing directory with the same name as the document # check for existing directory with the same name as the document
url = url_for_key(user, directory, key) url = url_for_key(user, directory, key)
do_head_request("#{url}/") do |res| do_head_request("#{url}/") do |res|

View File

@ -64,6 +64,46 @@ describe "App" do
root_items = redis.smembers "rs_meta:phil:/:items" root_items = redis.smembers "rs_meta:phil:/:items"
root_items.must_equal ["food/"] root_items.must_equal ["food/"]
end end
describe "name collision checks" do
it "is successful when there is no name collision" do
put_stub = OpenStruct.new(headers: {etag: "bla"})
RestClient.stub :put, put_stub do
put "/phil/food/aguacate", "si"
end
last_response.status.must_equal 200
metadata = redis.hgetall "rs_meta:phil:food/aguacate"
metadata["size"].must_equal "2"
end
it "conflicts when there is a directory with same name as document" do
put_stub = OpenStruct.new(headers: {etag: "bla"})
RestClient.stub :put, put_stub do
put "/phil/food/aguacate", "si"
put "/phil/food", "wontwork"
end
last_response.status.must_equal 409
metadata = redis.hgetall "rs_meta:phil:food"
metadata.must_be_empty
end
it "conflicts when there is a document with same name as directory" do
put_stub = OpenStruct.new(headers: {etag: "bla"})
RestClient.stub :put, put_stub do
put "/phil/food/aguacate", "si"
put "/phil/food/aguacate/empanado", "wontwork"
end
last_response.status.must_equal 409
metadata = redis.hgetall "rs_meta:phil:food/aguacate/empanado"
metadata.must_be_empty
end
end
end end
end end