Ecto-backed persistence for sketches.
Stores serialized EXSK v2 frames in a SQL database via Ecto. This backend is useful for applications already using Ecto for persistence, or when sketches need to survive process and node restarts without managing ETS/DETS.
Dependency
This module requires :ecto_sql. If Ecto is not available, calls will
raise a clear error directing you to add the dependency.
Setup
Add the migration to your application:
mix ex_data_sketch.gen.migration --repo MyApp.Repo
Run the migration:
mix ecto.migrate
Use the storage API:
ExDataSketch.Storage.Ecto.save(sketch, MyApp.Repo, "cardinality:2024-01") {:ok, sketch} = ExDataSketch.Storage.Ecto.load(ExDataSketch.HLL, MyApp.Repo, "cardinality:2024-01")
Non-executable examples
The examples below use MyApp.Repo which must be configured in your
application. They are included for documentation purposes and will not
run in the standard test suite.
Examples
# Save a sketch
:ok = ExDataSketch.Storage.Ecto.save(sketch, MyApp.Repo, "cardinality:2024-01")
# Load a sketch
{:ok, sketch} = ExDataSketch.Storage.Ecto.load(ExDataSketch.HLL, MyApp.Repo, "cardinality:2024-01")
# Atomic merge (uses Ecto transaction)
:ok = ExDataSketch.Storage.Ecto.merge(partial, MyApp.Repo, "cardinality:2024-01")
# Delete
:ok = ExDataSketch.Storage.Ecto.delete(MyApp.Repo, "cardinality:2024-01")
Summary
Functions
Deletes a sketch from the database by key.
Returns whether Ecto is available at runtime.
Loads a sketch from the database by key.
Atomically merges a sketch into the persisted value at the given key.
Persists a sketch under the given key via Ecto.
Functions
@spec delete(module(), ExDataSketch.Storage.key()) :: :ok
Deletes a sketch from the database by key.
Arguments
repo-- the Ecto repo module.key-- the key to delete.
Returns
:ok always (even if the key did not exist).
Examples
iex> ExDataSketch.Storage.Ecto.delete(MyApp.Repo, "hll:test")
:ok
@spec ecto_available?() :: boolean()
Returns whether Ecto is available at runtime.
Examples
iex> is_boolean(ExDataSketch.Storage.Ecto.ecto_available?())
true
@spec load(module(), module(), ExDataSketch.Storage.key()) :: {:ok, struct()} | {:error, :not_found | term()}
Loads a sketch from the database by key.
Finds the most recent entry for the given key and deserializes it using
the given sketch module's deserialize/1 function.
Arguments
sketch_module-- the sketch module atom (e.g.,ExDataSketch.HLL).repo-- the Ecto repo module.key-- the key to look up.
Returns
{:ok, sketch}on success.{:error, :not_found}if no entry exists for the key.{:error, %DeserializationError{}}if the stored binary is corrupted.{:error, reason}on database failure.
Raises
RuntimeErrorif Ecto is not available.
Examples
iex> {:ok, loaded} = ExDataSketch.Storage.Ecto.load(ExDataSketch.HLL, MyApp.Repo, "hll:test")
iex> ExDataSketch.HLL.estimate(loaded) > 0.0
true
@spec merge(struct(), module(), ExDataSketch.Storage.key()) :: :ok | {:error, term()}
Atomically merges a sketch into the persisted value at the given key.
Uses an Ecto transaction for atomicity. If no sketch exists at the key,
this is equivalent to save/3.
Arguments
sketch-- the sketch to merge.repo-- the Ecto repo module.key-- the key whose persisted sketch to merge into.
Returns
:ok on success, {:error, reason} on failure.
Raises
RuntimeErrorif Ecto is not available.
Examples
iex> sketch_a = ExDataSketch.HLL.new(p: 10) |> ExDataSketch.HLL.update("a")
iex> ExDataSketch.Storage.Ecto.save(sketch_a, MyApp.Repo, "hll:test")
:ok
iex> sketch_b = ExDataSketch.HLL.new(p: 10) |> ExDataSketch.HLL.update("b")
iex> ExDataSketch.Storage.Ecto.merge(sketch_b, MyApp.Repo, "hll:test")
:ok
@spec save(struct(), module(), ExDataSketch.Storage.key()) :: :ok | {:error, term()}
Persists a sketch under the given key via Ecto.
If a sketch with the same key and type already exists, it is replaced (upsert). The sketch is serialized to an EXSK v2 binary frame before storage.
Arguments
sketch-- a sketch struct.repo-- the Ecto repo module (e.g.,MyApp.Repo).key-- the key under which to store the sketch.
Returns
:ok on success, {:error, changeset} on validation failure.
Raises
RuntimeErrorif Ecto is not available.
Examples
iex> sketch = ExDataSketch.HLL.new(p: 10) |> ExDataSketch.HLL.update("a")
iex> ExDataSketch.Storage.Ecto.save(sketch, MyApp.Repo, "hll:test")
:ok