ExDataSketch.Storage.Ecto (ExDataSketch v0.9.0)

Copy Markdown View Source

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

  1. Add the migration to your application:

    mix ex_data_sketch.gen.migration --repo MyApp.Repo

  2. Run the migration:

    mix ecto.migrate

  3. 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

delete(repo, key)

@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

ecto_available?()

@spec ecto_available?() :: boolean()

Returns whether Ecto is available at runtime.

Examples

iex> is_boolean(ExDataSketch.Storage.Ecto.ecto_available?())
true

load(sketch_module, repo, key)

@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

Examples

iex> {:ok, loaded} = ExDataSketch.Storage.Ecto.load(ExDataSketch.HLL, MyApp.Repo, "hll:test")
iex> ExDataSketch.HLL.estimate(loaded) > 0.0
true

merge(sketch, repo, key)

@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

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

save(sketch, repo, key)

@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

Examples

iex> sketch = ExDataSketch.HLL.new(p: 10) |> ExDataSketch.HLL.update("a")
iex> ExDataSketch.Storage.Ecto.save(sketch, MyApp.Repo, "hll:test")
:ok