LiveSync allows automatic updating of LiveView assigns by utilizing postgres replication.
This project builds on top of PostgreSQL replication and it requires PostgreSQL 14+. You must also enable replication in your PostgreSQL instance:
ALTER SYSTEM SET wal_level='logical';
ALTER SYSTEM SET max_wal_senders='64';
ALTER SYSTEM SET max_replication_slots='64';
Then you must restart your database.
Add live_sync
to the list of dependencies in mix.exs
:
def deps do
[
{:live_sync, "~> 0.1.0"}
]
end
Add a migration to setup replication (requires superuser permissions to subscribe to all tables):
defmodule MyApp.Repo.Migrations.SetupLiveSync do
use Ecto.Migration
def up do
LiveSync.Migration.up()
# If you don't have superuser you can pass specific tables
# LiveSync.Migration.up(["table1", "table2"])
end
def down do
LiveSync.Migration.down()
end
end
Add LiveSync
to your supervision tree:
-
repo
(required): The Ecto repo to use for replication. -
otp_app
(required): The OTP app to use to lookup schemas deriving the watch protocol.defmodule MyApp.Application do @moduledoc false use Application @impl true def start(_type, _args) do children = [ ... {LiveSync, [repo: MyApp.Repo, otp_app: :my_app]} ] opts = [strategy: :one_for_one, name: MyApp.Supervisor] Supervisor.start_link(children, opts) end end
For any Ecto schemas you want to watch, add the LiveSync.Watch
derive:
-
id
(optional): The primary key on the schema, defaults to:id
. -
subscription_key
(required): The field on the schema that is used to filter messages before sending to the client. -
table
(optional): The table to watch, defaults to the schema's table name, if using a view you need to specify the table namedefmodule MyApp.MyObject do use Ecto.Schema @derive {LiveSync.Watch, [ subscription_key: :organization_id, table: "objects" ]} schema "visible_objects" do field :name, :string field :organization_id, :integer end ... end
Add the LiveSync
macro to any LiveView module you want to automatically sync data:
-
subscription_key
(required): This is the key that MUST exist in the assigns of the LiveView and is used for the subscription. This value must match what is in the schema's@derive
attribute. -
watch
(required): A list of keys in assigns that should be watched. You may optionally specify a tuple with options. Schema is required for lists of objects to support inserting.use LiveSync, subscription_key: :organization_id, watch: [ :single_object, list_of_objects: [schema: MyApp.MyObject] ]
An optional callback can also be added to the LiveView module to handle the updated data. This callback will be called for each assign key that is watched and changed. It must return the socket with updated assigns.
def sync(:list_of_objects, updated, socket) do
updates =
updated
|> Enum.filter(&is_nil(&1.executed_at))
|> Enum.sort_by(& &1.name)
|> Repo.preload([...])
assign(socket, list_of_objects: updates)
end