View Source EctoFoundationDB.Sync (Ecto.Adapters.FoundationDB v0.6.1)
This module defines some conventions for integrating with Phoenix LiveView or any other stateful process. Via EctoFoundationDB watches, your application can automatically be kept up-to-date with changes to the database.
Simply call one of the provided sync* functions in mount/3 or handle_params/3, and this module will do the following:
- Read from the database and create necessary watches
- Call
Phoenix.Component.assign/2, using the providedlabel - Call
Phoenix.LiveView.attach_hook/4to set up a callback as a hook
Then, upon receiving a watch-ready message, the hook calls Phoenix.Component.assign/2
again with the updated data, and creates new watches as needed.
Socket requirements:
- The tenant must be stored in the
:privatefield of the providedsocket. - The sync functions will store a key called
:ecto_fdb_sync_datain the:privatefield.
Examples
These are quick, short examples. Please see Sync Engine III for end-to-end detail.
Quick example 1: Syncing a single record
Suppose you have a LiveView that displays a single user. You can use sync_one/5 to
automatically update the user whenever it is created, updated, or deleted.
defmodule MyApp.UserLive do
use Phoenix.LiveView
alias EctoFoundationDB.Sync
alias MyApp.Repo
alias MyApp.User
def mount(_params, _session, socket) do
user_id = "1"
{:ok,
socket
|> put_private(:tenant, open_tenant(socket))
|> Sync.sync_one(Repo, User, :user, user_id)}
end
endQuick example 2: Syncing a list of records
Suppose you have a LiveView that displays a list of users. You can use sync_all/4 to
automatically update the list whenever a user is created, updated, or deleted.
You must have already defined a SchemaMetadata index for the User schema for sync_all/4
to work.
defmodule MyApp.UserLive do
use Phoenix.LiveView
alias EctoFoundationDB.Sync
alias MyApp.Repo
alias MyApp.User
def mount(_params, _session, socket) do
{:ok,
socket
|> put_private(:tenant, open_tenant(socket))
|> Sync.sync_all(Repo, User)}
end
endQuick example 3: Syncing a group of records indivudually
Suppose your page displays serveral records simulataneously, but you wish to subscribe to change individually.
You can use sync_many/6. This integrates nicely with LiveComponents.
defmodule MyApp.UserLive do
use Phoenix.LiveView
alias EctoFoundationDB.Sync
alias MyApp.Repo
alias MyApp.User
def mount(_params, _session, socket) do
user_ids = ["1", "2", "3"]
{:ok,
socket
|> put_private(:tenant, open_tenant(socket))
|> Sync.sync_many(Repo, User, :users, user_ids)}
end
endLabels
The label argument is used to identify the data being synced. The label is
used as the key in the assigns map. It can be any term. Usually, you'll use an
atom for compatibility with Phoenix.
For example, you can provide the label :user and your assigns map will look like this:
iex> assigns
%{user: %User{
id: 1,
name: "Alice",
email: "alice@example.com"
}}Limitations
The sync* functions themselves define FDB transactions. Therefore, if your desired query
is more complex, or if you need to sync multiple collections with internal consistency,
you'll need to write your own logic to create and listen for watches. In these more
sophisticated cases, we encourage you to avoid the Sync module entirely, and instead
handle the {reference(), :ready} messages yourself.
Summary
Functions
Attaches a callback to the :handle_assigns event.
Cancels syncing for the provided label and, if none are left, detaches the hook.
Cancels all syncing and detaches the hook.
Detaches a callback from the :handle_assigns event.
This hook can be attached to a compatible Elixir process to automatically
process handle_info :ready messages from EctoFDB.
Initializes a Sync of one or more queryables.
Sets up syncing for a list of records in the database.
Sets up syncing for a list of records in the database, constrained by an indexed field.
Sets up syncing for a list of records in the database, with indivudual watches.
Sets up syncing for a single record in the database.
Functions
attach_callback(state, repo, name \\ :default, event, cb, opts \\ [])
View SourceAttaches a callback to the :handle_assigns event.
The callback will be called when the Sync module changes your assigns.
Arguments
state: A map with key:assignsand:private.privatemust be a map with key:tenantrepo: An Ecto repositoryname: The name of the callback. Defaults to:default.event: The event to attach the callback to. Only:handle_assignsis supported.cb: The callback function. Arity must be 2, with the first argument being thestateand the second being a map with the old assigns.opts: Options
Options
:replace: A boolean indicating whether to replace an existing callback with the same name and event. Defaults tofalse.
Cancels syncing for the provided label and, if none are left, detaches the hook.
Refer to cancel_all/3 for a discussion on when and why to cancel.
Arguments
state: A map with key:assignsand:private.privatemust be a map with key:tenantrepo: An Ecto repositorylabel: A label to cancel syncing foropts: Options
Options
assign: A boolean indicating whether or not to assign the label tonilor[]. Defaults totrue.detach_container_hook: A function that takesstate, name, repo, optsand modifies state as needed to detach a container hook. When not provided: ifPhoenix.LiveViewis available, we usePhoenix.LiewView.detach_hook/3, otherwise we do nothing.
Return
Returns an updated state, with :private updated with the following values:
private
- We cancel and clear the futures in
:ecto_fdb_sync_data.
Cancels all syncing and detaches the hook.
Canceling syncing is optional. EctoFoundationDB will automatically clean up watches when your process exits.
Arguments
state: A map with key:assignsand:private.privatemust be a map with key:tenantrepo: An Ecto repositoryopts: Options
Options
detach_container_hook: A function that takesstate, name, repo, optsand modifies state as needed to detach a container hook. When not provided: ifPhoenix.LiveViewis available, we usePhoenix.LiewView.detach_hook/3, otherwise we do nothing.
Return
Returns an updated state, with :private updated with the following values:
private
- We cancel and clear the futures in
:ecto_fdb_sync_data.
Detaches a callback from the :handle_assigns event.
This hook can be attached to a compatible Elixir process to automatically
process handle_info :ready messages from EctoFDB.
This hook is designed to be used with LiveView's attach_hook. If you're using
one of the sync_* function in this module along with LiveView, the hook is
attached automatically. You do not need to call this function.
Arguments
repo: An Ecto repository.info: A message received on the process mailbox. We will inspect messages of the form{ref, :ready} when is_reference(ref), and ignore all others (returning{:cont, state}). Or a list of such messages.state: A map with key:assignsand:private.privatemust be a map with keys:tenantand:ecto_fdb_sync_data.opts: Options
Options
assign: A function that takes the current socket and new assigns and returns a tuple of new assigns and state. By default, we simply update the assigns map with the new labels. The default is not sufficient for LiveView's assign
Result behavior
Either {:cont, state} or {:halt, state} is returned.
:cont: Returned when the message was not processed by the Repo.:halt: Returned when the ready message is relevant to the providedfutures. Theassignsandprivateare updated accordingly based on the label provided to the matching future. The watches are re-initialized so that the expected syncing behavior will continue.
Initializes a Sync of one or more queryables.
This is to be paired with handle_ready/3 to provide automatic updating of assigns in state. If you're using
the default LiveView attach_hook as described in the Options, then handle_ready/3 will be set up for you
automatically.
Arguments
state: A map with key:assignsand:private.privatemust be a map with key:tenantrepo: An Ecto repositoryqueryable_assigns: A list ofAll,One, orManystructsopts: Options
Options
assign: A function that takes the current socket and new assigns and returns the updated state. When not provided: ifPhoenix.Componentis available, we usePhoenix.Component.assign/3, otherwise we useMap.put/3.attach_container_hook: A function that takesstate, name, repo, optsand modifies state as needed to attach a hook. When not provided: ifPhoenix.LiveViewis available, we usePhoenix.LiewView.attach_hook/4, otherwise we do nothing.
Return
Returns an updated state, with :assigns and :private updated with the following values:
assigns
- Provided labels from
queryable_assignsare used to register the results from the database.
private
- We add or append to the
:ecto_fdb_sync_dataas needed for internal purposes.
Sets up syncing for a list of records in the database.
A watch is created for changes to the list with SchemaMetadata.
See sync/4 for more.
Options
watch_action: An atom representing the signal from theSchemaMetadatayou're interested in syncing. Defaults to:changes
Sets up syncing for a list of records in the database, constrained by an indexed field.
A watch is created for changes to the list with SchemaMetadata.
See sync/4 for more.
Options
watch_action: An atom representing the signal from theSchemaMetadatayou're interested in syncing. Defaults to:changes
Sets up syncing for a list of records in the database, with indivudual watches.
See sync/4 for more.
Sets up syncing for a single record in the database.
See sync/4 for more.