cubdb v0.1.0 CubDB View Source

Top-level module providing the database public API.

CubDB is a pure-Elixir embedded key-value database, designed to be simple to use. It runs locally, and is backed by a file.

Both keys and values can be any Elixir (or Erlang) term, so no serialization and de-serialization is necessary.

CubDB uses an immutable data structure that ensures robustness to data corruption. Read and select operations are performed on immutable "snapshots", so they are always consistent, run concurrently, and do not block write operations, nor are blocked by them.

Link to this section Summary

Functions

Returns a specification to start this module under a supervisor.

Runs a database compaction.

Deletes the entry associated to key from the database.

Get the value associated to key from the database.

Returns a tuple indicating if key is associated to a value in the database.

Invoked when the server is started. start_link/3 or start/3 will block until it returns.

Writes an entry in the database, associating key to value.

Selects a range of entries from the database, and optionally performs a pipeline of operations on them.

Returns the number of entries present in the database.

Starts the CubDB database without a link.

Starts the CubDB database process linked to the current process.

Link to this section Functions

Returns a specification to start this module under a supervisor.

See Supervisor.

Link to this function

compact(db) View Source
compact(GenServer.server()) :: :ok | {:error, binary()}

Runs a database compaction.

As write operations are performed on a database, its file grows. Occasionally, a compaction operation can be ran to shrink the file to its optimal size. Compaction is ran in the background and does not block operations.

Only one compaction operation can run at any time, therefore if this function is called when a compaction is already running, it returns {:error, :pending_compaction}.

Link to this function

compaction_file?(file_name) View Source
compaction_file?(binary()) :: boolean()

Link to this function

cubdb_file?(file_name) View Source
cubdb_file?(binary()) :: boolean()

Link to this function

db_file?(file_name) View Source
db_file?(binary()) :: boolean()

Link to this function

delete(db, key) View Source
delete(GenServer.server(), any()) :: :ok

Deletes the entry associated to key from the database.

If key was not present in the database, nothing is done.

Get the value associated to key from the database.

If no value is associated with key, nil is returned.

Link to this function

has_key?(db, key) View Source
has_key?(GenServer.server(), any()) :: {boolean(), any()}

Returns a tuple indicating if key is associated to a value in the database.

If key is associated to a value, it returns {true, value}. Otherwise, it returns {false, nil}

Invoked when the server is started. start_link/3 or start/3 will block until it returns.

init_arg is the argument term (second argument) passed to start_link/3.

Returning {:ok, state} will cause start_link/3 to return {:ok, pid} and the process to enter its loop.

Returning {:ok, state, timeout} is similar to {:ok, state} except handle_info(:timeout, state) will be called after timeout milliseconds if no messages are received within the timeout.

Returning {:ok, state, :hibernate} is similar to {:ok, state} except the process is hibernated before entering the loop. See c:handle_call/3 for more information on hibernation.

Returning {:ok, state, {:continue, continue}} is similar to {:ok, state} except that immediately after entering the loop the c:handle_continue/2 callback will be invoked with the value continue as first argument.

Returning :ignore will cause start_link/3 to return :ignore and the process will exit normally without entering the loop or calling c:terminate/2. If used when part of a supervision tree the parent supervisor will not fail to start nor immediately try to restart the GenServer. The remainder of the supervision tree will be started and so the GenServer should not be required by other processes. It can be started later with Supervisor.restart_child/2 as the child specification is saved in the parent supervisor. The main use cases for this are:

  • The GenServer is disabled by configuration but might be enabled later.
  • An error occurred and it will be handled by a different mechanism than the Supervisor. Likely this approach involves calling Supervisor.restart_child/2 after a delay to attempt a restart.

Returning {:stop, reason} will cause start_link/3 to return {:error, reason} and the process to exit with reason reason without entering the loop or calling c:terminate/2.

Callback implementation for GenServer.init/1.

Link to this function

put(db, key, value) View Source
put(GenServer.server(), any(), any()) :: :ok

Writes an entry in the database, associating key to value.

If key was already present, it is overwritten.

Link to this function

select(db, options \\ [], timeout \\ 5000) View Source
select(GenServer.server(), Keyword.t(), timeout()) ::
  {:ok, any()} | {:error, Exception.t()}

Selects a range of entries from the database, and optionally performs a pipeline of operations on them.

It returns {:ok, result} if successful, or {:error, exception} if an exception is raised.

Options

The min_key and max_key the range of entriethat is selected. All entries that have a key greater or equal than min_key and less or equal then max_key are selected. One or both of min_key and max_key can be omitted or set to nil, in which case the range is open-ended.

The reverse option, when set to true, causes the entries to be selected and traversed in reverse order.

The pipe option specifies an optional list of operations performed sequentially on the selected entries. The given order of operations is respected. The available operations, specified as tuples, are:

  • {:filter, fun} filters entries for which fun returns a truthy value

  • {:map, fun} maps each entry to the value returned by the function fun

  • {:take, n} takes the first n entries

  • {:drop, n} skips the first n entries

  • {:take_while, fun} takes entries while fun returns a truthy value

  • {:drop_while, fun} skips entries while fun returns a truthy value

The reduce option specifies how the selected entries are aggregated. If reduce is omitted, the entries are returned as a list. If reduce is a function, it is used to reduce the collection of entries. If reduce is a tuple, the first element is the starting value of the reduction, and the second is the reducing function.

Examples

To select all entries with keys between :a and :c as a list of {key, value} we can do:

{:ok, entries} = CubDB.select(db, min_key: :a, max_key: :c)

To select the last 3 entries, we can do:

{:ok, entries} = CubDB.select(db, reverse: true, pipe: [take: 3])

If we want to obtain the sum of the first 10 positive numeric values associated to keys from :a to :f, we can do:

{:ok, sum} = CubDB.select(db,
  min_key: :a,
  max_key: :f,
  pipe: [
    map: fn {_key, value} -> value end, # map values
    filter: fn n -> is_number(n) and n > 0 end # only positive numbers
    take: 10, # take only the first 10 entries in the range
  ],
  reduce: fn n, sum -> sum + n end # reduce to the sum of selected values
)

Returns the number of entries present in the database.

Starts the CubDB database without a link.

See start_link/2 for more informations.

Link to this function

start_link(data_dir, options \\ []) View Source

Starts the CubDB database process linked to the current process.

The data_dir argument is a directory name where the database files will be stored. Only one CubDB instance can run per directory, so if you run several databases, they should each use their own data directory.

The options are passed to GenServer.start_link/3.