Documentation, again.
This commit is contained in:
parent
78d6db2cc4
commit
3c0e1b7608
@ -3,6 +3,29 @@ require "file_utils"
|
||||
require "./exceptions.cr"
|
||||
require "./indexer.cr"
|
||||
|
||||
# Basic indexes for 1-to-1 relations.
|
||||
# Uncached version.
|
||||
#
|
||||
# This index provides a file-system representation, enabling the administrators to
|
||||
# select a value based on its index. The following example presents an index named "id"
|
||||
# with some data indexed by an UUID attribute.
|
||||
#
|
||||
# ```plain
|
||||
# storage
|
||||
# ├── data
|
||||
# │ ├── 0000000000
|
||||
# │ ├── 0000000001
|
||||
# │ └── 0000000002
|
||||
# ├── indices
|
||||
# │ └── by_id <- this is an example of index named "id"
|
||||
# │ ├── 6e109b82-25de-4250-9c67-e7e8415ad5a7 -> ../../data/0000000000
|
||||
# │ ├── 2080131b-97d7-4300-afa9-55b93cdfd124 -> ../../data/0000000001
|
||||
# │ └── 8b4e83e3-ef95-40dc-a6e5-e6e697ce6323 -> ../../data/0000000002
|
||||
# ```
|
||||
#
|
||||
# NOTE: no cache, thus considered as *slow* for creation, deletion **and retrieval**.
|
||||
# NOTE: see `CachedIndex` for a cached version, faster for retrieval.
|
||||
# NOTE: for fast operations without fs representation, see `RAMOnlyIndex`.
|
||||
class DODB::Index(V) < DODB::Indexer(V)
|
||||
property name : String
|
||||
property key_proc : Proc(V, String | NoIndex) | Proc(V, String)
|
||||
@ -10,11 +33,15 @@ class DODB::Index(V) < DODB::Indexer(V)
|
||||
|
||||
@storage : DODB::Storage(V)
|
||||
|
||||
# To create an index from a database, use `DODB::Storage#new_index` to create
|
||||
# a cached index, `DODB::Storage#new_uncached_index` for an uncached index or
|
||||
# `DODB::Storage#new_RAM_index` for a RAM-only index.
|
||||
def initialize(@storage, @storage_root, @name, @key_proc)
|
||||
Dir.mkdir_p indexing_directory
|
||||
end
|
||||
|
||||
def check!(key, value, old_value)
|
||||
# :inherit:
|
||||
def check!(key : String, value : V, old_value : V?)
|
||||
index_key = key_proc.call value
|
||||
|
||||
return if index_key.is_a? NoIndex
|
||||
@ -33,7 +60,8 @@ class DODB::Index(V) < DODB::Indexer(V)
|
||||
end
|
||||
end
|
||||
|
||||
def index(key, value)
|
||||
# :inherit:
|
||||
def index(key : String, value : V)
|
||||
index_key = key_proc.call value
|
||||
return if index_key.is_a? NoIndex
|
||||
|
||||
@ -44,7 +72,8 @@ class DODB::Index(V) < DODB::Indexer(V)
|
||||
::File.symlink get_data_symlink_index(key), symlink
|
||||
end
|
||||
|
||||
def deindex(key, value)
|
||||
# :inherit:
|
||||
def deindex (key : String, value : V)
|
||||
index_key = key_proc.call value
|
||||
return if index_key.is_a? NoIndex
|
||||
|
||||
@ -56,23 +85,51 @@ class DODB::Index(V) < DODB::Indexer(V)
|
||||
end
|
||||
end
|
||||
|
||||
# Get the key (ex: 343) for an entry in the DB.
|
||||
# Without caching, it translates to walk the file-system in `db/indices/by_#{name}/<index>`.
|
||||
# Gets the key (ex: 343) for an entry in the DB from an indexed value.
|
||||
#
|
||||
# Reads the link in `db/indices/by_#{name}/<index>`.
|
||||
#
|
||||
# Useful for internal purposes, to retrieve a value use `#get`.
|
||||
#
|
||||
# ```
|
||||
# internal_database_key_for_the_corvet = cars_by_name.get_key "Corvet"
|
||||
# ```
|
||||
#
|
||||
# NOTE: used for internal operations.
|
||||
def get_key(index : String) : Int32
|
||||
get_key_on_fs index
|
||||
end
|
||||
|
||||
# Gets data from an indexed value (throws an exception on a missing entry).
|
||||
#
|
||||
# ```
|
||||
# corvet = cars_by_name.get "Corvet"
|
||||
# ```
|
||||
#
|
||||
# WARNING: throws an exception if the value isn't found.
|
||||
# NOTE: for a safe version, use `#get?`.
|
||||
def get(index : String) : V
|
||||
@storage[get_key index]
|
||||
end
|
||||
|
||||
# Gets data from an indexed value without throwing an exception on a missing entry.
|
||||
#
|
||||
# ```
|
||||
# corvet = cars_by_name.get? "Corvet"
|
||||
# ```
|
||||
#
|
||||
# NOTE: safe version of `#get`, returns a *nil* value in case of a missing entry instead of an exception.
|
||||
def get?(index : String) : V?
|
||||
get index
|
||||
rescue MissingEntry
|
||||
nil
|
||||
end
|
||||
|
||||
# FIXME: Unlock on exception.
|
||||
# Gets data from an indexed value (thread-safe via two file locks) and gives it to a provided block of code.
|
||||
#
|
||||
# WARNING: should be thread-safe only between other `#safe_get` and `#safe_get?` calls,
|
||||
# index creations and deletions do not use the same locks!
|
||||
# NOTE: on exception, releases all locks.
|
||||
def safe_get(index : String) : Nil
|
||||
@storage.request_lock @name, index
|
||||
internal_key = get_key(index).to_s
|
||||
@ -82,8 +139,20 @@ class DODB::Index(V) < DODB::Indexer(V)
|
||||
|
||||
@storage.release_lock internal_key
|
||||
@storage.release_lock @name, index
|
||||
|
||||
rescue e
|
||||
# On exception, returns the exception after releasing locks.
|
||||
@storage.release_lock internal_key
|
||||
@storage.release_lock @name, index
|
||||
raise e
|
||||
end
|
||||
|
||||
# Same as `#safe_get` but doesn't throw an exception on a missing value
|
||||
# (provided block of code receives a *nil* value).
|
||||
#
|
||||
# WARNING: should be thread-safe only between other `#safe_get` and `#safe_get?` calls,
|
||||
# index creations and deletions do not use the same locks!
|
||||
# NOTE: on exception, releases all locks.
|
||||
def safe_get?(index : String, &block : Proc(V | Nil, Nil)) : Nil
|
||||
safe_get index, &block
|
||||
rescue MissingEntry
|
||||
@ -151,6 +220,31 @@ class DODB::Index(V) < DODB::Indexer(V)
|
||||
end
|
||||
end
|
||||
|
||||
# Basic indexes for 1-to-1 relations.
|
||||
# Cached version.
|
||||
#
|
||||
# The cache makes this index fast and since the index doesn't contain
|
||||
# the full value but just an attribute and a key, memory usage is still reasonable.
|
||||
#
|
||||
# A few file-system operations are required on index creation and deletion,
|
||||
# thus this version still is slow for both these operations.
|
||||
#
|
||||
# ```plain
|
||||
# storage
|
||||
# ├── data
|
||||
# │ ├── 0000000000
|
||||
# │ ├── 0000000001
|
||||
# │ └── 0000000002
|
||||
# ├── indices
|
||||
# │ └── by_id <- this is an example of index named "id"
|
||||
# │ ├── 6e109b82-25de-4250-9c67-e7e8415ad5a7 -> ../../data/0000000000
|
||||
# │ ├── 2080131b-97d7-4300-afa9-55b93cdfd124 -> ../../data/0000000001
|
||||
# │ └── 8b4e83e3-ef95-40dc-a6e5-e6e697ce6323 -> ../../data/0000000002
|
||||
# ```
|
||||
#
|
||||
# NOTE: cached, reasonable amount of memory used since it's just an index.
|
||||
# NOTE: fast for retrieval, slow for index creation and deletion (fs operations).
|
||||
# NOTE: for fast operations without fs representation, see `RAMOnlyIndex`.
|
||||
class DODB::CachedIndex(V) < DODB::Index(V)
|
||||
# This hash contains the relation between the index key and the data key.
|
||||
property data = Hash(String, Int32).new
|
||||
@ -206,9 +300,16 @@ class DODB::CachedIndex(V) < DODB::Index(V)
|
||||
end
|
||||
end
|
||||
|
||||
# `DODB::RAMOnlyIndex` enables the flexibility of indexes without a file-system representation.
|
||||
# Absolute efficiency, exactly as easy to use as the other index implementations.
|
||||
|
||||
# Basic indexes for 1-to-1 relations.
|
||||
# RAM-only version, no file-system representation.
|
||||
#
|
||||
# Since there is no file-system operations, all the operations are fast.
|
||||
# `DODB::RAMOnlyIndex` enables the flexibility of indexes without a file-system representation
|
||||
# for absolute efficiency.
|
||||
# Exactly as easy to use as the other index implementations.
|
||||
#
|
||||
# NOTE: reasonable amount of memory used since it's just an index.
|
||||
# NOTE: fast for all operations, but no file-system representation.
|
||||
class DODB::RAMOnlyIndex(V) < DODB::CachedIndex(V)
|
||||
def index(key, value)
|
||||
index_key = key_proc.call value
|
||||
|
@ -2,24 +2,36 @@
|
||||
# the indexes (basic indexes, partitions, tags, etc.).
|
||||
abstract class DODB::Indexer(V)
|
||||
|
||||
# Indexes a value.
|
||||
# Indexes a value, used for **internal operations**.
|
||||
#
|
||||
# NOTE: used for internal operations.
|
||||
abstract def index (key : String, value : V)
|
||||
|
||||
# Removes the index of a value.
|
||||
# Removes the index of a value, used for **internal operations**.
|
||||
#
|
||||
# NOTE: used for internal operations.
|
||||
abstract def deindex (key : String, value : V)
|
||||
|
||||
# Verifies whether a new value will create a collision with the index of currently stored value.
|
||||
# Verifies whether a new value will create a collision with the index of
|
||||
# currently stored value, used for **internal operations**.
|
||||
#
|
||||
# NOTE: used for internal operations.
|
||||
abstract def check! (key : String, value : V, old_value : V?)
|
||||
|
||||
# Name of the index, such as *id* or *color* for example.
|
||||
# This is an arbitrary value, mostly to create the index directory.
|
||||
#
|
||||
# NOTE: used for internal operations.
|
||||
abstract def name : String
|
||||
|
||||
# Directory where the values will be written.
|
||||
#
|
||||
# NOTE: used for internal operations.
|
||||
abstract def indexing_directory : String
|
||||
|
||||
# Removes all the index entries.
|
||||
# By default, removes the `#indexing_directory`.
|
||||
# Removes all the index entries, removes the `#indexing_directory` by default.
|
||||
#
|
||||
# NOTE: used for internal operations.
|
||||
def nuke_index
|
||||
FileUtils.rm_rf indexing_directory
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user