Proper replacement of values.

It’s no longer needed to `hash.delete old_data`, `hash[id] = new_data`.
Previously existing data are properly de-indexed, then replaced by the
new data, which are then properly re-indexed. As you’d expect from a
database thingy.
remotes/1702071717368387340/master
Luka Vandervelden 2019-12-10 20:28:49 +01:00
parent 7a46fa3872
commit 2f00b56f0c
2 changed files with 95 additions and 22 deletions

View File

@ -104,14 +104,22 @@ class FS::Hash(K, V)
end
def []=(key : K, value : V)
# FIXME: Update partitions pointing to previous value (if any)
# Removes any old indices or partitions pointing to a value about
# to be replaced.
self.[key]?.try do |old_value|
remove_partitions key, old_value
end
# avoid corruption in case of crash during file writing
# Avoids corruption in case the application crashes while writing.
file_path(key).tap do |path|
::File.write "#{path}.new", value.to_json
::FileUtils.mv "#{path}.new", path
end
write_partitions key, value
end
def write_partitions(key : K, value : V)
@partitions.each do |index|
index_key = index.key_proc.call value
@ -156,40 +164,44 @@ class FS::Hash(K, V)
def delete(key : K)
value = self[key]?
return if value.nil?
begin
::File.delete file_path key
rescue
# FIXME: Only intercept “no such file" errors
end
unless value.nil?
@partitions.each do |index|
index_key = index.key_proc.call value
remove_partitions key, value
case index
when IndexData
symlink = file_path_indexes(key.to_s, index.name)
value
end
::File.delete symlink
when PartitionData
symlink = file_path_partition(key, index.name, index_key)
def remove_partitions(key : K, value : V)
@partitions.each do |index|
index_key = index.key_proc.call value
::File.delete symlink
end
end
case index
when IndexData
symlink = file_path_indexes(key.to_s, index.name)
@nn_partitions.each do |nn|
indices = nn.key_proc.call value
::File.delete symlink
when PartitionData
symlink = file_path_partition(key, index.name, index_key)
indices.each do |index_key|
symlink = file_path_nn(key.to_s, nn.name, index_key)
::File.delete symlink
end
::File.delete symlink
end
end
value
@nn_partitions.each do |nn|
indices = nn.key_proc.call value
indices.each do |index_key|
symlink = file_path_nn(key.to_s, nn.name, index_key)
::File.delete symlink
end
end
end
##

61
test-edit.cr Normal file
View File

@ -0,0 +1,61 @@
require "json"
require "./src/fs.cr"
require "uuid"
# This test basically works if no data is obtained when fetching "broken"
# partitions/indices/tags.
class Ship
JSON.mapping({
id: String,
class: String,
name: String,
tags: Array(String)
})
def initialize(@name, @class = @name, @tags = [] of String)
@id = UUID.random.to_s
end
getter name
getter id
end
ships = FS::Hash(String, Ship).new "test-edit"
by_name = ships.new_index "name", &.name
by_class = ships.new_partition "class", &.class
by_id = ships.new_index "id", &.id
by_tags = ships.new_nn_partition "tags", &.tags
ship = Ship.new "Satsuki", "Mutsuki", tags: ["kuchikukan"]
ships[ship.id] = ship
ship = Ship.new "Mutsuki", "Mutsuki", tags: ["kuchikukan"]
ships[ship.id] = ship
ship = Ship.new "Kisaragi", "broken", tags: ["broken"]
kisaragi = ship
ships[ship.id] = ship
ship = Ship.new "Kisaragi", "Mutsuki", tags: ["kuchikukan"]
ship.id = kisaragi.id # Overwriting the “broken” Kisaragi entry.
ships[ship.id] = ship
puts "Database entries"
ships.each do |id, ship|
p "#{ship.name} (#{ship.class}) [#{ship.tags.join ", "}]"
end
no_broken = Array(Array(Ship)).new
puts
puts "Partitions/indices"
pp! ships.get_partition("class", "Mutsuki").map &.name
pp! ships.get_nn_partition("tags", "kuchikukan").map &.name
pp! no_broken << ships.get_partition("class", "broken")
pp! no_broken << ships.get_nn_partition("tags", "broken")
if no_broken.flatten.size > 0
puts "ERROR: the test failed"
end