From 66d43b47f8be52c9c40d6cc0ed285a5049dddd3c Mon Sep 17 00:00:00 2001 From: RTLS Date: Tue, 17 Feb 2026 21:27:31 -0800 Subject: [PATCH] Add libcluster_postgres for automatic node discovery Use PostgreSQL LISTEN/NOTIFY via libcluster_postgres for cluster formation, removing the need for DNS-based discovery. This enables distributed features (syn, global, MutexedSupervisor) across nodes using the existing Postgres database. Co-Authored-By: Claude Opus 4.6 --- config/config.exs | 2 ++ config/dev.exs | 28 ++++++++++++++++++++++------ config/runtime.exs | 26 ++++++++++++++++++++++++++ lib/sequin/application.ex | 1 + mix.exs | 2 +- mix.lock | 1 + 6 files changed, 53 insertions(+), 7 deletions(-) diff --git a/config/config.exs b/config/config.exs index eb39155f6..f4d861dfb 100644 --- a/config/config.exs +++ b/config/config.exs @@ -15,6 +15,8 @@ config :esbuild, :version, "0.17.11" # Used by broadway_sqs config :ex_aws, http_client: ExAws.Request.Req +config :libcluster, topologies: [] + config :logger, :console, format: {Sequin.ConsoleLogger, :format}, metadata: :all diff --git a/config/dev.exs b/config/dev.exs index 5b4a50a04..b75c42758 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -1,5 +1,21 @@ import Config +config :libcluster, + topologies: [ + postgres: [ + strategy: LibclusterPostgres.Strategy, + config: [ + hostname: "localhost", + username: "postgres", + password: "postgres", + database: "sequin_dev", + port: 5432, + channel_name: "sequin_cluster", + heartbeat_interval: 5_000 + ] + ] + ] + config :mix_test_interactive, clear: true @@ -81,6 +97,12 @@ config :sequin, # Arbitrarily high memory limit in dev max_memory_bytes: ("MAX_MEMORY_MB" |> System.get_env("5000") |> String.to_integer()) * 1024 * 1024 +# Enable dev routes for dashboard and mailbox +config :sequin, dev_routes: true, self_hosted: false + +# Disable swoosh api client as it is only required for production adapters. +config :swoosh, :api_client, false + # esbuild: {Esbuild, :install_and_run, [:sequin, ~w(--sourcemap=inline --watch)]}, # ## SSL Support @@ -108,9 +130,6 @@ config :sequin, # Watch static and templates for browser reloading. -# Enable dev routes for dashboard and mailbox -config :sequin, dev_routes: true, self_hosted: false - # Do not include metadata nor timestamps in development logs # Set a higher stacktrace during development. Avoid configuring such @@ -120,9 +139,6 @@ config :sequin, dev_routes: true, self_hosted: false # Include HEEx debug annotations as HTML comments in rendered markup # Enable helpful, but potentially expensive runtime checks -# Disable swoosh api client as it is only required for production adapters. -config :swoosh, :api_client, false - if "dev.secret.exs" |> Path.expand(__DIR__) |> File.exists?() do import_config "dev.secret.exs" end diff --git a/config/runtime.exs b/config/runtime.exs index 32914c665..a5614700b 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -163,6 +163,19 @@ if config_env() == :prod and self_hosted do :ok end + config :libcluster, + topologies: [ + postgres: [ + strategy: LibclusterPostgres.Strategy, + config: + Keyword.merge( + Application.get_env(:sequin, Sequin.Repo), + channel_name: "sequin_cluster", + heartbeat_interval: 5_000 + ) + ] + ] + config :sequin, Sequin.Posthog, req_opts: [base_url: "https://us.i.posthog.com"], api_key: "phc_i9k28nZwjjJG9DzUK0gDGASxXtGNusdI1zdaz9cuA7h", @@ -224,6 +237,19 @@ if config_env() == :prod and not self_hosted do function_transforms = if System.get_env("FEATURE_FUNCTION_TRANSFORMS", "disabled") in enabled_feature_values, do: :enabled, else: :disabled + config :libcluster, + topologies: [ + postgres: [ + strategy: LibclusterPostgres.Strategy, + config: + Keyword.merge( + Application.get_env(:sequin, Sequin.Repo), + channel_name: "sequin_cluster", + heartbeat_interval: 5_000 + ) + ] + ] + config :logger, default_handler: [ formatter: {Datadog, metadata: :all, redactors: [{Redactor, []}]} diff --git a/lib/sequin/application.ex b/lib/sequin/application.ex index 323abfa1e..f903670a3 100644 --- a/lib/sequin/application.ex +++ b/lib/sequin/application.ex @@ -75,6 +75,7 @@ defmodule Sequin.Application do Sequin.Redis.connect_cluster() [ + {Cluster.Supervisor, [Application.get_env(:libcluster, :topologies, []), [name: Sequin.ClusterSupervisor]]}, Sequin.Repo, Sequin.Vault, Sequin.PubSub.child_spec(), diff --git a/mix.exs b/mix.exs index 4fc6dc547..a3ff9402f 100644 --- a/mix.exs +++ b/mix.exs @@ -114,7 +114,7 @@ defmodule Sequin.MixProject do {:eredis, github: "acco/eredis", override: true}, # Clustering and Distribution - {:dns_cluster, "~> 0.1.1"}, + {:libcluster_postgres, "~> 0.2"}, # Data Processing and Types {:flow, "~> 1.2"}, diff --git a/mix.lock b/mix.lock index bc9bc679d..9f4d8f543 100644 --- a/mix.lock +++ b/mix.lock @@ -68,6 +68,7 @@ "kafka_protocol": {:hex, :kafka_protocol, "4.1.10", "f917b6c90c8df0de2b40a87d6b9ae1cfce7788e91a65818e90e40cf76111097a", [:rebar3], [{:crc32cer, "0.1.11", [hex: :crc32cer, repo: "hexpm", optional: false]}], "hexpm", "df680a3706ead8695f8b306897c0a33e8063c690da9308db87b462cfd7029d04"}, "kcl": {:hex, :kcl, "1.4.2", "8b73a55a14899dc172fcb05a13a754ac171c8165c14f65043382d567922f44ab", [:mix], [{:curve25519, ">= 1.0.4", [hex: :curve25519, repo: "hexpm", optional: false]}, {:ed25519, "~> 1.3", [hex: :ed25519, repo: "hexpm", optional: false]}, {:poly1305, "~> 1.0", [hex: :poly1305, repo: "hexpm", optional: false]}, {:salsa20, "~> 1.0", [hex: :salsa20, repo: "hexpm", optional: false]}], "hexpm", "9f083dd3844d902df6834b258564a82b21a15eb9f6acdc98e8df0c10feeabf05"}, "libcluster": {:hex, :libcluster, "3.4.1", "271d2da892763bbef53c2872036c936fe8b80111eb1feefb2d30a3bb15c9b4f6", [:mix], [{:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.3", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1d568157f069c6afa70ec0d736704cf799734bdbb6343f0322af4a980301c853"}, + "libcluster_postgres": {:hex, :libcluster_postgres, "0.2.0", "14a5064b78f891c46935a66489454814d949a52b447fc1daff5d4a440e6f5847", [:mix], [{:libcluster, "~> 3.3", [hex: :libcluster, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "ab2e952371c5a0a0fcb263216c7eae2a2267977b3bb3236650daed3054a93edd"}, "live_svelte": {:hex, :live_svelte, "0.13.3", "54e7c55d30b5b143674011d32c85bfbba9f38f7684f6dbc58a39b082606874da", [:mix], [{:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}, {:nodejs, "~> 2.0", [hex: :nodejs, repo: "hexpm", optional: false]}, {:phoenix, ">= 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, ">= 3.3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, ">= 0.18.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "57dbc0e136d0db20bc0bd8282bbb4d4be3972359c47a787c144b09269f23851b"}, "logger_json": {:hex, :logger_json, "6.0.3", "16275236b5c2feeb065974c245fcbb358999623befb02bc6ab803bfdda817ee0", [:mix], [{:ecto, "~> 3.11", [hex: :ecto, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "ea0913b52525f0857ed66398b7647c609bb5a33257a6786eb58692c82ea7db04"}, "lz4b": {:hex, :lz4b, "0.0.13", "120cbea04908c8f56f2d56d0674ff7a9454ec3f638055760c9829f399b034270", [:rebar3], [], "hexpm", "ded395a22225f43d3a9030ceef2e9c652fa2c137146e652f7da82b795c49e7df"},