class FDB::DirectoryLayer
Attributes
path[W]
Public Class Methods
new(options={})
click to toggle source
# File lib/fdbdirectory.rb, line 146 def initialize(options={}) defaults = { :node_subspace => Subspace.new([], "\xfe"), :content_subspace =>Subspace.new, :allow_manual_prefixes => false } options = defaults.merge(options) @content_subspace = options[:content_subspace] @node_subspace = options[:node_subspace] @allow_manual_prefixes = options[:allow_manual_prefixes] @root_node = @node_subspace[@node_subspace.key] @allocator = HighContentionAllocator.new(@root_node['hca']) @path = [] @layer = '' end
Public Instance Methods
create(db_or_tr, path, options={})
click to toggle source
# File lib/fdbdirectory.rb, line 183 def create(db_or_tr, path, options={}) create_or_open_internal(db_or_tr, path, true, false, options) end
create_or_open(db_or_tr, path, options={})
click to toggle source
# File lib/fdbdirectory.rb, line 175 def create_or_open(db_or_tr, path, options={}) create_or_open_internal(db_or_tr, path, true, true, options) end
exists?(db_or_tr, path=[])
click to toggle source
# File lib/fdbdirectory.rb, line 264 def exists?(db_or_tr, path=[]) db_or_tr.transact do |tr| check_version(tr, false) path = to_unicode_path(path) node = find(tr, path).prefetch_metadata(tr) next false if !node.exists? if node.is_in_partition? next node.get_contents(self).exists?(tr, node.get_partition_subpath) end true end end
layer()
click to toggle source
# File lib/fdbdirectory.rb, line 168 def layer return @layer.dup end
list(db_or_tr, path=[])
click to toggle source
# File lib/fdbdirectory.rb, line 247 def list(db_or_tr, path=[]) db_or_tr.transact do |tr| check_version(tr, false) path = to_unicode_path(path) node = find(tr, path).prefetch_metadata(tr) raise ArgumentError, 'The directory does not exist.' unless node.exists? if node.is_in_partition?(nil, true) next node.get_contents(self).list(tr, node.get_partition_subpath) end subdir_names_and_nodes(tr, node.subspace).map { |name, node| name } end end
move(db_or_tr, old_path, new_path)
click to toggle source
# File lib/fdbdirectory.rb, line 191 def move(db_or_tr, old_path, new_path) db_or_tr.transact do |tr| check_version(tr, true) old_path = to_unicode_path(old_path) new_path = to_unicode_path(new_path) if old_path == new_path[0...old_path.length] raise ArgumentError, 'The desination directory cannot be a subdirectory of the source directory.' end old_node = find(tr, old_path).prefetch_metadata(tr) new_node = find(tr, new_path).prefetch_metadata(tr) raise ArgumentError, 'The source directory does not exist.' unless old_node.exists? if old_node.is_in_partition? || new_node.is_in_partition? if !old_node.is_in_partition? || !new_node.is_in_partition? || old_node.path != new_node.path then raise ArgumentError, 'Cannot move between partitions' end next new_node .get_contents(self) .move(tr, old_node.get_partition_subpath, new_node.get_partition_subpath) end if new_node.exists? raise ArgumentError, 'The destination directory already exists. Remove it first.' end parent_node = find(tr, new_path[0...-1]) if !parent_node.exists? raise ArgumentError, 'The parent directory of the destination directory does not exist. Create it first.' end tr[parent_node.subspace[@@SUBDIRS][new_path[-1]]] = @node_subspace.unpack(old_node.subspace.key)[0] remove_from_parent(tr, old_path) contents_of_node(old_node.subspace, new_path, old_node.layer) end end
move_to(db_or_tr, new_absolute_path)
click to toggle source
# File lib/fdbdirectory.rb, line 187 def move_to(db_or_tr, new_absolute_path) raise 'The root directory cannot be moved.' end
open(db_or_tr, path, options={})
click to toggle source
# File lib/fdbdirectory.rb, line 179 def open(db_or_tr, path, options={}) create_or_open_internal(db_or_tr, path, false, true, options) end
path()
click to toggle source
# File lib/fdbdirectory.rb, line 164 def path return @path.dup end
remove(db_or_tr, path=[])
click to toggle source
# File lib/fdbdirectory.rb, line 239 def remove(db_or_tr, path=[]) remove_internal(db_or_tr, path, true) end
remove_if_exists(db_or_tr, path=[])
click to toggle source
# File lib/fdbdirectory.rb, line 243 def remove_if_exists(db_or_tr, path=[]) remove_internal(db_or_tr, path, false) end
Protected Instance Methods
create_directory(tr, path, options)
click to toggle source
# File lib/fdbdirectory.rb, line 328 def create_directory(tr, path, options) check_version(tr, true) prefix = options[:prefix] if prefix.nil? prefix = @content_subspace.key + @allocator.allocate(tr) if !tr.get_range_start_with(prefix, { :limit => 1 }).to_a.empty? raise "The database has keys stored at the prefix chosen by the automatic prefix allocator: #{prefix.dump}." end if !is_prefix_free?(tr.snapshot, prefix) raise 'The directory layer has manually allocated prefixes that conflict with the automatic prefix allocator.' end elsif !is_prefix_free?(tr, prefix) raise ArgumentError, 'The given prefix is already in use.' end parent_node = if path[0...-1].length > 0 node_with_prefix(create_or_open(tr, path[0...-1]).key) else @root_node end raise 'The parent directory does not exist.' unless parent_node node = node_with_prefix(prefix) tr[parent_node[@@SUBDIRS][path[-1]]] = prefix tr[node['layer']] = options[:layer] contents_of_node(node, path, options[:layer]) end
create_or_open_internal(db_or_tr, path, allow_create, allow_open, options={})
click to toggle source
# File lib/fdbdirectory.rb, line 283 def create_or_open_internal(db_or_tr, path, allow_create, allow_open, options={}) defaults = { :layer => '', :prefix => nil } options = defaults.merge(options) if !options[:prefix].nil? and allow_open and allow_create raise ArgumentError, 'Cannot specify a prefix when calling create_or_open.' end if !options[:prefix].nil? and !@allow_manual_prefixes if @path.length == 0 raise ArgumentError, 'Cannot specify a prefix unless manual prefixes are enabled.' else raise ArgumentError, 'Cannot specify a prefix in a partition.' end end db_or_tr.transact do |tr| check_version(tr, false) path = to_unicode_path(path) raise ArgumentError, 'The root directory cannot be opened.' if path.length == 0 existing_node = find(tr, path).prefetch_metadata(tr) if existing_node.exists? if existing_node.is_in_partition? subpath = existing_node.get_partition_subpath existing_node.get_contents(self).directory_layer.create_or_open_internal(tr, subpath, allow_create, allow_open, options) else raise ArgumentError, 'The directory already exists.' unless allow_open open_directory(path, options, existing_node) end else raise ArgumentError, 'The directory does not exist.' unless allow_create create_directory(tr, path, options) end end end
open_directory(path, options, existing_node)
click to toggle source
# File lib/fdbdirectory.rb, line 321 def open_directory(path, options, existing_node) if options[:layer] and !options[:layer].empty? and options[:layer] != existing_node.layer raise 'The directory was created with an incompatible layer.' end existing_node.get_contents(self) end
remove_internal(db_or_tr, path, fail_on_nonexistent)
click to toggle source
# File lib/fdbdirectory.rb, line 360 def remove_internal(db_or_tr, path, fail_on_nonexistent) db_or_tr.transact do |tr| check_version(tr, true) path = to_unicode_path(path) if path.empty? raise ArgumentError, 'The root directory cannot be removed.' end node = find(tr, path).prefetch_metadata(tr) if !node.exists? raise ArgumentError, 'The directory does not exist.' if fail_on_nonexistent next false end if node.is_in_partition? next node.get_contents(self).directory_layer .remove_internal(tr, node.get_partition_subpath, fail_on_nonexistent) end remove_recursive(tr, node.subspace) remove_from_parent(tr, path) true end end
Private Instance Methods
check_version(tr, write_access)
click to toggle source
# File lib/fdbdirectory.rb, line 390 def check_version(tr, write_access) version = tr[@root_node['version']] initialize_directory(tr) if !version && write_access return if !version version = version.to_s.unpack('III<') dir_ver = "#{version[0]}.#{version[1]}.#{version[2]}" layer_ver = "#{@@VERSION[0]}.#{@@VERSION[1]}.#{@@VERSION[2]}" if version[0] != @@VERSION[0] raise "Cannot load directory with version #{dir_ver} using directory layer #{layer_ver}" elsif version[1] != @@VERSION[1] && write_access raise "Directory with version #{dir_ver} is read-only when opened using directory layer #{layer_ver}" end end
contents_of_node(node, path, layer='')
click to toggle source
# File lib/fdbdirectory.rb, line 429 def contents_of_node(node, path, layer='') prefix = @node_subspace.unpack(node.key)[0] if layer == 'partition' DirectoryPartition.new(@path + path, prefix, self) else DirectorySubspace.new(@path + path, prefix, self, layer) end end
convert_path_element(name)
click to toggle source
# File lib/fdbdirectory.rb, line 479 def convert_path_element(name) if !name.kind_of? String raise TypeError, 'Invalid path: must be a unicode string or an array of unicode strings' end name.dup.force_encoding('UTF-8') end
find(tr, path)
click to toggle source
# File lib/fdbdirectory.rb, line 438 def find(tr, path) node = Internal::Node.new(@root_node, [], path) path.each_with_index do |name, index| node = Internal::Node.new(node_with_prefix(tr[node.subspace[@@SUBDIRS][name]]), path[0..index], path) return node unless node.exists? and node.layer(tr) != 'partition' end node end
initialize_directory(tr)
click to toggle source
# File lib/fdbdirectory.rb, line 409 def initialize_directory(tr) tr[@root_node['version']] = @@VERSION.pack('III<') end
is_prefix_free?(tr, prefix)
click to toggle source
# File lib/fdbdirectory.rb, line 471 def is_prefix_free?(tr, prefix) prefix && prefix.length > 0 && !node_containing_key(tr, prefix) && tr.get_range(@node_subspace.pack([prefix]), @node_subspace.pack([FDB.strinc(prefix)]), { :limit => 1 }).to_a.empty? end
node_containing_key(tr, key)
click to toggle source
# File lib/fdbdirectory.rb, line 413 def node_containing_key(tr, key) return @root_node if key.start_with?(@node_subspace.key) tr.get_range(@node_subspace.range[0], @node_subspace.pack([key]) + "\x00", { :reverse => true, :limit => 1}) .map { |kv| prev_prefix = @node_subspace.unpack(kv.key)[0] node_with_prefix(prev_prefix) if key.start_with?(prev_prefix) }[0] end
node_with_prefix(prefix)
click to toggle source
# File lib/fdbdirectory.rb, line 425 def node_with_prefix(prefix) @node_subspace[prefix] if !prefix.nil? end
remove_from_parent(tr, path)
click to toggle source
# File lib/fdbdirectory.rb, line 457 def remove_from_parent(tr, path) parent = find(tr, path[0...-1]) tr.clear(parent.subspace[@@SUBDIRS][path[-1]]) end
remove_recursive(tr, node)
click to toggle source
# File lib/fdbdirectory.rb, line 462 def remove_recursive(tr, node) subdir_names_and_nodes(tr, node).each do |name, subnode| remove_recursive(tr, subnode) end tr.clear_range_start_with(@node_subspace.unpack(node.key)[0]) tr.clear_range(node.range[0], node.range[1]) end
subdir_names_and_nodes(tr, node)
click to toggle source
# File lib/fdbdirectory.rb, line 450 def subdir_names_and_nodes(tr, node) subdir = node[@@SUBDIRS] tr.get_range(subdir.range[0], subdir.range[1]).map { |kv| [subdir.unpack(kv.key)[0], node_with_prefix(kv.value)] } end
to_unicode_path(path)
click to toggle source
# File lib/fdbdirectory.rb, line 486 def to_unicode_path(path) if path.respond_to? 'each_with_index' path.each_with_index { |name, index| path[index] = convert_path_element(name) } else [convert_path_element(path)] end end