Current section

Files

Jump to
electric lib electric shapes.ex
Raw

lib/electric/shapes.ex

defmodule Electric.Shapes do
alias Electric.Replication.LogOffset
alias Electric.ShapeCache.Storage
alias Electric.ShapeCache
alias Electric.Shapes.Shape
require Logger
@type shape_id :: Electric.ShapeCacheBehaviour.shape_id()
@doc """
Get snapshot for the shape ID
"""
def get_snapshot(config, shape_id) do
{shape_cache, opts} = Access.get(config, :shape_cache, {ShapeCache, []})
storage = shape_storage(config, shape_id)
if shape_cache.has_shape?(shape_id, opts) do
with :started <- shape_cache.await_snapshot_start(shape_id, opts) do
{:ok, Storage.get_snapshot(storage)}
end
else
{:error, "invalid shape_id #{inspect(shape_id)}"}
end
end
@doc """
Get stream of the log since a given offset
"""
def get_log_stream(config, shape_id, opts) do
{shape_cache, shape_cache_opts} = Access.get(config, :shape_cache, {ShapeCache, []})
offset = Access.get(opts, :since, LogOffset.before_all())
max_offset = Access.get(opts, :up_to, LogOffset.last())
storage = shape_storage(config, shape_id)
if shape_cache.has_shape?(shape_id, shape_cache_opts) do
Storage.get_log_stream(offset, max_offset, storage)
else
raise "Unknown shape: #{shape_id}"
end
end
@doc """
Get the shape that corresponds to this shape definition and return it along with the latest offset of the shape
"""
@spec get_shape(keyword(), Shape.t()) :: {shape_id(), LogOffset.t()}
def get_shape(config, shape_def) do
{shape_cache, opts} = Access.get(config, :shape_cache, {ShapeCache, []})
shape_cache.get_shape(shape_def, opts)
end
@doc """
Get or create a shape ID and return it along with the latest offset of the shape
"""
@spec get_or_create_shape_id(keyword(), Shape.t()) :: {shape_id(), LogOffset.t()}
def get_or_create_shape_id(config, shape_def) do
{shape_cache, opts} = Access.get(config, :shape_cache, {ShapeCache, []})
shape_cache.get_or_create_shape_id(shape_def, opts)
end
@doc """
Get the last exclusive offset of the chunk starting from the given offset
If `nil` is returned, chunk is not complete and the shape's latest offset should be used
"""
@spec get_chunk_end_log_offset(keyword(), shape_id(), LogOffset.t()) ::
LogOffset.t() | nil
def get_chunk_end_log_offset(config, shape_id, offset) do
storage = shape_storage(config, shape_id)
Storage.get_chunk_end_log_offset(offset, storage)
end
@doc """
Check whether the log has an entry for a given shape ID
"""
@spec has_shape?(keyword(), shape_id()) :: boolean()
def has_shape?(config, shape_id) do
{shape_cache, opts} = Access.get(config, :shape_cache, {ShapeCache, []})
shape_cache.has_shape?(shape_id, opts)
end
@doc """
Clean up all data (meta data and shape log + snapshot) associated with the given shape ID
"""
@spec clean_shape(shape_id(), keyword()) :: :ok
def clean_shape(shape_id, opts \\ []) do
{shape_cache, opts} = Access.get(opts, :shape_cache, {ShapeCache, []})
shape_cache.clean_shape(shape_id, opts)
:ok
end
@spec clean_shapes([shape_id()], keyword()) :: :ok
def clean_shapes(shape_ids, opts \\ []) do
{shape_cache, opts} = Access.get(opts, :shape_cache, {ShapeCache, []})
for shape_id <- shape_ids do
shape_cache.clean_shape(shape_id, opts)
end
:ok
end
defp shape_storage(config, shape_id) do
Storage.for_shape(shape_id, Access.fetch!(config, :storage))
end
end