Showing preview only (2,147K chars total). Download the full file or copy to clipboard to get everything.
Repository: supabase/realtime
Branch: main
Commit: 0916b0dceaf9
Files: 475
Total size: 2.0 MB
Directory structure:
gitextract_musv60dn/
├── .credo.exs
├── .dockerignore
├── .formatter.exs
├── .github/
│ ├── actionlint.yaml
│ └── workflows/
│ ├── beacon_tests.yml
│ ├── docker-build.yml
│ ├── integration_tests.yml
│ ├── lint.yml
│ ├── manual_prod_build.yml
│ ├── mirror.yml
│ ├── prod_build.yml
│ ├── prod_linter.yml
│ ├── tests.yml
│ └── update-supabase-js.yml
├── .gitignore
├── .releaserc
├── .sobelow-conf
├── .tool-versions
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── assets/
│ ├── css/
│ │ ├── app.css
│ │ └── phoenix.css
│ ├── js/
│ │ └── app.js
│ ├── package.json
│ ├── tailwind.config.js
│ └── vendor/
│ └── topbar.js
├── beacon/
│ ├── .formatter.exs
│ ├── .gitignore
│ ├── README.md
│ ├── config/
│ │ └── config.exs
│ ├── lib/
│ │ ├── beacon/
│ │ │ ├── adapter/
│ │ │ │ └── erl_dist.ex
│ │ │ ├── adapter.ex
│ │ │ ├── partition.ex
│ │ │ ├── scope.ex
│ │ │ └── supervisor.ex
│ │ └── beacon.ex
│ ├── mix.exs
│ └── test/
│ ├── beacon/
│ │ └── partition_test.exs
│ ├── beacon_test.exs
│ ├── support/
│ │ └── peer.ex
│ └── test_helper.exs
├── bench/
│ ├── gen_counter.exs
│ └── secrets.exs
├── config/
│ ├── config.exs
│ ├── dev.exs
│ ├── prod.exs
│ ├── runtime.exs
│ └── test.exs
├── coveralls.json
├── deploy/
│ └── fly/
│ ├── prod.toml
│ ├── qa.toml
│ └── staging.toml
├── dev/
│ └── postgres/
│ └── 00-supabase-schema.sql
├── docker-compose.dbs.yml
├── docker-compose.tests.yml
├── docker-compose.yml
├── lib/
│ ├── extensions/
│ │ ├── extensions.ex
│ │ └── postgres_cdc_rls/
│ │ ├── cdc_rls.ex
│ │ ├── db_settings.ex
│ │ ├── message_dispatcher.ex
│ │ ├── replication_poller.ex
│ │ ├── replications.ex
│ │ ├── subscription_manager.ex
│ │ ├── subscriptions.ex
│ │ ├── subscriptions_checker.ex
│ │ ├── supervisor.ex
│ │ └── worker_supervisor.ex
│ ├── realtime/
│ │ ├── adapters/
│ │ │ ├── changes.ex
│ │ │ └── postgres/
│ │ │ ├── decoder.ex
│ │ │ ├── oid_database.ex
│ │ │ ├── protocol/
│ │ │ │ ├── keep_alive.ex
│ │ │ │ └── write.ex
│ │ │ └── protocol.ex
│ │ ├── api/
│ │ │ ├── extensions.ex
│ │ │ ├── message.ex
│ │ │ └── tenant.ex
│ │ ├── api.ex
│ │ ├── application.ex
│ │ ├── beacon_pub_sub_adapter.ex
│ │ ├── crypto.ex
│ │ ├── database.ex
│ │ ├── gen_counter/
│ │ │ └── gen_counter.ex
│ │ ├── gen_rpc/
│ │ │ └── pub_sub.ex
│ │ ├── gen_rpc.ex
│ │ ├── helpers.ex
│ │ ├── log_filter.ex
│ │ ├── logs.ex
│ │ ├── messages.ex
│ │ ├── metrics_cleaner.ex
│ │ ├── metrics_pusher.ex
│ │ ├── monitoring/
│ │ │ ├── distributed_metrics.ex
│ │ │ ├── erl_sys_mon.ex
│ │ │ ├── gen_rpc_metrics.ex
│ │ │ ├── latency.ex
│ │ │ ├── os_metrics.ex
│ │ │ ├── peep/
│ │ │ │ └── partitioned.ex
│ │ │ ├── prom_ex/
│ │ │ │ └── plugins/
│ │ │ │ ├── channels.ex
│ │ │ │ ├── distributed.ex
│ │ │ │ ├── gen_rpc.ex
│ │ │ │ ├── osmon.ex
│ │ │ │ ├── phoenix.ex
│ │ │ │ ├── tenant.ex
│ │ │ │ ├── tenant_global.ex
│ │ │ │ └── tenants.ex
│ │ │ ├── prom_ex.ex
│ │ │ ├── prometheus.ex
│ │ │ └── tenant_prom_ex.ex
│ │ ├── nodes.ex
│ │ ├── operations.ex
│ │ ├── postgres_cdc.ex
│ │ ├── rate_counter/
│ │ │ ├── dynamic_supervisor.ex
│ │ │ └── rate_counter.ex
│ │ ├── release.ex
│ │ ├── repo.ex
│ │ ├── repo_replica.ex
│ │ ├── rpc.ex
│ │ ├── signal_handler.ex
│ │ ├── syn/
│ │ │ └── postgres_cdc.ex
│ │ ├── syn_handler.ex
│ │ ├── telemetry/
│ │ │ ├── logger.ex
│ │ │ └── telemetry.ex
│ │ ├── tenants/
│ │ │ ├── authorization/
│ │ │ │ ├── policies/
│ │ │ │ │ ├── broadcast_policies.ex
│ │ │ │ │ └── presence_policies.ex
│ │ │ │ └── policies.ex
│ │ │ ├── authorization.ex
│ │ │ ├── batch_broadcast.ex
│ │ │ ├── cache.ex
│ │ │ ├── connect/
│ │ │ │ ├── check_connection.ex
│ │ │ │ ├── get_tenant.ex
│ │ │ │ ├── piper.ex
│ │ │ │ ├── reconcile_migrations.ex
│ │ │ │ └── register_process.ex
│ │ │ ├── connect.ex
│ │ │ ├── janitor/
│ │ │ │ └── maintenance_task.ex
│ │ │ ├── janitor.ex
│ │ │ ├── migrations.ex
│ │ │ ├── rebalancer.ex
│ │ │ ├── replication_connection/
│ │ │ │ └── watchdog.ex
│ │ │ ├── replication_connection.ex
│ │ │ ├── repo/
│ │ │ │ └── migrations/
│ │ │ │ ├── 20211116024918_create_realtime_subscription_table.ex
│ │ │ │ ├── 20211116045059_create_realtime_check_filters_trigger.ex
│ │ │ │ ├── 20211116050929_create_realtime_quote_wal2json_function.ex
│ │ │ │ ├── 20211116051442_create_realtime_check_equality_op_function.ex
│ │ │ │ ├── 20211116212300_create_realtime_build_prepared_statement_sql_function.ex
│ │ │ │ ├── 20211116213355_create_realtime_cast_function.ex
│ │ │ │ ├── 20211116213934_create_realtime_is_visible_through_filters_function.ex
│ │ │ │ ├── 20211116214523_create_realtime_apply_rls_function.ex
│ │ │ │ ├── 20211122062447_grant_realtime_usage_to_authenticated_role.ex
│ │ │ │ ├── 20211124070109_enable_realtime_apply_rls_function_postgrest_9_compatibility.ex
│ │ │ │ ├── 20211202204204_update_realtime_subscription_check_filters_function_security.ex
│ │ │ │ ├── 20211202204605_update_realtime_build_prepared_statement_sql_function_for_compatibility_with_all_types.ex
│ │ │ │ ├── 20211210212804_enable_generic_subscription_claims.ex
│ │ │ │ ├── 20211228014915_add_wal_payload_on_errors_in_apply_rls_function.ex
│ │ │ │ ├── 20220107221237_update_change_timestamp_to_iso_8601_zulu_format.ex
│ │ │ │ ├── 20220228202821_update_subscription_check_filters_function_dynamic_table_name.ex
│ │ │ │ ├── 20220312004840_update_apply_rls_function_to_apply_iso_8601.ex
│ │ │ │ ├── 20220603231003_add_quoted_regtypes_support.ex
│ │ │ │ ├── 20220603232444_add_output_for_data_less_than_equal_64_bytes_when_payload_too_large.ex
│ │ │ │ ├── 20220615214548_add_quoted_regtypes_backward_compatibility_support.ex
│ │ │ │ ├── 20220712093339_recreate_realtime_build_prepared_statement_sql_function.ex
│ │ │ │ ├── 20220908172859_null_passes_filters_recreate_is_visible_through_filters.ex
│ │ │ │ ├── 20220916233421_update_apply_rls_function_to_pass_through_delete_events_on_filter.ex
│ │ │ │ ├── 20230119133233_millisecond_precision_for_walrus.ex
│ │ │ │ ├── 20230128025114_add_in_op_to_filters.ex
│ │ │ │ ├── 20230128025212_enable_filtering_on_delete_record.ex
│ │ │ │ ├── 20230227211149_update_subscription_check_filters_for_in_filter_non_text_types.ex
│ │ │ │ ├── 20230228184745_convert_commit_timestamp_to_utc.ex
│ │ │ │ ├── 20230308225145_output_full_record_when_unchanged_toast.ex
│ │ │ │ ├── 20230328144023_create_list_changes_function.ex
│ │ │ │ ├── 20231018144023_create_channels.ex
│ │ │ │ ├── 20231204144023_set_required_grants.ex
│ │ │ │ ├── 20231204144024_create_rls_helper_functions.ex
│ │ │ │ ├── 20231204144025_enable_channels_rls.ex
│ │ │ │ ├── 20240108234812_add_channels_column_for_write_check.ex
│ │ │ │ ├── 20240109165339_add_update_grant_to_channels.ex
│ │ │ │ ├── 20240227174441_add_broadcast_permissions_table.ex
│ │ │ │ ├── 20240311171622_add_insert_and_delete_grant_to_channels.ex
│ │ │ │ ├── 20240321100241_add_presences_permissions_table.ex
│ │ │ │ ├── 20240401105812_create_realtime_admin_and_move_ownership.ex
│ │ │ │ ├── 20240418121054_remove_check_columns.ex
│ │ │ │ ├── 20240523004032_redefine_authorization_tables.ex
│ │ │ │ ├── 20240618124746_fix_walrus_role_handling.ex
│ │ │ │ ├── 20240801235015_unlogged_messages_table.ex
│ │ │ │ ├── 20240805133720_logged_messages_table.ex
│ │ │ │ ├── 20240827160934_filter_delete_postgres_changes.ex
│ │ │ │ ├── 20240919163303_add_payload_to_messages.ex
│ │ │ │ ├── 20240919163305_change_messages_id_type.ex
│ │ │ │ ├── 20241019105805_uuid_auto_generation.ex
│ │ │ │ ├── 20241030150047_messages_partitioning.ex
│ │ │ │ ├── 20241108114728_messages_using_uuid.ex
│ │ │ │ ├── 20241121104152_fix_send_function_.ex
│ │ │ │ ├── 20241130184212_recreate_entity_index_using_btree.ex
│ │ │ │ ├── 20241220035512_fix_send_function_partition_creation.ex
│ │ │ │ ├── 20241220123912_realtime_send_handle_exceptions_remove_partition_creation.ex
│ │ │ │ ├── 20241224161212_realtime_send_sets_config.ex
│ │ │ │ ├── 20250107150512_realtime_subscription_unlogged.ex
│ │ │ │ ├── 20250110162412_realtime_subscription_logged.ex
│ │ │ │ ├── 20250123174212_remove_unused_publications.ex
│ │ │ │ ├── 20250128220012_realtime_send_sets_topic_config.ex
│ │ │ │ ├── 20250506224012_subscription_index_bridging_disabled.ex
│ │ │ │ ├── 20250523164012_run_subscription_index_bridging_disabled.ex
│ │ │ │ ├── 20250714121412_broadcast_send_error_logging.ex
│ │ │ │ ├── 20250905041441_create_messages_replay_index.ex
│ │ │ │ ├── 20251103001201_broadcast_send_include_payload_id.ex
│ │ │ │ ├── 20251120212548_add_action_to_subscriptions.ex
│ │ │ │ ├── 20251120215549_filter_action_postgres_changes.ex
│ │ │ │ └── 20260218120000_fix_bytea_double_encoding_in_cast.ex
│ │ │ └── repo.ex
│ │ ├── tenants.ex
│ │ └── users_counter.ex
│ ├── realtime.ex
│ ├── realtime_web/
│ │ ├── api_spec.ex
│ │ ├── channels/
│ │ │ ├── auth/
│ │ │ │ ├── channels_authorization.ex
│ │ │ │ └── jwt_verification.ex
│ │ │ ├── payloads/
│ │ │ │ ├── broadcast/
│ │ │ │ │ └── replay.ex
│ │ │ │ ├── broadcast.ex
│ │ │ │ ├── config.ex
│ │ │ │ ├── flexible_boolean.ex
│ │ │ │ ├── join.ex
│ │ │ │ ├── postgres_change.ex
│ │ │ │ └── presence.ex
│ │ │ ├── presence.ex
│ │ │ ├── realtime_channel/
│ │ │ │ ├── assign.ex
│ │ │ │ ├── broadcast_handler.ex
│ │ │ │ ├── logging.ex
│ │ │ │ ├── message_dispatcher.ex
│ │ │ │ ├── presence_handler.ex
│ │ │ │ └── tracker.ex
│ │ │ ├── realtime_channel.ex
│ │ │ ├── socket_disconnect.ex
│ │ │ ├── tenant_rate_limiters.ex
│ │ │ └── user_socket.ex
│ │ ├── controllers/
│ │ │ ├── broadcast_controller.ex
│ │ │ ├── fallback_controller.ex
│ │ │ ├── legacy_metrics_controller.ex
│ │ │ ├── metrics_controller.ex
│ │ │ ├── page_controller.ex
│ │ │ ├── ping_controller.ex
│ │ │ └── tenant_controller.ex
│ │ ├── dashboard/
│ │ │ ├── process_dump.ex
│ │ │ └── tenant_info.ex
│ │ ├── endpoint.ex
│ │ ├── gettext.ex
│ │ ├── live/
│ │ │ ├── components.ex
│ │ │ ├── inspector_live/
│ │ │ │ ├── conn_component.ex
│ │ │ │ ├── conn_component.html.heex
│ │ │ │ ├── index.ex
│ │ │ │ └── index.html.heex
│ │ │ ├── page_live/
│ │ │ │ ├── index.ex
│ │ │ │ └── index.html.heex
│ │ │ ├── ping_live.ex
│ │ │ ├── status_live/
│ │ │ │ ├── index.ex
│ │ │ │ └── index.html.heex
│ │ │ ├── tenants_live/
│ │ │ │ ├── index.ex
│ │ │ │ └── index.html.heex
│ │ │ └── time_live.ex
│ │ ├── open_api_schemas.ex
│ │ ├── plugs/
│ │ │ ├── assign_tenant.ex
│ │ │ ├── auth_tenant.ex
│ │ │ ├── baggage_request_id.ex
│ │ │ ├── metrics_mode.ex
│ │ │ └── rate_limiter.ex
│ │ ├── router.ex
│ │ ├── socket/
│ │ │ ├── user_broadcast.ex
│ │ │ └── v2_serializer.ex
│ │ ├── telemetry.ex
│ │ ├── templates/
│ │ │ └── layout/
│ │ │ ├── app.html.heex
│ │ │ ├── live.html.heex
│ │ │ └── root.html.heex
│ │ ├── tenant_broadcaster.ex
│ │ └── views/
│ │ ├── changeset_view.ex
│ │ ├── error_helpers.ex
│ │ ├── error_view.ex
│ │ ├── layout_view.ex
│ │ └── tenant_view.ex
│ └── realtime_web.ex
├── mix.exs
├── phx_join.schema.json
├── priv/
│ ├── gettext/
│ │ ├── en/
│ │ │ └── LC_MESSAGES/
│ │ │ └── errors.po
│ │ └── errors.pot
│ ├── repo/
│ │ ├── dev_seeds.exs
│ │ ├── migrations/
│ │ │ ├── .formatter.exs
│ │ │ ├── 20210706140551_create_tenant.exs
│ │ │ ├── 20220329161857_add_extensions_table.exs
│ │ │ ├── 20220410212326_add_tenant_max_eps.exs
│ │ │ ├── 20220506102948_rename_poll_interval_to_poll_interval_ms.exs
│ │ │ ├── 20220527210857_add_external_id_uniq_index.exs
│ │ │ ├── 20220815211129_new_max_events_per_second_default.exs
│ │ │ ├── 20220815215024_set_current_max_events_per_second.exs
│ │ │ ├── 20220818141501_change_limits_defaults.exs
│ │ │ ├── 20221018173709_add_cdc_default.exs
│ │ │ ├── 20221102172703_rename_pg_type.exs
│ │ │ ├── 20221223010058_drop_tenants_uniq_external_id_index.exs
│ │ │ ├── 20230110180046_add_limits_fields_to_tenants.exs
│ │ │ ├── 20230810220907_alter_tenants_table_columns_to_text.exs
│ │ │ ├── 20230810220924_alter_extensions_table_columns_to_text.exs
│ │ │ ├── 20231024094642_add_tenant_suspend_flag.exs
│ │ │ ├── 20240306114423_add_tenant_jwt_jwks.exs
│ │ │ ├── 20240418082835_add_authorization_flag.exs
│ │ │ ├── 20240625211759_remove_enable_authorization_flag.exs
│ │ │ ├── 20240704172020_add_notify_private_alpha.exs
│ │ │ ├── 20240902173232_add_extension_external_id_index.exs
│ │ │ ├── 20241106103258_add_private_only_flag_column_to_tenant.exs
│ │ │ ├── 20250424203323_add_migrations_ran_to_tenant.exs
│ │ │ ├── 20250613072131_add_tenant_broadcast_adapter.exs
│ │ │ ├── 20250711044927_change_default_broadcast_adapter_to_gen_rpc.exs
│ │ │ ├── 20250811121559_add_max_presence_events_per_second.exs
│ │ │ ├── 20250926223044_set_default_presence_value.exs
│ │ │ ├── 20251204170944_nullable_jwt_secrets.exs
│ │ │ ├── 20251218000543_ensure_jwt_secret_is_text.exs
│ │ │ ├── 20260209232800_add_max_client_presence_events_per_second.exs
│ │ │ └── 20260304000000_add_presence_enabled_to_tenants.exs
│ │ ├── seeds.exs
│ │ └── seeds_before_migration.exs
│ └── static/
│ ├── robots.txt
│ └── worker.js
├── rel/
│ ├── env.bat.eex
│ ├── env.sh.eex
│ ├── overlays/
│ │ ├── bin/
│ │ │ ├── migrate
│ │ │ ├── migrate.bat
│ │ │ ├── server
│ │ │ └── server.bat
│ │ └── config.example.yml
│ └── vm.args.eex
├── run.sh
└── test/
├── api_jwt_secret_test.exs
├── e2e/
│ ├── .gitignore
│ ├── .template.env
│ ├── .tool-versions
│ ├── README.md
│ ├── flake.nix
│ ├── legacy/
│ │ ├── .tool-versions
│ │ ├── README.md
│ │ └── tests.ts
│ ├── package.json
│ ├── realtime-check.ts
│ └── supabase/
│ ├── .branches/
│ │ └── _current_branch
│ └── .temp/
│ └── cli-latest
├── extensions/
│ ├── extensions_test.exs
│ └── postgres_cdc_rls/
│ ├── db_settings_test.exs
│ ├── message_dispatcher_test.exs
│ ├── replications_test.exs
│ └── worker_supervisor_test.exs
├── integration/
│ ├── distributed_realtime_channel_test.exs
│ ├── measure_traffic_test.exs
│ ├── region_aware_migrations_test.exs
│ ├── region_aware_routing_test.exs
│ ├── rt_channel/
│ │ ├── authorization_test.exs
│ │ ├── billable_events_test.exs
│ │ ├── broadcast_test.exs
│ │ ├── connection_lifecycle_test.exs
│ │ ├── postgres_changes_test.exs
│ │ ├── presence_test.exs
│ │ ├── token_handling_test.exs
│ │ └── wal_bloat_test.exs
│ ├── tests.ts
│ └── tracker_test.exs
├── realtime/
│ ├── adapters/
│ │ └── postgres/
│ │ └── protocol_test.exs
│ ├── api/
│ │ └── extensions_test.exs
│ ├── api_test.exs
│ ├── database_distributed_test.exs
│ ├── database_test.exs
│ ├── extensions/
│ │ └── cdc_rls/
│ │ ├── cdc_rls_test.exs
│ │ ├── replication_poller_test.exs
│ │ ├── replications_test.exs
│ │ ├── subscription_manager_test.exs
│ │ ├── subscriptions_checker_distributed_test.exs
│ │ ├── subscriptions_checker_test.exs
│ │ └── subscriptions_test.exs
│ ├── gen_counter/
│ │ └── gen_counter_test.exs
│ ├── gen_rpc_pub_sub/
│ │ └── worker_test.exs
│ ├── gen_rpc_pub_sub_test.exs
│ ├── gen_rpc_test.exs
│ ├── helpers_test.exs
│ ├── log_filter_test.exs
│ ├── logs_test.exs
│ ├── messages_test.exs
│ ├── metrics_cleaner_test.exs
│ ├── metrics_pusher_test.exs
│ ├── monitoring/
│ │ ├── distributed_metrics_test.exs
│ │ ├── erl_sys_mon_test.exs
│ │ ├── gen_rpc_metrics_test.exs
│ │ ├── latency_test.exs
│ │ ├── peep/
│ │ │ └── partitioned_test.exs
│ │ ├── prom_ex/
│ │ │ └── plugins/
│ │ │ ├── distributed_test.exs
│ │ │ ├── gen_rpc_test.exs
│ │ │ ├── phoenix_test.exs
│ │ │ ├── tenant_test.exs
│ │ │ └── tenants_test.exs
│ │ ├── prom_ex_test.exs
│ │ └── prometheus_test.exs
│ ├── nodes_test.exs
│ ├── oid_test.exs
│ ├── postgres_decoder_test.exs
│ ├── rate_counter/
│ │ └── rate_counter_test.exs
│ ├── repo_replica_test.exs
│ ├── rpc_test.exs
│ ├── signal_handler_test.exs
│ ├── syn_handler_test.exs
│ ├── telemetry/
│ │ └── logger_test.exs
│ ├── tenants/
│ │ ├── authorization_remote_test.exs
│ │ ├── authorization_test.exs
│ │ ├── batch_broadcast_test.exs
│ │ ├── cache_test.exs
│ │ ├── connect/
│ │ │ ├── get_tenant_test.exs
│ │ │ ├── piper_test.exs
│ │ │ ├── reconcile_migrations_test.exs
│ │ │ └── register_process_test.exs
│ │ ├── connect_test.exs
│ │ ├── janitor/
│ │ │ └── maintenance_task_test.exs
│ │ ├── janitor_test.exs
│ │ ├── migrations_test.exs
│ │ ├── rebalancer_test.exs
│ │ ├── replication_connection/
│ │ │ └── watchdog_test.exs
│ │ ├── replication_connection_test.exs
│ │ └── repo_test.exs
│ ├── tenants_test.exs
│ └── users_counter_test.exs
├── realtime_web/
│ ├── channels/
│ │ ├── auth/
│ │ │ ├── channels_authorization_test.exs
│ │ │ └── jwt_verification_test.exs
│ │ ├── payloads/
│ │ │ ├── flexible_boolean_test.exs
│ │ │ └── join_test.exs
│ │ ├── realtime_channel/
│ │ │ ├── broadcast_handler_test.exs
│ │ │ ├── logging_test.exs
│ │ │ ├── message_dispatcher_test.exs
│ │ │ ├── presence_handler_test.exs
│ │ │ └── tracker_test.exs
│ │ ├── realtime_channel_test.exs
│ │ ├── socket_disconnect_test.exs
│ │ ├── tenant_rate_limiters_test.exs
│ │ └── user_socket_test.exs
│ ├── controllers/
│ │ ├── broadcast_controller_test.exs
│ │ ├── fallback_controller_test.exs
│ │ ├── legacy_metrics_controller_test.exs
│ │ ├── live_dasboard_test.exs
│ │ ├── metrics_controller_test.exs
│ │ ├── openapi_controller_test.exs
│ │ ├── page_controller_test.exs
│ │ └── tenant_controller_test.exs
│ ├── dashboard/
│ │ └── tenant_info_test.exs
│ ├── integration/
│ │ └── tracing_test.exs
│ ├── live/
│ │ ├── inspector_live/
│ │ │ └── index_test.exs
│ │ ├── page_live/
│ │ │ └── index_test.exs
│ │ ├── status_live/
│ │ │ └── index_test.exs
│ │ └── tenants_live/
│ │ └── index_test.exs
│ ├── plugs/
│ │ ├── assign_tenant_test.exs
│ │ ├── auth_tenant_test.exs
│ │ ├── baggage_request_id_test.exs
│ │ ├── metrics_mode_test.exs
│ │ └── rate_limiter_test.exs
│ ├── socket/
│ │ └── v2_serializer_test.exs
│ ├── tenant_broadcaster_test.exs
│ └── views/
│ ├── error_view_test.exs
│ ├── layout_view_test.exs
│ └── page_view_test.exs
├── support/
│ ├── channel_case.ex
│ ├── cleanup.ex
│ ├── clustered.ex
│ ├── conn_case.ex
│ ├── containers/
│ │ └── container.ex
│ ├── containers.ex
│ ├── data_case.ex
│ ├── generators.ex
│ ├── integrations.ex
│ ├── joken_current_time_mock.ex
│ ├── metrics_helper.ex
│ ├── prometheus_fixtures.ex
│ ├── rate_counter_helper.ex
│ ├── replication_test_handler.ex
│ ├── tenant_connection.ex
│ ├── tracing.ex
│ └── websocket_client.ex
└── test_helper.exs
================================================
FILE CONTENTS
================================================
================================================
FILE: .credo.exs
================================================
%{
configs: [
%{
name: "default",
files: %{
included: ["lib/", "src/", "web/", "apps/"],
excluded: []
},
plugins: [],
requires: [],
strict: false,
parse_timeout: 5000,
color: true,
checks: %{
disabled: [
{Credo.Check.Design.TagTODO, []},
{Credo.Check.Consistency.ExceptionNames, []},
{Credo.Check.Refactor.Nesting, []},
{Credo.Check.Refactor.CyclomaticComplexity, []},
{Credo.Check.Readability.WithSingleClause, []},
{Credo.Check.Readability.AliasOrder, []},
{Credo.Check.Readability.StringSigils, []},
{Credo.Check.Refactor.Apply, []}
]
}
}
]
}
================================================
FILE: .dockerignore
================================================
# This file excludes paths from the Docker build context.
#
# By default, Docker's build context includes all files (and folders) in the
# current directory. Even if a file isn't copied into the container it is still sent to
# the Docker daemon.
#
# There are multiple reasons to exclude files from the build context:
#
# 1. Prevent nested folders from being copied into the container (ex: exclude
# /assets/node_modules when copying /assets)
# 2. Reduce the size of the build context and improve build time (ex. /build, /deps, /doc)
# 3. Avoid sending files containing sensitive information
#
# More information on using .dockerignore is available here:
# https://docs.docker.com/engine/reference/builder/#dockerignore-file
.dockerignore
# Ignore git, but keep git HEAD and refs to access current commit hash if needed:
#
# $ cat .git/HEAD | awk '{print ".git/"$2}' | xargs cat
# d0b8727759e1e0e7aa3d41707d12376e373d5ecc
.git
!.git/HEAD
!.git/refs
# Common development/test artifacts
/cover/
/doc/
/test/
/tmp/
.elixir_ls
# Mix artifacts
/_build/
/deps/
*.ez
# Generated on crash by the VM
erl_crash.dump
# Static artifacts - These should be fetched and built inside the Docker image
/assets/node_modules/
/priv/static/assets/
/priv/static/cache_manifest.json
================================================
FILE: .formatter.exs
================================================
[
import_deps: [:ecto, :ecto_sql, :phoenix, :open_api_spex],
subdirectories: ["priv/*/migrations"],
plugins: [],
inputs: ["*.{heex,ex,exs}", "{config,lib,test}/**/*.{heex,ex,exs}", "priv/*/*seeds*.exs"],
line_length: 120
]
================================================
FILE: .github/actionlint.yaml
================================================
self-hosted-runner:
labels:
- blacksmith-4vcpu-ubuntu-2404
- blacksmith-8vcpu-ubuntu-2404
================================================
FILE: .github/workflows/beacon_tests.yml
================================================
name: Beacon Tests
defaults:
run:
shell: bash
working-directory: ./beacon
on:
pull_request:
paths:
- "beacon/**"
- ".github/workflows/beacon_tests.yml"
push:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
MIX_ENV: test
jobs:
tests:
name: Tests & Lint
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- uses: actions/checkout@v6
- name: Setup elixir
id: beam
uses: erlef/setup-beam@v1
with:
otp-version: 27.x # Define the OTP version [required]
elixir-version: 1.18.x # Define the elixir version [required]
- name: Cache Mix
uses: actions/cache@v5
with:
path: |
beacon/deps
beacon/_build
key: ${{ github.workflow }}-${{ runner.os }}-mix-${{ steps.beam.outputs.elixir-version }}-${{ steps.beam.outputs.otp-version }}-${{ hashFiles('beacon/mix.lock') }}
restore-keys: |
${{ github.workflow }}-${{ runner.os }}-mix-${{ steps.beam.outputs.elixir-version }}-${{ steps.beam.outputs.otp-version }}-
- name: Install dependencies
run: mix deps.get
- name: Start epmd
run: epmd -daemon
- name: Run tests
run: MIX_ENV=test mix test
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Check for warnings
run: mix compile --force --warnings-as-errors
- name: Run format check
run: mix format --check-formatted
================================================
FILE: .github/workflows/docker-build.yml
================================================
name: Docker Build
on:
pull_request:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker image
run: docker build .
================================================
FILE: .github/workflows/integration_tests.yml
================================================
name: Integration Tests
on:
pull_request:
paths:
- "lib/**"
- "test/**"
- "config/**"
- "priv/**"
- "assets/**"
- "rel/**"
- "mix.exs"
- "Dockerfile"
- "run.sh"
- "docker-compose.test.yml"
- ".github/workflows/integration_tests.yml"
push:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
POSTGRES_IMAGE: supabase/postgres:17.6.1.074
DENO_IMAGE: denoland/deno:alpine-2.5.6
jobs:
tests:
name: Tests
runs-on: blacksmith-8vcpu-ubuntu-2404
steps:
- uses: actions/checkout@v6
- name: Cache Docker images
uses: actions/cache@v5
id: docker-cache
with:
path: /tmp/docker-images
key: docker-images-integration-zstd-${{ env.POSTGRES_IMAGE }}-${{ env.DENO_IMAGE }}
- name: Load Docker images from cache
if: steps.docker-cache.outputs.cache-hit == 'true'
run: |
zstd -d --stdout /tmp/docker-images/postgres.tar.zst | docker image load &
PID1=$!
zstd -d --stdout /tmp/docker-images/deno.tar.zst | docker image load &
PID2=$!
wait $PID1 || exit $?
wait $PID2 || exit $?
- name: Pull and save Docker images
if: steps.docker-cache.outputs.cache-hit != 'true'
run: |
docker pull ${{ env.POSTGRES_IMAGE }} &
PID1=$!
docker pull ${{ env.DENO_IMAGE }} &
PID2=$!
wait $PID1 || exit $?
wait $PID2 || exit $?
mkdir -p /tmp/docker-images
docker image save ${{ env.POSTGRES_IMAGE }} | zstd -T0 -o /tmp/docker-images/postgres.tar.zst
docker image save ${{ env.DENO_IMAGE }} | zstd -T0 -o /tmp/docker-images/deno.tar.zst
- name: Run integration test
run: docker compose -f docker-compose.tests.yml up --abort-on-container-exit --exit-code-from test-runner
================================================
FILE: .github/workflows/lint.yml
================================================
name: Lint
on:
pull_request:
paths:
- "lib/**"
- "test/**"
- "config/**"
- "priv/**"
- "assets/**"
- "rel/**"
- "mix.exs"
- "Dockerfile"
- "run.sh"
- ".github/workflows/lint.yml"
push:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
tests:
name: Lint
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- uses: actions/checkout@v6
- name: Setup elixir
id: beam
uses: erlef/setup-beam@v1
with:
otp-version: 27.x # Define the OTP version [required]
elixir-version: 1.18.x # Define the elixir version [required]
- name: Cache Mix
uses: actions/cache@v5
with:
path: |
deps
_build
key: ${{ github.workflow }}-${{ runner.os }}-mix-${{ env.elixir }}-${{ env.otp }}-${{ hashFiles('**/mix.lock') }}
restore-keys: |
${{ github.workflow }}-${{ runner.os }}-mix-${{ env.elixir }}-${{ env.otp }}-
- name: Install dependencies
run: mix deps.get
- name: Check for warnings
run: mix compile --force --warnings-as-errors
- name: Run format check
run: mix format --check-formatted
- name: Credo checks
run: mix credo
- name: Run hex audit
run: mix hex.audit
- name: Run mix_audit
run: mix deps.audit
- name: Run sobelow
run: mix sobelow --config .sobelow-conf
- name: Retrieve PLT Cache
uses: actions/cache@v5
id: plt-cache
with:
path: priv/plts
key: ${{ runner.os }}-${{ steps.beam.outputs.otp-version }}-${{ steps.beam.outputs.elixir-version }}-plts-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }}
- name: Create PLTs
if: steps.plt-cache.outputs.cache-hit != 'true'
run: |
mkdir -p priv/plts
mix dialyzer.build
- name: Run dialyzer
run: mix dialyzer
================================================
FILE: .github/workflows/manual_prod_build.yml
================================================
name: Manual Build Production
on:
workflow_dispatch:
inputs:
branch:
description: "Branch to run the workflow"
required: true
docker_tag:
description: "Tag to be used by the docker image on push"
required: true
jobs:
docker_x86_release:
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 120
env:
arch: amd64
outputs:
image_digest: ${{ steps.build.outputs.digest }}
steps:
- id: meta
uses: docker/metadata-action@v5
with:
images: |
supabase/realtime
tags: |
type=raw,value=v${{ github.event.inputs.docker_tag }}_${{ env.arch }}
- name: Setup Blacksmith Builder
uses: useblacksmith/setup-docker-builder@v1
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- id: build
uses: useblacksmith/build-push-action@v2
with:
push: true
tags: ${{ steps.meta.outputs.tags }}
platforms: linux/${{ env.arch }}
docker_arm_release:
runs-on: arm-runner
timeout-minutes: 120
env:
arch: arm64
outputs:
image_digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/checkout@v6
- id: meta
uses: docker/metadata-action@v5
with:
images: |
supabase/realtime
tags: |
type=raw,value=v${{ github.event.inputs.docker_tag }}_${{ env.arch }}
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Setup Blacksmith Builder
uses: useblacksmith/setup-docker-builder@v1
- id: build
uses: useblacksmith/build-push-action@v2
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
platforms: linux/${{ env.arch }}
no-cache: true
merge_manifest:
needs: [docker_x86_release, docker_arm_release]
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
contents: read
packages: write
id-token: write
steps:
- name: Setup Blacksmith Builder
uses: useblacksmith/setup-docker-builder@v1
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Merge multi-arch manifests for custom output
run: |
docker buildx imagetools create -t supabase/realtime:v${{ github.event.inputs.docker_tag }} \
supabase/realtime@${{ needs.docker_x86_release.outputs.image_digest }} \
supabase/realtime@${{ needs.docker_arm_release.outputs.image_digest }}
- name: configure aws credentials
uses: aws-actions/configure-aws-credentials@v5
with:
role-to-assume: ${{ secrets.PROD_AWS_ROLE }}
aws-region: us-east-1
- name: Login to ECR
uses: docker/login-action@v3
with:
registry: public.ecr.aws
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Mirror to ECR
uses: akhilerm/tag-push-action@v2.2.0
with:
src: docker.io/supabase/realtime:v${{ github.event.inputs.docker_tag }}
dst: |
public.ecr.aws/supabase/realtime:v${{ github.event.inputs.docker_tag }}
ghcr.io/supabase/realtime:v${{ github.event.inputs.docker_tag }}
================================================
FILE: .github/workflows/mirror.yml
================================================
name: Mirror Image
on:
workflow_dispatch:
inputs:
version:
description: "Image tag"
required: true
type: string
jobs:
mirror:
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
contents: read
packages: write
id-token: write
steps:
- name: configure aws credentials
uses: aws-actions/configure-aws-credentials@v5
with:
role-to-assume: ${{ secrets.PROD_AWS_ROLE }}
aws-region: us-east-1
- uses: docker/login-action@v3
with:
registry: public.ecr.aws
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: akhilerm/tag-push-action@v2.2.0
with:
src: docker.io/supabase/realtime:${{ inputs.version }}
dst: |
public.ecr.aws/supabase/realtime:${{ inputs.version }}
ghcr.io/supabase/realtime:${{ inputs.version }}
================================================
FILE: .github/workflows/prod_build.yml
================================================
name: Build Production
on:
push:
branches:
- "main"
paths:
- "lib/**"
- "config/**"
- "priv/**"
- "assets/**"
- "rel/**"
- "mix.exs"
- "Dockerfile"
- "run.sh"
- ".github/workflows/prod_build.yml"
jobs:
release:
runs-on: blacksmith-4vcpu-ubuntu-2404
outputs:
published: ${{ steps.semantic.outputs.new_release_published }}
version: ${{ steps.semantic.outputs.new_release_version }}
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- id: semantic
uses: cycjimmy/semantic-release-action@v6
with:
semantic_version: 24
extra_plugins: |
@semantic-release/exec
@semantic-release/git
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_PROJECT_ACTION }}
docker_x86_release:
needs: release
runs-on: blacksmith-4vcpu-ubuntu-2404
if: needs.release.outputs.published == 'true'
timeout-minutes: 120
env:
arch: amd64
outputs:
image_digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/checkout@v6
with:
ref: v${{ needs.release.outputs.version }}
- id: meta
uses: docker/metadata-action@v5
with:
images: |
supabase/realtime
tags: |
type=raw,value=v${{ needs.release.outputs.version }}_${{ env.arch }}
type=raw,value=latest_${{ env.arch }}
- name: Setup Blacksmith Builder
uses: useblacksmith/setup-docker-builder@v1
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- id: build
uses: useblacksmith/build-push-action@v2
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
platforms: linux/${{ env.arch }}
docker_arm_release:
needs: release
runs-on: arm-runner
if: needs.release.outputs.published == 'true'
timeout-minutes: 120
env:
arch: arm64
outputs:
image_digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/checkout@v6
with:
ref: v${{ needs.release.outputs.version }}
- id: meta
uses: docker/metadata-action@v5
with:
images: |
supabase/realtime
tags: |
type=raw,value=v${{ needs.release.outputs.version }}_${{ env.arch }}
type=raw,value=latest_${{ env.arch }}
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Setup Blacksmith Builder
uses: useblacksmith/setup-docker-builder@v1
- id: build
uses: useblacksmith/build-push-action@v2
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
platforms: linux/${{ env.arch }}
no-cache: true
merge_manifest:
needs: [release, docker_x86_release, docker_arm_release]
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
contents: read
packages: write
id-token: write
steps:
- name: Setup Blacksmith Builder
uses: useblacksmith/setup-docker-builder@v1
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Merge multi-arch manifests for versioned output
run: |
docker buildx imagetools create -t supabase/realtime:v${{ needs.release.outputs.version }} \
supabase/realtime@${{ needs.docker_x86_release.outputs.image_digest }} \
supabase/realtime@${{ needs.docker_arm_release.outputs.image_digest }}
- name: Merge multi-arch manifests for latest output
run: |
docker buildx imagetools create -t supabase/realtime:latest \
supabase/realtime@${{ needs.docker_x86_release.outputs.image_digest }} \
supabase/realtime@${{ needs.docker_arm_release.outputs.image_digest }}
- name: configure aws credentials
uses: aws-actions/configure-aws-credentials@v5
with:
role-to-assume: ${{ secrets.PROD_AWS_ROLE }}
aws-region: us-east-1
- name: Login to ECR
uses: docker/login-action@v3
with:
registry: public.ecr.aws
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Mirror to ECR
uses: akhilerm/tag-push-action@v2.2.0
with:
src: docker.io/supabase/realtime:v${{ needs.release.outputs.version }}
dst: |
public.ecr.aws/supabase/realtime:v${{ needs.release.outputs.version }}
ghcr.io/supabase/realtime:v${{ needs.release.outputs.version }}
update-branch-name:
needs: [release, docker_x86_release, docker_arm_release, merge_manifest]
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Checkout branch
uses: actions/checkout@v6
with:
ref: refs/heads/main
- name: Update branch name
run: |
git branch -m main releases/v${{ needs.release.outputs.version }}
git push origin HEAD:releases/v${{ needs.release.outputs.version }}
================================================
FILE: .github/workflows/prod_linter.yml
================================================
name: Production Formatting Checks
on:
pull_request:
branches:
- release
jobs:
format:
name: Formatting Checks
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- uses: actions/checkout@v6
- name: Setup elixir
id: beam
uses: erlef/setup-beam@v1
with:
otp-version: 27.x # Define the OTP version [required]
elixir-version: 1.18.x # Define the elixir version [required]
- name: Cache Mix
uses: actions/cache@v5
with:
path: deps
key: ${{ runner.os }}-mix-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }}
restore-keys: |
${{ runner.os }}-mix-
- name: Install dependencies
run: mix deps.get
- name: Set up Postgres
run: docker compose -f docker-compose.dbs.yml up -d
- name: Run database migrations
run: mix ecto.migrate
- name: Run format check
run: mix format --check-formatted
- name: Credo checks
run: mix credo --strict --mute-exit-status
- name: Retrieve PLT Cache
uses: actions/cache@v5
id: plt-cache
with:
path: priv/plts
key: ${{ runner.os }}-${{ steps.beam.outputs.otp-version }}-${{ steps.beam.outputs.elixir-version }}-plts-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }}
- name: Create PLTs
if: steps.plt-cache.outputs.cache-hit != 'true'
run: |
mkdir -p priv/plts
mix dialyzer.build
- name: Run dialyzer
run: mix dialyzer
- name: Run tests
run: mix test
================================================
FILE: .github/workflows/tests.yml
================================================
name: Tests
on:
pull_request:
paths:
- "lib/**"
- "test/**"
- "config/**"
- "priv/**"
- "assets/**"
- "rel/**"
- "native/**"
- "mix.exs"
- "Dockerfile"
- "run.sh"
- ".github/workflows/tests.yml"
push:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
MIX_ENV: test
POSTGRES_IMAGE: supabase/postgres:17.6.1.074
jobs:
tests:
name: Tests (Partition ${{ matrix.partition }})
runs-on: blacksmith-8vcpu-ubuntu-2404
strategy:
fail-fast: false
matrix:
partition: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v6
- name: Setup elixir
id: beam
uses: erlef/setup-beam@v1
with:
otp-version: 27.x # Define the OTP version [required]
elixir-version: 1.18.x # Define the elixir version [required]
- name: Cache Mix
uses: actions/cache@v5
with:
path: |
deps
_build
priv/native
key: ${{ github.workflow }}-${{ runner.os }}-mix-${{ env.elixir }}-${{ env.otp }}-${{ hashFiles('**/mix.lock') }}
restore-keys: |
${{ github.workflow }}-${{ runner.os }}-mix-${{ env.elixir }}-${{ env.otp }}-
- name: Cache Docker images
uses: actions/cache@v5
id: docker-cache
with:
path: /tmp/docker-images
key: docker-images-zstd-${{ env.POSTGRES_IMAGE }}
- name: Load Docker images from cache
if: steps.docker-cache.outputs.cache-hit == 'true'
run: zstd -d --stdout /tmp/docker-images/postgres.tar.zst | docker image load
- name: Pull and save Docker images
if: steps.docker-cache.outputs.cache-hit != 'true'
run: |
docker pull ${{ env.POSTGRES_IMAGE }}
mkdir -p /tmp/docker-images
docker image save ${{ env.POSTGRES_IMAGE }} | zstd -T0 -o /tmp/docker-images/postgres.tar.zst
- name: Install dependencies
run: mix deps.get
- name: Set up Postgres
run: docker compose -f docker-compose.dbs.yml up -d
- name: Start epmd
run: epmd -daemon
- name: Run tests
run: MIX_TEST_PARTITION=${{ matrix.partition }} mix coveralls.lcov --partitions 4
- name: Upload coverage artifact
uses: actions/upload-artifact@v4
with:
name: coverage-partition-${{ matrix.partition }}
path: cover/lcov.info
coverage:
name: Merge Coverage
needs: tests
if: ${{ needs.tests.result == 'success' }}
runs-on: blacksmith-8vcpu-ubuntu-2404
steps:
- uses: actions/checkout@v6
- name: Download all coverage artifacts
uses: actions/download-artifact@v4
with:
pattern: coverage-partition-*
path: coverage
- name: Upload merged coverage to Coveralls
uses: coverallsapp/github-action@v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
files: coverage/coverage-partition-1/lcov.info coverage/coverage-partition-2/lcov.info coverage/coverage-partition-3/lcov.info coverage/coverage-partition-4/lcov.info
================================================
FILE: .github/workflows/update-supabase-js.yml
================================================
name: Update @supabase/supabase-js
on:
workflow_dispatch:
inputs:
version:
description: "Version to update to"
required: true
type: string
source:
description: "Source of the update"
required: false
type: string
default: "manual"
permissions:
pull-requests: read
contents: read
jobs:
update-supabase-js:
runs-on: ubuntu-latest
concurrency:
group: ${{ github.workflow }}-supabase-update-${{ inputs.version }}
cancel-in-progress: false
steps:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
with:
ref: ${{ github.event.repository.default_branch }}
- name: Setup Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: "20"
cache: "npm"
cache-dependency-path: assets/package-lock.json
- name: Update @supabase/supabase-js
working-directory: assets
run: |
npm pkg set "dependencies.@supabase/supabase-js=${{ inputs.version }}"
npm install --package-lock-only --ignore-scripts
- name: Generate token
id: app-token
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
with:
app-id: ${{ secrets.GH_AUTOFIX_APP_ID }}
private-key: ${{ secrets.GH_AUTOFIX_PRIVATE_KEY }}
- name: Create pull request
uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # v6.1.0
with:
token: ${{ steps.app-token.outputs.token }}
commit-message: "chore: update @supabase/supabase-js to v${{ inputs.version }}"
title: "chore: update @supabase/supabase-js to v${{ inputs.version }}"
body: |
This PR updates `@supabase/supabase-js` to v${{ inputs.version }}.
**Source**: ${{ inputs.source }}
This PR was created automatically.
branch: "gha/auto-update-supabase-js-v${{ inputs.version }}"
base: ${{ github.event.repository.default_branch }}
================================================
FILE: .gitignore
================================================
# The directory Mix will write compiled artifacts to.
/_build/
# If you run "mix test --cover", coverage assets end up here.
/cover/
# The directory Mix downloads your dependencies sources to.
/deps/
# Where 3rd-party dependencies like ExDoc output generated docs.
/doc/
# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch
# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump
# Also ignore archive artifacts (built via "mix archive.build").
*.ez
# Ignore package tarball (built via "mix hex.build").
realtime-*.tar
# Ignore assets that are produced by build tools.
/priv/static/assets/
# Ignore Dialyzer .plt
/priv/plts/*
node_modules
.supabase
config/prod.secret.exs
demo/.env
.lexical
.vscode
================================================
FILE: .releaserc
================================================
{
"branches": ["main"],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
[
"@semantic-release/exec",
{
"prepareCmd": "sed -i 's/version: \"[^\"]*\"/version: \"${nextRelease.version}\"/' mix.exs"
}
],
[
"@semantic-release/git",
{
"assets": ["mix.exs"],
"message": "chore(release): ${nextRelease.version} [skip ci]"
}
],
"@semantic-release/github"
]
}
================================================
FILE: .sobelow-conf
================================================
[
verbose: true,
private: false,
skip: false,
router: nil,
exit: :low,
format: "txt",
out: nil,
threshold: :medium,
ignore: ["Config.CSP", "Config.HTTPS"],
ignore_files: [],
version: false
]
================================================
FILE: .tool-versions
================================================
elixir 1.18.4-otp-27
nodejs 24
erlang 27
================================================
FILE: Dockerfile
================================================
ARG ELIXIR_VERSION=1.18
ARG OTP_VERSION=27.3
ARG DEBIAN_VERSION=bookworm-20250929-slim
ARG BUILDER_IMAGE="hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}"
ARG RUNNER_IMAGE="debian:${DEBIAN_VERSION}"
FROM ${BUILDER_IMAGE} AS builder
ENV MIX_ENV="prod"
RUN apt-get update -y \
&& apt-get install curl -y \
&& apt-get install -y build-essential git \
&& apt-get clean
RUN set -uex; \
apt-get update; \
apt-get install -y ca-certificates curl gnupg; \
mkdir -p /etc/apt/keyrings; \
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \
| gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg; \
NODE_MAJOR=24; \
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" \
> /etc/apt/sources.list.d/nodesource.list; \
apt-get -qy update; \
apt-get -qy install nodejs;
# prepare build dir
WORKDIR /app
# install hex + rebar
RUN mix local.hex --force && \
mix local.rebar --force
# install mix dependencies
COPY mix.exs mix.lock ./
COPY beacon beacon
RUN mix deps.get --only $MIX_ENV
RUN mkdir config
# copy compile-time config files before we compile dependencies
# to ensure any relevant config change will trigger the dependencies
# to be re-compiled.
COPY config/config.exs config/${MIX_ENV}.exs config/
RUN mix deps.compile
COPY priv priv
COPY lib lib
COPY assets assets
# compile assets with esbuild and npm
RUN cd assets \
&& npm install \
&& cd .. \
&& mix assets.deploy
# Compile the release
RUN mix compile
# Changes to config/runtime.exs don't require recompiling the code
COPY config/runtime.exs config/
COPY rel rel
RUN mix release
# start a new build stage so that the final image will only contain
# the compiled release and other runtime necessities
FROM ${RUNNER_IMAGE}
ARG SLOT_NAME_SUFFIX
ENV SLOT_NAME_SUFFIX="${SLOT_NAME_SUFFIX}" \
LANG="en_US.UTF-8" \
LANGUAGE="en_US:en" \
LC_ALL="en_US.UTF-8" \
MIX_ENV="prod" \
ECTO_IPV6="true" \
ERL_AFLAGS="-proto_dist inet6_tcp"
RUN apt-get update -y && \
apt-get install -y libstdc++6 openssl libncurses5 locales iptables sudo tini curl awscli jq && \
apt-get clean && rm -f /var/lib/apt/lists/*_*
# Set the locale
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen
WORKDIR "/app"
RUN chown nobody /app
COPY --from=builder --chown=nobody:root /app/_build/${MIX_ENV}/rel/realtime ./
COPY run.sh run.sh
RUN ls -la /app
ENTRYPOINT ["/usr/bin/tini", "-s", "-g", "--", "/app/run.sh"]
CMD ["/app/bin/server"]
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2019 Supabase
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: Makefile
================================================
CLUSTER_STRATEGIES ?= EPMD
NODE_NAME ?= pink
PORT ?= 4000
.PHONY: dev dev.orange seed prod bench.% dev_db start start.% stop stop.% rebuild rebuild.%
.DEFAULT_GOAL := help
# Common commands
dev: ## Start a dev server
ELIXIR_ERL_OPTIONS="+hmax 1000000000" SLOT_NAME_SUFFIX=some_sha PORT=$(PORT) MIX_ENV=dev SECURE_CHANNELS=true API_JWT_SECRET=dev METRICS_JWT_SECRET=dev REGION=us-east-1 DB_ENC_KEY="1234567890123456" CLUSTER_STRATEGIES=$(CLUSTER_STRATEGIES) ERL_AFLAGS="-kernel shell_history enabled" GEN_RPC_TCP_SERVER_PORT=5369 GEN_RPC_TCP_CLIENT_PORT=5469 iex --name $(NODE_NAME)@127.0.0.1 --cookie cookie -S mix phx.server
dev.orange: ## Start another dev server (orange) on port 4001
ELIXIR_ERL_OPTIONS="+hmax 1000000000" SLOT_NAME_SUFFIX=some_sha PORT=4001 MIX_ENV=dev SECURE_CHANNELS=true API_JWT_SECRET=dev METRICS_JWT_SECRET=dev REGION=eu-west-1 DB_ENC_KEY="1234567890123456" CLUSTER_STRATEGIES=$(CLUSTER_STRATEGIES) ERL_AFLAGS="-kernel shell_history enabled" GEN_RPC_TCP_SERVER_PORT=5469 GEN_RPC_TCP_CLIENT_PORT=5369 iex --name orange@127.0.0.1 --cookie cookie -S mix phx.server
seed: ## Seed the database
DB_ENC_KEY="1234567890123456" FLY_ALLOC_ID=123e4567-e89b-12d3-a456-426614174000 mix run priv/repo/dev_seeds.exs
prod: ## Start a server with a MIX_ENV=prod
ELIXIR_ERL_OPTIONS="+hmax 1000000000" SLOT_NAME_SUFFIX=some_sha MIX_ENV=prod FLY_APP_NAME=realtime-local API_KEY=dev SECURE_CHANNELS=true API_JWT_SECRET=dev METRICS_JWT_SECRET=dev FLY_REGION=fra FLY_ALLOC_ID=123e4567-e89b-12d3-a456-426614174000 DB_ENC_KEY="1234567890123456" SECRET_KEY_BASE=M+55t7f6L9VWyhH03R5N7cIhrdRlZaMDfTE6Udz0eZS7gCbnoLQ8PImxwhEyao6D DASHBOARD_USER=realtime_local DASHBOARD_PASSWORD=password ERL_AFLAGS="-kernel shell_history enabled" iex -S mix phx.server
bench.%: ## Run benchmark with a specific file. e.g. bench.secrets
ELIXIR_ERL_OPTIONS="+hmax 1000000000" SLOT_NAME_SUFFIX=some_sha MIX_ENV=dev SECURE_CHANNELS=true API_JWT_SECRET=dev METRICS_JWT_SECRET=dev FLY_REGION=fra FLY_ALLOC_ID=123e4567-e89b-12d3-a456-426614174000 DB_ENC_KEY="1234567890123456" ERL_AFLAGS="-kernel shell_history enabled" mix run bench/$*
dev_db: ## Start dev databases using docker
docker-compose -f docker-compose.dbs.yml up -d && mix ecto.migrate --log-migrator-sql
# Docker specific commands
start: ## Start main docker compose
docker-compose up
start.%: ## Start docker compose with a specific file. e.g. start.dbs
docker-compose -f docker-compose.$*.yml up
stop: ## Stop main docker compose
docker-compose down --remove-orphans
stop.%: ## Stop docker compose with a specific file. e.g. stop.dbs
docker-compose -f docker-compose.yml -f docker-compose.$*.yml down --remove-orphans
rebuild: ## Rebuild main docker compose images
make stop
docker-compose build
docker-compose up --force-recreate --build
rebuild.%: ## Rebuild docker compose images with a specific file. e.g. rebuild.dbs
make stop.$*
docker-compose -f docker-compose.yml -f docker-compose.$*.yml build
docker-compose -f docker-compose.yml -f docker-compose.$*.yml up --force-recreate --build
# Based on https://gist.github.com/prwhite/8168133
.DEFAULT_GOAL:=help
.PHONY: help
help: ## Display this help
$(info Realtime commands)
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[%.a-zA-Z0-9_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
================================================
FILE: README.md
================================================
<br />
<p align="center">
<a href="https://supabase.io">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/supabase-logo-wordmark--dark.svg">
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/supabase-logo-wordmark--light.svg">
<img alt="Supabase Logo" width="300" src="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/logo-preview.jpg">
</picture>
</a>
<h1 align="center">Supabase Realtime</h1>
<p align="center">
Send ephemeral messages, track and synchronize shared state, and listen to Postgres changes all over WebSockets.
<br />
<a href="https://multiplayer.dev">Multiplayer Demo</a>
·
<a href="https://github.com/supabase/realtime/issues/new?assignees=&labels=enhancement&template=2.Feature_request.md">Request Feature</a>
·
<a href="https://github.com/supabase/realtime/issues/new?assignees=&labels=bug&template=1.Bug_report.md">Report Bug</a>
<br />
</p>
</p>
## Status
[](https://github.com/supabase/realtime/blob/main/LICENSE)
[](https://coveralls.io/github/supabase/realtime?branch=main)
| Features | v1 | v2 | Status |
| ---------------- | --- | --- | ------ |
| Postgres Changes | ✔ | ✔ | GA |
| Broadcast | | ✔ | GA |
| Presence | | ✔ | GA |
This repository focuses on version 2 but you can still access the previous version's [code](https://github.com/supabase/realtime/tree/v1) and [Docker image](https://hub.docker.com/layers/supabase/realtime/v1.0.0/images/sha256-e2766e0e3b0d03f7e9aa1b238286245697d0892c2f6f192fd2995dca32a4446a). For the latest Docker images go to https://hub.docker.com/r/supabase/realtime.
The codebase is under heavy development and the documentation is constantly evolving. Give it a try and let us know what you think by creating an issue. Watch [releases](https://github.com/supabase/realtime/releases) of this repo to get notified of updates. And give us a star if you like it!
## Overview
### What is this?
This is a server built with Elixir using the [Phoenix Framework](https://www.phoenixframework.org) that enables the following functionality:
- Broadcast: Send ephemeral messages from client to clients with low latency.
- Presence: Track and synchronize shared state between clients.
- Postgres Changes: Listen to Postgres database changes and send them to authorized clients.
For a more detailed overview head over to [Realtime guides](https://supabase.com/docs/guides/realtime).
### Does this server guarantee message delivery?
The server does not guarantee that every message will be delivered to your clients so keep that in mind as you're using Realtime.
## Quick start
You can check out the [Supabase UI Library](https://supabase.com/ui) Realtime components and the [multiplayer.dev](https://multiplayer.dev) demo app source code [here](https://github.com/supabase/multiplayer.dev)
## Client libraries
- [JavaScript](https://github.com/supabase/supabase-js/tree/master/packages/core/realtime-js)
- [Flutter/Dart](https://github.com/supabase/supabase-flutter/tree/main/packages/realtime_client)
- [Python](https://github.com/supabase/supabase-py/tree/main/src/realtime)
- [Swift](https://github.com/supabase/supabase-swift/tree/main/Sources/Realtime)
## Server Setup
To get started, spin up your Postgres database and Realtime server containers defined in `docker-compose.yml`. As an example, you may run `docker-compose -f docker-compose.yml up`.
> **Note**
> Supabase runs Realtime in production with a separate database that keeps track of all tenants. However, a schema, `_realtime`, is created when spinning up containers via `docker-compose.yml` to simplify local development.
A tenant has already been added on your behalf. You can confirm this by checking the `_realtime.tenants` and `_realtime.extensions` tables inside the database.
You can add your own by making a `POST` request to the server. You must change both `name` and `external_id` while you may update other values as you see fit:
```bash
curl -X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiIiLCJpYXQiOjE2NzEyMzc4NzMsImV4cCI6MTcwMjc3Mzk5MywiYXVkIjoiIiwic3ViIjoiIn0._ARixa2KFUVsKBf3UGR90qKLCpGjxhKcXY4akVbmeNQ' \
-d $'{
"tenant" : {
"name": "realtime-dev",
"external_id": "realtime-dev",
"jwt_secret": "a1d99c8b-91b6-47b2-8f3c-aa7d9a9ad20f",
"extensions": [
{
"type": "postgres_cdc_rls",
"settings": {
"db_name": "postgres",
"db_host": "host.docker.internal",
"db_user": "postgres",
"db_password": "postgres",
"db_port": "5432",
"region": "us-west-1",
"poll_interval_ms": 100,
"poll_max_record_bytes": 1048576,
"ssl_enforced": false
}
}
]
}
}' \
http://localhost:4000/api/tenants
```
> **Note**
> The `Authorization` token is signed with the secret set by `API_JWT_SECRET` in `docker-compose.yml`.
If you want to listen to Postgres changes, you can create a table and then add the table to the `supabase_realtime` publication:
```sql
create table test (
id serial primary key
);
alter publication supabase_realtime add table test;
```
You can start playing around with Broadcast, Presence, and Postgres Changes features either with the client libs (e.g. `@supabase/realtime-js`), or use the built in Realtime Inspector on localhost, `http://localhost:4000/inspector/new` (make sure the port is correct for your development environment).
The WebSocket URL must contain the subdomain, `external_id` of the tenant on the `_realtime.tenants` table, and the token must be signed with the `jwt_secret` that was inserted along with the tenant.
If you're using the default tenant, the URL is `ws://realtime-dev.localhost:4000/socket` (make sure the port is correct for your development environment), and you can use `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MDMwMjgwODcsInJvbGUiOiJwb3N0Z3JlcyJ9.tz_XJ89gd6bN8MBpCl7afvPrZiBH6RB65iA1FadPT3Y` for the token. The token must have `exp` and `role` (database role) keys.
**Environment Variables**
| Variable | Type | Description |
| ----------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| PORT | number | Port which you can connect your client/listeners |
| DB_HOST | string | Database host URL |
| DB_PORT | number | Database port |
| DB_USER | string | Database user |
| DB_PASSWORD | string | Database password |
| DB_NAME | string | Postgres database name |
| DB_ENC_KEY | string | Key used to encrypt sensitive fields in \_realtime.tenants and \_realtime.extensions tables. Recommended: 16 characters. |
| DB_AFTER_CONNECT_QUERY | string | Query that is run after server connects to database. |
| DB_IP_VERSION | string | Sets the IP Version to be used. Allowed values are "ipv6" and "ipv4". If none are set we will try to infer the correct version |
| DB_SSL | boolean | Whether or not the connection will be set-up using SSL |
| DB_SSL_CA_CERT | string | Filepath to a CA trust store (e.g.: /etc/cacert.pem). If defined it enables server certificate verification |
| API_JWT_SECRET | string | Secret that is used to sign tokens used to manage tenants and their extensions via HTTP requests. |
| SECRET_KEY_BASE | string | Secret used by the server to sign cookies. Recommended: 64 characters. |
| ERL_AFLAGS | string | Set to either "-proto_dist inet_tcp" or "-proto_dist inet6_tcp" depending on whether or not your network uses IPv4 or IPv6, respectively. |
| APP_NAME | string | A name of the server. |
| DNS_NODES | string | Node name used when running server in a cluster. |
| MAX_CONNECTIONS | string | Set the soft maximum for WebSocket connections. Defaults to '16384'. |
| MAX_HEADER_LENGTH | string | Set the maximum header length for connections (in bytes). Defaults to '4096'. |
| NUM_ACCEPTORS | string | Set the number of server processes that will relay incoming WebSocket connection requests. Defaults to '100'. |
| DB_QUEUE_TARGET | string | Maximum time to wait for a connection from the pool. Defaults to '5000' or 5 seconds. See for more info: [DBConnection](https://hexdocs.pm/db_connection/DBConnection.html#start_link/2-queue-config). |
| DB_QUEUE_INTERVAL | string | Interval to wait to check if all connections were checked out under DB_QUEUE_TARGET. If all connections surpassed the target during this interval than the target is doubled. Defaults to '5000' or 5 seconds. See for more info: [DBConnection](https://hexdocs.pm/db_connection/DBConnection.html#start_link/2-queue-config). |
| DB_POOL_SIZE | string | Sets the number of connections in the database pool. Defaults to '5'. |
| DB_REPLICA_HOST | string | Hostname for the replica database. If set, enables the main replica connection pool. |
| DB_REPLICA_POOL_SIZE | string | Sets the number of connections in the replica database pool. Defaults to '5'. |
| SLOT_NAME_SUFFIX | string | This is appended to the replication slot which allows making a custom slot name. May contain lowercase letters, numbers, and the underscore character. Together with the default `supabase_realtime_replication_slot`, slot name should be up to 64 characters long. |
| TENANT_CACHE_EXPIRATION_IN_MS | string | Set tenant cache TTL in milliseconds |
| TENANT_MAX_BYTES_PER_SECOND | string | The default value of maximum bytes per second that each tenant can support, used when creating a tenant for the first time. Defaults to '100_000'. |
| TENANT_MAX_CHANNELS_PER_CLIENT | string | The default value of maximum number of channels each tenant can support, used when creating a tenant for the first time. Defaults to '100'. |
| TENANT_MAX_CONCURRENT_USERS | string | The default value of maximum concurrent users per channel that each tenant can support, used when creating a tenant for the first time. Defaults to '200'. |
| TENANT_MAX_EVENTS_PER_SECOND | string | The default value of maximum events per second that each tenant can support, used when creating a tenant for the first time. Defaults to '100'. |
| TENANT_MAX_JOINS_PER_SECOND | string | The default value of maximum channel joins per second that each tenant can support, used when creating a tenant for the first time. Defaults to '100'. |
| CLIENT_PRESENCE_MAX_CALLS | number | Maximum number of presence calls allowed per client (per WebSocket connection) within the time window. Defaults to '5'. |
| CLIENT_PRESENCE_WINDOW_MS | number | Time window in milliseconds for per-client presence rate limiting. Defaults to '30000' (30 seconds). |
| SEED_SELF_HOST | boolean | Seeds the system with default tenant |
| SELF_HOST_TENANT_NAME | string | Tenant reference to be used for self host. Do keep in mind to use a URL compatible name |
| LOG_LEVEL | string | Sets log level for Realtime logs. Defaults to info, supported levels are: info, emergency, alert, critical, error, warning, notice, debug |
| DISABLE_HEALTHCHECK_LOGGING | boolean | Disables request logging for healthcheck endpoints (/healthcheck and /api/tenants/:tenant_id/health). Defaults to false. |
| RUN_JANITOR | boolean | Do you want to janitor tasks to run |
| JANITOR_SCHEDULE_TIMER_IN_MS | number | Time in ms to run the janitor task |
| JANITOR_SCHEDULE_RANDOMIZE | boolean | Adds a randomized value of minutes to the timer |
| JANITOR_RUN_AFTER_IN_MS | number | Tells system when to start janitor tasks after boot |
| JANITOR_CLEANUP_MAX_CHILDREN | number | Maximum number of concurrent tasks working on janitor cleanup |
| JANITOR_CLEANUP_CHILDREN_TIMEOUT | number | Timeout for each async task for janitor cleanup |
| JANITOR_CHUNK_SIZE | number | Number of tenants to process per chunk. Each chunk will be processed by a Task |
| MIGRATION_PARTITION_SLOTS | number | Number of dynamic supervisor partitions used by the migrations process |
| CONNECT_PARTITION_SLOTS | number | Number of dynamic supervisor partitions used by the Connect, ReplicationConnect processes |
| METRICS_CLEANER_SCHEDULE_TIMER_IN_MS | number | Time in ms to run the Metric Cleaner task |
| METRICS_RPC_TIMEOUT_IN_MS | number | Time in ms to wait for RPC call to fetch Metric per node |
| WEBSOCKET_MAX_HEAP_SIZE | number | Max number of bytes to be allocated as heap for the WebSocket transport process. If the limit is reached the process is brutally killed. Defaults to 50MB. |
| REQUEST_ID_BAGGAGE_KEY | string | OTEL Baggage key to be used as request id |
| OTEL_SDK_DISABLED | boolean | Disable OpenTelemetry tracing completely when 'true' |
| OTEL_TRACES_EXPORTER | string | Possible values: `otlp` or `none`. See [https://github.com/open-telemetry/opentelemetry-erlang/tree/v1.4.0/apps#os-environment] for more details on how to configure the traces exporter. |
| OTEL_TRACES_SAMPLER | string | Default to `parentbased_always_on` . More info [here](https://opentelemetry.io/docs/languages/erlang/sampling/#environment-variables) |
| GEN_RPC_TCP_SERVER_PORT | number | Port served by `gen_rpc`. Must be secured just like the Erlang distribution port. Defaults to 5369 |
| GEN_RPC_TCP_CLIENT_PORT | number | `gen_rpc` connects to another node using this port. Most of the time it should be the same as GEN_RPC_TCP_SERVER_PORT. Defaults to 5369 |
| GEN_RPC_SSL_SERVER_PORT | number | Port served by `gen_rpc` secured with TLS. Must also define GEN_RPC_CERTFILE, GEN_RPC_KEYFILE and GEN_RPC_CACERTFILE. If this is defined then only TLS connections will be set-up. |
| GEN_RPC_SSL_CLIENT_PORT | number | `gen_rpc` connects to another node using this port. Most of the time it should be the same as GEN_RPC_SSL_SERVER_PORT. Defaults to 6369 |
| GEN_RPC_CERTFILE | string | Path to the public key in PEM format. Only needs to be provided if GEN_RPC_SSL_SERVER_PORT is defined |
| GEN_RPC_KEYFILE | string | Path to the private key in PEM format. Only needs to be provided if GEN_RPC_SSL_SERVER_PORT is defined |
| GEN_RPC_CACERTFILE | string | Path to the certificate authority public key in PEM format. Only needs to be provided if GEN_RPC_SSL_SERVER_PORT is defined |
| GEN_RPC_CONNECT_TIMEOUT_IN_MS | number | `gen_rpc` client connect timeout in milliseconds. Defaults to 10000. |
| GEN_RPC_SEND_TIMEOUT_IN_MS | number | `gen_rpc` client and server send timeout in milliseconds. Defaults to 10000. |
| GEN_RPC_SOCKET_IP | string | Interface which `gen_rpc` will bind to. Defaults to "0.0.0.0" (ipv4) which means that all interfaces are going to expose the `gen_rpc` port. |
| GEN_RPC_IPV6_ONLY | boolean | Configure `gen_rpc` to use IPv6 only. |
| GEN_RPC_MAX_BATCH_SIZE | integer | Configure `gen_rpc` to batch when possible RPC casts. Defaults to 0 |
| GEN_RPC_COMPRESS | integer | Configure `gen_rpc` to compress or not payloads. 0 means no compression and 9 max compression level. Defaults to 0. |
| GEN_RPC_COMPRESSION_THRESHOLD_IN_BYTES | integer | Configure `gen_rpc` to compress only above a certain threshold in bytes. Defaults to 1000. |
| MAX_GEN_RPC_CLIENTS | number | Max amount of `gen_rpc` TCP connections per node-to-node channel |
| REBALANCE_CHECK_INTERVAL_IN_MS | number | Time in ms to check if process is in the right region |
| NODE_BALANCE_UPTIME_THRESHOLD_IN_MS | number | Minimum node uptime in ms before using load-aware node picker. Nodes below this threshold use random selection as their metrics are not yet reliable. Defaults to 5 minutes. |
| DISCONNECT_SOCKET_ON_NO_CHANNELS_INTERVAL_IN_MS | number | Time in ms to check if a socket has no channels open and if so, disconnect it |
| BROADCAST_POOL_SIZE | number | Number of processes to relay Phoenix.PubSub messages across the cluster |
| PRESENCE_POOL_SIZE | number | Number of tracker processes for Presence feature. Defaults to 10. Higher values improve concurrency for presence tracking across many channels. |
| PRESENCE_BROADCAST_PERIOD_IN_MS | number | Interval in milliseconds to send presence delta broadcasts across the cluster. Defaults to 1500 (1.5 seconds). Lower values increase network traffic but reduce presence sync latency. |
| PRESENCE_PERMDOWN_PERIOD_IN_MS | number | Interval in milliseconds to flag a replica as permanently down and discard its state. Defaults to 1200000 (20 minutes). Must be greater than down_period. Higher values are more forgiving of temporary network issues but slower to clean up truly dead replicas. |
| POSTGRES_CDC_SCOPE_SHARDS | number | Number of dynamic supervisor partitions used by the Postgres CDC extension. Defaults to 5. |
| USERS_SCOPE_SHARDS | number | Number of dynamic supervisor partitions used by the Users extension. Defaults to 5. |
| REGION_MAPPING | string | Custom mapping of platform regions to tenant regions. Must be a valid JSON object with string keys and values (e.g., `{"custom-region-1": "us-east-1", "eu-north-1": "eu-west-2"}`). If not provided, uses the default hardcoded region mapping. When set, only the specified mappings are used (no fallback to defaults). |
| METRICS_PUSHER_ENABLED | boolean | Enable periodic push of Prometheus metrics. Defaults to 'false'. Requires METRICS_PUSHER_URL to be set. |
| METRICS_PUSHER_URL | string | Full URL endpoint to push metrics using Prometheus exposition format (e.g., 'https://example.com/api/v1/import/prometheus'). Required when METRICS_PUSHER_ENABLED is 'true'. |
| METRICS_PUSHER_USER | string | Username for Basic auth (RFC 7617) on metrics pushes. Defaults to 'realtime'. Used together with METRICS_PUSHER_AUTH to form the Authorization header as `Basic Base64("user:password")`. |
| METRICS_PUSHER_AUTH | string | Password for Basic auth (RFC 7617) on metrics pushes. Used together with METRICS_PUSHER_USER to form the Authorization header as `Basic Base64("user:password")`. If not set, requests will be sent without authorization. Keep this secret if used. |
| METRICS_PUSHER_INTERVAL_MS | number | Interval in milliseconds between metrics pushes. Defaults to '30000' (30 seconds). |
| METRICS_PUSHER_TIMEOUT_MS | number | HTTP request timeout in milliseconds for metrics push operations. Defaults to '15000' (15 seconds). |
| METRICS_PUSHER_COMPRESS | boolean | Enable gzip compression for metrics payloads. Defaults to 'true'. |
| DASHBOARD_AUTH | string | Authentication method for the admin dashboard (`/admin`). Accepted values: `basic_auth` (default) or `zta`. When `basic_auth`, `DASHBOARD_USER` and `DASHBOARD_PASSWORD` are required. When `zta`, `CF_TEAM_DOMAIN` is required. |
| DASHBOARD_USER | string | Username for admin dashboard basic auth. Required when `DASHBOARD_AUTH` is `basic_auth`. |
| DASHBOARD_PASSWORD | string | Password for admin dashboard basic auth. Required when `DASHBOARD_AUTH` is `basic_auth`. |
| CF_TEAM_DOMAIN | string | Cloudflare Zero Trust team domain used for ZTA authentication. Required when `DASHBOARD_AUTH` is `zta`. |
The OpenTelemetry variables mentioned above are not an exhaustive list of all [supported environment variables](https://opentelemetry.io/docs/languages/sdk-configuration/).
## WebSocket URL
The WebSocket URL is in the following format for local development: `ws://[external_id].localhost:4000/socket/websocket`
If you're using Supabase's hosted Realtime in production the URL is `wss://[project-ref].supabase.co/realtime/v1/websocket?apikey=[anon-token]&log_level=info&vsn=1.0.0"`
## WebSocket Connection Authorization
WebSocket connections are authorized via symmetric JWT verification. Only supports JWTs signed with the following algorithms:
- HS256
- HS384
- HS512
Verify JWT claims by setting JWT_CLAIM_VALIDATORS:
> e.g. {'iss': 'Issuer', 'nbf': 1610078130}
>
> Then JWT's "iss" value must equal "Issuer" and "nbf" value must equal 1610078130.
**Note:**
> JWT expiration is checked automatically. `exp` and `role` (database role) keys are mandatory.
**Authorizing Client Connection**: You can pass in the JWT by following the instructions under the Realtime client lib. For example, refer to the **Usage** section in the [@supabase/realtime-js](https://github.com/supabase/realtime-js) client library.
## Error Operational Codes
This is the list of operational codes that can help you understand your deployment and your usage.
| Code | Description |
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| TopicNameRequired | You are trying to use Realtime without a topic name set |
| InvalidJoinPayload | The payload provided to Realtime on connect is invalid |
| RealtimeDisabledForConfiguration | The configuration provided to Realtime on connect will not be able to provide you any Postgres Changes |
| TenantNotFound | The tenant you are trying to connect to does not exist |
| ErrorConnectingToWebsocket | Error when trying to connect to the WebSocket server |
| ErrorAuthorizingWebsocket | Error when trying to authorize the WebSocket connection |
| TableHasSpacesInName | The table you are trying to listen to has spaces in its name which we are unable to support |
| UnableToDeleteTenant | Error when trying to delete a tenant |
| UnableToSetPolicies | Error when setting up Authorization Policies |
| UnableCheckoutConnection | Error when trying to checkout a connection from the tenant pool |
| UnableToSubscribeToPostgres | Error when trying to subscribe to Postgres changes |
| ReconnectSubscribeToPostgres | Postgres changes still waiting to be subscribed |
| ChannelRateLimitReached | The number of channels you can create has reached its limit |
| ConnectionRateLimitReached | The number of connected clients as reached its limit |
| ClientJoinRateLimitReached | The rate of joins per second from your clients has reached the channel limits |
| DatabaseConnectionRateLimitReached | The rate of attempts to connect to tenants database has reached the limit |
| MessagePerSecondRateLimitReached | The rate of messages per second from your clients has reached the channel limits |
| RealtimeDisabledForTenant | Realtime has been disabled for the tenant |
| UnableToConnectToTenantDatabase | Realtime was not able to connect to the tenant's database |
| DatabaseLackOfConnections | Realtime was not able to connect to the tenant's database due to not having enough available connections |
| RealtimeNodeDisconnected | Realtime is a distributed application and this means that one the system is unable to communicate with one of the distributed nodes |
| MigrationsFailedToRun | Error when running the migrations against the Tenant database that are required by Realtime |
| StartReplicationFailed | Error when starting the replication and listening of errors for database broadcasting |
| ReplicationConnectionTimeout | Replication connection timed out during initialization |
| ReplicationMaxWalSendersReached | Maximum number of WAL senders reached in tenant database, check how to increase this value in this [link](https://supabase.com/docs/guides/database/custom-postgres-config#cli-configurable-settings) |
| MigrationCheckFailed | Check to see if we require to run migrations fails |
| PartitionCreationFailed | Error when creating partitions for realtime.messages |
| ErrorStartingPostgresCDCStream | Error when starting the Postgres CDC stream which is used for Postgres Changes |
| UnknownDataProcessed | An unknown data type was processed by the Realtime system |
| ErrorStartingPostgresCDC | Error when starting the Postgres CDC extension which is used for Postgres Changes |
| ReplicationSlotBeingUsed | The replication slot is being used by another transaction |
| PoolingReplicationPreparationError | Error when preparing the replication slot |
| PoolingReplicationError | Error when pooling the replication slot |
| SubscriptionCleanupFailed | Error when trying to clean up all subscriptions on subscription manager initialization or OID change |
| SubscriptionDeletionFailed | Error when trying to delete a subscription for postgres changes |
| SubscriptionsCheckerConnectionFailed | Error when the subscriptions checker process fails to connect to the database on startup |
| ReplicationPollerConnectionFailed | Error when the replication poller process fails to connect to the database on startup |
| SubscriptionManagerConnectionFailed | Error when the subscription manager process fails to connect to the database on startup |
| PgStatActivityQueryFailed | Error when querying pg_stat_activity to diagnose a replication slot conflict |
| RateCounterError | Error when retrieving the subscription rate counter, falling back to blocking new subscriptions |
| UnableToDeletePhantomSubscriptions | Error when trying to delete subscriptions that are no longer being used |
| UnableToCheckProcessesOnRemoteNode | Error when trying to check the processes on a remote node |
| UnhandledProcessMessage | Unhandled message received by a Realtime process |
| UnableToTrackPresence | Error when handling track presence for this socket |
| UnknownPresenceEvent | Presence event type not recognized by service |
| IncreaseConnectionPool | The number of connections you have set for Realtime are not enough to handle your current use case |
| RlsPolicyError | Error on RLS policy used for authorization |
| ConnectionInitializing | Database is initializing connection |
| DatabaseConnectionIssue | Database had connection issues and connection was not able to be established |
| UnableToConnectToProject | Unable to connect to Project database |
| InvalidJWTExpiration | JWT exp claim value it's incorrect |
| JwtSignatureError | JWT signature was not able to be validated |
| MalformedJWT | Token received does not comply with the JWT format |
| Unauthorized | Unauthorized access to Realtime channel |
| RealtimeRestarting | Realtime is currently restarting |
| UnableToProcessListenPayload | Payload sent in NOTIFY operation was JSON parsable |
| UnprocessableEntity | Received a HTTP request with a body that was not able to be processed by the endpoint |
| InitializingProjectConnection | Connection against Tenant database is still starting |
| TimeoutOnRpcCall | RPC request within the Realtime server as timed out. |
| ErrorOnRpcCall | Error when calling another realtime node |
| ErrorExecutingTransaction | Error executing a database transaction in tenant database |
| SynInitializationError | Our framework to syncronize processes has failed to properly startup a connection to the database |
| JanitorFailedToDeleteOldMessages | Scheduled task for realtime.message cleanup was unable to run |
| UnableToEncodeJson | An error were we are not handling correctly the response to be sent to the end user |
| UnableToBroadcastChanges | Error when trying to broadcast database changes to subscribers |
| UnexpectedMessageReceived | An unexpected message was received by the replication connection process |
| ErrorRunningQuery | Error when running a query against the tenant database |
| UnknownError | An unhandled error occurred |
| UnknownErrorOnController | An error we are not handling correctly was triggered on a controller |
| UnknownErrorOnChannel | An error we are not handling correctly was triggered on a channel |
| PresenceRateLimitReached | Limit of presence events reached |
| ClientPresenceRateLimitReached | Limit of presence events reached on socket |
| UnableToReplayMessages | An error while replaying messages |
| JwtSignerError | Failed to generate a JWT signer — check your JWT secret or JWKS configuration |
| MalformedWebSocketMessage | Received a WebSocket message that is empty, invalid JSON, or missing required fields (`ref`, `topic`, or `event`). The connection is kept alive but the message is dropped |
| UnknownErrorOnWebSocketMessage | An unexpected error occurred while processing an incoming WebSocket message. The connection is kept alive but the message is dropped |
## Observability and Metrics
Supabase Realtime exposes comprehensive metrics for monitoring performance, resource usage, and application behavior. These metrics are exposed in Prometheus format and can be scraped by any compatible monitoring system (Victoria Metrics, Prometheus, Grafana Agent, etc.).
### Metrics Endpoints
Metrics are split across two endpoints with different priorities, allowing you to configure different scrape intervals in your monitoring system:
| Endpoint | Priority | Recommended Scrape Interval | Contents |
| ----------------------------- | -------- | --------------------------- | ------------------------------------------------------------------------------------------------ |
| `GET /metrics` | **High** | 30s | BEAM/VM, OS, Phoenix, distributed infra, and global aggregated tenant totals (no `tenant` label) |
| `GET /tenant-metrics` | **Low** | 60s | Per-tenant labeled metrics (connection counts, channel events, replication, authorization) |
| `GET /metrics/:region` | **High** | 30s | Same as `/metrics` scoped to a specific region |
| `GET /tenant-metrics/:region` | **Low** | 60s | Same as `/tenant-metrics` scoped to a specific region |
All endpoints require a `Bearer` JWT token in the `Authorization` header signed with `METRICS_JWT_SECRET`.
**Victoria Metrics scrape configuration example:**
```yaml
scrape_configs:
- job_name: realtime_global
scrape_interval: 30s
bearer_token: <METRICS_JWT_SECRET_TOKEN>
static_configs:
- targets: ["<host>:4000"]
metrics_path: /metrics
- job_name: realtime_tenant
scrape_interval: 60s
bearer_token: <METRICS_JWT_SECRET_TOKEN>
static_configs:
- targets: ["<host>:4000"]
metrics_path: /tenant-metrics
```
### Metric Scopes
Metrics are classified by their scope to help you understand what they measure:
- **Per-Tenant**: Metrics tagged with a `tenant` label measure activity scoped to individual tenants. Exposed on `/tenant-metrics`.
- **Global Aggregate**: Metrics prefixed with `realtime_channel_global_*` or `realtime_connections_global_*` aggregate tenant data without the `tenant` label, suitable for cluster-wide dashboards. Exposed on `/metrics`.
- **Per-Node**: Metrics measure activity on the current Realtime node. Without explicit per-node indication, assume metrics apply to the local node.
- **BEAM/Erlang VM**: Metrics prefixed with `beam_*` and `phoenix_*` expose Erlang runtime internals. Exposed on `/metrics`.
- **Infrastructure**: Metrics prefixed with `osmon_*`, `gen_rpc_*`, and `dist_*` measure system-level resources and cluster communication. Exposed on `/metrics`.
### Connection & Tenant Metrics
These metrics track WebSocket connections and tenant activity across the Realtime cluster.
| Metric | Type | Description | Scope | Endpoint |
| ----------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | ----------------- |
| `realtime_tenants_connected` | Gauge | Number of connected tenants per Realtime node. Use this to understand tenant distribution across your cluster and identify load imbalances. | Per-Node | `/metrics` |
| `realtime_connections_global_connected` | Gauge | Node total of active WebSocket connections across all tenants. Aggregated without a `tenant` label for cluster-wide dashboards. | Global Aggregate | `/metrics` |
| `realtime_connections_global_connected_cluster` | Gauge | Cluster-wide total of active WebSocket connections across all tenants. | Global Aggregate | `/metrics` |
| `realtime_connections_connected` | Gauge | Active WebSocket connections that have at least one subscribed channel. Indicates active client engagement with Realtime features. | **Per-Tenant** | `/tenant-metrics` |
| `realtime_connections_connected_cluster` | Gauge | Cluster-wide active WebSocket connections for each individual tenant. | **Per-Tenant** | `/tenant-metrics` |
| `phoenix_connections_total` | Gauge | Total open connections to the Ranch listener (includes idle connections waiting for data). | Per-Node | `/metrics` |
| `phoenix_connections_active` | Gauge | Connections actively processing a WebSocket frame or HTTP request. Divide by `phoenix_connections_max` to get a saturation ratio. | Per-Node | `/metrics` |
| `phoenix_connections_max` | Gauge | The configured Ranch connection limit. When `phoenix_connections_total` approaches this the node is saturated and new connections will be queued. | Per-Node | `/metrics` |
| `realtime_channel_joins` | Counter | Rate of channel join attempts per second per tenant. | **Per-Tenant** | `/tenant-metrics` |
| `realtime_channel_global_joins` | Counter | Global rate of channel join attempts per second across all tenants. | Global Aggregate | `/metrics` |
### Event Metrics
These metrics measure the volume and types of events flowing through your Realtime system, segmented by feature type.
| Metric | Type | Description | Scope | Endpoint |
| ----------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------- | ---------------- | ----------------- |
| `realtime_channel_events` | Counter | Broadcast events per second per tenant. | **Per-Tenant** | `/tenant-metrics` |
| `realtime_channel_presence_events` | Counter | Presence events per second per tenant. Includes online/offline status updates and custom presence metadata synchronization. | **Per-Tenant** | `/tenant-metrics` |
| `realtime_channel_db_events` | Counter | Postgres Changes events per second per tenant. | **Per-Tenant** | `/tenant-metrics` |
| `realtime_channel_global_events` | Counter | Global broadcast events per second across all tenants. Compare against per-tenant values for outlier detection. | Global Aggregate | `/metrics` |
| `realtime_channel_global_presence_events` | Counter | Global presence events per second across all tenants. | Global Aggregate | `/metrics` |
| `realtime_channel_global_db_events` | Counter | Global Postgres Changes events per second across all tenants. | Global Aggregate | `/metrics` |
### Payload & Traffic Metrics
These metrics provide insight into data volume, message sizes, and network I/O characteristics.
| Metric | Type | Description | Scope | Endpoint |
| -------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------- | ---------------- | ----------------- |
| `realtime_payload_size_bucket` | Histogram | Global payload size distribution across all tenants, tagged by message type. Use for cluster-wide sizing and capacity planning. | Global Aggregate | `/metrics` |
| `realtime_tenants_payload_size_bucket` | Histogram | Per-tenant payload size distribution. Use this to identify tenants generating unusually large messages. | **Per-Tenant** | `/tenant-metrics` |
| `realtime_channel_input_bytes` | Counter | Total ingress bytes per tenant. | **Per-Tenant** | `/tenant-metrics` |
| `realtime_channel_output_bytes` | Counter | Total egress bytes per tenant. | **Per-Tenant** | `/tenant-metrics` |
| `realtime_channel_global_input_bytes` | Counter | Global total ingress bytes across all tenants. | Global Aggregate | `/metrics` |
| `realtime_channel_global_output_bytes` | Counter | Global total egress bytes across all tenants. | Global Aggregate | `/metrics` |
### Latency & Performance Metrics
These metrics measure end-to-end latency and processing performance across different Realtime operations.
| Metric | Type | Description | Scope | Endpoint |
| ---------------------------------------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------- | ---------------- | ----------------- |
| `realtime_replication_poller_query_duration_bucket` | Histogram | Postgres Changes query latency in milliseconds per tenant. High values may indicate database performance issues. | **Per-Tenant** | `/tenant-metrics` |
| `realtime_replication_poller_query_duration_count` | Counter | Number of database polling queries executed per tenant. | **Per-Tenant** | `/tenant-metrics` |
| `realtime_tenants_broadcast_from_database_latency_committed_at_bucket` | Histogram | Time from database commit to client broadcast per tenant. | **Per-Tenant** | `/tenant-metrics` |
| `realtime_tenants_broadcast_from_database_latency_inserted_at_bucket` | Histogram | Alternative latency using insert timestamp per tenant. | **Per-Tenant** | `/tenant-metrics` |
| `realtime_tenants_replay_bucket` | Histogram | Broadcast replay latency per tenant. | **Per-Tenant** | `/tenant-metrics` |
| `realtime_global_rpc_bucket` | Histogram | Inter-node RPC call latency distribution, tagged by `success` and `mechanism`. | Global Aggregate | `/metrics` |
| `realtime_global_rpc_count` | Counter | Total inter-node RPC calls. Divide failed by total to get error rate. | Global Aggregate | `/metrics` |
| `realtime_tenants_read_authorization_check_bucket` | Histogram | RLS policy evaluation time for read operations per tenant. | **Per-Tenant** | `/tenant-metrics` |
| `realtime_tenants_read_authorization_check_count` | Counter | Number of read authorization checks per tenant. | **Per-Tenant** | `/tenant-metrics` |
| `realtime_tenants_write_authorization_check_bucket` | Histogram | RLS policy evaluation time for write operations per tenant. | **Per-Tenant** | `/tenant-metrics` |
| `phoenix_channel_handled_in_duration_milliseconds_bucket` | Histogram | Time for the application to respond to a channel message. High p99 values indicate slow message handlers. | Per-Node | `/metrics` |
| `phoenix_socket_connected_duration_milliseconds_bucket` | Histogram | Time to establish a WebSocket socket connection, tagged by `result`/`transport`/`serializer`. | Per-Node | `/metrics` |
### Authorization & Error Metrics
These metrics track security policy enforcement and error rates.
| Metric | Type | Description | Scope | Endpoint |
| ------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | ----------------- |
| `realtime_channel_error` | Counter | Unhandled channel errors per tenant. Any non-zero value warrants investigation. | **Per-Tenant** | `/tenant-metrics` |
| `realtime_channel_global_error` | Counter | Global unhandled channel error count across all tenants, tagged by error code. | Global Aggregate | `/metrics` |
| `phoenix_channel_joined_total` | Counter | WebSocket channel join attempts tagged by `result` (`ok`/`error`) and `transport`. Use `result="error"` rate to detect client or policy issues. | Per-Node | `/metrics` |
### BEAM/Erlang VM Metrics
These metrics provide insight into the underlying Erlang runtime that powers Realtime, critical for capacity planning and debugging performance issues.
All BEAM/Erlang VM metrics are served from `GET /metrics`.
#### Memory Metrics
| Metric | Type | Description |
| ----------------------------------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `beam_memory_allocated_bytes` | Gauge | Total memory allocated by the Erlang VM. Compare this to the container memory limit to ensure you have headroom. Steady increase may indicate a memory leak. |
| `beam_memory_atom_total_bytes` | Gauge | Memory used by the atom table. Atoms in Erlang are never garbage collected, so this should remain relatively stable. Unbounded growth indicates a bug creating new atoms. |
| `beam_memory_binary_total_bytes` | Gauge | Memory used by binary data (WebSocket payloads, database results). This metric closely correlates with active connection volume and message sizes. |
| `beam_memory_code_total_bytes` | Gauge | Memory used by compiled Erlang bytecode. Changes only during code reloads and should remain stable in production. |
| `beam_memory_ets_total_bytes` | Gauge | Memory used by ETS (in-memory tables) including channel subscriptions and presence state. Monitor this to understand session storage overhead. |
| `beam_memory_processes_total_bytes` | Gauge | Memory used by Erlang processes themselves. Each channel connection and background task consumes memory; this scales with concurrency. |
| `beam_memory_persistent_term_total_bytes` | Gauge | Memory used by persistent terms (immutable shared state). Should be minimal and stable in typical Realtime deployments. |
#### Process & Resource Metrics
| Metric | Type | Description |
| -------------------------- | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `beam_stats_process_count` | Gauge | Number of active Erlang processes. Each WebSocket connection spawns processes; high values correlate with connection count. Sudden spikes may indicate process leaks. |
| `beam_stats_port_count` | Gauge | Number of open port connections (network sockets, pipes). Should correlate roughly with connection count plus internal cluster communications. |
| `beam_stats_ets_count` | Gauge | Number of active ETS tables used for caching and state. Changes reflect dynamic supervisor activity and feature usage patterns. |
| `beam_stats_atom_count` | Gauge | Total atoms in the atom table. Should remain relatively stable; unbounded growth indicates code bugs. |
#### Performance Metrics
| Metric | Type | Description |
| -------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `beam_stats_uptime_milliseconds_count` | Counter | Node uptime in milliseconds. Use this to track restarts and validate deployment stability. Unexpected resets indicate crashes. |
| `beam_stats_port_io_byte_count` | Counter | Total bytes transferred through network ports. Compare ingress and egress to identify asymmetric traffic patterns. |
| `beam_stats_gc_count` | Counter | Garbage collection events executed by the Erlang VM. Frequent GC indicates high memory churn; infrequent GC suggests stable state. |
| `beam_stats_gc_reclaimed_bytes` | Counter | Bytes reclaimed by garbage collection. Divide by GC count to understand average cleanup size. Low reclaim per GC may indicate inefficient memory allocation patterns. |
| `beam_stats_reduction_count` | Counter | Total reductions (work units) executed by the VM. Correlates with CPU usage; high reduction rates under stable load indicate inefficient algorithms. |
| `beam_stats_context_switch_count` | Counter | Process context switches by the Erlang scheduler. High values indicate contention between many processes; compare with process count to gauge congestion. |
| `beam_stats_active_task_count` | Gauge | Tasks currently executing on dirty schedulers (non-Erlang operations). High values indicate CPU-bound work or blocking I/O. |
| `beam_stats_run_queue_count` | Gauge | Processes waiting to be scheduled. High values indicate CPU saturation; the node cannot keep up with work demand. |
### Infrastructure Metrics
These metrics expose system-level resource usage and inter-node cluster communication. All infrastructure metrics are served from `GET /metrics`.
#### Node Metrics
| Metric | Type | Description |
| ----------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `osmon_cpu_util` | Gauge | Current CPU utilization percentage (0-100). Monitor this to trigger horizontal scaling and identify CPU-bound bottlenecks. |
| `osmon_cpu_avg1` | Gauge | 1-minute CPU load average. Sharp increases indicate sudden load spikes; values > CPU count indicate sustained overload. |
| `osmon_cpu_avg5` | Gauge | 5-minute CPU load average. Smooths short-term spikes; use this to detect sustained load increases. |
| `osmon_cpu_avg15` | Gauge | 15-minute CPU load average. Indicates long-term trends; use for capacity planning and detecting gradual load growth. |
| `osmon_ram_usage` | Gauge | RAM utilization percentage (0-100). Combined with `beam_memory_allocated_bytes`, this indicates kernel memory overhead and other processes on the node. |
#### Distributed System Metrics
| Metric | Type | Description |
| ---------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `gen_rpc_queue_size_bytes` | Gauge | Outbound queue size for gen_rpc inter-node communication in bytes. Large values indicate a receiving node cannot keep up with message rate. |
| `gen_rpc_send_pending_bytes` | Gauge | Bytes pending transmission in gen_rpc queues. Combined with queue size, helps identify network saturation or slow receivers. |
| `gen_rpc_send_bytes` | Counter | Total bytes sent via gen_rpc across the cluster. Monitor this to understand inter-node traffic and plan network capacity. |
| `gen_rpc_recv_bytes` | Counter | Total bytes received via gen_rpc from other nodes. Compare with send bytes to identify asymmetric communication patterns. |
| `dist_queue_size` | Gauge | Erlang distribution queue size for cluster communication. High values indicate network congestion or unbalanced load across nodes. |
| `dist_send_pending_bytes` | Gauge | Bytes pending in Erlang distribution queues. Works with queue size to diagnose cluster communication issues. |
| `dist_send_bytes` | Counter | Total bytes sent via Erlang distribution protocol. Includes all cluster metadata and RPC traffic. |
| `dist_recv_bytes` | Counter | Total bytes received via Erlang distribution protocol. Compare with send to validate symmetric communication. |
## License
This repo is licensed under Apache 2.0.
## Credits
- [Phoenix](https://github.com/phoenixframework/phoenix) - `Realtime` server is built with the amazing Elixir framework.
- [Phoenix Channels JavaScript Client](https://github.com/phoenixframework/phoenix/tree/master/assets/js/phoenix) - [@supabase/realtime-js](https://github.com/supabase/realtime-js) client library heavily draws from the Phoenix Channels client library.
================================================
FILE: assets/css/app.css
================================================
@tailwind base;
@tailwind components;
@tailwind utilities;
================================================
FILE: assets/css/phoenix.css
================================================
/* Includes some default style for the starter application.
* This can be safely deleted to start fresh.
*/
/* Milligram v1.3.0 https://milligram.github.io
* Copyright (c) 2017 CJ Patoilo Licensed under the MIT license
*/
*,*:after,*:before{box-sizing:inherit}html{box-sizing:border-box;font-size:62.5%}body{color:#000000;font-family:'Helvetica', 'Arial', sans-serif;font-size:1.6em;font-weight:300;line-height:1.6}blockquote{border-left:0.3rem solid #d1d1d1;margin-left:0;margin-right:0;padding:1rem 1.5rem}blockquote *:last-child{margin-bottom:0}.button,button,input[type='button'],input[type='reset'],input[type='submit']{background-color:#0069d9;border:0.1rem solid #0069d9;border-radius:.4rem;color:#fff;cursor:pointer;display:inline-block;font-size:1.1rem;font-weight:700;height:3.8rem;letter-spacing:.1rem;line-height:3.8rem;padding:0 3.0rem;text-align:center;text-decoration:none;text-transform:uppercase;white-space:nowrap}.button:focus,.button:hover,button:focus,button:hover,input[type='button']:focus,input[type='button']:hover,input[type='reset']:focus,input[type='reset']:hover,input[type='submit']:focus,input[type='submit']:hover{background-color:#606c76;border-color:#606c76;color:#fff;outline:0}.button[disabled],button[disabled],input[type='button'][disabled],input[type='reset'][disabled],input[type='submit'][disabled]{cursor:default;opacity:.5}.button[disabled]:focus,.button[disabled]:hover,button[disabled]:focus,button[disabled]:hover,input[type='button'][disabled]:focus,input[type='button'][disabled]:hover,input[type='reset'][disabled]:focus,input[type='reset'][disabled]:hover,input[type='submit'][disabled]:focus,input[type='submit'][disabled]:hover{background-color:#0069d9;border-color:#0069d9}.button.button-outline,button.button-outline,input[type='button'].button-outline,input[type='reset'].button-outline,input[type='submit'].button-outline{background-color:transparent;color:#0069d9}.button.button-outline:focus,.button.button-outline:hover,button.button-outline:focus,button.button-outline:hover,input[type='button'].button-outline:focus,input[type='button'].button-outline:hover,input[type='reset'].button-outline:focus,input[type='reset'].button-outline:hover,input[type='submit'].button-outline:focus,input[type='submit'].button-outline:hover{background-color:transparent;border-color:#606c76;color:#606c76}.button.button-outline[disabled]:focus,.button.button-outline[disabled]:hover,button.button-outline[disabled]:focus,button.button-outline[disabled]:hover,input[type='button'].button-outline[disabled]:focus,input[type='button'].button-outline[disabled]:hover,input[type='reset'].button-outline[disabled]:focus,input[type='reset'].button-outline[disabled]:hover,input[type='submit'].button-outline[disabled]:focus,input[type='submit'].button-outline[disabled]:hover{border-color:inherit;color:#0069d9}.button.button-clear,button.button-clear,input[type='button'].button-clear,input[type='reset'].button-clear,input[type='submit'].button-clear{background-color:transparent;border-color:transparent;color:#0069d9}.button.button-clear:focus,.button.button-clear:hover,button.button-clear:focus,button.button-clear:hover,input[type='button'].button-clear:focus,input[type='button'].button-clear:hover,input[type='reset'].button-clear:focus,input[type='reset'].button-clear:hover,input[type='submit'].button-clear:focus,input[type='submit'].button-clear:hover{background-color:transparent;border-color:transparent;color:#606c76}.button.button-clear[disabled]:focus,.button.button-clear[disabled]:hover,button.button-clear[disabled]:focus,button.button-clear[disabled]:hover,input[type='button'].button-clear[disabled]:focus,input[type='button'].button-clear[disabled]:hover,input[type='reset'].button-clear[disabled]:focus,input[type='reset'].button-clear[disabled]:hover,input[type='submit'].button-clear[disabled]:focus,input[type='submit'].button-clear[disabled]:hover{color:#0069d9}code{background:#f4f5f6;border-radius:.4rem;font-size:86%;margin:0 .2rem;padding:.2rem .5rem;white-space:nowrap}pre{background:#f4f5f6;border-left:0.3rem solid #0069d9;overflow-y:hidden}pre>code{border-radius:0;display:block;padding:1rem 1.5rem;white-space:pre}hr{border:0;border-top:0.1rem solid #f4f5f6;margin:3.0rem 0}input[type='email'],input[type='number'],input[type='password'],input[type='search'],input[type='tel'],input[type='text'],input[type='url'],textarea,select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent;border:0.1rem solid #d1d1d1;border-radius:.4rem;box-shadow:none;box-sizing:inherit;height:3.8rem;padding:.6rem 1.0rem;width:100%}input[type='email']:focus,input[type='number']:focus,input[type='password']:focus,input[type='search']:focus,input[type='tel']:focus,input[type='text']:focus,input[type='url']:focus,textarea:focus,select:focus{border-color:#0069d9;outline:0}select{background:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="14" viewBox="0 0 29 14" width="29"><path fill="%23d1d1d1" d="M9.37727 3.625l5.08154 6.93523L19.54036 3.625"/></svg>') center right no-repeat;padding-right:3.0rem}select:focus{background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="14" viewBox="0 0 29 14" width="29"><path fill="%230069d9" d="M9.37727 3.625l5.08154 6.93523L19.54036 3.625"/></svg>')}textarea{min-height:6.5rem}label,legend{display:block;font-size:1.6rem;font-weight:700;margin-bottom:.5rem}fieldset{border-width:0;padding:0}input[type='checkbox'],input[type='radio']{display:inline}.label-inline{display:inline-block;font-weight:normal;margin-left:.5rem}.row{display:flex;flex-direction:column;padding:0;width:100%}.row.row-no-padding{padding:0}.row.row-no-padding>.column{padding:0}.row.row-wrap{flex-wrap:wrap}.row.row-top{align-items:flex-start}.row.row-bottom{align-items:flex-end}.row.row-center{align-items:center}.row.row-stretch{align-items:stretch}.row.row-baseline{align-items:baseline}.row .column{display:block;flex:1 1 auto;margin-left:0;max-width:100%;width:100%}.row .column.column-offset-10{margin-left:10%}.row .column.column-offset-20{margin-left:20%}.row .column.column-offset-25{margin-left:25%}.row .column.column-offset-33,.row .column.column-offset-34{margin-left:33.3333%}.row .column.column-offset-50{margin-left:50%}.row .column.column-offset-66,.row .column.column-offset-67{margin-left:66.6666%}.row .column.column-offset-75{margin-left:75%}.row .column.column-offset-80{margin-left:80%}.row .column.column-offset-90{margin-left:90%}.row .column.column-10{flex:0 0 10%;max-width:10%}.row .column.column-20{flex:0 0 20%;max-width:20%}.row .column.column-25{flex:0 0 25%;max-width:25%}.row .column.column-33,.row .column.column-34{flex:0 0 33.3333%;max-width:33.3333%}.row .column.column-40{flex:0 0 40%;max-width:40%}.row .column.column-50{flex:0 0 50%;max-width:50%}.row .column.column-60{flex:0 0 60%;max-width:60%}.row .column.column-66,.row .column.column-67{flex:0 0 66.6666%;max-width:66.6666%}.row .column.column-75{flex:0 0 75%;max-width:75%}.row .column.column-80{flex:0 0 80%;max-width:80%}.row .column.column-90{flex:0 0 90%;max-width:90%}.row .column .column-top{align-self:flex-start}.row .column .column-bottom{align-self:flex-end}.row .column .column-center{-ms-grid-row-align:center;align-self:center}@media (min-width: 40rem){.row{flex-direction:row;margin-left:-1.0rem;width:calc(100% + 2.0rem)}.row .column{margin-bottom:inherit;padding:0 1.0rem}}a{color:#0069d9;text-decoration:none}a:focus,a:hover{color:#606c76}dl,ol,ul{list-style:none;margin-top:0;padding-left:0}dl dl,dl ol,dl ul,ol dl,ol ol,ol ul,ul dl,ul ol,ul ul{font-size:90%;margin:1.5rem 0 1.5rem 3.0rem}ol{list-style:decimal inside}ul{list-style:circle inside}.button,button,dd,dt,li{margin-bottom:1.0rem}fieldset,input,select,textarea{margin-bottom:1.5rem}blockquote,dl,figure,form,ol,p,pre,table,ul{margin-bottom:2.5rem}table{border-spacing:0;width:100%}td,th{border-bottom:0.1rem solid #e1e1e1;padding:1.2rem 1.5rem;text-align:left}td:first-child,th:first-child{padding-left:0}td:last-child,th:last-child{padding-right:0}b,strong{font-weight:bold}p{margin-top:0}h1,h2,h3,h4,h5,h6{font-weight:300;letter-spacing:-.1rem;margin-bottom:2.0rem;margin-top:0}h1{font-size:4.6rem;line-height:1.2}h2{font-size:3.6rem;line-height:1.25}h3{font-size:2.8rem;line-height:1.3}h4{font-size:2.2rem;letter-spacing:-.08rem;line-height:1.35}h5{font-size:1.8rem;letter-spacing:-.05rem;line-height:1.5}h6{font-size:1.6rem;letter-spacing:0;line-height:1.4}img{max-width:100%}.clearfix:after{clear:both;content:' ';display:table}.float-left{float:left}.float-right{float:right}
/* General style */
h1{font-size: 3.6rem; line-height: 1.25}
h2{font-size: 2.8rem; line-height: 1.3}
h3{font-size: 2.2rem; letter-spacing: -.08rem; line-height: 1.35}
h4{font-size: 1.8rem; letter-spacing: -.05rem; line-height: 1.5}
h5{font-size: 1.6rem; letter-spacing: 0; line-height: 1.4}
h6{font-size: 1.4rem; letter-spacing: 0; line-height: 1.2}
pre{padding: 1em;}
.container{
margin: 0 auto;
max-width: 80.0rem;
padding: 0 2.0rem;
position: relative;
width: 100%
}
select {
width: auto;
}
/* Phoenix promo and logo */
.phx-hero {
text-align: center;
border-bottom: 1px solid #e3e3e3;
background: #eee;
border-radius: 6px;
padding: 3em 3em 1em;
margin-bottom: 3rem;
font-weight: 200;
font-size: 120%;
}
.phx-hero input {
background: #ffffff;
}
.phx-logo {
min-width: 300px;
margin: 1rem;
display: block;
}
.phx-logo img {
width: auto;
display: block;
}
/* Headers */
header {
width: 100%;
background: #fdfdfd;
border-bottom: 1px solid #eaeaea;
margin-bottom: 2rem;
}
header section {
align-items: center;
display: flex;
flex-direction: column;
justify-content: space-between;
}
header section :first-child {
order: 2;
}
header section :last-child {
order: 1;
}
header nav ul,
header nav li {
margin: 0;
padding: 0;
display: block;
text-align: right;
white-space: nowrap;
}
header nav ul {
margin: 1rem;
margin-top: 0;
}
header nav a {
display: block;
}
@media (min-width: 40.0rem) { /* Small devices (landscape phones, 576px and up) */
header section {
flex-direction: row;
}
header nav ul {
margin: 1rem;
}
.phx-logo {
flex-basis: 527px;
margin: 2rem 1rem;
}
}
================================================
FILE: assets/js/app.js
================================================
import "../css/app.css";
import "phoenix_html";
import { Socket } from "phoenix";
import { LiveSocket } from "phoenix_live_view";
import topbar from "../vendor/topbar";
import { createClient } from "@supabase/supabase-js";
// LiveView is managing this page because we have Phoenix running
// We're using LiveView to handle the Realtime client via LiveView Hooks
const Hooks = {};
Hooks.payload = {
initRealtime(
channelName,
host,
log_level,
token,
schema,
table,
filter,
bearer,
enable_presence,
enable_db_changes,
private_channel
) {
// Instantiate our client with the Realtime server and params to connect with
const opts = {
realtime: {
params: {
log_level: log_level,
},
},
};
this.realtimeSocket = createClient(host, token, opts);
if (bearer !== "") {
this.realtimeSocket.realtime.setAuth(bearer);
}
private_channel = private_channel === "true";
// Join the Channel 'any'
// Channels can be named anything
// All clients on the same Channel will get messages sent to that Channel
this.channel = this.realtimeSocket.channel(channelName, {
config: {
broadcast: { self: true },
private: private_channel,
},
});
// Hack to confirm Postgres is subscribed
// Need to add 'extension' key in the 'payload'
this.channel.on("system", {}, (payload) => {
if (payload.extension === "postgres_changes" && payload.status === "ok") {
this.pushEventTo("#conn_info", "postgres_subscribed", {});
}
const ts = new Date();
const line = `<tr class="bg-white border-b hover:bg-gray-50">
<td class="py-4 px-6">SYSTEM</td>
<td class="py-4 px-6">${ts.toISOString()}</td>
<td class="py-4 px-6">${JSON.stringify(payload)}</td>
</tr>`;
const list = document.querySelector("#plist");
list.innerHTML = line + list.innerHTML;
});
// Listen for all (`*`) `broadcast` events
// The event name can by anything
// Match on specific event names to filter for only those types of events and do something with them
this.channel.on("broadcast", { event: "*" }, (payload) => {
const ts = new Date();
const line = `<tr class="bg-white border-b hover:bg-gray-50">
<td class="py-4 px-6">BROADCAST</td>
<td class="py-4 px-6">${ts.toISOString()}</td>
<td class="py-4 px-6">${JSON.stringify(payload)}</td>
</tr>`;
const list = document.querySelector("#plist");
list.innerHTML = line + list.innerHTML;
});
// Listen for all (`*`) `presence` events
if (enable_presence === "true") {
console.log("enable_presence", enable_presence);
this.channel.on("presence", { event: "*" }, (payload) => {
this.pushEventTo("#conn_info", "presence_subscribed", {});
const ts = new Date();
const line = `<tr class="bg-white border-b hover:bg-gray-50">
<td class="py-4 px-6">PRESENCE</td>
<td class="py-4 px-6">${ts.toISOString()}</td>
<td class="py-4 px-6">${JSON.stringify(payload)}</td>
</tr>`;
const list = document.querySelector("#plist");
list.innerHTML = line + list.innerHTML;
});
}
// Listen for all (`*`) `postgres_changes` events on tables in the `public` schema
if (enable_db_changes === "true") {
const postgres_changes_opts = {
event: "*",
schema: schema,
table: table,
};
if (filter !== "") {
postgres_changes_opts.filter = filter;
}
this.channel.on("postgres_changes", postgres_changes_opts, (payload) => {
const ts = performance.now() + performance.timeOrigin;
const iso_ts = new Date();
const payload_ts = Date.parse(payload.commit_timestamp);
const latency = ts - payload_ts;
const line = `<tr class="bg-white border-b hover:bg-gray-50">
<td class="py-4 px-6">POSTGRES</td>
<td class="py-4 px-6">${iso_ts.toISOString()}</td>
<td class="py-4 px-6">
<div class="pb-3">${JSON.stringify(payload)}</div>
<div class="pt-3 border-t hover:bg-gray-50">Latency: ${latency.toFixed(
1
)} ms</div>
</td>
</tr>`;
const list = document.querySelector("#plist");
list.innerHTML = line + list.innerHTML;
});
}
// Finally, subscribe to the Channel we just setup
this.channel.subscribe(async (status, error) => {
if (status === "SUBSCRIBED") {
console.log(`Realtime Channel status: ${status}`);
// Let LiveView know we connected so we can update the button text
this.pushEventTo("#conn_info", "broadcast_subscribed", { host: host });
// Save params to local storage if `SUBSCRIBED`
localStorage.setItem("host", host);
localStorage.setItem("token", token);
localStorage.setItem("log_level", log_level);
localStorage.setItem("channel", channelName);
localStorage.setItem("schema", schema);
localStorage.setItem("table", table);
localStorage.setItem("filter", filter);
localStorage.setItem("bearer", bearer);
localStorage.setItem("enable_presence", enable_presence);
localStorage.setItem("enable_db_changes", enable_db_changes);
localStorage.setItem("private_channel", private_channel);
// Initiate Presence for a connected user
// Now when a new user connects and sends a `TRACK` message all clients will receive a message like:
// {
// "event":"join",
// "key":"2b88be54-3b41-11ed-9887-1a9e1a785cf8",
// "currentPresences":[
//
// ],
// "newPresences":[
// {
// "name":"realtime_presence_55",
// "t":1968.1000000238419,
// "presence_ref":"Fxd_ZWlhIIfuIwlD"
// }
// ]
// }
//
// And when `TRACK`ed users leave we'll receive an event like:
//
// {
// "event":"leave",
// "key":"2b88be54-3b41-11ed-9887-1a9e1a785cf8",
// "currentPresences":[
//
// ],
// "leftPresences":[
// {
// "name":"realtime_presence_55",
// "t":1968.1000000238419,
// "presence_ref":"Fxd_ZWlhIIfuIwlD"
// }
// ]
// }
if (enable_presence === "true") {
const name = "user_name_" + Math.floor(Math.random() * 100);
await this.channel.track({
name: name,
t: performance.now(),
});
}
} else {
console.error(`Realtime Channel error status: ${status}`);
console.error(`Realtime Channel error: ${error}`);
}
});
},
sendRealtime(event, payload) {
// Send a `broadcast` message over the Channel
// All connected clients will receive this message if they're subscribed
// to `broadcast` events and matching on the `event` name or using `*` to match all event names
this.channel.send({
type: "broadcast",
event: event,
payload: payload,
});
},
disconnectRealtime() {
// Send a `broadcast` message over the Channel
// All connected clients will receive this message if they're subscribed
// to `broadcast` events and matching on the `event` name or using `*` to match all event names
this.channel.unsubscribe();
},
clearLocalStorage() {
localStorage.clear();
},
mounted() {
const params = {
log_level: localStorage.getItem("log_level"),
token: localStorage.getItem("token"),
host: localStorage.getItem("host"),
channel: localStorage.getItem("channel"),
schema: localStorage.getItem("schema"),
table: localStorage.getItem("table"),
filter: localStorage.getItem("filter"),
bearer: localStorage.getItem("bearer"),
enable_presence: localStorage.getItem("enable_presence"),
enable_db_changes: localStorage.getItem("enable_db_changes"),
private_channel: localStorage.getItem("private_channel"),
};
this.pushEventTo("#conn_form", "local_storage", params);
this.handleEvent("connect", ({ connection }) =>
this.initRealtime(
connection.channel,
connection.host,
connection.log_level,
connection.token,
connection.schema,
connection.table,
connection.filter,
connection.bearer,
connection.enable_presence,
connection.enable_db_changes,
connection.private_channel
)
);
this.handleEvent("send_message", ({ message }) =>
this.sendRealtime(message.event, message.payload)
);
this.handleEvent("disconnect", () => this.disconnectRealtime());
this.handleEvent("clear_local_storage", () => this.clearLocalStorage());
},
};
Hooks.latency = {
mounted() {
this.handleEvent("ping", (params) => this.pong(params));
},
pong(params) {
this.pushEventTo("#ping", "pong", params);
},
};
const csrfToken = document
.querySelector("meta[name='csrf-token']")
.getAttribute("content");
const liveSocket = new LiveSocket("/live", Socket, {
hooks: Hooks,
params: { _csrf_token: csrfToken },
});
topbar.config({ barColors: { 0: "#29d" }, shadowColor: "rgba(0, 0, 0, .3)" });
window.addEventListener("phx:page-loading-start", () => topbar.show());
window.addEventListener("phx:page-loading-stop", () => topbar.hide());
liveSocket.connect();
window.liveSocket = liveSocket;
================================================
FILE: assets/package.json
================================================
{
"dependencies": {
"@supabase/supabase-js": "2.100.0-canary.0"
}
}
================================================
FILE: assets/tailwind.config.js
================================================
const plugin = require("tailwindcss/plugin")
const colors = require('tailwindcss/colors')
module.exports = {
content: [
'./js/**/*.js',
'../lib/*_web.ex',
'../lib/*_web/**/*.*ex',
],
theme: {
colors: {
transparent: 'transparent',
current: 'currentColor',
black: colors.black,
white: colors.white,
gray: colors.gray,
emerald: colors.emerald,
indigo: colors.indigo,
yellow: colors.yellow,
green: colors.green
},
fontFamily: {
sans: ['custom-font', 'Helvetica Neue', 'Helvetica', 'Arial', 'sans-serif'],
mono: ['Source Code Pro', 'Menlo', 'monospace'],
},
},
plugins: [
require("@tailwindcss/forms"),
require('@tailwindcss/typography'),
plugin(({addVariant}) => addVariant("phx-no-feedback", [".phx-no-feedback&", ".phx-no-feedback &"])),
plugin(({addVariant}) => addVariant("phx-click-loading", [".phx-click-loading&", ".phx-click-loading &"])),
plugin(({addVariant}) => addVariant("phx-submit-loading", [".phx-submit-loading&", ".phx-submit-loading &"])),
plugin(({addVariant}) => addVariant("phx-change-loading", [".phx-change-loading&", ".phx-change-loading &"]))
]
};
================================================
FILE: assets/vendor/topbar.js
================================================
/**
* @license MIT
* topbar 1.0.0, 2021-01-06
* https://buunguyen.github.io/topbar
* Copyright (c) 2021 Buu Nguyen
*/
(function (window, document) {
"use strict";
// https://gist.github.com/paulirish/1579671
(function () {
var lastTime = 0;
var vendors = ["ms", "moz", "webkit", "o"];
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame =
window[vendors[x] + "RequestAnimationFrame"];
window.cancelAnimationFrame =
window[vendors[x] + "CancelAnimationFrame"] ||
window[vendors[x] + "CancelRequestAnimationFrame"];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function (callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function () {
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function (id) {
clearTimeout(id);
};
})();
var canvas,
progressTimerId,
fadeTimerId,
currentProgress,
showing,
addEvent = function (elem, type, handler) {
if (elem.addEventListener) elem.addEventListener(type, handler, false);
else if (elem.attachEvent) elem.attachEvent("on" + type, handler);
else elem["on" + type] = handler;
},
options = {
autoRun: true,
barThickness: 3,
barColors: {
0: "rgba(26, 188, 156, .9)",
".25": "rgba(52, 152, 219, .9)",
".50": "rgba(241, 196, 15, .9)",
".75": "rgba(230, 126, 34, .9)",
"1.0": "rgba(211, 84, 0, .9)",
},
shadowBlur: 10,
shadowColor: "rgba(0, 0, 0, .6)",
className: null,
},
repaint = function () {
canvas.width = window.innerWidth;
canvas.height = options.barThickness * 5; // need space for shadow
var ctx = canvas.getContext("2d");
ctx.shadowBlur = options.shadowBlur;
ctx.shadowColor = options.shadowColor;
var lineGradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
for (var stop in options.barColors)
lineGradient.addColorStop(stop, options.barColors[stop]);
ctx.lineWidth = options.barThickness;
ctx.beginPath();
ctx.moveTo(0, options.barThickness / 2);
ctx.lineTo(
Math.ceil(currentProgress * canvas.width),
options.barThickness / 2
);
ctx.strokeStyle = lineGradient;
ctx.stroke();
},
createCanvas = function () {
canvas = document.createElement("canvas");
var style = canvas.style;
style.position = "fixed";
style.top = style.left = style.right = style.margin = style.padding = 0;
style.zIndex = 100001;
style.display = "none";
if (options.className) canvas.classList.add(options.className);
document.body.appendChild(canvas);
addEvent(window, "resize", repaint);
},
topbar = {
config: function (opts) {
for (var key in opts)
if (options.hasOwnProperty(key)) options[key] = opts[key];
},
show: function () {
if (showing) return;
showing = true;
if (fadeTimerId !== null) window.cancelAnimationFrame(fadeTimerId);
if (!canvas) createCanvas();
canvas.style.opacity = 1;
canvas.style.display = "block";
topbar.progress(0);
if (options.autoRun) {
(function loop() {
progressTimerId = window.requestAnimationFrame(loop);
topbar.progress(
"+" + 0.05 * Math.pow(1 - Math.sqrt(currentProgress), 2)
);
})();
}
},
progress: function (to) {
if (typeof to === "undefined") return currentProgress;
if (typeof to === "string") {
to =
(to.indexOf("+") >= 0 || to.indexOf("-") >= 0
? currentProgress
: 0) + parseFloat(to);
}
currentProgress = to > 1 ? 1 : to;
repaint();
return currentProgress;
},
hide: function () {
if (!showing) return;
showing = false;
if (progressTimerId != null) {
window.cancelAnimationFrame(progressTimerId);
progressTimerId = null;
}
(function loop() {
if (topbar.progress("+.1") >= 1) {
canvas.style.opacity -= 0.05;
if (canvas.style.opacity <= 0.05) {
canvas.style.display = "none";
fadeTimerId = null;
return;
}
}
fadeTimerId = window.requestAnimationFrame(loop);
})();
},
};
if (typeof module === "object" && typeof module.exports === "object") {
module.exports = topbar;
} else if (typeof define === "function" && define.amd) {
define(function () {
return topbar;
});
} else {
this.topbar = topbar;
}
}.call(this, window, document));
================================================
FILE: beacon/.formatter.exs
================================================
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]
================================================
FILE: beacon/.gitignore
================================================
# The directory Mix will write compiled artifacts to.
/_build/
# If you run "mix test --cover", coverage assets end up here.
/cover/
# The directory Mix downloads your dependencies sources to.
/deps/
# Where third-party dependencies like ExDoc output generated docs.
/doc/
# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump
# Also ignore archive artifacts (built via "mix archive.build").
*.ez
# Ignore package tarball (built via "mix hex.build").
beacon-*.tar
# Temporary files, for example, from tests.
/tmp/
================================================
FILE: beacon/README.md
================================================
# Beacon
Beacon is a scalable process group manager. The main use case for this library is to have membership counts available on the cluster without spamming whenever a process joins or leaves a group. A node can have thousands of processes joining and leaving hundreds of groups while sending just the membership count to other nodes.
The main features are:
* Process pids are available only to the node the where the processes reside;
* Groups are partitioned locally to allow greater concurrency while joining different groups;
* Group counts are periodically broadcasted (defaults to every 5 seconds) to update group membership numbers to all participating nodes;
* Sub-cluster nodes join by using same scope;
## Installation
The package can be installed by adding `beacon` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:beacon, "~> 1.0"}
]
end
```
## Using
Add Beacon to your application's supervision tree specifying a scope name (here it's `:users`)
```elixir
def start(_type, _args) do
children =
[
{Beacon, :users},
# Or passing options:
# {Beacon, [:users, opts]}
# See Beacon.start_link/2 for the options
```
Now process can join groups
```elixir
iex> pid = self()
#PID<0.852.0>
iex> Beacon.join(:users, {:tenant, 123}, pid)
:ok
iex> Beacon.local_member_count(:users, {:tenant, 123})
1
iex> Beacon.local_members(:users, {:tenant, 123})
[#PID<0.852.0>]
iex> Beacon.local_member?(:users, {:tenant, 123}, pid)
true
```
From another node part of the same scope:
```elixir
iex> Beacon.member_counts(:users)
%{{:tenant, 123} => 1}
iex> Beacon.member_count(:users, {:tenant, 123})
1
```
================================================
FILE: beacon/config/config.exs
================================================
import Config
# Print nothing during tests unless captured or a test failure happens
config :logger, backends: [], level: :debug
================================================
FILE: beacon/lib/beacon/adapter/erl_dist.ex
================================================
defmodule Beacon.Adapter.ErlDist do
@moduledoc false
import Kernel, except: [send: 2]
@behaviour Beacon.Adapter
@impl true
def register(scope) do
Process.register(self(), Beacon.Supervisor.name(scope))
:ok
end
@impl true
def broadcast(scope, message) do
name = Beacon.Supervisor.name(scope)
Enum.each(Node.list(), fn node -> :erlang.send({name, node}, message, [:noconnect]) end)
end
@impl true
def broadcast(scope, nodes, message) do
name = Beacon.Supervisor.name(scope)
Enum.each(nodes, fn node -> :erlang.send({name, node}, message, [:noconnect]) end)
end
@impl true
def send(scope, node, message) do
:erlang.send({Beacon.Supervisor.name(scope), node}, message, [:noconnect])
end
end
================================================
FILE: beacon/lib/beacon/adapter.ex
================================================
defmodule Beacon.Adapter do
@moduledoc """
Behaviour module for Beacon messaging adapters.
"""
@doc "Register the current process to receive messages for the given scope"
@callback register(scope :: atom) :: :ok
@doc "Broadcast a message to all nodes in the given scope"
@callback broadcast(scope :: atom, message :: term) :: any
@doc "Broadcast a message to specific nodes in the given scope"
@callback broadcast(scope :: atom, [node], message :: term) :: any
@doc "Send a message to a specific node in the given scope"
@callback send(scope :: atom, node, message :: term) :: any
end
================================================
FILE: beacon/lib/beacon/partition.ex
================================================
defmodule Beacon.Partition do
@moduledoc false
use GenServer
require Logger
defmodule State do
@moduledoc false
@type t :: %__MODULE__{
name: atom,
scope: atom,
entries_table: atom,
monitors: %{{Beacon.group(), pid} => reference}
}
defstruct [:name, :scope, :entries_table, monitors: %{}]
end
@spec join(atom, Beacon.group(), pid) :: :ok
def join(partition_name, group, pid), do: GenServer.call(partition_name, {:join, group, pid})
@spec leave(atom, Beacon.group(), pid) :: :ok
def leave(partition_name, group, pid), do: GenServer.call(partition_name, {:leave, group, pid})
@spec members(atom, Beacon.group()) :: [pid]
def members(partition_name, group) do
partition_name
|> Beacon.Supervisor.partition_entries_table()
|> :ets.select([{{{group, :"$1"}}, [], [:"$1"]}])
end
@spec member_count(atom, Beacon.group()) :: non_neg_integer
def member_count(partition_name, group), do: :ets.lookup_element(partition_name, group, 2, 0)
@spec member_counts(atom) :: %{Beacon.group() => non_neg_integer}
def member_counts(partition_name) do
partition_name
|> :ets.tab2list()
|> Map.new()
end
@spec member?(atom, Beacon.group(), pid) :: boolean
def member?(partition_name, group, pid) do
partition_name
|> Beacon.Supervisor.partition_entries_table()
|> :ets.lookup({group, pid})
|> case do
[{{^group, ^pid}}] -> true
[] -> false
end
end
@spec groups(atom) :: [Beacon.group()]
def groups(partition_name), do: :ets.select(partition_name, [{{:"$1", :_}, [], [:"$1"]}])
@spec group_count(atom) :: non_neg_integer
def group_count(partition_name), do: :ets.info(partition_name, :size)
@spec start_link(atom, atom, atom) :: GenServer.on_start()
def start_link(scope, partition_name, partition_entries_table),
do:
GenServer.start_link(__MODULE__, [scope, partition_name, partition_entries_table],
name: partition_name
)
@impl true
@spec init(any) :: {:ok, State.t()}
def init([scope, name, entries_table]) do
{:ok, %State{scope: scope, name: name, entries_table: entries_table},
{:continue, :rebuild_monitors_and_counters}}
end
@impl true
@spec handle_continue(:rebuild_monitors_and_counters, State.t()) :: {:noreply, State.t()}
def handle_continue(:rebuild_monitors_and_counters, state) do
# Here we delete all counters and rebuild them based on entries table
:ets.delete_all_objects(state.name)
monitors =
:ets.tab2list(state.entries_table)
|> Enum.reduce(%{}, fn {{group, pid}}, monitors_acc ->
ref = Process.monitor(pid, tag: {:DOWN, group})
:ets.update_counter(state.name, group, {2, 1}, {group, 0})
Map.put(monitors_acc, {group, pid}, ref)
end)
{:noreply, %{state | monitors: monitors}}
end
@impl true
@spec handle_call({:join, Beacon.group(), pid}, GenServer.from(), State.t()) ::
{:reply, :ok, State.t()}
def handle_call({:join, group, pid}, _from, state) do
if :ets.insert_new(state.entries_table, {{group, pid}}) do
case :ets.lookup_element(state.name, group, 2, 0) do
0 ->
:ets.insert(state.name, {group, 1})
:telemetry.execute([:beacon, state.scope, :group, :occupied], %{}, %{group: group})
count when count > 0 ->
:ets.insert(state.name, {group, count + 1})
end
ref = Process.monitor(pid, tag: {:DOWN, group})
monitors = Map.put(state.monitors, {group, pid}, ref)
{:reply, :ok, %{state | monitors: monitors}}
else
{:reply, :ok, state}
end
end
def handle_call({:leave, group, pid}, _from, state) do
state = remove(group, pid, state)
{:reply, :ok, state}
end
@impl true
@spec handle_info({{:DOWN, Beacon.group()}, reference, :process, pid, term}, State.t()) ::
{:noreply, State.t()}
def handle_info({{:DOWN, group}, _ref, :process, pid, _reason}, state) do
state = remove(group, pid, state)
{:noreply, state}
end
def handle_info(_, state), do: {:noreply, state}
defp remove(group, pid, state) do
case :ets.lookup(state.entries_table, {group, pid}) do
[{{^group, ^pid}}] ->
:ets.delete(state.entries_table, {group, pid})
# Delete or decrement counter
case :ets.lookup_element(state.name, group, 2, 0) do
1 ->
:ets.delete(state.name, group)
:telemetry.execute([:beacon, state.scope, :group, :vacant], %{}, %{group: group})
count when count > 1 ->
:ets.update_counter(state.name, group, {2, -1})
end
[] ->
Logger.warning(
"Beacon[#{node()}|#{state.scope}] Trying to remove an unknown process #{inspect(pid)}"
)
:ok
end
case Map.pop(state.monitors, {group, pid}) do
{nil, _} ->
state
{ref, new_monitors} ->
Process.demonitor(ref, [:flush])
%{state | monitors: new_monitors}
end
end
end
================================================
FILE: beacon/lib/beacon/scope.ex
================================================
defmodule Beacon.Scope do
@moduledoc false
# Responsible to discover and keep track of all Beacon peers in the cluster
use GenServer
require Logger
@default_broadcast_interval 5_000
@spec member_counts(atom) :: %{Beacon.group() => non_neg_integer}
def member_counts(scope) do
scope
|> table_name()
|> :ets.select([{{:_, :"$1"}, [], [:"$1"]}])
|> Enum.reduce(%{}, fn member_counts, acc ->
Map.merge(acc, member_counts, fn _k, v1, v2 -> v1 + v2 end)
end)
end
@spec member_count(atom, Beacon.group()) :: non_neg_integer
def member_count(scope, group) do
scope
|> table_name()
|> :ets.select([{{:_, %{group => :"$1"}}, [], [:"$1"]}])
|> Enum.sum()
end
@spec member_count(atom, Beacon.group(), node) :: non_neg_integer
def member_count(scope, group, node) do
case :ets.lookup(table_name(scope), node) do
[{^node, member_counts}] -> Map.get(member_counts, group, 0)
[] -> 0
end
end
@spec groups(atom) :: MapSet.t(Beacon.group())
def groups(scope) do
scope
|> table_name()
|> :ets.select([{{:_, :"$1"}, [], [:"$1"]}])
|> Enum.reduce(MapSet.new(), fn member_counts, acc ->
member_counts
|> Map.keys()
|> MapSet.new()
|> MapSet.union(acc)
end)
end
@typep member_counts :: %{Beacon.group() => non_neg_integer}
defp table_name(scope), do: :"#{scope}_beacon_peer_counts"
defmodule State do
@moduledoc false
@type t :: %__MODULE__{
scope: atom,
message_module: module,
broadcast_interval: non_neg_integer,
peer_counts_table: :ets.tid(),
peers: %{pid => reference}
}
defstruct [
:scope,
:message_module,
:broadcast_interval,
:peer_counts_table,
peers: %{}
]
end
@spec start_link(atom, Keyword.t()) :: GenServer.on_start()
def start_link(scope, opts \\ []), do: GenServer.start_link(__MODULE__, [scope, opts])
@impl true
def init([scope, opts]) do
:ok = :net_kernel.monitor_nodes(true)
peer_counts_table =
:ets.new(table_name(scope), [:set, :protected, :named_table, read_concurrency: true])
broadcast_interval =
Keyword.get(opts, :broadcast_interval_in_ms, @default_broadcast_interval)
message_module = Keyword.get(opts, :message_module, Beacon.Adapter.ErlDist)
Logger.info("Beacon[#{node()}|#{scope}] Starting")
:ok = message_module.register(scope)
{:ok,
%State{
scope: scope,
message_module: message_module,
broadcast_interval: broadcast_interval,
peer_counts_table: peer_counts_table
}, {:continue, :discover}}
end
@impl true
@spec handle_continue(:discover, State.t()) :: {:noreply, State.t()}
def handle_continue(:discover, state) do
state.message_module.broadcast(state.scope, {:discover, self()})
Process.send_after(self(), :broadcast_counts, state.broadcast_interval)
{:noreply, state}
end
@impl true
@spec handle_info(
{:discover, pid}
| {:sync, pid, member_counts}
| :broadcast_counts
| {:nodeup, node}
| {:nodedown, node}
| {:DOWN, reference, :process, pid, term},
State.t()
) :: {:noreply, State.t()}
# A remote peer is discovering us
def handle_info({:discover, peer}, state) do
Logger.info(
"Beacon[#{node()}|#{state.scope}] Received DISCOVER request from node #{node(peer)}"
)
state.message_module.send(
state.scope,
node(peer),
{:sync, self(), Beacon.local_member_counts(state.scope)}
)
# We don't do anything if we already know about this peer
if Map.has_key?(state.peers, peer) do
Logger.debug(
"Beacon[#{node()}|#{state.scope}] already know peer #{inspect(peer)} from node #{node(peer)}"
)
{:noreply, state}
else
Logger.debug(
"Beacon[#{node()}|#{state.scope}] discovered peer #{inspect(peer)} from node #{node(peer)}"
)
ref = Process.monitor(peer)
new_peers = Map.put(state.peers, peer, ref)
state.message_module.send(state.scope, node(peer), {:discover, self()})
{:noreply, %State{state | peers: new_peers}}
end
end
# A remote peer has sent us its local member counts
def handle_info({:sync, peer, member_counts}, state) do
:ets.insert(state.peer_counts_table, {node(peer), member_counts})
{:noreply, state}
end
# Periodic broadcast of our local member counts to all known peers
def handle_info(:broadcast_counts, state) do
nodes =
state.peers
|> Map.keys()
|> Enum.map(&node/1)
state.message_module.broadcast(
state.scope,
nodes,
{:sync, self(), Beacon.local_member_counts(state.scope)}
)
Process.send_after(self(), :broadcast_counts, state.broadcast_interval)
{:noreply, state}
end
# Do nothing if the node that came up is our own node
def handle_info({:nodeup, node}, state) when node == node(), do: {:noreply, state}
# Send a discover message to the node that just connected
def handle_info({:nodeup, node}, state) do
:telemetry.execute([:beacon, state.scope, :node, :up], %{}, %{node: node})
Logger.info(
"Beacon[#{node()}|#{state.scope}] Node #{node} has joined the cluster, sending discover message"
)
state.message_module.send(state.scope, node, {:discover, self()})
{:noreply, state}
end
# Do nothing and wait for the DOWN message from monitor
def handle_info({:nodedown, _node}, state), do: {:noreply, state}
# A remote peer has disconnected/crashed
# We forget about it and remove its member counts
def handle_info({:DOWN, ref, :process, peer, reason}, state) do
Logger.info(
"Beacon[#{node()}|#{state.scope}] Scope process is DOWN on node #{node(peer)}: #{inspect(reason)}"
)
case Map.pop(state.peers, peer) do
{nil, _} ->
{:noreply, state}
{^ref, new_peers} ->
:ets.delete(state.peer_counts_table, node(peer))
:telemetry.execute([:beacon, state.scope, :node, :down], %{}, %{node: node(peer)})
{:noreply, %State{state | peers: new_peers}}
end
end
def handle_info(_msg, state), do: {:noreply, state}
end
================================================
FILE: beacon/lib/beacon/supervisor.ex
================================================
defmodule Beacon.Supervisor do
@moduledoc false
use Supervisor
def name(scope), do: :"#{scope}_beacon"
def supervisor_name(scope), do: :"#{scope}_beacon_supervisor"
def partition_name(scope, partition), do: :"#{scope}_beacon_partition_#{partition}"
def partition_entries_table(partition_name), do: :"#{partition_name}_entries"
@spec partition(atom, Scope.group()) :: atom
def partition(scope, group) do
case :persistent_term.get(scope, :unknown) do
:unknown -> raise "Beacon for scope #{inspect(scope)} is not started"
partition_names -> elem(partition_names, :erlang.phash2(group, tuple_size(partition_names)))
end
end
@spec partitions(atom) :: [atom]
def partitions(scope) do
case :persistent_term.get(scope, :unknown) do
:unknown -> raise "Beacon for scope #{inspect(scope)} is not started"
partition_names -> Tuple.to_list(partition_names)
end
end
@spec start_link(atom, pos_integer(), Keyword.t()) :: Supervisor.on_start()
def start_link(scope, partitions, opts \\ []) do
args = [scope, partitions, opts]
Supervisor.start_link(__MODULE__, args, name: supervisor_name(scope))
end
@impl true
def init([scope, partitions, opts]) do
children =
for i <- 0..(partitions - 1) do
partition_name = partition_name(scope, i)
partition_entries_table = partition_entries_table(partition_name)
^partition_entries_table =
:ets.new(partition_entries_table, [:set, :public, :named_table, read_concurrency: true])
^partition_name =
:ets.new(partition_name, [:set, :public, :named_table, read_concurrency: true])
%{
id: i,
start: {Beacon.Partition, :start_link, [scope, partition_name, partition_entries_table]}
}
end
partition_names = for i <- 0..(partitions - 1), do: partition_name(scope, i)
:persistent_term.put(scope, List.to_tuple(partition_names))
children = [
%{id: :scope, start: {Beacon.Scope, :start_link, [scope, opts]}} | children
]
Supervisor.init(children, strategy: :one_for_one)
end
end
================================================
FILE: beacon/lib/beacon.ex
================================================
defmodule Beacon do
@moduledoc """
Distributed process group membership tracking.
"""
alias Beacon.Partition
alias Beacon.Scope
@type group :: any
@type start_option ::
{:partitions, pos_integer()} | {:broadcast_interval_in_ms, non_neg_integer()}
@doc "Returns a supervisor child specification for a Beacon scope"
def child_spec([scope]) when is_atom(scope), do: child_spec([scope, []])
def child_spec(scope) when is_atom(scope), do: child_spec([scope, []])
def child_spec([scope, opts]) when is_atom(scope) and is_list(opts) do
%{
id: Beacon,
start: {__MODULE__, :start_link, [scope, opts]},
type: :supervisor
}
end
@doc """
Starts the Beacon supervision tree for `scope`.
Options:
* `:partitions` - number of partitions to use (default: number of schedulers online)
* `:broadcast_interval_in_ms`: - interval in milliseconds to broadcast membership counts to other nodes (default: 5000 ms)
* `:message_module` - module implementing `Beacon.Adapter` behaviour (default: `Beacon.Adapter.ErlDist`)
"""
@spec start_link(atom, [start_option]) :: Supervisor.on_start()
def start_link(scope, opts \\ []) when is_atom(scope) do
{partitions, opts} = Keyword.pop(opts, :partitions, System.schedulers_online())
broadcast_interval_in_ms = Keyword.get(opts, :broadcast_interval_in_ms)
if not (is_integer(partitions) and partitions >= 1) do
raise ArgumentError,
"expected :partitions to be a positive integer, got: #{inspect(partitions)}"
end
if broadcast_interval_in_ms != nil and
not (is_integer(broadcast_interval_in_ms) and broadcast_interval_in_ms > 0) do
raise ArgumentError,
"expected :broadcast_interval_in_ms to be a positive integer, got: #{inspect(broadcast_interval_in_ms)}"
end
Beacon.Supervisor.start_link(scope, partitions, opts)
end
@doc "Join pid to group in scope"
@spec join(atom, any, pid) :: :ok | {:error, :not_local}
def join(_scope, _group, pid) when is_pid(pid) and node(pid) != node(), do: {:error, :not_local}
def join(scope, group, pid) when is_atom(scope) and is_pid(pid) do
Partition.join(Beacon.Supervisor.partition(scope, group), group, pid)
end
@doc "Leave pid from group in scope"
@spec leave(atom, group, pid) :: :ok
def leave(scope, group, pid) when is_atom(scope) and is_pid(pid) do
Partition.leave(Beacon.Supervisor.partition(scope, group), group, pid)
end
@doc "Get total members count per group in scope"
@spec member_counts(atom) :: %{group => non_neg_integer}
def member_counts(scope) when is_atom(scope) do
remote_counts = Scope.member_counts(scope)
scope
|> local_member_counts()
|> Map.merge(remote_counts, fn _k, v1, v2 -> v1 + v2 end)
end
@doc "Get total member count of group in scope"
@spec member_count(atom, group) :: non_neg_integer
def member_count(scope, group) do
local_member_count(scope, group) + Scope.member_count(scope, group)
end
@doc "Get total member count of group in scope on specific node"
@spec member_count(atom, group, node) :: non_neg_integer
def member_count(scope, group, node) when node == node(), do: local_member_count(scope, group)
def member_count(scope, group, node), do: Scope.member_count(scope, group, node)
@doc "Get local members of group in scope"
@spec local_members(atom, group) :: [pid]
def local_members(scope, group) when is_atom(scope) do
Partition.members(Beacon.Supervisor.partition(scope, group), group)
end
@doc "Get local member count of group in scope"
@spec local_member_count(atom, group) :: non_neg_integer
def local_member_count(scope, group) when is_atom(scope) do
Partition.member_count(Beacon.Supervisor.partition(scope, group), group)
end
@doc "Get local members count per group in scope"
@spec local_member_counts(atom) :: %{group => non_neg_integer}
def local_member_counts(scope) when is_atom(scope) do
Enum.reduce(Beacon.Supervisor.partitions(scope), %{}, fn partition_name, acc ->
Map.merge(acc, Partition.member_counts(partition_name))
end)
end
@doc "Check if pid is a local member of group in scope"
@spec local_member?(atom, group, pid) :: boolean
def local_member?(scope, group, pid) when is_atom(scope) and is_pid(pid) do
Partition.member?(Beacon.Supervisor.partition(scope, group), group, pid)
end
@doc "Get all local groups in scope"
@spec local_groups(atom) :: [group]
def local_groups(scope) when is_atom(scope) do
Enum.flat_map(Beacon.Supervisor.partitions(scope), fn partition_name ->
Partition.groups(partition_name)
end)
end
@doc "Get local group count in scope"
@spec local_group_count(atom) :: non_neg_integer
def local_group_count(scope) when is_atom(scope) do
Enum.sum_by(Beacon.Supervisor.partitions(scope), fn partition_name ->
Partition.group_count(partition_name)
end)
end
@doc "Get groups in scope"
@spec groups(atom) :: [group]
def groups(scope) when is_atom(scope) do
remote_groups = Scope.groups(scope)
scope
|> local_groups()
|> MapSet.new()
|> MapSet.union(remote_groups)
|> MapSet.to_list()
end
@doc "Get group count in scope"
@spec group_count(atom) :: non_neg_integer
def group_count(scope) when is_atom(scope) do
remote_groups = Scope.groups(scope)
scope
|> local_groups()
|> MapSet.new()
|> MapSet.union(remote_groups)
|> MapSet.size()
end
end
================================================
FILE: beacon/mix.exs
================================================
defmodule Beacon.MixProject do
use Mix.Project
def project do
[
app: :beacon,
version: "1.0.0",
elixir: "~> 1.18",
start_permanent: Mix.env() == :prod,
elixirc_paths: elixirc_paths(Mix.env()),
deps: deps()
]
end
# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger]
]
end
# Specifies which paths to compile per environment.
defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(_), do: ["lib"]
# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:telemetry, "~> 1.3"},
{:mix_test_watch, "~> 1.0", only: [:dev, :test], runtime: false}
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
]
end
end
================================================
FILE: beacon/test/beacon/partition_test.exs
================================================
defmodule Beacon.PartitionTest do
use ExUnit.Case, async: true
alias Beacon.Partition
@scope __MODULE__
setup do
partition_name = Beacon.Supervisor.partition_name(@scope, System.unique_integer([:positive]))
entries_table = Beacon.Supervisor.partition_entries_table(partition_name)
^partition_name =
:ets.new(partition_name, [:set, :public, :named_table, read_concurrency: true])
^entries_table =
:ets.new(entries_table, [:set, :public, :named_table, read_concurrency: true])
spec = %{
id: partition_name,
start: {Partition, :start_link, [@scope, partition_name, entries_table]},
type: :supervisor,
restart: :temporary
}
pid = start_supervised!(spec)
ref =
:telemetry_test.attach_event_handlers(self(), [
[:beacon, @scope, :group, :occupied],
[:beacon, @scope, :group, :vacant]
])
{:ok, partition_name: partition_name, partition_pid: pid, ref: ref}
end
test "members/2 returns empty list for non-existent group", %{partition_name: partition} do
assert Partition.members(partition, :nonexistent) == []
end
test "member_count/2 returns 0 for non-existent group", %{partition_name: partition} do
assert Partition.member_count(partition, :nonexistent) == 0
end
test "member?/3 returns false for non-member", %{partition_name: partition} do
pid = spawn_link(fn -> Process.sleep(:infinity) end)
refute Partition.member?(partition, :group1, pid)
end
test "join and query member", %{partition_name: partition, ref: ref} do
pid = spawn_link(fn -> Process.sleep(:infinity) end)
assert :ok = Partition.join(partition, :group9, pid)
assert Partition.member?(partition, :group9, pid)
assert Partition.member_count(partition, :group9) == 1
assert pid in Partition.members(partition, :group9)
assert_receive {[:beacon, @scope, :group, :occupied], ^ref, %{}, %{group: :group9}}
refute_receive {_, ^ref, _, _}
end
test "join multiple times and query member", %{partition_name: partition, ref: ref} do
pid = spawn_link(fn -> Process.sleep(:infinity) end)
assert :ok = Partition.join(partition, :group1, pid)
assert :ok = Partition.join(partition, :group1, pid)
assert :ok = Partition.join(partition, :group1, pid)
assert Partition.member?(partition, :group1, pid)
assert Partition.member_count(partition, :group1) == 1
assert pid in Partition.members(partition, :group1)
assert_receive {[:beacon, @scope, :group, :occupied], ^ref, %{}, %{group: :group1}}
refute_receive {_, ^ref, _, _}
end
test "occupied event only when first member joins", %{partition_name: partition, ref: ref} do
pid1 = spawn_link(fn -> Process.sleep(:infinity) end)
pid2 = spawn_link(fn -> Process.sleep(:infinity) end)
Partition.join(partition, :group1, pid1)
Partition.join(partition, :group1, pid2)
assert_receive {[:beacon, @scope, :group, :occupied], ^ref, %{}, %{group: :group1}}
refute_receive {_, ^ref, _, _}
end
test "leave removes member", %{partition_name: partition, ref: ref} do
pid = spawn_link(fn -> Process.sleep(:infinity) end)
Partition.join(partition, :group1, pid)
assert Partition.member?(partition, :group1, pid)
Partition.leave(partition, :group1, pid)
refute Partition.member?(partition, :group1, pid)
assert_receive {[:beacon, @scope, :group, :occupied], ^ref, %{}, %{group: :group1}}
assert_receive {[:beacon, @scope, :group, :vacant], ^ref, %{}, %{group: :group1}}
refute_receive {_, ^ref, _, _}
end
test "vacant event only when no members left", %{partition_name: partition, ref: ref} do
pid1 = spawn_link(fn -> Process.sleep(:infinity) end)
pid2 = spawn_link(fn -> Process.sleep(:infinity) end)
Partition.join(partition, :group1, pid1)
Partition.join(partition, :group1, pid2)
assert_receive {[:beacon, @scope, :group, :occupied], ^ref, %{}, %{group: :group1}}
refute_receive {_, ^ref, _, _}
Partition.leave(partition, :group1, pid1)
refute_receive {_, ^ref, _, _}
Partition.leave(partition, :group1, pid2)
assert_receive {[:beacon, @scope, :group, :vacant], ^ref, %{}, %{group: :group1}}
refute_receive {_, ^ref, _, _}
end
test "leave multiple times removes member", %{partition_name: partition, ref: ref} do
pid = spawn_link(fn -> Process.sleep(:infinity) end)
Partition.join(partition, :group1, pid)
assert Partition.member?(partition, :group1, pid)
Partition.leave(partition, :group1, pid)
Partition.leave(partition, :group1, pid)
Partition.leave(partition, :group1, pid)
refute Partition.member?(partition, :group1, pid)
assert_receive {[:beacon, @scope, :group, :occupied], ^ref, %{}, %{group: :group1}}
assert_receive {[:beacon, @scope, :group, :vacant], ^ref, %{}, %{group: :group1}}
refute_receive {_, ^ref, _, _}
end
test "member_counts returns counts for all groups", %{partition_name: partition} do
pid1 = spawn_link(fn -> Process.sleep(:infinity) end)
pid2 = spawn_link(fn -> Process.sleep(:infinity) end)
pid3 = spawn_link(fn -> Process.sleep(:infinity) end)
Partition.join(partition, :group1, pid1)
Partition.join(partition, :group1, pid2)
Partition.join(partition, :group2, pid3)
counts = Partition.member_counts(partition)
assert map_size(counts) == 2
assert counts[:group1] == 2
assert counts[:group2] == 1
end
test "groups returns all groups", %{partition_name: partition} do
pid1 = spawn_link(fn -> Process.sleep(:infinity) end)
pid2 = spawn_link(fn -> Process.sleep(:infinity) end)
Partition.join(partition, :group1, pid1)
Partition.join(partition, :group2, pid2)
groups = Partition.groups(partition)
assert :group1 in groups
assert :group2 in groups
end
test "group_counts returns number of groups", %{partition_name: partition} do
pid1 = spawn_link(fn -> Process.sleep(:infinity) end)
pid2 = spawn_link(fn -> Process.sleep(:infinity) end)
pid3 = spawn_link(fn -> Process.sleep(:infinity) end)
pid4 = spawn_link(fn -> Process.sleep(:infinity) end)
Partition.join(partition, :group1, pid1)
Partition.join(partition, :group1, pid2)
Partition.join(partition, :group2, pid3)
Partition.join(partition, :group3, pid4)
assert Partition.group_count(partition) == 3
end
test "process death removes member from group", %{partition_name: partition} do
pid = spawn(fn -> Process.sleep(:infinity) end)
Partition.join(partition, :group1, pid)
assert Partition.member?(partition, :group1, pid)
Process.exit(pid, :kill)
Process.sleep(50)
refute Partition.member?(partition, :group1, pid)
assert Partition.member_count(partition, :group1) == 0
end
test "partition recovery monitors processes again", %{
partition_name: partition,
partition_pid: partition_pid
} do
pid1 = spawn(fn -> Process.sleep(:infinity) end)
pid2 = spawn(fn -> Process.sleep(:infinity) end)
Partition.join(partition, :group1, pid1)
Partition.join(partition, :group2, pid2)
monitors = Process.info(partition_pid, [:monitors])[:monitors] |> Enum.map(&elem(&1, 1))
assert length(monitors)
assert monitors |> Enum.member?(pid1)
assert monitors |> Enum.member?(pid2)
assert %{{:group1, ^pid1} => _ref1, {:group2, ^pid2} => _ref2} =
:sys.get_state(partition_pid).monitors
Process.monitor(partition_pid)
Process.exit(partition_pid, :kill)
assert_receive {:DOWN, _ref, :process, ^partition_pid, :killed}
spec = %{
id: :recover,
start:
{Partition, :start_link,
[@scope, partition, Beacon.Supervisor.partition_entries_table(partition)]},
type: :supervisor
}
partition_pid = start_supervised!(spec)
assert %{{:group1, ^pid1} => _ref1, {:group2, ^pid2} => _ref2} =
:sys.get_state(partition_pid).monitors
monitors = Process.info(partition_pid, [:monitors])[:monitors] |> Enum.map(&elem(&1, 1))
assert length(monitors)
assert monitors |> Enum.member?(pid1)
assert monitors |> Enum.member?(pid2)
assert Partition.member_count(partition, :group1) == 1
assert Partition.member_count(partition, :group2) == 1
assert Partition.member?(partition, :group1, pid1)
assert Partition.member?(partition, :group2, pid2)
end
end
================================================
FILE: beacon/test/beacon_test.exs
================================================
defmodule BeaconTest do
use ExUnit.Case, async: true
setup do
scope = :"test_scope#{System.unique_integer([:positive])}"
%{scope: scope}
end
defp spec(scope, opts) do
%{
id: scope,
start: {Beacon, :start_link, [scope, opts]},
type: :supervisor
}
end
describe "start_link/2" do
test "starts beacon with default partitions", %{scope: scope} do
pid = start_supervised!({Beacon, [scope, []]})
assert Process.alive?(pid)
assert is_list(Beacon.Supervisor.partitions(scope))
assert length(Beacon.Supervisor.partitions(scope)) == System.schedulers_online()
end
test "starts beacon with custom partition count", %{scope: scope} do
pid = start_supervised!(spec(scope, partitions: 3))
assert Process.alive?(pid)
assert length(Beacon.Supervisor.partitions(scope)) == 3
end
test "raises on invalid partition count", %{scope: scope} do
assert_raise ArgumentError, ~r/expected :partitions to be a positive integer/, fn ->
Beacon.start_link(scope, partitions: 0)
end
assert_raise ArgumentError, ~r/expected :partitions to be a positive integer/, fn ->
Beacon.start_link(scope, partitions: -1)
end
assert_raise ArgumentError, ~r/expected :partitions to be a positive integer/, fn ->
Beacon.start_link(scope, partitions: :invalid)
end
end
test "raises on invalid broadcast_interval_in_ms", %{scope: scope} do
assert_raise ArgumentError,
~r/expected :broadcast_interval_in_ms to be a positive integer/,
fn ->
Beacon.start_link(scope, broadcast_interval_in_ms: 0)
end
assert_raise ArgumentError,
~r/expected :broadcast_interval_in_ms to be a positive integer/,
fn ->
Beacon.start_link(scope, broadcast_interval_in_ms: -1)
end
assert_raise ArgumentError,
~r/expected :broadcast_interval_in_ms to be a positive integer/,
fn ->
Beacon.start_link(scope, broadcast_interval_in_ms: :invalid)
end
end
end
describe "join/3 and leave/3" do
setup %{scope: scope} do
start_supervised!(spec(scope, partitions: 2))
:ok
end
test "can join a group", %{scope: scope} do
pid = spawn_link(fn -> Process.sleep(:infinity) end)
assert :ok = Beacon.join(scope, :group1, pid)
assert Beacon.local_member?(scope, :group1, pid)
end
test "can leave a group", %{scope: scope} do
pid = spawn_link(fn -> Process.sleep(:infinity) end)
assert :ok = Beacon.join(scope, :group1, pid)
assert Beacon.local_member?(scope, :group1, pid)
assert :ok = Beacon.leave(scope, :group1, pid)
refute Beacon.local_member?(scope, :group1, pid)
end
test "joining same group twice is idempotent", %{scope: scope} do
pid = spawn_link(fn -> Process.sleep(:infinity) end)
assert :ok = Beacon.join(scope, :group1, pid)
assert :ok = Beacon.join(scope, :group1, pid)
assert Beacon.local_member_count(scope, :group1) == 1
end
test "multiple processes can join same group", %{scope: scope} do
pid1 = spawn_link(fn -> Process.sleep(:infinity) end)
pid2 = spawn_link(fn -> Process.sleep(:infinity) end)
assert :ok = Beacon.join(scope, :group1, pid1)
assert :ok = Beacon.join(scope, :group1, pid2)
members = Beacon.local_members(scope, :group1)
assert length(members) == 2
assert pid1 in members
assert pid2 in members
end
test "process can join multiple groups", %{scope: scope} do
pid = spawn_link(fn -> Process.sleep(:infinity) end)
assert :ok = Beacon.join(scope, :group1, pid)
assert :ok = Beacon.join(scope, :group2, pid)
assert Beacon.local_member?(scope, :group1, pid)
assert Beacon.local_member?(scope, :group2, pid)
end
test "automatically removes member when process dies", %{scope: scope} do
pid = spawn(fn -> Process.sleep(:infinity) end)
assert :ok = Beacon.join(scope, :group1, pid)
assert Beacon.local_member?(scope, :group1, pid)
Process.exit(pid, :kill)
Process.sleep(50)
refute Beacon.local_member?(scope, :group1, pid)
assert Beacon.local_member_count(scope, :group1) == 0
end
end
describe "local_members/2" do
setup %{scope: scope} do
start_supervised!(spec(scope, partitions: 2))
:ok
end
test "returns empty list for non-existent group", %{scope: scope} do
assert Beacon.local_members(scope, :nonexistent) == []
end
test "returns all members of a group", %{scope: scope} do
pid1 = spawn_link(fn -> Process.sleep(:infinity) end)
pid2 = spawn_link(fn -> Process.sleep(:infinity) end)
pid3 = spawn_link(fn -> Process.sleep(:infinity) end)
Beacon.join(scope, :group1, pid1)
Beacon.join(scope, :group1, pid2)
Beacon.join(scope, :group2, pid3)
members = Beacon.local_members(scope, :group1)
assert length(members) == 2
assert pid1 in members
assert pid2 in members
refute pid3 in members
end
end
describe "local_member_count/2" do
setup %{scope: scope} do
start_supervised!(spec(scope, partitions: 2))
:ok
end
test "returns 0 for non-existent group", %{scope: scope} do
assert Beacon.local_member_count(scope, :nonexistent) == 0
end
test "returns correct count", %{scope: scope} do
pid1 = spawn_link(fn -> Process.sleep(:infinity) end)
pid2 = spawn_link(fn -> Process.sleep(:infinity) end)
assert Beacon.local_member_count(scope, :group1) == 0
Beacon.join(scope, :group1, pid1)
assert Beacon.local_member_count(scope, :group1) == 1
Beacon.join(scope, :group1, pid2)
assert Beacon.local_member_count(scope, :group1) == 2
Beacon.leave(scope, :group1, pid1)
assert Beacon.local_member_count(scope, :group1) == 1
end
end
describe "local_member_counts/1" do
gitextract_musv60dn/
├── .credo.exs
├── .dockerignore
├── .formatter.exs
├── .github/
│ ├── actionlint.yaml
│ └── workflows/
│ ├── beacon_tests.yml
│ ├── docker-build.yml
│ ├── integration_tests.yml
│ ├── lint.yml
│ ├── manual_prod_build.yml
│ ├── mirror.yml
│ ├── prod_build.yml
│ ├── prod_linter.yml
│ ├── tests.yml
│ └── update-supabase-js.yml
├── .gitignore
├── .releaserc
├── .sobelow-conf
├── .tool-versions
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── assets/
│ ├── css/
│ │ ├── app.css
│ │ └── phoenix.css
│ ├── js/
│ │ └── app.js
│ ├── package.json
│ ├── tailwind.config.js
│ └── vendor/
│ └── topbar.js
├── beacon/
│ ├── .formatter.exs
│ ├── .gitignore
│ ├── README.md
│ ├── config/
│ │ └── config.exs
│ ├── lib/
│ │ ├── beacon/
│ │ │ ├── adapter/
│ │ │ │ └── erl_dist.ex
│ │ │ ├── adapter.ex
│ │ │ ├── partition.ex
│ │ │ ├── scope.ex
│ │ │ └── supervisor.ex
│ │ └── beacon.ex
│ ├── mix.exs
│ └── test/
│ ├── beacon/
│ │ └── partition_test.exs
│ ├── beacon_test.exs
│ ├── support/
│ │ └── peer.ex
│ └── test_helper.exs
├── bench/
│ ├── gen_counter.exs
│ └── secrets.exs
├── config/
│ ├── config.exs
│ ├── dev.exs
│ ├── prod.exs
│ ├── runtime.exs
│ └── test.exs
├── coveralls.json
├── deploy/
│ └── fly/
│ ├── prod.toml
│ ├── qa.toml
│ └── staging.toml
├── dev/
│ └── postgres/
│ └── 00-supabase-schema.sql
├── docker-compose.dbs.yml
├── docker-compose.tests.yml
├── docker-compose.yml
├── lib/
│ ├── extensions/
│ │ ├── extensions.ex
│ │ └── postgres_cdc_rls/
│ │ ├── cdc_rls.ex
│ │ ├── db_settings.ex
│ │ ├── message_dispatcher.ex
│ │ ├── replication_poller.ex
│ │ ├── replications.ex
│ │ ├── subscription_manager.ex
│ │ ├── subscriptions.ex
│ │ ├── subscriptions_checker.ex
│ │ ├── supervisor.ex
│ │ └── worker_supervisor.ex
│ ├── realtime/
│ │ ├── adapters/
│ │ │ ├── changes.ex
│ │ │ └── postgres/
│ │ │ ├── decoder.ex
│ │ │ ├── oid_database.ex
│ │ │ ├── protocol/
│ │ │ │ ├── keep_alive.ex
│ │ │ │ └── write.ex
│ │ │ └── protocol.ex
│ │ ├── api/
│ │ │ ├── extensions.ex
│ │ │ ├── message.ex
│ │ │ └── tenant.ex
│ │ ├── api.ex
│ │ ├── application.ex
│ │ ├── beacon_pub_sub_adapter.ex
│ │ ├── crypto.ex
│ │ ├── database.ex
│ │ ├── gen_counter/
│ │ │ └── gen_counter.ex
│ │ ├── gen_rpc/
│ │ │ └── pub_sub.ex
│ │ ├── gen_rpc.ex
│ │ ├── helpers.ex
│ │ ├── log_filter.ex
│ │ ├── logs.ex
│ │ ├── messages.ex
│ │ ├── metrics_cleaner.ex
│ │ ├── metrics_pusher.ex
│ │ ├── monitoring/
│ │ │ ├── distributed_metrics.ex
│ │ │ ├── erl_sys_mon.ex
│ │ │ ├── gen_rpc_metrics.ex
│ │ │ ├── latency.ex
│ │ │ ├── os_metrics.ex
│ │ │ ├── peep/
│ │ │ │ └── partitioned.ex
│ │ │ ├── prom_ex/
│ │ │ │ └── plugins/
│ │ │ │ ├── channels.ex
│ │ │ │ ├── distributed.ex
│ │ │ │ ├── gen_rpc.ex
│ │ │ │ ├── osmon.ex
│ │ │ │ ├── phoenix.ex
│ │ │ │ ├── tenant.ex
│ │ │ │ ├── tenant_global.ex
│ │ │ │ └── tenants.ex
│ │ │ ├── prom_ex.ex
│ │ │ ├── prometheus.ex
│ │ │ └── tenant_prom_ex.ex
│ │ ├── nodes.ex
│ │ ├── operations.ex
│ │ ├── postgres_cdc.ex
│ │ ├── rate_counter/
│ │ │ ├── dynamic_supervisor.ex
│ │ │ └── rate_counter.ex
│ │ ├── release.ex
│ │ ├── repo.ex
│ │ ├── repo_replica.ex
│ │ ├── rpc.ex
│ │ ├── signal_handler.ex
│ │ ├── syn/
│ │ │ └── postgres_cdc.ex
│ │ ├── syn_handler.ex
│ │ ├── telemetry/
│ │ │ ├── logger.ex
│ │ │ └── telemetry.ex
│ │ ├── tenants/
│ │ │ ├── authorization/
│ │ │ │ ├── policies/
│ │ │ │ │ ├── broadcast_policies.ex
│ │ │ │ │ └── presence_policies.ex
│ │ │ │ └── policies.ex
│ │ │ ├── authorization.ex
│ │ │ ├── batch_broadcast.ex
│ │ │ ├── cache.ex
│ │ │ ├── connect/
│ │ │ │ ├── check_connection.ex
│ │ │ │ ├── get_tenant.ex
│ │ │ │ ├── piper.ex
│ │ │ │ ├── reconcile_migrations.ex
│ │ │ │ └── register_process.ex
│ │ │ ├── connect.ex
│ │ │ ├── janitor/
│ │ │ │ └── maintenance_task.ex
│ │ │ ├── janitor.ex
│ │ │ ├── migrations.ex
│ │ │ ├── rebalancer.ex
│ │ │ ├── replication_connection/
│ │ │ │ └── watchdog.ex
│ │ │ ├── replication_connection.ex
│ │ │ ├── repo/
│ │ │ │ └── migrations/
│ │ │ │ ├── 20211116024918_create_realtime_subscription_table.ex
│ │ │ │ ├── 20211116045059_create_realtime_check_filters_trigger.ex
│ │ │ │ ├── 20211116050929_create_realtime_quote_wal2json_function.ex
│ │ │ │ ├── 20211116051442_create_realtime_check_equality_op_function.ex
│ │ │ │ ├── 20211116212300_create_realtime_build_prepared_statement_sql_function.ex
│ │ │ │ ├── 20211116213355_create_realtime_cast_function.ex
│ │ │ │ ├── 20211116213934_create_realtime_is_visible_through_filters_function.ex
│ │ │ │ ├── 20211116214523_create_realtime_apply_rls_function.ex
│ │ │ │ ├── 20211122062447_grant_realtime_usage_to_authenticated_role.ex
│ │ │ │ ├── 20211124070109_enable_realtime_apply_rls_function_postgrest_9_compatibility.ex
│ │ │ │ ├── 20211202204204_update_realtime_subscription_check_filters_function_security.ex
│ │ │ │ ├── 20211202204605_update_realtime_build_prepared_statement_sql_function_for_compatibility_with_all_types.ex
│ │ │ │ ├── 20211210212804_enable_generic_subscription_claims.ex
│ │ │ │ ├── 20211228014915_add_wal_payload_on_errors_in_apply_rls_function.ex
│ │ │ │ ├── 20220107221237_update_change_timestamp_to_iso_8601_zulu_format.ex
│ │ │ │ ├── 20220228202821_update_subscription_check_filters_function_dynamic_table_name.ex
│ │ │ │ ├── 20220312004840_update_apply_rls_function_to_apply_iso_8601.ex
│ │ │ │ ├── 20220603231003_add_quoted_regtypes_support.ex
│ │ │ │ ├── 20220603232444_add_output_for_data_less_than_equal_64_bytes_when_payload_too_large.ex
│ │ │ │ ├── 20220615214548_add_quoted_regtypes_backward_compatibility_support.ex
│ │ │ │ ├── 20220712093339_recreate_realtime_build_prepared_statement_sql_function.ex
│ │ │ │ ├── 20220908172859_null_passes_filters_recreate_is_visible_through_filters.ex
│ │ │ │ ├── 20220916233421_update_apply_rls_function_to_pass_through_delete_events_on_filter.ex
│ │ │ │ ├── 20230119133233_millisecond_precision_for_walrus.ex
│ │ │ │ ├── 20230128025114_add_in_op_to_filters.ex
│ │ │ │ ├── 20230128025212_enable_filtering_on_delete_record.ex
│ │ │ │ ├── 20230227211149_update_subscription_check_filters_for_in_filter_non_text_types.ex
│ │ │ │ ├── 20230228184745_convert_commit_timestamp_to_utc.ex
│ │ │ │ ├── 20230308225145_output_full_record_when_unchanged_toast.ex
│ │ │ │ ├── 20230328144023_create_list_changes_function.ex
│ │ │ │ ├── 20231018144023_create_channels.ex
│ │ │ │ ├── 20231204144023_set_required_grants.ex
│ │ │ │ ├── 20231204144024_create_rls_helper_functions.ex
│ │ │ │ ├── 20231204144025_enable_channels_rls.ex
│ │ │ │ ├── 20240108234812_add_channels_column_for_write_check.ex
│ │ │ │ ├── 20240109165339_add_update_grant_to_channels.ex
│ │ │ │ ├── 20240227174441_add_broadcast_permissions_table.ex
│ │ │ │ ├── 20240311171622_add_insert_and_delete_grant_to_channels.ex
│ │ │ │ ├── 20240321100241_add_presences_permissions_table.ex
│ │ │ │ ├── 20240401105812_create_realtime_admin_and_move_ownership.ex
│ │ │ │ ├── 20240418121054_remove_check_columns.ex
│ │ │ │ ├── 20240523004032_redefine_authorization_tables.ex
│ │ │ │ ├── 20240618124746_fix_walrus_role_handling.ex
│ │ │ │ ├── 20240801235015_unlogged_messages_table.ex
│ │ │ │ ├── 20240805133720_logged_messages_table.ex
│ │ │ │ ├── 20240827160934_filter_delete_postgres_changes.ex
│ │ │ │ ├── 20240919163303_add_payload_to_messages.ex
│ │ │ │ ├── 20240919163305_change_messages_id_type.ex
│ │ │ │ ├── 20241019105805_uuid_auto_generation.ex
│ │ │ │ ├── 20241030150047_messages_partitioning.ex
│ │ │ │ ├── 20241108114728_messages_using_uuid.ex
│ │ │ │ ├── 20241121104152_fix_send_function_.ex
│ │ │ │ ├── 20241130184212_recreate_entity_index_using_btree.ex
│ │ │ │ ├── 20241220035512_fix_send_function_partition_creation.ex
│ │ │ │ ├── 20241220123912_realtime_send_handle_exceptions_remove_partition_creation.ex
│ │ │ │ ├── 20241224161212_realtime_send_sets_config.ex
│ │ │ │ ├── 20250107150512_realtime_subscription_unlogged.ex
│ │ │ │ ├── 20250110162412_realtime_subscription_logged.ex
│ │ │ │ ├── 20250123174212_remove_unused_publications.ex
│ │ │ │ ├── 20250128220012_realtime_send_sets_topic_config.ex
│ │ │ │ ├── 20250506224012_subscription_index_bridging_disabled.ex
│ │ │ │ ├── 20250523164012_run_subscription_index_bridging_disabled.ex
│ │ │ │ ├── 20250714121412_broadcast_send_error_logging.ex
│ │ │ │ ├── 20250905041441_create_messages_replay_index.ex
│ │ │ │ ├── 20251103001201_broadcast_send_include_payload_id.ex
│ │ │ │ ├── 20251120212548_add_action_to_subscriptions.ex
│ │ │ │ ├── 20251120215549_filter_action_postgres_changes.ex
│ │ │ │ └── 20260218120000_fix_bytea_double_encoding_in_cast.ex
│ │ │ └── repo.ex
│ │ ├── tenants.ex
│ │ └── users_counter.ex
│ ├── realtime.ex
│ ├── realtime_web/
│ │ ├── api_spec.ex
│ │ ├── channels/
│ │ │ ├── auth/
│ │ │ │ ├── channels_authorization.ex
│ │ │ │ └── jwt_verification.ex
│ │ │ ├── payloads/
│ │ │ │ ├── broadcast/
│ │ │ │ │ └── replay.ex
│ │ │ │ ├── broadcast.ex
│ │ │ │ ├── config.ex
│ │ │ │ ├── flexible_boolean.ex
│ │ │ │ ├── join.ex
│ │ │ │ ├── postgres_change.ex
│ │ │ │ └── presence.ex
│ │ │ ├── presence.ex
│ │ │ ├── realtime_channel/
│ │ │ │ ├── assign.ex
│ │ │ │ ├── broadcast_handler.ex
│ │ │ │ ├── logging.ex
│ │ │ │ ├── message_dispatcher.ex
│ │ │ │ ├── presence_handler.ex
│ │ │ │ └── tracker.ex
│ │ │ ├── realtime_channel.ex
│ │ │ ├── socket_disconnect.ex
│ │ │ ├── tenant_rate_limiters.ex
│ │ │ └── user_socket.ex
│ │ ├── controllers/
│ │ │ ├── broadcast_controller.ex
│ │ │ ├── fallback_controller.ex
│ │ │ ├── legacy_metrics_controller.ex
│ │ │ ├── metrics_controller.ex
│ │ │ ├── page_controller.ex
│ │ │ ├── ping_controller.ex
│ │ │ └── tenant_controller.ex
│ │ ├── dashboard/
│ │ │ ├── process_dump.ex
│ │ │ └── tenant_info.ex
│ │ ├── endpoint.ex
│ │ ├── gettext.ex
│ │ ├── live/
│ │ │ ├── components.ex
│ │ │ ├── inspector_live/
│ │ │ │ ├── conn_component.ex
│ │ │ │ ├── conn_component.html.heex
│ │ │ │ ├── index.ex
│ │ │ │ └── index.html.heex
│ │ │ ├── page_live/
│ │ │ │ ├── index.ex
│ │ │ │ └── index.html.heex
│ │ │ ├── ping_live.ex
│ │ │ ├── status_live/
│ │ │ │ ├── index.ex
│ │ │ │ └── index.html.heex
│ │ │ ├── tenants_live/
│ │ │ │ ├── index.ex
│ │ │ │ └── index.html.heex
│ │ │ └── time_live.ex
│ │ ├── open_api_schemas.ex
│ │ ├── plugs/
│ │ │ ├── assign_tenant.ex
│ │ │ ├── auth_tenant.ex
│ │ │ ├── baggage_request_id.ex
│ │ │ ├── metrics_mode.ex
│ │ │ └── rate_limiter.ex
│ │ ├── router.ex
│ │ ├── socket/
│ │ │ ├── user_broadcast.ex
│ │ │ └── v2_serializer.ex
│ │ ├── telemetry.ex
│ │ ├── templates/
│ │ │ └── layout/
│ │ │ ├── app.html.heex
│ │ │ ├── live.html.heex
│ │ │ └── root.html.heex
│ │ ├── tenant_broadcaster.ex
│ │ └── views/
│ │ ├── changeset_view.ex
│ │ ├── error_helpers.ex
│ │ ├── error_view.ex
│ │ ├── layout_view.ex
│ │ └── tenant_view.ex
│ └── realtime_web.ex
├── mix.exs
├── phx_join.schema.json
├── priv/
│ ├── gettext/
│ │ ├── en/
│ │ │ └── LC_MESSAGES/
│ │ │ └── errors.po
│ │ └── errors.pot
│ ├── repo/
│ │ ├── dev_seeds.exs
│ │ ├── migrations/
│ │ │ ├── .formatter.exs
│ │ │ ├── 20210706140551_create_tenant.exs
│ │ │ ├── 20220329161857_add_extensions_table.exs
│ │ │ ├── 20220410212326_add_tenant_max_eps.exs
│ │ │ ├── 20220506102948_rename_poll_interval_to_poll_interval_ms.exs
│ │ │ ├── 20220527210857_add_external_id_uniq_index.exs
│ │ │ ├── 20220815211129_new_max_events_per_second_default.exs
│ │ │ ├── 20220815215024_set_current_max_events_per_second.exs
│ │ │ ├── 20220818141501_change_limits_defaults.exs
│ │ │ ├── 20221018173709_add_cdc_default.exs
│ │ │ ├── 20221102172703_rename_pg_type.exs
│ │ │ ├── 20221223010058_drop_tenants_uniq_external_id_index.exs
│ │ │ ├── 20230110180046_add_limits_fields_to_tenants.exs
│ │ │ ├── 20230810220907_alter_tenants_table_columns_to_text.exs
│ │ │ ├── 20230810220924_alter_extensions_table_columns_to_text.exs
│ │ │ ├── 20231024094642_add_tenant_suspend_flag.exs
│ │ │ ├── 20240306114423_add_tenant_jwt_jwks.exs
│ │ │ ├── 20240418082835_add_authorization_flag.exs
│ │ │ ├── 20240625211759_remove_enable_authorization_flag.exs
│ │ │ ├── 20240704172020_add_notify_private_alpha.exs
│ │ │ ├── 20240902173232_add_extension_external_id_index.exs
│ │ │ ├── 20241106103258_add_private_only_flag_column_to_tenant.exs
│ │ │ ├── 20250424203323_add_migrations_ran_to_tenant.exs
│ │ │ ├── 20250613072131_add_tenant_broadcast_adapter.exs
│ │ │ ├── 20250711044927_change_default_broadcast_adapter_to_gen_rpc.exs
│ │ │ ├── 20250811121559_add_max_presence_events_per_second.exs
│ │ │ ├── 20250926223044_set_default_presence_value.exs
│ │ │ ├── 20251204170944_nullable_jwt_secrets.exs
│ │ │ ├── 20251218000543_ensure_jwt_secret_is_text.exs
│ │ │ ├── 20260209232800_add_max_client_presence_events_per_second.exs
│ │ │ └── 20260304000000_add_presence_enabled_to_tenants.exs
│ │ ├── seeds.exs
│ │ └── seeds_before_migration.exs
│ └── static/
│ ├── robots.txt
│ └── worker.js
├── rel/
│ ├── env.bat.eex
│ ├── env.sh.eex
│ ├── overlays/
│ │ ├── bin/
│ │ │ ├── migrate
│ │ │ ├── migrate.bat
│ │ │ ├── server
│ │ │ └── server.bat
│ │ └── config.example.yml
│ └── vm.args.eex
├── run.sh
└── test/
├── api_jwt_secret_test.exs
├── e2e/
│ ├── .gitignore
│ ├── .template.env
│ ├── .tool-versions
│ ├── README.md
│ ├── flake.nix
│ ├── legacy/
│ │ ├── .tool-versions
│ │ ├── README.md
│ │ └── tests.ts
│ ├── package.json
│ ├── realtime-check.ts
│ └── supabase/
│ ├── .branches/
│ │ └── _current_branch
│ └── .temp/
│ └── cli-latest
├── extensions/
│ ├── extensions_test.exs
│ └── postgres_cdc_rls/
│ ├── db_settings_test.exs
│ ├── message_dispatcher_test.exs
│ ├── replications_test.exs
│ └── worker_supervisor_test.exs
├── integration/
│ ├── distributed_realtime_channel_test.exs
│ ├── measure_traffic_test.exs
│ ├── region_aware_migrations_test.exs
│ ├── region_aware_routing_test.exs
│ ├── rt_channel/
│ │ ├── authorization_test.exs
│ │ ├── billable_events_test.exs
│ │ ├── broadcast_test.exs
│ │ ├── connection_lifecycle_test.exs
│ │ ├── postgres_changes_test.exs
│ │ ├── presence_test.exs
│ │ ├── token_handling_test.exs
│ │ └── wal_bloat_test.exs
│ ├── tests.ts
│ └── tracker_test.exs
├── realtime/
│ ├── adapters/
│ │ └── postgres/
│ │ └── protocol_test.exs
│ ├── api/
│ │ └── extensions_test.exs
│ ├── api_test.exs
│ ├── database_distributed_test.exs
│ ├── database_test.exs
│ ├── extensions/
│ │ └── cdc_rls/
│ │ ├── cdc_rls_test.exs
│ │ ├── replication_poller_test.exs
│ │ ├── replications_test.exs
│ │ ├── subscription_manager_test.exs
│ │ ├── subscriptions_checker_distributed_test.exs
│ │ ├── subscriptions_checker_test.exs
│ │ └── subscriptions_test.exs
│ ├── gen_counter/
│ │ └── gen_counter_test.exs
│ ├── gen_rpc_pub_sub/
│ │ └── worker_test.exs
│ ├── gen_rpc_pub_sub_test.exs
│ ├── gen_rpc_test.exs
│ ├── helpers_test.exs
│ ├── log_filter_test.exs
│ ├── logs_test.exs
│ ├── messages_test.exs
│ ├── metrics_cleaner_test.exs
│ ├── metrics_pusher_test.exs
│ ├── monitoring/
│ │ ├── distributed_metrics_test.exs
│ │ ├── erl_sys_mon_test.exs
│ │ ├── gen_rpc_metrics_test.exs
│ │ ├── latency_test.exs
│ │ ├── peep/
│ │ │ └── partitioned_test.exs
│ │ ├── prom_ex/
│ │ │ └── plugins/
│ │ │ ├── distributed_test.exs
│ │ │ ├── gen_rpc_test.exs
│ │ │ ├── phoenix_test.exs
│ │ │ ├── tenant_test.exs
│ │ │ └── tenants_test.exs
│ │ ├── prom_ex_test.exs
│ │ └── prometheus_test.exs
│ ├── nodes_test.exs
│ ├── oid_test.exs
│ ├── postgres_decoder_test.exs
│ ├── rate_counter/
│ │ └── rate_counter_test.exs
│ ├── repo_replica_test.exs
│ ├── rpc_test.exs
│ ├── signal_handler_test.exs
│ ├── syn_handler_test.exs
│ ├── telemetry/
│ │ └── logger_test.exs
│ ├── tenants/
│ │ ├── authorization_remote_test.exs
│ │ ├── authorization_test.exs
│ │ ├── batch_broadcast_test.exs
│ │ ├── cache_test.exs
│ │ ├── connect/
│ │ │ ├── get_tenant_test.exs
│ │ │ ├── piper_test.exs
│ │ │ ├── reconcile_migrations_test.exs
│ │ │ └── register_process_test.exs
│ │ ├── connect_test.exs
│ │ ├── janitor/
│ │ │ └── maintenance_task_test.exs
│ │ ├── janitor_test.exs
│ │ ├── migrations_test.exs
│ │ ├── rebalancer_test.exs
│ │ ├── replication_connection/
│ │ │ └── watchdog_test.exs
│ │ ├── replication_connection_test.exs
│ │ └── repo_test.exs
│ ├── tenants_test.exs
│ └── users_counter_test.exs
├── realtime_web/
│ ├── channels/
│ │ ├── auth/
│ │ │ ├── channels_authorization_test.exs
│ │ │ └── jwt_verification_test.exs
│ │ ├── payloads/
│ │ │ ├── flexible_boolean_test.exs
│ │ │ └── join_test.exs
│ │ ├── realtime_channel/
│ │ │ ├── broadcast_handler_test.exs
│ │ │ ├── logging_test.exs
│ │ │ ├── message_dispatcher_test.exs
│ │ │ ├── presence_handler_test.exs
│ │ │ └── tracker_test.exs
│ │ ├── realtime_channel_test.exs
│ │ ├── socket_disconnect_test.exs
│ │ ├── tenant_rate_limiters_test.exs
│ │ └── user_socket_test.exs
│ ├── controllers/
│ │ ├── broadcast_controller_test.exs
│ │ ├── fallback_controller_test.exs
│ │ ├── legacy_metrics_controller_test.exs
│ │ ├── live_dasboard_test.exs
│ │ ├── metrics_controller_test.exs
│ │ ├── openapi_controller_test.exs
│ │ ├── page_controller_test.exs
│ │ └── tenant_controller_test.exs
│ ├── dashboard/
│ │ └── tenant_info_test.exs
│ ├── integration/
│ │ └── tracing_test.exs
│ ├── live/
│ │ ├── inspector_live/
│ │ │ └── index_test.exs
│ │ ├── page_live/
│ │ │ └── index_test.exs
│ │ ├── status_live/
│ │ │ └── index_test.exs
│ │ └── tenants_live/
│ │ └── index_test.exs
│ ├── plugs/
│ │ ├── assign_tenant_test.exs
│ │ ├── auth_tenant_test.exs
│ │ ├── baggage_request_id_test.exs
│ │ ├── metrics_mode_test.exs
│ │ └── rate_limiter_test.exs
│ ├── socket/
│ │ └── v2_serializer_test.exs
│ ├── tenant_broadcaster_test.exs
│ └── views/
│ ├── error_view_test.exs
│ ├── layout_view_test.exs
│ └── page_view_test.exs
├── support/
│ ├── channel_case.ex
│ ├── cleanup.ex
│ ├── clustered.ex
│ ├── conn_case.ex
│ ├── containers/
│ │ └── container.ex
│ ├── containers.ex
│ ├── data_case.ex
│ ├── generators.ex
│ ├── integrations.ex
│ ├── joken_current_time_mock.ex
│ ├── metrics_helper.ex
│ ├── prometheus_fixtures.ex
│ ├── rate_counter_helper.ex
│ ├── replication_test_handler.ex
│ ├── tenant_connection.ex
│ ├── tracing.ex
│ └── websocket_client.ex
└── test_helper.exs
SYMBOL INDEX (1913 symbols across 387 files)
FILE: assets/js/app.js
method initRealtime (line 13) | initRealtime(
method sendRealtime (line 198) | sendRealtime(event, payload) {
method disconnectRealtime (line 209) | disconnectRealtime() {
method clearLocalStorage (line 216) | clearLocalStorage() {
method mounted (line 220) | mounted() {
method mounted (line 264) | mounted() {
method pong (line 268) | pong(params) {
FILE: beacon/lib/beacon.ex
class Beacon (line 1) | defmodule Beacon
method member_count (line 79) | def member_count(scope, group) do
method member_count (line 86) | def member_count(scope, group, node), do: Scope.member_count(scope, gr...
FILE: beacon/lib/beacon/adapter.ex
class Beacon.Adapter (line 1) | defmodule Beacon.Adapter
FILE: beacon/lib/beacon/adapter/erl_dist.ex
class Beacon.Adapter.ErlDist (line 1) | defmodule Beacon.Adapter.ErlDist
method register (line 9) | def register(scope) do
method broadcast (line 15) | def broadcast(scope, message) do
method broadcast (line 21) | def broadcast(scope, nodes, message) do
method send (line 27) | def send(scope, node, message) do
FILE: beacon/lib/beacon/partition.ex
class Beacon.Partition (line 1) | defmodule Beacon.Partition
method join (line 19) | def join(partition_name, group, pid), do: GenServer.call(partition_nam...
method leave (line 22) | def leave(partition_name, group, pid), do: GenServer.call(partition_na...
method members (line 25) | def members(partition_name, group) do
method member_count (line 32) | def member_count(partition_name, group), do: :ets.lookup_element(parti...
method member_counts (line 35) | def member_counts(partition_name) do
method member? (line 42) | def member?(partition_name, group, pid) do
method groups (line 53) | def groups(partition_name), do: :ets.select(partition_name, [{{:"$1", ...
method group_count (line 56) | def group_count(partition_name), do: :ets.info(partition_name, :size)
method start_link (line 59) | def start_link(scope, partition_name, partition_entries_table),
method init (line 67) | def init([scope, name, entries_table]) do
method handle_continue (line 74) | def handle_continue(:rebuild_monitors_and_counters, state) do
method handle_call (line 92) | def handle_call({:join, group, pid}, _from, state) do
method handle_call (line 111) | def handle_call({:leave, group, pid}, _from, state) do
method handle_info (line 119) | def handle_info({{:DOWN, group}, _ref, :process, pid, _reason}, state) do
method handle_info (line 124) | def handle_info(_, state), do: {:noreply, state}
method remove (line 126) | defp remove(group, pid, state) do
class State (line 7) | defmodule State
FILE: beacon/lib/beacon/scope.ex
class Beacon.Scope (line 1) | defmodule Beacon.Scope
method member_counts (line 11) | def member_counts(scope) do
method member_count (line 21) | def member_count(scope, group) do
method member_count (line 29) | def member_count(scope, group, node) do
method groups (line 37) | def groups(scope) do
method table_name (line 51) | defp table_name(scope), do: :"#{scope}_beacon_peer_counts"
method start_link (line 72) | def start_link(scope, opts \\ []), do: GenServer.start_link(__MODULE__...
method init (line 75) | def init([scope, opts]) do
method handle_continue (line 101) | def handle_continue(:discover, state) do
method handle_info (line 118) | def handle_info({:discover, peer}, state) do
method handle_info (line 149) | def handle_info({:sync, peer, member_counts}, state) do
method handle_info (line 155) | def handle_info(:broadcast_counts, state) do
method handle_info (line 175) | def handle_info({:nodeup, node}, state) do
method handle_info (line 187) | def handle_info({:nodedown, _node}, state), do: {:noreply, state}
method handle_info (line 191) | def handle_info({:DOWN, ref, :process, peer, reason}, state) do
method handle_info (line 207) | def handle_info(_msg, state), do: {:noreply, state}
class State (line 53) | defmodule State
FILE: beacon/lib/beacon/supervisor.ex
class Beacon.Supervisor (line 1) | defmodule Beacon.Supervisor
method name (line 5) | def name(scope), do: :"#{scope}_beacon"
method supervisor_name (line 6) | def supervisor_name(scope), do: :"#{scope}_beacon_supervisor"
method partition_name (line 7) | def partition_name(scope, partition), do: :"#{scope}_beacon_partition_...
method partition_entries_table (line 8) | def partition_entries_table(partition_name), do: :"#{partition_name}_e...
method partition (line 11) | def partition(scope, group) do
method partitions (line 19) | def partitions(scope) do
method start_link (line 27) | def start_link(scope, partitions, opts \\ []) do
method init (line 33) | def init([scope, partitions, opts]) do
FILE: beacon/mix.exs
class Beacon.MixProject (line 1) | defmodule Beacon.MixProject
method project (line 4) | def project do
method application (line 16) | def application do
method elixirc_paths (line 23) | defp elixirc_paths(:test), do: ["lib", "test/support"]
method elixirc_paths (line 24) | defp elixirc_paths(_), do: ["lib"]
method deps (line 27) | defp deps do
FILE: beacon/test/beacon/partition_test.exs
class Beacon.PartitionTest (line 1) | defmodule Beacon.PartitionTest
FILE: beacon/test/beacon_test.exs
class BeaconTest (line 1) | defmodule BeaconTest
method spec (line 10) | defp spec(scope, opts) do
FILE: beacon/test/support/peer.ex
class Peer (line 1) | defmodule Peer
method start (line 27) | def start(opts \\ []) do
method start_disconnected (line 39) | def start_disconnected(opts \\ []) do
FILE: config/runtime.exs
class Env (line 3) | defmodule Env
method get_integer (line 4) | def get_integer(env, default) do
method get_charlist (line 9) | def get_charlist(env, default) do
method get_boolean (line 14) | def get_boolean(env, default) do
FILE: lib/extensions/extensions.ex
class Realtime.Extensions (line 1) | defmodule Realtime.Extensions
method db_settings (line 5) | def db_settings(type) do
FILE: lib/extensions/postgres_cdc_rls/cdc_rls.ex
class Extensions.PostgresCdcRls (line 1) | defmodule Extensions.PostgresCdcRls
method handle_connect (line 17) | def handle_connect(args) do
method handle_after_connect (line 32) | def handle_after_connect({manager_pid, conn}, settings, params_list, t...
method create_subscription (line 60) | def create_subscription(conn, tenant, publication, pool_size, subscrip...
method with_rate_counter (line 75) | defp with_rate_counter(tenant, pool_size, fun) do
method rate_counter (line 88) | defp rate_counter(tenant_id, pool_size) do
method subscription_list (line 95) | defp subscription_list(params_list) do
method handle_subscribe (line 108) | def handle_subscribe(_, tenant, metadata) do
method start_distributed (line 135) | def start_distributed(%{"region" => region, "id" => tenant} = args) do
method get_manager_conn (line 176) | def get_manager_conn(id) do
method supervisor_id (line 187) | def supervisor_id(tenant, region) do
method update_meta (line 193) | def update_meta(tenant, manager_pid, subs_pool) do
FILE: lib/extensions/postgres_cdc_rls/db_settings.ex
class Extensions.PostgresCdcRls.DbSettings (line 1) | defmodule Extensions.PostgresCdcRls.DbSettings
method default (line 6) | def default do
method required (line 16) | def required do
FILE: lib/extensions/postgres_cdc_rls/message_dispatcher.ex
class Extensions.PostgresCdcRls.MessageDispatcher (line 4) | defmodule Extensions.PostgresCdcRls.MessageDispatcher
method dispatch (line 11) | def dispatch([_ | _] = topic_subscriptions, _from, {type, payload, sub...
method broadcast_message (line 46) | defp broadcast_message(cache, fastlane_pid, msg, serializer) do
FILE: lib/extensions/postgres_cdc_rls/replication_poller.ex
class Extensions.PostgresCdcRls.ReplicationPoller (line 1) | defmodule Extensions.PostgresCdcRls.ReplicationPoller
method start_link (line 26) | def start_link(opts), do: GenServer.start_link(__MODULE__, opts)
method init (line 29) | def init(args) do
method handle_continue (line 60) | def handle_continue({:connect, tenant}, state) do
method handle_continue (line 72) | def handle_continue(:prepare, state) do
method handle_info (line 77) | def handle_info(
method handle_info (line 153) | def handle_info(:retry, %{retry_ref: retry_ref} = state) do
method slot_name_suffix (line 158) | def slot_name_suffix do
method convert_errors (line 165) | defp convert_errors([_ | _] = errors), do: errors
method convert_errors (line 167) | defp convert_errors(_), do: nil
method prepare_replication (line 169) | defp prepare_replication(%{backoff: backoff, conn: conn, slot_name: sl...
method record_list_changes_telemetry (line 184) | defp record_list_changes_telemetry(time, tenant_id) do
method handle_list_changes_result (line 192) | defp handle_list_changes_result(
method handle_list_changes_result (line 248) | defp handle_list_changes_result({:ok, _}, _, _, _), do: {:ok, 0}
method handle_list_changes_result (line 249) | defp handle_list_changes_result({:error, reason}, _, _, _), do: {:erro...
method collect_subscription_nodes (line 251) | defp collect_subscription_nodes(subscribers_nodes_table, subscription_...
method generate_record (line 341) | def generate_record(_), do: nil
FILE: lib/extensions/postgres_cdc_rls/replications.ex
class Extensions.PostgresCdcRls.Replications (line 1) | defmodule Extensions.PostgresCdcRls.Replications
method prepare_replication (line 10) | def prepare_replication(conn, slot_name) do
method terminate_backend (line 30) | def terminate_backend(conn, slot_name) do
method get_pg_stat_activity_diff (line 54) | def get_pg_stat_activity_diff(conn, db_pid) do
method list_changes (line 73) | def list_changes(conn, slot_name, publication, max_changes, max_record...
FILE: lib/extensions/postgres_cdc_rls/subscription_manager.ex
class Extensions.PostgresCdcRls.SubscriptionManager (line 1) | defmodule Extensions.PostgresCdcRls.SubscriptionManager
method start_link (line 57) | def start_link(opts) do
method init (line 64) | def init(args) do
method handle_continue (line 71) | def handle_continue({:connect, args}, _) do
method handle_info (line 123) | def handle_info({:subscribed, {pid, id}}, state) do
method handle_info (line 134) | def handle_info(
method handle_info (line 162) | def handle_info(
method handle_info (line 189) | def handle_info(:check_delete_queue, %State{delete_queue: %{ref: ref, ...
method handle_info (line 215) | def handle_info(:check_no_users, %{subscribers_pids_table: tid, no_use...
method handle_info (line 235) | def handle_info({:check_region, previous_nodes_set}, state) do
method handle_info (line 251) | def handle_info(msg, state) do
method check_oids (line 259) | defp check_oids, do: Process.send_after(self(), :check_oids, @check_oi...
method now (line 261) | defp now, do: System.system_time(:millisecond)
method check_no_users (line 263) | defp check_no_users, do: Process.send_after(self(), :check_no_users, @...
method check_delete_queue (line 265) | defp check_delete_queue(timeout \\ @timeout),
method send_region_check_message (line 268) | defp send_region_check_message(check_region_interval) do
method rebalance_check_interval_in_ms (line 272) | defp rebalance_check_interval_in_ms(), do: Application.fetch_env!(:rea...
class State (line 22) | defmodule State
FILE: lib/extensions/postgres_cdc_rls/subscriptions.ex
class Extensions.PostgresCdcRls.Subscriptions (line 1) | defmodule Extensions.PostgresCdcRls.Subscriptions
method create (line 20) | def create(conn, publication, subscription_list, manager, caller) do
method query (line 48) | defp query(conn, publication, id, claims, subscription_params) do
method params_to_log (line 89) | defp params_to_log({action_filter, schema, table, filters}) do
method delete (line 95) | def delete(conn, id) do
method delete_all (line 114) | def delete_all(conn) do
method delete_multi (line 127) | def delete_multi(conn, ids) do
method delete_all_if_table_exists (line 134) | def delete_all_if_table_exists(conn) do
method fetch_publication_tables (line 165) | def fetch_publication_tables(conn, publication) do
method parse_subscription_params (line 239) | def parse_subscription_params(params) do
method action_filter (line 280) | defp action_filter(%{"event" => "*"}), do: "*"
method action_filter (line 291) | defp action_filter(_), do: "*"
method format_filter_value (line 293) | defp format_filter_value(filter, value) do
FILE: lib/extensions/postgres_cdc_rls/subscriptions_checker.ex
class Extensions.PostgresCdcRls.SubscriptionsChecker (line 1) | defmodule Extensions.PostgresCdcRls.SubscriptionsChecker
method start_link (line 36) | def start_link(opts) do
method init (line 43) | def init(args) do
method handle_continue (line 50) | def handle_continue({:connect, args}, _) do
method handle_info (line 80) | def handle_info(:check_active_pids, %State{check_active_pids: ref, del...
method handle_info (line 107) | def handle_info(:check_delete_queue, %State{delete_queue: %{ref: ref, ...
method pop_not_alive_pids (line 136) | def pop_not_alive_pids(pids, subscribers_pids_table, subscribers_nodes...
method subscribers_by_node (line 167) | def subscribers_by_node(tid) do
method not_alive_pids_dist (line 177) | def not_alive_pids_dist(pids) do
method not_alive_pids (line 195) | def not_alive_pids(pids) do
method check_delete_queue (line 199) | defp check_delete_queue, do: Process.send_after(self(), :check_delete_...
method check_active_pids (line 201) | defp check_active_pids, do: Process.send_after(self(), :check_active_p...
class State (line 18) | defmodule State
FILE: lib/extensions/postgres_cdc_rls/supervisor.ex
class Extensions.PostgresCdcRls.Supervisor (line 1) | defmodule Extensions.PostgresCdcRls.Supervisor
method start_link (line 10) | def start_link do
method init (line 15) | def init(_args) do
method load_migrations_modules (line 30) | defp load_migrations_modules do
FILE: lib/extensions/postgres_cdc_rls/worker_supervisor.ex
class Extensions.PostgresCdcRls.WorkerSupervisor (line 1) | defmodule Extensions.PostgresCdcRls.WorkerSupervisor
method start_link (line 12) | def start_link(args) do
FILE: lib/realtime.ex
class Realtime (line 1) | defmodule Realtime
FILE: lib/realtime/adapters/changes.ex
class Realtime.Adapters.Changes (line 6) | defmodule Realtime.Adapters.Changes
class Transaction (line 10) | defmodule Transaction
class NewRecord (line 15) | defmodule NewRecord
class UpdatedRecord (line 30) | defmodule UpdatedRecord
class DeletedRecord (line 46) | defmodule DeletedRecord
class TruncatedRelation (line 61) | defmodule TruncatedRelation
FILE: lib/realtime/adapters/postgres/decoder.ex
class Realtime.Adapters.Postgres.Decoder (line 4) | defmodule Realtime.Adapters.Postgres.Decoder
method decode_message_impl (line 157) | defp decode_message_impl(<<"B", lsn::binary-8, timestamp::integer-64, ...
method decode_message_impl (line 165) | defp decode_message_impl(
method decode_message_impl (line 178) | defp decode_message_impl(<<"O", lsn::binary-8, name::binary>>, _relati...
method decode_message_impl (line 185) | defp decode_message_impl(<<"R", id::integer-32, rest::binary>>, _relat...
method decode_message_impl (line 209) | defp decode_message_impl(
method decode_message_impl (line 224) | defp decode_message_impl(<<"Y", data_type_id::integer-32, namespace_an...
method decode_message_impl (line 235) | defp decode_message_impl(binary, _relations), do: %Unsupported{data: b...
method decode_tuple_data (line 237) | defp decode_tuple_data(binary, columns_remaining, relations, accumulat...
method decode_tuple_data (line 242) | defp decode_tuple_data(<<"n", rest::binary>>, columns_remaining, [_ | ...
method decode_tuple_data (line 245) | defp decode_tuple_data(<<"u", rest::binary>>, columns_remaining, [_ | ...
method decode_tuple_data (line 249) | defp decode_tuple_data(
method decode_columns (line 284) | defp decode_columns(binary, accumulator \\ [])
method decode_columns (line 285) | defp decode_columns(<<>>, accumulator), do: Enum.reverse(accumulator)
method decode_columns (line 287) | defp decode_columns(<<flags::integer-8, rest::binary>>, accumulator) do
method decode_lsn (line 314) | defp decode_lsn(<<xlog_file::integer-32, xlog_offset::integer-32>>),
class Messages (line 8) | defmodule Messages
class Begin (line 12) | defmodule Begin
class Commit (line 23) | defmodule Commit
class Origin (line 35) | defmodule Origin
class Relation (line 45) | defmodule Relation
class Column (line 57) | defmodule Column
class Insert (line 70) | defmodule Insert
class Update (line 80) | defmodule Update
class Delete (line 92) | defmodule Delete
class Truncate (line 103) | defmodule Truncate
class Type (line 114) | defmodule Type
class Unsupported (line 125) | defmodule Unsupported
FILE: lib/realtime/adapters/postgres/oid_database.ex
class Realtime.Adapters.Postgres.OidDatabase (line 18) | defmodule Realtime.Adapters.Postgres.OidDatabase
method name_for_type_id (line 36) | def name_for_type_id(type_id) do
FILE: lib/realtime/adapters/postgres/protocol.ex
class Realtime.Adapters.Postgres.Protocol (line 1) | defmodule Realtime.Adapters.Postgres.Protocol
method parse (line 11) | def parse(<<?w, server_wal_start::64, server_wal_end::64, server_syste...
method parse (line 20) | def parse(<<?k, wal_end::64, clock::64, reply::8>>) do
method standby_status (line 38) | def standby_status(last_wal_received, last_wal_flushed, last_wal_appli...
method standby_status (line 40) | def standby_status(last_wal_received, last_wal_flushed, last_wal_appli...
method standby_status (line 44) | def standby_status(last_wal_received, last_wal_flushed, last_wal_appli...
method hold (line 59) | def hold, do: []
method current_time (line 62) | def current_time, do: System.os_time(:microsecond) - @epoch
FILE: lib/realtime/adapters/postgres/protocol/keep_alive.ex
class Realtime.Adapters.Postgres.Protocol.KeepAlive (line 1) | defmodule Realtime.Adapters.Postgres.Protocol.KeepAlive
FILE: lib/realtime/adapters/postgres/protocol/write.ex
class Realtime.Adapters.Postgres.Protocol.Write (line 1) | defmodule Realtime.Adapters.Postgres.Protocol.Write
FILE: lib/realtime/api.ex
class Realtime.Api (line 1) | defmodule Realtime.Api
method list_tenants (line 47) | def list_tenants do
method get_tenant! (line 101) | def get_tenant!(id), do: Replica.replica().get!(Tenant, id)
method create_tenant (line 115) | def create_tenant(attrs) do
method update_tenant (line 150) | defp update_tenant(%Tenant{} = tenant, attrs) do
method delete_tenant_by_external_id (line 170) | def delete_tenant_by_external_id(id) do
method get_tenant_by_external_id (line 181) | def get_tenant_by_external_id(external_id, opts \\ []) do
method list_extensions (line 196) | defp list_extensions(type) do
method rename_settings_field (line 202) | def rename_settings_field(from, to) do
method update_migrations_ran (line 222) | def update_migrations_ran(external_id, count) do
method preload_counters (line 240) | def preload_counters(nil), do: nil
method preload_counters (line 242) | def preload_counters(%Tenant{} = tenant) do
method preload_counters (line 248) | def preload_counters(nil, _rate), do: nil
method preload_counters (line 250) | def preload_counters(%Tenant{} = tenant, counters_rate) do
method maybe_restart_rate_counters (line 277) | defp maybe_restart_rate_counters(changeset) do
method maybe_update_cache (line 295) | defp maybe_update_cache(_tenant, _changeset), do: :ok
method maybe_trigger_disconnect (line 302) | defp maybe_trigger_disconnect(_changeset), do: nil
method maybe_restart_db_connection (line 318) | defp maybe_restart_db_connection(_changeset), do: nil
method master_region? (line 320) | defp master_region? do
method call (line 326) | defp call(operation, args, tenant_id) do
method wrapped_call (line 335) | defp wrapped_call(master_node, operation, args, tenant_id) do
FILE: lib/realtime/api/extensions.ex
class Realtime.Api.Extensions (line 1) | defmodule Realtime.Api.Extensions
method changeset (line 21) | def changeset(extension, attrs) do
method encrypt_settings (line 44) | def encrypt_settings(changeset, required) do
method validate_required_settings (line 57) | def validate_required_settings(changeset, required) do
FILE: lib/realtime/api/message.ex
class Realtime.Api.Message (line 1) | defmodule Realtime.Api.Message
method changeset (line 23) | def changeset(message, attrs) do
method put_timestamp (line 39) | defp put_timestamp(changeset, field) do
method maybe_put_timestamp (line 43) | defp maybe_put_timestamp(changeset, field) do
FILE: lib/realtime/api/tenant.ex
class Realtime.Api.Tenant (line 1) | defmodule Realtime.Api.Tenant
method changeset (line 48) | def changeset(tenant, attrs) do
method maybe_set_default (line 102) | def maybe_set_default(changeset, property, config_key) do
method encrypt_jwt_secret (line 112) | def encrypt_jwt_secret(%Ecto.Changeset{valid?: true} = changeset),
method encrypt_jwt_secret (line 115) | def encrypt_jwt_secret(changeset), do: changeset
FILE: lib/realtime/application.ex
class Realtime.Application (line 1) | defmodule Realtime.Application
method start (line 18) | def start(_type, _args) do
method extensions_supervisors (line 139) | defp extensions_supervisors do
method janitor_tasks (line 155) | defp janitor_tasks do
method metrics_pusher_children (line 176) | defp metrics_pusher_children do
method opentelemetry_setup (line 184) | defp opentelemetry_setup do
method pubsub_adapter (line 190) | defp pubsub_adapter do
method setup_region_mapping (line 198) | defp setup_region_mapping do
class JwtSecretError (line 14) | defmodule JwtSecretError
class JwtClaimValidatorsError (line 15) | defmodule JwtClaimValidatorsError
class RegionMappingError (line 16) | defmodule RegionMappingError
FILE: lib/realtime/beacon_pub_sub_adapter.ex
class Realtime.BeaconPubSubAdapter (line 1) | defmodule Realtime.BeaconPubSubAdapter
method register (line 9) | def register(scope) do
method broadcast (line 14) | def broadcast(scope, message) do
method broadcast (line 19) | def broadcast(scope, _nodes, message) do
method send (line 28) | def send(scope, node, message) do
method topic (line 32) | defp topic(scope), do: "beacon:#{scope}"
FILE: lib/realtime/crypto.ex
class Realtime.Crypto (line 1) | defmodule Realtime.Crypto
method encrypt! (line 10) | def encrypt!(text) do
method decrypt! (line 22) | def decrypt!(base64_text) do
method pad (line 31) | defp pad(data) do
method unpad (line 36) | defp unpad(data) do
FILE: lib/realtime/database.ex
class Realtime.Database (line 1) | defmodule Realtime.Database
method from_tenant (line 48) | def from_tenant(%Tenant{} = tenant, application_name, backoff \\ :rand...
method from_settings (line 58) | def from_settings(settings, application_name, backoff \\ :rand_exp) do
method check_tenant_connection (line 93) | def check_tenant_connection(nil), do: {:error, :tenant_not_found}
method check_tenant_connection (line 95) | def check_tenant_connection(tenant) do
method query_connection_info (line 137) | defp query_connection_info(conn) do
method connect (line 158) | def connect(tenant, application_name, backoff \\ :stop) do
method default_ssl_param (line 171) | def default_ssl_param(_), do: true
method transaction (line 177) | def transaction(db_conn, func, opts \\ [], metadata \\ [])
method transaction (line 179) | def transaction(%DBConnection{} = db_conn, func, opts, metadata),
method transaction (line 185) | def transaction(db_conn, func, opts, metadata) do
method transaction_catched (line 196) | defp transaction_catched(db_conn, func, opts, metadata) do
method connect_db (line 218) | def connect_db(%__MODULE__{} = settings) do
method pool_size_by_application_name (line 269) | def pool_size_by_application_name(application_name, settings) do
method replication_slot_teardown (line 324) | def replication_slot_teardown(tenant) do
method replication_slot_teardown (line 344) | def replication_slot_teardown(%Tenant{} = tenant, slot_name) do
method replication_slot_teardown (line 350) | def replication_slot_teardown(conn, slot_name) do
method opts (line 383) | def opts(%__MODULE__{} = settings) do
method tenant_pool_requirements (line 390) | defp tenant_pool_requirements(settings) do
FILE: lib/realtime/gen_counter/gen_counter.ex
class Realtime.GenCounter (line 1) | defmodule Realtime.GenCounter
method start_link (line 12) | def start_link(_), do: GenServer.start_link(__MODULE__, :ok, name: @name)
method add (line 15) | def add(term), do: add(term, 1)
method add (line 17) | def add(term, count), do: :ets.update_counter(@table, term, count, {te...
method get (line 20) | def get(term) do
method reset (line 29) | def reset(term) do
method delete (line 45) | def delete(term) do
method init (line 51) | def init(_) do
FILE: lib/realtime/gen_rpc.ex
class Realtime.GenRpc (line 1) | defmodule Realtime.GenRpc
method cast (line 37) | def cast(node, mod, func, args, opts \\ [])
method do_call (line 106) | defp do_call(node, mod, func, args, opts) do
method telemetry_success (line 200) | defp telemetry_success(node, latency) do
method telemetry_failure (line 208) | defp telemetry_failure(node, latency) do
method max_clients (line 217) | defp max_clients(), do: Application.fetch_env!(:realtime, :max_gen_rpc...
method rpc_nodes (line 219) | defp rpc_nodes(nodes, key), do: Enum.map(nodes, &rpc_node(&1, key))
method rpc_node (line 223) | defp rpc_node(node, nil), do: {node, :rand.uniform(max_clients())}
method rpc_node (line 227) | defp rpc_node(node, key), do: {node, :erlang.phash2(key, max_clients()...
method unwrap_reason (line 229) | defp unwrap_reason({:unknown_error, {{:badrpc, reason}, _}}), do: reason
method unwrap_reason (line 230) | defp unwrap_reason(reason), do: reason
method default_rpc_timeout (line 232) | defp default_rpc_timeout, do: Application.get_env(:realtime, :rpc_time...
method async_call (line 238) | defp async_call(node, mod, func, args), do: :gen_rpc.async_call(node, ...
method nb_yield (line 241) | defp nb_yield(_node, ref, timeout), do: :gen_rpc.nb_yield(ref, timeout)
FILE: lib/realtime/gen_rpc/pub_sub.ex
class Realtime.GenRpcPubSub (line 1) | defmodule Realtime.GenRpcPubSub
method node_name (line 13) | def node_name(_), do: node()
method start_link (line 17) | def start_link(opts) do
method init (line 29) | def init({adapter_name, pubsub, pool_size}) do
method worker_name (line 42) | defp worker_name(adapter_name, key) do
method broadcast (line 48) | def broadcast(adapter_name, topic, message, dispatcher) do
method nodes_from_other_regions (line 69) | defp nodes_from_other_regions(my_region, key) do
method direct_broadcast (line 83) | def direct_broadcast(adapter_name, node_name, topic, message, dispatch...
class Realtime.GenRpcPubSub.Worker (line 89) | defmodule Realtime.GenRpcPubSub.Worker
method forward_to_local (line 93) | def forward_to_local(topic, message, dispatcher), do: {:ftl, topic, ...
method forward_to_region (line 94) | def forward_to_region(topic, message, dispatcher), do: {:ftr, topic,...
method start_link (line 97) | def start_link({pubsub, worker}), do: GenServer.start_link(__MODULE_...
method init (line 100) | def init({pubsub, worker}) do
method handle_info (line 108) | def handle_info({:ftl, topic, message, dispatcher}, {pubsub, worker}...
method handle_info (line 114) | def handle_info({:ftr, topic, message, dispatcher}, {pubsub, worker}...
method handle_info (line 130) | def handle_info(_, pubsub), do: {:noreply, pubsub}
FILE: lib/realtime/helpers.ex
class Realtime.Helpers (line 1) | defmodule Realtime.Helpers
method cancel_timer (line 8) | def cancel_timer(nil), do: nil
method cancel_timer (line 9) | def cancel_timer(ref), do: Process.cancel_timer(ref)
method queue_take (line 25) | def queue_take(q, count) do
FILE: lib/realtime/log_filter.ex
class Realtime.LogFilter (line 1) | defmodule Realtime.LogFilter
method setup (line 11) | def setup do
method filter (line 23) | def filter(
method filter (line 29) | def filter(%{meta: %{mfa: {DBConnection.Connection, _, _}}}, _), do: :...
method filter (line 32) | def filter(%{msg: {:format, @ranch_format, [_, _, _, :killed]}}, _), d...
method filter (line 34) | def filter(event, _), do: event
FILE: lib/realtime/logs.ex
class Realtime.Logs (line 1) | defmodule Realtime.Logs
method to_log (line 19) | def to_log(value), do: inspect(value, pretty: true)
FILE: lib/realtime/messages.ex
class Realtime.Messages (line 1) | defmodule Realtime.Messages
method replay (line 43) | def replay(_, _, _, _, _), do: {:error, :invalid_replay_params}
method messages (line 45) | defp messages(conn, tenant_id, topic, since, limit) do
method delete_old_messages (line 73) | def delete_old_messages(conn) do
FILE: lib/realtime/metrics_cleaner.ex
class Realtime.MetricsCleaner (line 1) | defmodule Realtime.MetricsCleaner
method handle_beacon_event (line 9) | def handle_beacon_event([:beacon, :users, :group, :vacant], _, %{group...
method handle_beacon_event (line 13) | def handle_beacon_event([:beacon, :users, :group, :occupied], _, %{gro...
method handle_syn_event (line 17) | def handle_syn_event([:syn, Realtime.Tenants.Connect, :unregistered], ...
method handle_syn_event (line 21) | def handle_syn_event([:syn, Realtime.Tenants.Connect, :registered], _,...
method start_link (line 25) | def start_link(opts), do: GenServer.start_link(__MODULE__, opts)
method init (line 31) | def init(opts) do
method terminate (line 73) | def terminate(_reason, _state) do
method handle_info (line 80) | def handle_info(:check, %{interval: interval} = state) do
method handle_info (line 98) | def handle_info(msg, state) do
method check (line 103) | defp check(interval), do: Process.send_after(self(), :check, interval)
method loop_and_cleanup_metrics_table (line 105) | defp loop_and_cleanup_metrics_table(cleaner_table, vacant_metric_clean...
FILE: lib/realtime/metrics_pusher.ex
class Realtime.MetricsPusher (line 1) | defmodule Realtime.MetricsPusher
method start_link (line 15) | def start_link(opts) do
method init (line 28) | def init(opts) do
method handle_info (line 81) | def handle_info(:push, state) do
method handle_info (line 92) | def handle_info(msg, state) do
method schedule_push (line 97) | defp schedule_push(delay), do: Process.send_after(self(), :push, delay)
method push (line 99) | defp push(req_options) do
method send_metrics (line 118) | defp send_metrics(req_options, metrics) do
method handle_response (line 123) | defp handle_response({:ok, %{status: status} = response}), do: {:error...
method handle_response (line 124) | defp handle_response({:error, reason}), do: {:error, reason}
FILE: lib/realtime/monitoring/distributed_metrics.ex
class Realtime.DistributedMetrics (line 1) | defmodule Realtime.DistributedMetrics
method info (line 9) | def info do
method info (line 35) | defp info({node, info}, port_addresses) do
method info (line 48) | defp info(node, port_addresses, dist_pid, state, address) do
method inet_stats (line 64) | defp inet_stats(port) do
method node_queue_size (line 74) | defp node_queue_size(node) do
FILE: lib/realtime/monitoring/erl_sys_mon.ex
class Realtime.ErlSysMon (line 1) | defmodule Realtime.ErlSysMon
method start_link (line 18) | def start_link(args), do: GenServer.start_link(__MODULE__, args)
method init (line 20) | def init(args) do
method handle_info (line 32) | def handle_info(msg, state) do
method log_process_info (line 37) | defp log_process_info(msg, pid) do
FILE: lib/realtime/monitoring/gen_rpc_metrics.ex
class Realtime.GenRpcMetrics (line 1) | defmodule Realtime.GenRpcMetrics
method info (line 10) | def info do
method info (line 55) | defp info({node, _}, client_ports, server_ports) do
method inet_stats (line 70) | defp inet_stats(ports) do
method queue_size (line 79) | defp queue_size(ports) do
method server_port (line 86) | defp server_port() do
method ip_address_node (line 94) | defp ip_address_node(nodes_info) do
FILE: lib/realtime/monitoring/latency.ex
class Realtime.Latency (line 1) | defmodule Realtime.Latency
method start_link (line 37) | def start_link(args) do
method init (line 41) | def init(_args) do
method handle_info (line 47) | def handle_info(:ping, state) do
method handle_info (line 54) | def handle_info(msg, state) do
method handle_cast (line 59) | def handle_cast({:ping, pong_timeout, timer_timeout, yield_timeout}, s...
method ping (line 73) | def ping(pong_timeout \\ 0, timer_timeout \\ 5_000, yield_timeout \\ 5...
method pong (line 145) | def pong do
method ping_after (line 156) | defp ping_after do
class Payload (line 12) | defmodule Payload
FILE: lib/realtime/monitoring/os_metrics.ex
class Realtime.OsMetrics (line 1) | defmodule Realtime.OsMetrics
method ram_usage (line 7) | def ram_usage do
method cpu_la (line 14) | def cpu_la do
method cpu_util (line 23) | def cpu_util do
FILE: lib/realtime/monitoring/peep/partitioned.ex
class Realtime.Monitoring.Peep.Partitioned (line 1) | defmodule Realtime.Monitoring.Peep.Partitioned
method storage_size (line 27) | def storage_size({tid, _}) do
method insert_metric (line 35) | def insert_metric({tid, partitions}, id, %Metrics.Counter{}, _value, %...
method insert_metric (line 40) | def insert_metric({tid, partitions}, id, %Metrics.Sum{}, value, %{} = ...
method insert_metric (line 45) | def insert_metric({tid, _partitions}, id, %Metrics.LastValue{}, value,...
method insert_metric (line 50) | def insert_metric({tid, _partitions}, id, %Metrics.Distribution{} = me...
method get_all_metrics (line 79) | def get_all_metrics({tid, _partitions}, %Peep.Persistent{ids_to_metric...
method get_metric (line 85) | def get_metric({tid, _partitions}, id, %Metrics.Counter{}, tags) do
method get_metric (line 90) | def get_metric({tid, _partitions}, id, %Metrics.Sum{}, tags) do
method get_metric (line 95) | def get_metric({tid, _partitions}, id, %Metrics.LastValue{}, tags) do
method get_metric (line 102) | def get_metric({tid, _partitions}, id, %Metrics.Distribution{}, tags) do
method prune_tags (line 112) | def prune_tags({tid, _partitions}, patterns) do
method group_metrics (line 137) | defp group_metrics([], _itm, acc) do
method group_metrics (line 141) | defp group_metrics([metric | rest], itm, acc) do
method group_metric (line 146) | defp group_metric({{id, tags, _}, value}, itm, acc) do
method group_metric (line 151) | defp group_metric({{id, tags}, %Storage.Atomics{} = atomics}, itm, acc...
method group_metric (line 156) | defp group_metric({{id, tags}, value}, itm, acc) do
FILE: lib/realtime/monitoring/prom_ex.ex
class Realtime.PromEx (line 1) | defmodule Realtime.PromEx
method plugins (line 97) | def plugins do
method dashboard_assigns (line 112) | def dashboard_assigns do
method dashboards (line 119) | def dashboards do
method get_global_metrics (line 134) | def get_global_metrics do
method get_metrics (line 144) | def get_metrics, do: get_global_metrics()
class Store (line 73) | defmodule Store
method scrape (line 80) | def scrape(name) do
method child_spec (line 86) | def child_spec(name, metrics) do
FILE: lib/realtime/monitoring/prom_ex/plugins/channels.ex
class Realtime.PromEx.Plugins.Channels (line 1) | defmodule Realtime.PromEx.Plugins.Channels
method event_metrics (line 9) | def event_metrics(_opts) do
FILE: lib/realtime/monitoring/prom_ex/plugins/distributed.ex
class Realtime.PromEx.Plugins.Distributed (line 1) | defmodule Realtime.PromEx.Plugins.Distributed
method polling_metrics (line 17) | def polling_metrics(opts) do
method metrics (line 25) | defp metrics(poll_rate) do
method execute_metrics (line 78) | def execute_metrics do
method execute_inet_stats (line 87) | defp execute_inet_stats(node, info) do
method execute_queue_size (line 102) | defp execute_queue_size(node, info) do
FILE: lib/realtime/monitoring/prom_ex/plugins/gen_rpc.ex
class Realtime.PromEx.Plugins.GenRpc (line 1) | defmodule Realtime.PromEx.Plugins.GenRpc
method polling_metrics (line 18) | def polling_metrics(opts) do
method metrics (line 26) | defp metrics(poll_rate) do
method execute_metrics (line 79) | def execute_metrics do
method execute_inet_stats (line 88) | defp execute_inet_stats(node, info) do
method execute_queue_size (line 103) | defp execute_queue_size(node, info) do
FILE: lib/realtime/monitoring/prom_ex/plugins/osmon.ex
class Realtime.PromEx.Plugins.OsMon (line 1) | defmodule Realtime.PromEx.Plugins.OsMon
method polling_metrics (line 15) | def polling_metrics(opts) do
method metrics (line 23) | defp metrics(poll_rate) do
method execute_metrics (line 65) | def execute_metrics do
method execute_metrics (line 71) | defp execute_metrics(event, metrics) do
FILE: lib/realtime/monitoring/prom_ex/plugins/tenant.ex
class Realtime.PromEx.Plugins.Tenant (line 1) | defmodule Realtime.PromEx.Plugins.Tenant
method polling_metrics (line 11) | def polling_metrics(opts) do
method event_metrics (line 20) | def event_metrics(_opts) do
method payload_size_metrics (line 35) | defp payload_size_metrics do
method concurrent_connections (line 52) | defp concurrent_connections(poll_rate) do
method execute_tenant_metrics (line 77) | def execute_tenant_metrics do
method replication_metrics (line 104) | defp replication_metrics do
method channel_events (line 136) | defp channel_events do
class PayloadSize.Buckets (line 29) | defmodule PayloadSize.Buckets
class Replication.Buckets (line 98) | defmodule Replication.Buckets
class PolicyAuthorization.Buckets (line 121) | defmodule PolicyAuthorization.Buckets
class BroadcastFromDatabase.Buckets (line 126) | defmodule BroadcastFromDatabase.Buckets
class Replay.Buckets (line 131) | defmodule Replay.Buckets
FILE: lib/realtime/monitoring/prom_ex/plugins/tenant_global.ex
class Realtime.PromEx.Plugins.TenantGlobal (line 1) | defmodule Realtime.PromEx.Plugins.TenantGlobal
method polling_metrics (line 18) | def polling_metrics(opts) do
method event_metrics (line 46) | def event_metrics(_opts) do
method execute_global_connection_metrics (line 53) | def execute_global_connection_metrics do
method payload_global_size_metrics (line 67) | defp payload_global_size_metrics do
method channel_global_events (line 84) | defp channel_global_events do
FILE: lib/realtime/monitoring/prom_ex/plugins/tenants.ex
class Realtime.PromEx.Plugins.Tenants (line 1) | defmodule Realtime.PromEx.Plugins.Tenants
method event_metrics (line 19) | def event_metrics(_) do
method polling_metrics (line 34) | def polling_metrics(opts) do
method execute_metrics (line 55) | def execute_metrics do
method execute_metrics (line 64) | defp execute_metrics(event, metrics) do
class Buckets (line 11) | defmodule Buckets
FILE: lib/realtime/monitoring/prometheus.ex
class Realtime.Monitoring.Prometheus (line 2) | defmodule Realtime.Monitoring.Prometheus
method export (line 11) | def export(metrics) do
method format (line 19) | defp format({%Counter{}, _series} = metric, cache) do
method format (line 23) | defp format({%Sum{} = spec, _series} = metric, cache) do
method format (line 27) | defp format({%LastValue{} = spec, _series} = metric, cache) do
method format (line 31) | defp format({%Distribution{} = metric, tagged_series}, cache) do
method format_distribution (line 44) | defp format_distribution(name, tags, buckets, cache) do
method format_standard (line 79) | defp format_standard({metric, series}, type, cache) do
method format_labels (line 98) | defp format_labels(labels, cache) do
method format_name (line 104) | defp format_name(name, cache) do
method format_name_start (line 125) | defp format_name_start(<<rest::binary>>),
method format_name_rest (line 133) | defp format_name_rest(<<_, rest::binary>>, acc), do: format_name_rest(...
method format_name_rest (line 134) | defp format_name_rest(<<>>, acc), do: acc
method format_value (line 136) | defp format_value(true), do: "1"
method format_value (line 137) | defp format_value(false), do: "0"
method format_value (line 138) | defp format_value(nil), do: "0"
method escape (line 142) | defp escape(nil, _cache), do: "nil"
method escape (line 144) | defp escape(value, cache) do
method safe_to_string (line 161) | defp safe_to_string(value) do
method do_escape (line 168) | defp do_escape(<<?\", rest::binary>>, acc), do: do_escape(rest, [acc, ...
method do_escape (line 169) | defp do_escape(<<?\\, rest::binary>>, acc), do: do_escape(rest, [acc, ...
method do_escape (line 170) | defp do_escape(<<?\n, rest::binary>>, acc), do: do_escape(rest, [acc, ...
method do_escape (line 171) | defp do_escape(<<h, rest::binary>>, acc), do: do_escape(rest, [acc, h])
method do_escape (line 172) | defp do_escape(<<>>, acc), do: acc
method escape_help (line 174) | defp escape_help(value) do
method escape_help (line 180) | defp escape_help(<<?\\, rest::binary>>, acc), do: escape_help(rest, <<...
method escape_help (line 181) | defp escape_help(<<?\n, rest::binary>>, acc), do: escape_help(rest, <<...
method escape_help (line 182) | defp escape_help(<<h, rest::binary>>, acc), do: escape_help(rest, <<ac...
method escape_help (line 183) | defp escape_help(<<>>, acc), do: acc
method prefix_sums (line 185) | defp prefix_sums(buckets), do: prefix_sums(buckets, [], 0)
method prefix_sums (line 186) | defp prefix_sums([], acc, sum), do: {Enum.reverse(acc), sum}
method prefix_sums (line 188) | defp prefix_sums([{bucket, count} | rest], acc, sum) do
FILE: lib/realtime/monitoring/tenant_prom_ex.ex
class Realtime.TenantPromEx (line 1) | defmodule Realtime.TenantPromEx
method plugins (line 18) | def plugins do
method get_metrics (line 27) | def get_metrics do
FILE: lib/realtime/nodes.ex
class Realtime.Nodes (line 1) | defmodule Realtime.Nodes
method get_node_for_tenant (line 13) | def get_node_for_tenant(nil), do: {:error, :tenant_not_found}
method get_node_for_tenant (line 15) | def get_node_for_tenant(%Tenant{} = tenant) do
method platform_region_translator (line 30) | def platform_region_translator(nil), do: nil
method default_region_mapping (line 40) | defp default_region_mapping(tenant_region) do
method region_nodes (line 76) | def region_nodes(nil), do: []
method node_from_region (line 97) | def node_from_region(_, _), do: {:error, :not_available}
method two_random_nodes (line 159) | defp two_random_nodes(tenant_id, nodes, node_count) do
method all_node_regions (line 240) | def all_node_regions(), do: :syn.group_names(RegionNodes)
method uptime_ms (line 242) | defp uptime_ms do
FILE: lib/realtime/operations.ex
class Realtime.Operations (line 1) | defmodule Realtime.Operations
method rebalance (line 10) | def rebalance do
method kill_connections_to_tenant_id_in_all_nodes (line 34) | def kill_connections_to_tenant_id_in_all_nodes(tenant_id, reason \\ :n...
method kill_connections_to_tenant_id (line 49) | def kill_connections_to_tenant_id(tenant_id, reason) do
method dirty_terminate_runners (line 74) | def dirty_terminate_runners do
method stop_user_tenant_process (line 90) | defp stop_user_tenant_process(tenant, platform_region, acc) do
FILE: lib/realtime/postgres_cdc.ex
class Realtime.PostgresCdc (line 1) | defmodule Realtime.PostgresCdc
method connect (line 15) | def connect(module, opts) do
method after_connect (line 19) | def after_connect(module, connect_response, extension, params, tenant) do
method subscribe (line 23) | def subscribe(module, pg_change_params, tenant, metadata) do
method stop (line 29) | def stop(module, tenant, timeout \\ @timeout) do
method stop_all (line 42) | def stop_all(tenant, timeout \\ @timeout) do
method available_drivers (line 55) | def available_drivers do
method filter_settings (line 62) | def filter_settings(key, extensions) do
method driver (line 73) | def driver(tenant_key) do
class Exception (line 11) | defmodule Exception
FILE: lib/realtime/rate_counter/dynamic_supervisor.ex
class Realtime.RateCounter.DynamicSupervisor (line 1) | defmodule Realtime.RateCounter.DynamicSupervisor
method start_link (line 9) | def start_link(args) do
method init (line 14) | def init(_args) do
FILE: lib/realtime/rate_counter/rate_counter.ex
class Realtime.RateCounter (line 1) | defmodule Realtime.RateCounter
method start_link (line 71) | def start_link(args) do
method new (line 85) | def new(%Args{id: id} = args, opts \\ []) do
method publish_update (line 97) | def publish_update(id), do: Phoenix.PubSub.broadcast(Realtime.PubSub, ...
method get (line 106) | def get(%Args{id: id} = args) do
method do_get (line 120) | defp do_get(id) do
method update_topic (line 127) | defp update_topic(id), do: "rate_counter:#{inspect(id)}"
method init (line 130) | def init(args) do
method handle_info (line 195) | def handle_info(:tick, state) do
method handle_info (line 223) | def handle_info(:idle_shutdown, state) do
method handle_info (line 235) | def handle_info(:update, state) do
method handle_info (line 241) | def handle_info(_, state), do: {:noreply, state}
method shutdown (line 243) | defp shutdown(state) do
method maybe_trigger_limit (line 258) | defp maybe_trigger_limit(%{limit: %{log: false}} = state), do: state
method maybe_trigger_limit (line 260) | defp maybe_trigger_limit(%{limit: %{triggered: true, measurement: meas...
method maybe_trigger_limit (line 270) | defp maybe_trigger_limit(%{limit: %{measurement: measurement}} = state...
method tick (line 280) | defp tick(every) do
method shutdown_after (line 284) | defp shutdown_after(ms) do
class Args (line 17) | defmodule Args
FILE: lib/realtime/release.ex
class Realtime.Release (line 1) | defmodule Realtime.Release
method migrate (line 8) | def migrate do
method rollback (line 16) | def rollback(repo, version) do
method seeds (line 21) | def seeds(repo) do
method repos (line 37) | defp repos do
method load_app (line 41) | defp load_app do
FILE: lib/realtime/repo.ex
class Realtime.Repo (line 1) | defmodule Realtime.Repo
method with_dynamic_repo (line 6) | def with_dynamic_repo(config, callback) do
FILE: lib/realtime/repo_replica.ex
class Realtime.Repo.Replica (line 1) | defmodule Realtime.Repo.Replica
method replica (line 44) | def replica do
method configured_replica_module (line 72) | defp configured_replica_module(region) do
FILE: lib/realtime/rpc.ex
class Realtime.Rpc (line 1) | defmodule Realtime.Rpc
method call (line 12) | def call(node, mod, func, args, opts \\ []) do
method enhanced_call (line 30) | def enhanced_call(node, mod, func, args \\ [], opts \\ []) do
FILE: lib/realtime/signal_handler.ex
class Realtime.SignalHandler (line 1) | defmodule Realtime.SignalHandler
method shutdown_in_progress? (line 7) | def shutdown_in_progress? do
method init (line 15) | def init({%{handler_mod: _} = args, :ok}) do
method handle_event (line 20) | def handle_event(signal, %{handler_mod: handler_mod} = state) do
FILE: lib/realtime/syn/postgres_cdc.ex
class Realtime.Syn.PostgresCdc (line 1) | defmodule Realtime.Syn.PostgresCdc
method scope (line 10) | def scope(tenant_id) do
method scopes (line 16) | def scopes() do
method syn_topic_prefix (line 21) | def syn_topic_prefix(), do: "realtime_postgres_cdc_"
method syn_topic (line 22) | def syn_topic(tenant_id), do: "#{syn_topic_prefix()}#{tenant_id}"
FILE: lib/realtime/syn_handler.ex
class Realtime.SynHandler (line 1) | defmodule Realtime.SynHandler
method on_registry_process_updated (line 20) | def on_registry_process_updated(scope, tenant_id, _pid, meta, _reason) do
method on_process_registered (line 33) | def on_process_registered(scope, name, _pid, _meta, _reason) do
method on_process_unregistered (line 47) | def on_process_unregistered(scope, name, pid, _meta, reason) do
method resolve_registry_conflict (line 75) | def resolve_registry_conflict(mod, name, {pid1, _meta1, _time1}, {pid2...
method stop (line 94) | defp stop(pid_to_stop) do
method log (line 110) | defp log(message), do: Logger.warning("SynHandler(#{node()}): #{messag...
method decide (line 115) | defp decide(pid1, pid2, name) do
method topic (line 135) | defp topic(mod) do
FILE: lib/realtime/telemetry/logger.ex
class Realtime.Telemetry.Logger (line 1) | defmodule Realtime.Telemetry.Logger
method start_link (line 18) | def start_link(args) do
method init (line 22) | def init(handler_id: handler_id) do
method handle_event (line 31) | def handle_event(event, measurements, %{tenant: tenant}, _config) do
method handle_event (line 37) | def handle_event(_event, _measurements, _metadata, _config) do
method handle_info (line 41) | def handle_info(_msg, state) do
FILE: lib/realtime/telemetry/telemetry.ex
class Realtime.Telemetry (line 1) | defmodule Realtime.Telemetry
method execute (line 11) | def execute(event, measurements, metadata \\ %{}) do
FILE: lib/realtime/tenants.ex
class Realtime.Tenants (line 1) | defmodule Realtime.Tenants
method get_health_conn (line 27) | def get_health_conn(%Tenant{external_id: external_id}) do
method replication_connected? (line 113) | defp replication_connected?(external_id) do
method limiter_keys (line 124) | def limiter_keys(%Tenant{} = tenant) do
method requests_per_second_rate (line 136) | def requests_per_second_rate(%Tenant{} = tenant) do
method requests_per_second_key (line 142) | def requests_per_second_key(%Tenant{} = tenant) do
method joins_per_second_rate (line 148) | def joins_per_second_rate(%Tenant{} = tenant),
method joins_per_second_key (line 180) | def joins_per_second_key(%Tenant{} = tenant) do
method channels_per_client_key (line 190) | def channels_per_client_key(%Tenant{} = tenant) do
method events_per_second_rate (line 196) | def events_per_second_rate(tenant), do: events_per_second_rate(tenant....
method events_per_second_rate (line 198) | def events_per_second_rate(tenant_id, max_events_per_second) do
method events_per_second_key (line 234) | def events_per_second_key(%Tenant{} = tenant) do
method db_events_per_second_rate (line 240) | def db_events_per_second_rate(%Tenant{} = tenant),
method db_events_per_second_key (line 280) | def db_events_per_second_key(%Tenant{} = tenant) do
method presence_events_per_second_rate (line 286) | def presence_events_per_second_rate(tenant) do
method presence_events_per_second_rate (line 291) | def presence_events_per_second_rate(tenant_id, max_presence_events_per...
method presence_events_per_second_key (line 326) | def presence_events_per_second_key(%Tenant{} = tenant) do
method authorization_errors_per_second_rate (line 331) | def authorization_errors_per_second_rate(%Tenant{external_id: external...
method authorization_errors_per_second_key (line 349) | def authorization_errors_per_second_key(tenant_id), do: {:channel, :au...
method subscription_errors_per_second_rate (line 352) | def subscription_errors_per_second_rate(tenant_id, pool_size) do
method subscription_errors_per_second_key (line 370) | def subscription_errors_per_second_key(tenant_id), do: {:channel, :sub...
method connect_errors_per_second_rate (line 377) | def connect_errors_per_second_rate(%Tenant{external_id: external_id}) do
method connect_errors_per_second_rate (line 381) | def connect_errors_per_second_rate(tenant_id) do
method connect_errors_per_second_key (line 401) | def connect_errors_per_second_key(tenant_id), do: {:database, :connect...
method authorization_pool_size (line 403) | defp authorization_pool_size(%{extensions: [%{settings: settings} | _]...
method authorization_pool_size (line 407) | defp authorization_pool_size(_), do: 1
method get_tenant_by_external_id (line 433) | def get_tenant_by_external_id(external_id) do
method tenant_topic (line 457) | def tenant_topic(external_id, sub_topic, public? \\ true)
method tenant_topic (line 459) | def tenant_topic(%Tenant{external_id: external_id}, sub_topic, public?),
method tenant_topic (line 462) | def tenant_topic(external_id, sub_topic, false),
method tenant_topic (line 465) | def tenant_topic(external_id, sub_topic, true),
method suspend_tenant_by_external_id (line 472) | def suspend_tenant_by_external_id(external_id) do
method unsuspend_tenant_by_external_id (line 482) | def unsuspend_tenant_by_external_id(external_id) do
method run_migrations? (line 492) | def run_migrations?(%Tenant{} = tenant), do: run_migrations?(tenant.mi...
method broadcast_operation_event (line 501) | def broadcast_operation_event(action, external_id),
method region (line 509) | def region(%Tenant{extensions: [%{settings: settings}]}), do: Map.get(...
method region (line 510) | def region(_), do: nil
method validate_payload_size (line 522) | def validate_payload_size(%Tenant{max_payload_size_in_kb: max_payload_...
FILE: lib/realtime/tenants/authorization.ex
class Realtime.Tenants.Authorization (line 1) | defmodule Realtime.Tenants.Authorization
method build_authorization_params (line 46) | def build_authorization_params(map) do
method get_read_authorizations (line 64) | def get_read_authorizations(policies, db_conn, authorization_context, ...
method get_read_authorizations (line 79) | def get_read_authorizations(policies, db_conn, authorization_context, ...
method get_write_authorizations (line 113) | def get_write_authorizations(policies, db_conn, authorization_context,...
method get_write_authorizations (line 128) | def get_write_authorizations(policies, db_conn, authorization_context,...
method get_write_authorizations (line 155) | def get_write_authorizations(db_conn, authorization_context) do
method handle_policies_result (line 159) | defp handle_policies_result(result, rate_counter) do
method set_conn_config (line 190) | def set_conn_config(conn, authorization_context) do
method get_read_policies_for_connection (line 217) | defp get_read_policies_for_connection(conn, authorization_context, pol...
method get_write_policies_for_connection (line 246) | defp get_write_policies_for_connection(conn, authorization_context, po...
method extensions_to_check (line 268) | defp extensions_to_check(opts) do
method check_read_policies (line 274) | defp check_read_policies(conn, authorization_context, messages_by_exte...
method check_write_policies (line 292) | defp check_write_policies(conn, authorization_context, extensions, pol...
method rate_counter (line 313) | defp rate_counter(tenant_id) do
FILE: lib/realtime/tenants/authorization/policies.ex
class Realtime.Tenants.Authorization.Policies (line 1) | defmodule Realtime.Tenants.Authorization.Policies
method update_policies (line 25) | def update_policies(policies, key, sub_key, value) do
FILE: lib/realtime/tenants/authorization/policies/broadcast_policies.ex
class Realtime.Tenants.Authorization.Policies.BroadcastPolicies (line 1) | defmodule Realtime.Tenants.Authorization.Policies.BroadcastPolicies
FILE: lib/realtime/tenants/authorization/policies/presence_policies.ex
class Realtime.Tenants.Authorization.Policies.PresencePolicies (line 1) | defmodule Realtime.Tenants.Authorization.Policies.PresencePolicies
FILE: lib/realtime/tenants/batch_broadcast.ex
class Realtime.Tenants.BatchBroadcast (line 1) | defmodule Realtime.Tenants.BatchBroadcast
method broadcast (line 37) | def broadcast(auth_params, tenant, messages, super_user \\ false)
method broadcast (line 39) | def broadcast(%Plug.Conn{} = conn, %Tenant{} = tenant, messages, super...
method broadcast (line 51) | def broadcast(auth_params, %Tenant{} = tenant, messages, super_user) do
method broadcast (line 93) | def broadcast(_, nil, _, _), do: {:error, :tenant_not_found}
method changeset (line 95) | defp changeset(payload, attrs, tenant) do
method message_changeset (line 101) | defp message_changeset(message, tenant, attrs) do
method maybe_put_private_change (line 109) | defp maybe_put_private_change(changeset) do
method validate_payload_size (line 116) | defp validate_payload_size(changeset, tenant) do
method send_message_and_count (line 126) | defp send_message_and_count(tenant, events_per_second_rate, message, p...
method permissions_for_message (line 149) | defp permissions_for_message(_, nil, _), do: nil
method permissions_for_message (line 151) | defp permissions_for_message(tenant, auth_params, topic) do
method check_rate_limit (line 166) | defp check_rate_limit(events_per_second_rate, %Tenant{} = tenant, tota...
FILE: lib/realtime/tenants/cache.ex
class Realtime.Tenants.Cache (line 1) | defmodule Realtime.Tenants.Cache
method child_spec (line 11) | def child_spec(_) do
method get_tenant_by_external_id (line 20) | def get_tenant_by_external_id(tenant_id) do
method cache_key (line 33) | defp cache_key(tenant_id), do: {:get_tenant_by_external_id, tenant_id}
method invalidate_tenant_cache (line 38) | def invalidate_tenant_cache(tenant_id), do: Cachex.del(__MODULE__, cac...
method update_cache (line 47) | def update_cache(tenant) do
method global_cache_update (line 55) | def global_cache_update(tenant) do
FILE: lib/realtime/tenants/connect.ex
class Realtime.Tenants.Connect (line 1) | defmodule Realtime.Tenants.Connect
method list_tenants (line 61) | def list_tenants() do
method ready? (line 68) | def ready?(tenant_id) do
method get_status (line 125) | def get_status(tenant_id) do
method syn_topic (line 145) | def syn_topic(tenant_id), do: "connect:#{tenant_id}"
method wait_for_connection (line 147) | defp wait_for_connection(pid, tenant_id) do
method connect (line 180) | def connect(tenant_id, region, opts \\ []) do
method whereis (line 204) | def whereis(tenant_id) do
method replication_status (line 215) | def replication_status(tenant_id) do
method shutdown (line 226) | def shutdown(tenant_id) do
method start_link (line 237) | def start_link(opts) do
method init (line 263) | def init(%{tenant_id: tenant_id} = state) do
method handle_continue (line 270) | def handle_continue(:db_connect, state) do
method handle_continue (line 294) | def handle_continue(:run_migrations, state) do
method handle_continue (line 312) | def handle_continue(:start_replication, state) do
method handle_continue (line 319) | def handle_continue(:setup_connected_user_events, state) do
method handle_continue (line 332) | def handle_continue(:start_connect_region_check, state) do
method handle_info (line 338) | def handle_info(
method handle_info (line 354) | def handle_info({:check_connect_region, previous_nodes_set}, state) do
method handle_info (line 369) | def handle_info(:shutdown_no_connected_users, state) do
method handle_info (line 374) | def handle_info(:shutdown_connect, state) do
method handle_info (line 380) | def handle_info(
method handle_info (line 389) | def handle_info(
method handle_info (line 414) | def handle_info(:recover_replication_connection, %{replication_recover...
method handle_info (line 418) | def handle_info(:recover_replication_connection, state) do
method handle_info (line 449) | def handle_info(_, state), do: {:noreply, state}
method handle_call (line 452) | def handle_call(:ready?, _from, state) do
method terminate (line 459) | def terminate(reason, %{tenant_id: tenant_id}) do
method call_external_node (line 465) | defp call_external_node(tenant_id, opts) do
method update_connected_users_bucket (line 479) | defp update_connected_users_bucket(tenant_id, connected_users_bucket) do
method send_connected_user_check_message (line 485) | defp send_connected_user_check_message(
method send_connected_user_check_message (line 492) | defp send_connected_user_check_message(connected_users_bucket, check_c...
method send_connect_region_check_message (line 497) | defp send_connect_region_check_message(check_connect_region_interval) do
method tenant_suspended? (line 501) | defp tenant_suspended?(%Tenant{suspend: true}), do: {:error, :tenant_s...
method tenant_suspended? (line 502) | defp tenant_suspended?(_), do: :ok
method rebalance_check_interval_in_ms (line 504) | defp rebalance_check_interval_in_ms(), do: Application.fetch_env!(:rea...
method schedule_replication_retry (line 506) | defp schedule_replication_retry(%{backoff: backoff} = state) do
method update_syn_replication_conn (line 512) | defp update_syn_replication_conn(tenant_id, pid) do
method start_replication_connection (line 516) | defp start_replication_connection(state) do
FILE: lib/realtime/tenants/connect/check_connection.ex
class Realtime.Tenants.Connect.CheckConnection (line 1) | defmodule Realtime.Tenants.Connect.CheckConnection
method run (line 8) | def run(acc) do
FILE: lib/realtime/tenants/connect/get_tenant.ex
class Realtime.Tenants.Connect.GetTenant (line 1) | defmodule Realtime.Tenants.Connect.GetTenant
method run (line 11) | def run(acc) do
FILE: lib/realtime/tenants/connect/piper.ex
class Realtime.Tenants.Connect.Piper (line 1) | defmodule Realtime.Tenants.Connect.Piper
method run (line 8) | def run(pipers, init) do
FILE: lib/realtime/tenants/connect/reconcile_migrations.ex
class Realtime.Tenants.Connect.ReconcileMigrations (line 1) | defmodule Realtime.Tenants.Connect.ReconcileMigrations
method run (line 17) | def run(%{tenant: tenant, migrations_ran_on_database: migrations_ran_o...
FILE: lib/realtime/tenants/connect/register_process.ex
class Realtime.Tenants.Connect.RegisterProcess (line 1) | defmodule Realtime.Tenants.Connect.RegisterProcess
method run (line 9) | def run(acc) do
FILE: lib/realtime/tenants/janitor.ex
class Realtime.Tenants.Janitor (line 1) | defmodule Realtime.Tenants.Janitor
method start_link (line 27) | def start_link(_args) do
method init (line 46) | def init(%__MODULE__{start_after: start_after} = state) do
method handle_info (line 58) | def handle_info(:delete_old_messages, state) do
method handle_info (line 87) | def handle_info({:DOWN, ref, _, _, :normal}, state) do
method handle_info (line 94) | def handle_info({:DOWN, ref, _, _, :killed}, state) do
method handle_info (line 106) | def handle_info(_, state) do
method timer (line 112) | defp timer(%{timer: timer, randomize: true}), do: timer + :timer.minut...
method timer (line 115) | defp timer(%{timer: timer}), do: timer
method perform_maintenance_tasks (line 117) | defp perform_maintenance_tasks(tenants), do: Enum.map(tenants, &perfor...
method perform_maintenance_task (line 119) | defp perform_maintenance_task(tenant_external_id) do
FILE: lib/realtime/tenants/janitor/maintenance_task.ex
class Realtime.Tenants.Janitor.MaintenanceTask (line 1) | defmodule Realtime.Tenants.Janitor.MaintenanceTask
method run (line 9) | def run(tenant_external_id) do
FILE: lib/realtime/tenants/migrations.ex
class Realtime.Tenants.Migrations (line 1) | defmodule Realtime.Tenants.Migrations
method run_migrations (line 170) | def run_migrations(%Tenant{} = tenant) do
method start_migration (line 192) | def start_migration(attrs) do
method start_link (line 204) | def start_link(%__MODULE__{tenant_external_id: tenant_external_id} = a...
method init (line 209) | def init(%__MODULE__{tenant_external_id: tenant_external_id, settings:...
method migrate (line 226) | defp migrate(settings) do
method create_partitions (line 261) | def create_partitions(db_conn_pid) do
method migrations (line 292) | def migrations(), do: @migrations
FILE: lib/realtime/tenants/rebalancer.ex
class Realtime.Tenants.Rebalancer (line 1) | defmodule Realtime.Tenants.Rebalancer
FILE: lib/realtime/tenants/replication_connection.ex
class Realtime.Tenants.ReplicationConnection (line 1) | defmodule Realtime.Tenants.ReplicationConnection
method start (line 93) | def start(tenant, monitored_pid, init_timeout \\ @default_init_timeout...
method whereis (line 130) | def whereis(tenant_id) do
method health_check (line 138) | def health_check(pid, timeout), do: Postgrex.ReplicationConnection.cal...
method start_link (line 140) | def start_link(%__MODULE__{tenant_id: tenant_id} = attrs) do
method init (line 168) | def init(%__MODULE__{tenant_id: tenant_id, monitored_pid: monitored_pi...
method handle_connect (line 188) | def handle_connect(state) do
method handle_result (line 198) | def handle_result([%Postgrex.Result{num_rows: 1}], %__MODULE__{step: :...
method handle_result (line 203) | def handle_result([%Postgrex.Result{num_rows: 0}], %__MODULE__{step: :...
method handle_result (line 217) | def handle_result([%Postgrex.Result{}], %__MODULE__{step: :check_publi...
method handle_result (line 226) | def handle_result([%Postgrex.Result{num_rows: 0}], %__MODULE__{step: :...
method handle_result (line 235) | def handle_result([%Postgrex.Result{num_rows: 1}], %__MODULE__{step: :...
method handle_result (line 249) | def handle_result([%Postgrex.Result{rows: rows}], %__MODULE__{step: :v...
method handle_result (line 268) | def handle_result(%Postgrex.Error{postgres: %{message: message}}, %__M...
method handle_result (line 272) | def handle_result(%Postgrex.Error{message: message}, %__MODULE__{step:...
method handle_result (line 276) | def handle_result(results, %__MODULE__{step: :start_replication_slot} ...
method handle_result (line 303) | def handle_result(%Postgrex.Error{postgres: %{message: message}}, _sta...
method handle_data (line 326) | def handle_data(e, state) do
method handle_call (line 332) | def handle_call(:health_check, from, state) do
method handle_info (line 339) | def handle_info({:DOWN, _, :process, _, _}, _), do: {:disconnect, :shu...
method handle_info (line 340) | def handle_info(_, state), do: {:noreply, state}
method handle_message (line 342) | defp handle_message(%Decoder.Messages.Begin{commit_timestamp: commit_t...
method handle_message (line 347) | defp handle_message(%Decoder.Messages.Relation{} = msg, state) do
method handle_message (line 369) | defp handle_message(%Decoder.Messages.Insert{} = msg, state) do
method handle_message (line 422) | defp handle_message(_, state), do: {:noreply, state}
method handle_disconnect (line 424) | def handle_disconnect(state) do
method supervisor_spec (line 430) | def supervisor_spec(%Tenant{external_id: tenant_id}) do
method publication_name (line 434) | def publication_name(schema, table) do
method replication_slot_name (line 438) | def replication_slot_name(schema, table) do
method slot_suffix (line 442) | defp slot_suffix, do: Application.get_env(:realtime, :slot_name_suffix)
method tuple_to_map (line 444) | defp tuple_to_map(tuple_data, columns) do
method get_or_error (line 455) | defp get_or_error(map, key, error_type) do
class Wrapper (line 67) | defmodule Wrapper
method start_link (line 73) | def start_link(args, init_timeout) do
method init (line 78) | def init(args) do
FILE: lib/realtime/tenants/replication_connection/watchdog.ex
class Realtime.Tenants.ReplicationConnection.Watchdog (line 1) | defmodule Realtime.Tenants.ReplicationConnection.Watchdog
method start_link (line 14) | def start_link(opts), do: GenServer.start_link(__MODULE__, opts)
method init (line 17) | def init(opts) do
method handle_info (line 51) | def handle_info(:health_check, state) do
FILE: lib/realtime/tenants/repo.ex
class Realtime.Tenants.Repo (line 1) | defmodule Realtime.Tenants.Repo
method all (line 14) | def all(conn, query, result_struct, opts \\ []) do
method one (line 30) | def one(conn, query, result_struct, opts \\ []) do
method insert (line 46) | def insert(conn, changeset, result_struct, opts \\ []) do
method insert_all_entries (line 64) | def insert_all_entries(conn, changesets, result_struct, opts \\ []) do
method del (line 77) | def del(conn, query) do
method update (line 88) | def update(conn, changeset, result_struct, opts \\ []) do
method result_to_single_struct (line 96) | defp result_to_single_struct(
method result_to_single_struct (line 104) | defp result_to_single_struct({:error, _} = error, _, _), do: error
method result_to_single_struct (line 106) | defp result_to_single_struct({:ok, %Postgrex.Result{rows: []}}, _, _) do
method result_to_single_struct (line 110) | defp result_to_single_struct({:ok, %Postgrex.Result{rows: [row], colum...
method result_to_single_struct (line 115) | defp result_to_single_struct({:ok, %Postgrex.Result{num_rows: num_rows...
method result_to_structs (line 119) | defp result_to_structs({:error, _} = error, _), do: error
method result_to_structs (line 121) | defp result_to_structs({:ok, %Postgrex.Result{rows: rows, columns: col...
method insert_query_from_changeset (line 126) | defp insert_query_from_changeset(%{valid?: false} = changeset), do: {:...
method insert_query_from_changeset (line 128) | defp insert_query_from_changeset(changeset) do
method insert_all_query_from_changeset (line 160) | defp insert_all_query_from_changeset(changesets) do
method update_query_from_changeset (line 211) | defp update_query_from_changeset(%{valid?: false} = changeset), do: {:...
method update_query_from_changeset (line 213) | defp update_query_from_changeset(changeset) do
method run_all_query (line 221) | defp run_all_query(conn, query, opts) do
method run_delete_query (line 227) | defp run_delete_query(conn, query) do
method run_query_with_trap (line 233) | defp run_query_with_trap(conn, query, args, opts \\ []) do
FILE: lib/realtime/tenants/repo/migrations/20211116024918_create_realtime_subscription_table.ex
class Realtime.Tenants.Migrations.CreateRealtimeSubscriptionTable (line 1) | defmodule Realtime.Tenants.Migrations.CreateRealtimeSubscriptionTable
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20211116045059_create_realtime_check_filters_trigger.ex
class Realtime.Tenants.Migrations.CreateRealtimeCheckFiltersTrigger (line 1) | defmodule Realtime.Tenants.Migrations.CreateRealtimeCheckFiltersTrigger
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20211116050929_create_realtime_quote_wal2json_function.ex
class Realtime.Tenants.Migrations.CreateRealtimeQuoteWal2jsonFunction (line 1) | defmodule Realtime.Tenants.Migrations.CreateRealtimeQuoteWal2jsonFunction
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20211116051442_create_realtime_check_equality_op_function.ex
class Realtime.Tenants.Migrations.CreateRealtimeCheckEqualityOpFunction (line 1) | defmodule Realtime.Tenants.Migrations.CreateRealtimeCheckEqualityOpFunction
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20211116212300_create_realtime_build_prepared_statement_sql_function.ex
class Realtime.Tenants.Migrations.CreateRealtimeBuildPreparedStatementSqlFunction (line 1) | defmodule Realtime.Tenants.Migrations.CreateRealtimeBuildPreparedStateme...
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20211116213355_create_realtime_cast_function.ex
class Realtime.Tenants.Migrations.CreateRealtimeCastFunction (line 1) | defmodule Realtime.Tenants.Migrations.CreateRealtimeCastFunction
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20211116213934_create_realtime_is_visible_through_filters_function.ex
class Realtime.Tenants.Migrations.CreateRealtimeIsVisibleThroughFiltersFunction (line 1) | defmodule Realtime.Tenants.Migrations.CreateRealtimeIsVisibleThroughFilt...
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20211116214523_create_realtime_apply_rls_function.ex
class Realtime.Tenants.Migrations.CreateRealtimeApplyRlsFunction (line 1) | defmodule Realtime.Tenants.Migrations.CreateRealtimeApplyRlsFunction
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20211122062447_grant_realtime_usage_to_authenticated_role.ex
class Realtime.Tenants.Migrations.GrantRealtimeUsageToAuthenticatedRole (line 1) | defmodule Realtime.Tenants.Migrations.GrantRealtimeUsageToAuthenticatedRole
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20211124070109_enable_realtime_apply_rls_function_postgrest_9_compatibility.ex
class Realtime.Tenants.Migrations.EnableRealtimeApplyRlsFunctionPostgrest9Compatibility (line 1) | defmodule Realtime.Tenants.Migrations.EnableRealtimeApplyRlsFunctionPost...
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20211202204204_update_realtime_subscription_check_filters_function_security.ex
class Realtime.Tenants.Migrations.UpdateRealtimeSubscriptionCheckFiltersFunctionSecurity (line 1) | defmodule Realtime.Tenants.Migrations.UpdateRealtimeSubscriptionCheckFil...
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20211202204605_update_realtime_build_prepared_statement_sql_function_for_compatibility_with_all_types.ex
class Realtime.Tenants.Migrations.UpdateRealtimeBuildPreparedStatementSqlFunctionForCompatibilityWithAllTypes (line 1) | defmodule Realtime.Tenants.Migrations.UpdateRealtimeBuildPreparedStateme...
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20211210212804_enable_generic_subscription_claims.ex
class Realtime.Tenants.Migrations.EnableGenericSubscriptionClaims (line 1) | defmodule Realtime.Tenants.Migrations.EnableGenericSubscriptionClaims
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20211228014915_add_wal_payload_on_errors_in_apply_rls_function.ex
class Realtime.Tenants.Migrations.AddWalPayloadOnErrorsInApplyRlsFunction (line 1) | defmodule Realtime.Tenants.Migrations.AddWalPayloadOnErrorsInApplyRlsFun...
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20220107221237_update_change_timestamp_to_iso_8601_zulu_format.ex
class Realtime.Tenants.Migrations.UpdateChangeTimestampToIso8601ZuluFormat (line 1) | defmodule Realtime.Tenants.Migrations.UpdateChangeTimestampToIso8601Zulu...
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20220228202821_update_subscription_check_filters_function_dynamic_table_name.ex
class Realtime.Tenants.Migrations.UpdateSubscriptionCheckFiltersFunctionDynamicTableName (line 1) | defmodule Realtime.Tenants.Migrations.UpdateSubscriptionCheckFiltersFunc...
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20220312004840_update_apply_rls_function_to_apply_iso_8601.ex
class Realtime.Tenants.Migrations.UpdateApplyRlsFunctionToApplyIso8601 (line 1) | defmodule Realtime.Tenants.Migrations.UpdateApplyRlsFunctionToApplyIso8601
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20220603231003_add_quoted_regtypes_support.ex
class Realtime.Tenants.Migrations.AddQuotedRegtypesSupport (line 1) | defmodule Realtime.Tenants.Migrations.AddQuotedRegtypesSupport
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20220603232444_add_output_for_data_less_than_equal_64_bytes_when_payload_too_large.ex
class Realtime.Tenants.Migrations.AddOutputForDataLessThanEqual64BytesWhenPayloadTooLarge (line 1) | defmodule Realtime.Tenants.Migrations.AddOutputForDataLessThanEqual64Byt...
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20220615214548_add_quoted_regtypes_backward_compatibility_support.ex
class Realtime.Tenants.Migrations.AddQuotedRegtypesBackwardCompatibilitySupport (line 1) | defmodule Realtime.Tenants.Migrations.AddQuotedRegtypesBackwardCompatibi...
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20220712093339_recreate_realtime_build_prepared_statement_sql_function.ex
class Realtime.Tenants.Migrations.RecreateRealtimeBuildPreparedStatementSqlFunction (line 1) | defmodule Realtime.Tenants.Migrations.RecreateRealtimeBuildPreparedState...
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20220908172859_null_passes_filters_recreate_is_visible_through_filters.ex
class Realtime.Tenants.Migrations.NullPassesFiltersRecreateIsVisibleThroughFilters (line 1) | defmodule Realtime.Tenants.Migrations.NullPassesFiltersRecreateIsVisible...
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20220916233421_update_apply_rls_function_to_pass_through_delete_events_on_filter.ex
class Realtime.Tenants.Migrations.UpdateApplyRlsFunctionToPassThroughDeleteEventsOnFilter (line 1) | defmodule Realtime.Tenants.Migrations.UpdateApplyRlsFunctionToPassThroug...
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20230119133233_millisecond_precision_for_walrus.ex
class Realtime.Tenants.Migrations.MillisecondPrecisionForWalrus (line 1) | defmodule Realtime.Tenants.Migrations.MillisecondPrecisionForWalrus
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20230128025114_add_in_op_to_filters.ex
class Realtime.Tenants.Migrations.AddInOpToFilters (line 1) | defmodule Realtime.Tenants.Migrations.AddInOpToFilters
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20230128025212_enable_filtering_on_delete_record.ex
class Realtime.Tenants.Migrations.EnableFilteringOnDeleteRecord (line 1) | defmodule Realtime.Tenants.Migrations.EnableFilteringOnDeleteRecord
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20230227211149_update_subscription_check_filters_for_in_filter_non_text_types.ex
class Realtime.Tenants.Migrations.UpdateSubscriptionCheckFiltersForInFilterNonTextTypes (line 1) | defmodule Realtime.Tenants.Migrations.UpdateSubscriptionCheckFiltersForI...
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20230228184745_convert_commit_timestamp_to_utc.ex
class Realtime.Tenants.Migrations.ConvertCommitTimestampToUtc (line 1) | defmodule Realtime.Tenants.Migrations.ConvertCommitTimestampToUtc
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20230308225145_output_full_record_when_unchanged_toast.ex
class Realtime.Tenants.Migrations.OutputFullRecordWhenUnchangedToast (line 1) | defmodule Realtime.Tenants.Migrations.OutputFullRecordWhenUnchangedToast
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20230328144023_create_list_changes_function.ex
class Realtime.Tenants.Migrations.CreateListChangesFunction (line 1) | defmodule Realtime.Tenants.Migrations.CreateListChangesFunction
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20231018144023_create_channels.ex
class Realtime.Tenants.Migrations.CreateChannels (line 1) | defmodule Realtime.Tenants.Migrations.CreateChannels
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20231204144023_set_required_grants.ex
class Realtime.Tenants.Migrations.SetRequiredGrants (line 1) | defmodule Realtime.Tenants.Migrations.SetRequiredGrants
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20231204144024_create_rls_helper_functions.ex
class Realtime.Tenants.Migrations.CreateRlsHelperFunctions (line 1) | defmodule Realtime.Tenants.Migrations.CreateRlsHelperFunctions
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20231204144025_enable_channels_rls.ex
class Realtime.Tenants.Migrations.EnableChannelsRls (line 1) | defmodule Realtime.Tenants.Migrations.EnableChannelsRls
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20240108234812_add_channels_column_for_write_check.ex
class Realtime.Tenants.Migrations.AddChannelsColumnForWriteCheck (line 1) | defmodule Realtime.Tenants.Migrations.AddChannelsColumnForWriteCheck
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20240109165339_add_update_grant_to_channels.ex
class Realtime.Tenants.Migrations.AddUpdateGrantToChannels (line 1) | defmodule Realtime.Tenants.Migrations.AddUpdateGrantToChannels
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20240227174441_add_broadcast_permissions_table.ex
class Realtime.Tenants.Migrations.AddBroadcastsPoliciesTable (line 1) | defmodule Realtime.Tenants.Migrations.AddBroadcastsPoliciesTable
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20240311171622_add_insert_and_delete_grant_to_channels.ex
class Realtime.Tenants.Migrations.AddInsertAndDeleteGrantToChannels (line 1) | defmodule Realtime.Tenants.Migrations.AddInsertAndDeleteGrantToChannels
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20240321100241_add_presences_permissions_table.ex
class Realtime.Tenants.Migrations.AddPresencesPoliciesTable (line 1) | defmodule Realtime.Tenants.Migrations.AddPresencesPoliciesTable
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20240401105812_create_realtime_admin_and_move_ownership.ex
class Realtime.Tenants.Migrations.CreateRealtimeAdminAndMoveOwnership (line 1) | defmodule Realtime.Tenants.Migrations.CreateRealtimeAdminAndMoveOwnership
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20240418121054_remove_check_columns.ex
class Realtime.Tenants.Migrations.RemoveCheckColumns (line 1) | defmodule Realtime.Tenants.Migrations.RemoveCheckColumns
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20240523004032_redefine_authorization_tables.ex
class Realtime.Tenants.Migrations.RedefineAuthorizationTables (line 1) | defmodule Realtime.Tenants.Migrations.RedefineAuthorizationTables
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20240618124746_fix_walrus_role_handling.ex
class Realtime.Tenants.Migrations.FixWalrusRoleHandling (line 1) | defmodule Realtime.Tenants.Migrations.FixWalrusRoleHandling
method change (line 5) | def change do
FILE: lib/realtime/tenants/repo/migrations/20240801235015_unlogged_messages_table.ex
class Realtime.Tenants.Migrations.UnloggedMessagesTable (line 1) | defmodule Realtime.Tenants.Migrations.UnloggedMessagesTable
method change (line 5) | def change do
FILE: lib/realtime/tenants/repo/migrations/20240805133720_logged_messages_table.ex
class Realtime.Tenants.Migrations.LoggedMessagesTable (line 1) | defmodule Realtime.Tenants.Migrations.LoggedMessagesTable
method change (line 5) | def change do
FILE: lib/realtime/tenants/repo/migrations/20240827160934_filter_delete_postgres_changes.ex
class Realtime.Tenants.Migrations.FilterDeletePostgresChanges (line 1) | defmodule Realtime.Tenants.Migrations.FilterDeletePostgresChanges
method change (line 5) | def change do
FILE: lib/realtime/tenants/repo/migrations/20240919163303_add_payload_to_messages.ex
class Realtime.Tenants.Migrations.AddPayloadToMessages (line 1) | defmodule Realtime.Tenants.Migrations.AddPayloadToMessages
method change (line 5) | def change do
FILE: lib/realtime/tenants/repo/migrations/20240919163305_change_messages_id_type.ex
class Realtime.Tenants.Migrations.ChangeMessagesIdType (line 1) | defmodule Realtime.Tenants.Migrations.ChangeMessagesIdType
method change (line 5) | def change do
FILE: lib/realtime/tenants/repo/migrations/20241019105805_uuid_auto_generation.ex
class Realtime.Tenants.Migrations.UuidAutoGeneration (line 1) | defmodule Realtime.Tenants.Migrations.UuidAutoGeneration
method change (line 5) | def change do
FILE: lib/realtime/tenants/repo/migrations/20241030150047_messages_partitioning.ex
class Realtime.Tenants.Migrations.MessagesPartitioning (line 1) | defmodule Realtime.Tenants.Migrations.MessagesPartitioning
method change (line 5) | def change do
FILE: lib/realtime/tenants/repo/migrations/20241108114728_messages_using_uuid.ex
class Realtime.Tenants.Migrations.MessagesUsingUuid (line 1) | defmodule Realtime.Tenants.Migrations.MessagesUsingUuid
method change (line 5) | def change do
FILE: lib/realtime/tenants/repo/migrations/20241121104152_fix_send_function_.ex
class Realtime.Tenants.Migrations.FixSendFunction (line 1) | defmodule Realtime.Tenants.Migrations.FixSendFunction
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20241130184212_recreate_entity_index_using_btree.ex
class Realtime.Tenants.Migrations.RecreateEntityIndexUsingBtree (line 1) | defmodule Realtime.Tenants.Migrations.RecreateEntityIndexUsingBtree
method change (line 5) | def change do
FILE: lib/realtime/tenants/repo/migrations/20241220035512_fix_send_function_partition_creation.ex
class Realtime.Tenants.Migrations.FixSendFunctionPartitionCreation (line 1) | defmodule Realtime.Tenants.Migrations.FixSendFunctionPartitionCreation
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20241220123912_realtime_send_handle_exceptions_remove_partition_creation.ex
class Realtime.Tenants.Migrations.RealtimeSendHandleExceptionsRemovePartitionCreation (line 1) | defmodule Realtime.Tenants.Migrations.RealtimeSendHandleExceptionsRemove...
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20241224161212_realtime_send_sets_config.ex
class Realtime.Tenants.Migrations.RealtimeSendSetsConfig (line 1) | defmodule Realtime.Tenants.Migrations.RealtimeSendSetsConfig
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20250107150512_realtime_subscription_unlogged.ex
class Realtime.Tenants.Migrations.RealtimeSubscriptionUnlogged (line 1) | defmodule Realtime.Tenants.Migrations.RealtimeSubscriptionUnlogged
method change (line 5) | def change do
FILE: lib/realtime/tenants/repo/migrations/20250110162412_realtime_subscription_logged.ex
class Realtime.Tenants.Migrations.RealtimeSubscriptionLogged (line 1) | defmodule Realtime.Tenants.Migrations.RealtimeSubscriptionLogged
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20250123174212_remove_unused_publications.ex
class Realtime.Tenants.Migrations.RemoveUnusedPublications (line 1) | defmodule Realtime.Tenants.Migrations.RemoveUnusedPublications
method change (line 5) | def change do
FILE: lib/realtime/tenants/repo/migrations/20250128220012_realtime_send_sets_topic_config.ex
class Realtime.Tenants.Migrations.RealtimeSendSetsTopicConfig (line 1) | defmodule Realtime.Tenants.Migrations.RealtimeSendSetsTopicConfig
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20250506224012_subscription_index_bridging_disabled.ex
class Realtime.Tenants.Migrations.SubscriptionIndexBridgingDisabled (line 1) | defmodule Realtime.Tenants.Migrations.SubscriptionIndexBridgingDisabled
method change (line 5) | def change do
FILE: lib/realtime/tenants/repo/migrations/20250523164012_run_subscription_index_bridging_disabled.ex
class Realtime.Tenants.Migrations.RunSubscriptionIndexBridgingDisabled (line 1) | defmodule Realtime.Tenants.Migrations.RunSubscriptionIndexBridgingDisabled
method change (line 5) | def change do
FILE: lib/realtime/tenants/repo/migrations/20250714121412_broadcast_send_error_logging.ex
class Realtime.Tenants.Migrations.BroadcastSendErrorLogging (line 1) | defmodule Realtime.Tenants.Migrations.BroadcastSendErrorLogging
method change (line 5) | def change do
FILE: lib/realtime/tenants/repo/migrations/20250905041441_create_messages_replay_index.ex
class Realtime.Tenants.Migrations.CreateMessagesReplayIndex (line 1) | defmodule Realtime.Tenants.Migrations.CreateMessagesReplayIndex
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20251103001201_broadcast_send_include_payload_id.ex
class Realtime.Tenants.Migrations.BroadcastSendIncludePayloadId (line 1) | defmodule Realtime.Tenants.Migrations.BroadcastSendIncludePayloadId
method change (line 6) | def change do
FILE: lib/realtime/tenants/repo/migrations/20251120212548_add_action_to_subscriptions.ex
class Realtime.Tenants.Migrations.AddActionToSubscriptions (line 1) | defmodule Realtime.Tenants.Migrations.AddActionToSubscriptions
method up (line 5) | def up do
method down (line 20) | def down do
FILE: lib/realtime/tenants/repo/migrations/20251120215549_filter_action_postgres_changes.ex
class Realtime.Tenants.Migrations.FilterActionPostgresChanges (line 1) | defmodule Realtime.Tenants.Migrations.FilterActionPostgresChanges
method up (line 5) | def up do
method down (line 314) | def down do
FILE: lib/realtime/tenants/repo/migrations/20260218120000_fix_bytea_double_encoding_in_cast.ex
class Realtime.Tenants.Migrations.FixByteaDoubleEncodingInCast (line 1) | defmodule Realtime.Tenants.Migrations.FixByteaDoubleEncodingInCast
method up (line 6) | def up do
method down (line 26) | def down do
FILE: lib/realtime/users_counter.ex
class Realtime.UsersCounter (line 1) | defmodule Realtime.UsersCounter
method already_counted? (line 16) | def already_counted?(pid, tenant_id), do: Beacon.local_member?(:users,...
method local_tenants (line 20) | def local_tenants(), do: Beacon.local_groups(:users)
method tenant_users (line 26) | def tenant_users(tenant_id), do: Beacon.member_count(:users, tenant_id)
method tenant_counts (line 32) | def tenant_counts(), do: Beacon.member_counts(:users)
method local_tenant_counts (line 38) | def local_tenant_counts(), do: Beacon.local_member_counts(:users)
FILE: lib/realtime_web.ex
class RealtimeWeb (line 1) | defmodule RealtimeWeb
method static_paths (line 20) | def static_paths, do: ~w(assets fonts images favicon.svg robots.txt wo...
method controller (line 22) | def controller do
method view (line 34) | def view do
method live_view (line 49) | def live_view do
method live_component (line 58) | def live_component do
method component (line 66) | def component do
method router (line 74) | def router do
method channel (line 85) | def channel do
method view_helpers (line 92) | defp view_helpers do
method verified_routes (line 121) | def verified_routes do
FILE: lib/realtime_web/api_spec.ex
class RealtimeWeb.ApiSpec (line 1) | defmodule RealtimeWeb.ApiSpec
method spec (line 17) | def spec do
FILE: lib/realtime_web/channels/auth/channels_authorization.ex
class RealtimeWeb.ChannelsAuthorization (line 1) | defmodule RealtimeWeb.ChannelsAuthorization
method authorize (line 18) | def authorize(_token, _jwt_secret, _jwt_jwks), do: {:error, :invalid_t...
method authorize_conn (line 20) | def authorize_conn(token, jwt_secret, jwt_jwks) do
method clean_token (line 39) | defp clean_token(token), do: Regex.replace(~r/\s|\n/, URI.decode(token...
FILE: lib/realtime_web/channels/auth/jwt_verification.ex
class RealtimeWeb.JwtVerification (line 1) | defmodule RealtimeWeb.JwtVerification
method verify (line 50) | def verify(_token, _jwt_secret, _jwt_jwks), do: {:error, :not_a_string}
method check_claims_format (line 52) | defp check_claims_format(token) do
method check_header_format (line 60) | defp check_header_format(token) do
method generate_signer (line 124) | defp generate_signer(_header, _jwt_secret, _jwt_jwks), do: {:error, :e...
class JwtAuthToken (line 8) | defmodule JwtAuthToken
method token_config (line 13) | def token_config do
method add_claim_validator (line 21) | defp add_claim_validator(claims, "exp") do
method add_claim_validator (line 26) | defp add_claim_validator(claims, claim_key, expected_val) do
FILE: lib/realtime_web/channels/payloads/broadcast.ex
class RealtimeWeb.Channels.Payloads.Broadcast (line 1) | defmodule RealtimeWeb.Channels.Payloads.Broadcast
method changeset (line 16) | def changeset(broadcast, attrs) do
FILE: lib/realtime_web/channels/payloads/broadcast/replay.ex
class RealtimeWeb.Channels.Payloads.Broadcast.Replay (line 1) | defmodule RealtimeWeb.Channels.Payloads.Broadcast.Replay
method changeset (line 14) | def changeset(broadcast, attrs) do
FILE: lib/realtime_web/channels/payloads/config.ex
class RealtimeWeb.Channels.Payloads.Config (line 1) | defmodule RealtimeWeb.Channels.Payloads.Config
method changeset (line 20) | def changeset(config, attrs) do
FILE: lib/realtime_web/channels/payloads/flexible_boolean.ex
class RealtimeWeb.Channels.Payloads.FlexibleBoolean (line 1) | defmodule RealtimeWeb.Channels.Payloads.FlexibleBoolean
method type (line 14) | def type, do: :boolean
method cast (line 27) | def cast(_), do: :error
method load (line 30) | def load(value), do: {:ok, value}
method dump (line 34) | def dump(_), do: :error
FILE: lib/realtime_web/channels/payloads/join.ex
class RealtimeWeb.Channels.Payloads.Join (line 1) | defmodule RealtimeWeb.Channels.Payloads.Join
method changeset (line 17) | def changeset(join, attrs) do
method validate (line 24) | def validate(params) do
method presence_enabled? (line 35) | def presence_enabled?(%__MODULE__{config: %Config{presence: %Presence{...
method presence_enabled? (line 36) | def presence_enabled?(_), do: true
method presence_key (line 38) | def presence_key(%__MODULE__{config: %Config{presence: %Presence{key: ...
method presence_key (line 39) | def presence_key(%__MODULE__{config: %Config{presence: %Presence{key: ...
method presence_key (line 40) | def presence_key(_), do: UUID.uuid1()
method ack_broadcast? (line 42) | def ack_broadcast?(%__MODULE__{config: %Config{broadcast: %Broadcast{a...
method ack_broadcast? (line 43) | def ack_broadcast?(_), do: false
method self_broadcast? (line 45) | def self_broadcast?(%__MODULE__{config: %Config{broadcast: %Broadcast{...
method self_broadcast? (line 46) | def self_broadcast?(_), do: false
method private? (line 48) | def private?(%__MODULE__{config: %Config{private: private}}), do: private
method private? (line 49) | def private?(_), do: false
method error_message (line 51) | def error_message(_field, meta) do
method format_type (line 59) | defp format_type(RealtimeWeb.Channels.Payloads.FlexibleBoolean), do: :...
method format_type (line 60) | defp format_type(type), do: type
FILE: lib/realtime_web/channels/payloads/postgres_change.ex
class RealtimeWeb.Channels.Payloads.PostgresChange (line 1) | defmodule RealtimeWeb.Channels.Payloads.PostgresChange
method changeset (line 16) | def changeset(postgres_change, attrs) do
FILE: lib/realtime_web/channels/payloads/presence.ex
class RealtimeWeb.Channels.Payloads.Presence (line 1) | defmodule RealtimeWeb.Channels.Payloads.Presence
method changeset (line 15) | def changeset(presence, attrs) do
FILE: lib/realtime_web/channels/presence.ex
class RealtimeWeb.Presence (line 1) | defmodule RealtimeWeb.Presence
FILE: lib/realtime_web/channels/realtime_channel.ex
class RealtimeWeb.RealtimeChannel (line 1) | defmodule RealtimeWeb.RealtimeChannel
method join (line 37) | def join("realtime:", _params, socket) do
method join (line 41) | def join("realtime:" <> sub_topic = topic, params, socket) do
method handle_info (line 251) | def handle_info({:replay, messages}, socket) do
method handle_info (line 262) | def handle_info(:update_rate_counter, socket) do
method handle_info (line 275) | def handle_info(%{event: "postgres_cdc_rls_down"}, socket) do
method handle_info (line 283) | def handle_info(_msg, %{assigns: %{policies: %Policies{broadcast: %Bro...
method handle_info (line 288) | def handle_info(%{event: type, payload: payload} = msg, socket) do
method handle_info (line 295) | def handle_info(:postgres_subscribe, %{assigns: %{channel_name: channe...
method handle_info (line 355) | def handle_info(:confirm_token, %{assigns: %{pg_change_params: pg_chan...
method handle_info (line 372) | def handle_info(:disconnect, %{assigns: %{channel_name: channel_name}}...
method handle_info (line 378) | def handle_info(:sync_presence, %{assigns: %{presence_enabled?: true}}...
method handle_info (line 388) | def handle_info(:sync_presence, socket), do: {:noreply, socket}
method handle_info (line 389) | def handle_info(_, socket), do: {:noreply, socket}
method handle_in (line 392) | def handle_in("broadcast", payload, %{assigns: %{private?: true}} = so...
method handle_in (line 404) | def handle_in("broadcast", payload, %{assigns: %{private?: false}} = s...
method handle_in (line 408) | def handle_in("presence", payload, %{assigns: %{private?: true}} = soc...
method handle_in (line 431) | def handle_in("presence", payload, %{assigns: %{private?: false}} = so...
method handle_in (line 451) | def handle_in("access_token", %{"access_token" => "sb_" <> _}, socket) do
method handle_in (line 519) | def handle_in(type, payload, socket) do
method terminate (line 531) | def terminate(reason, %{transport_pid: transport_pid}) do
method postgres_subscribe (line 538) | defp postgres_subscribe(min \\ 1, max \\ 3) do
method backoff (line 542) | defp backoff(min, max) do
method limit_joins (line 547) | def limit_joins(tenant, socket) do
method limit_channels (line 566) | def limit_channels(tenant, %{transport_pid: pid}) do
method limit_max_users (line 577) | defp limit_max_users(tenant, transport_pid) do
method assign_counter (line 586) | defp assign_counter(socket, tenant) do
method assign_presence_counter (line 593) | defp assign_presence_counter(socket, tenant) do
method assign_client_presence_rate_limit (line 601) | defp assign_client_presence_rate_limit(socket, tenant) do
method count (line 626) | defp count(%{assigns: %{rate_counter: counter}}), do: GenCounter.add(c...
method presence_key (line 628) | defp presence_key(params) do
method assign_access_token (line 635) | defp assign_access_token(%{assigns: %{tenant_token: tenant_token}} = s...
method confirm_token (line 658) | defp confirm_token(%{assigns: assigns}) do
method push_system_message (line 715) | defp push_system_message(extension, socket, status, message, channel_n...
method new_api? (line 724) | defp new_api?(%{"config" => _}), do: true
method new_api? (line 725) | defp new_api?(_), do: false
method pg_change_params (line 727) | defp pg_change_params(true, params, channel_pid, claims, _) do
method pg_change_params (line 746) | defp pg_change_params(false, _, channel_pid, claims, sub_topic) do
method postgres_cdc_subscribe (line 764) | defp postgres_cdc_subscribe(_tenant, %{pg_change_params: []}), do: []
method postgres_cdc_subscribe (line 766) | defp postgres_cdc_subscribe(tenant, opts) do
method add_id_to_postgres_changes (line 793) | defp add_id_to_postgres_changes(pg_change_params) do
method assign_authorization_context (line 800) | defp assign_authorization_context(socket, topic, claims) do
method maybe_assign_policies (line 843) | defp maybe_assign_policies(_, _, socket), do: {:ok, assign(socket, pol...
method only_private? (line 845) | defp only_private?(tenant, %{assigns: %{private?: private?}}) do
method maybe_replay_messages (line 853) | defp maybe_replay_messages(%{"broadcast" => %{"replay" => _}}, _sub_to...
method maybe_replay_messages (line 879) | defp maybe_replay_messages(_, _, _, _, _), do: {:ok, MapSet.new()}
method presence_enabled? (line 881) | defp presence_enabled?(client_enabled?, %Tenant{presence_enabled: tena...
method max_heap_size (line 885) | defp max_heap_size(), do: Application.fetch_env!(:realtime, :websocket...
FILE: lib/realtime_web/channels/realtime_channel/assign.ex
class RealtimeWeb.RealtimeChannel.Assigns (line 1) | defmodule RealtimeWeb.RealtimeChannel.Assigns
FILE: lib/realtime_web/channels/realtime_channel/broadcast_handler.ex
class RealtimeWeb.RealtimeChannel.BroadcastHandler (line 1) | defmodule RealtimeWeb.RealtimeChannel.BroadcastHandler
method handle (line 22) | def handle(payload, %{assigns: %{private?: false}} = socket), do: hand...
method handle (line 25) | def handle(payload, db_conn, %{assigns: %{private?: true}} = socket) do
method handle (line 75) | def handle(payload, _db_conn, %{assigns: %{private?: false}} = socket) do
method send_message (line 105) | defp send_message(tenant_id, self_broadcast, tenant_topic, payload) do
method build_broadcast (line 133) | defp build_broadcast(topic, {user_event, user_payload_encoding, user_p...
method build_broadcast (line 142) | defp build_broadcast(topic, payload) do
method increment_rate_counter (line 146) | defp increment_rate_counter(%{assigns: %{policies: %Policies{broadcast...
method increment_rate_counter (line 150) | defp increment_rate_counter(%{assigns: %{rate_counter: counter}} = soc...
method run_authorization_check (line 155) | defp run_authorization_check(
method run_authorization_check (line 163) | defp run_authorization_check(socket, _db_conn, _authorization_context) do
FILE: lib/realtime_web/channels/realtime_channel/logging.ex
class RealtimeWeb.RealtimeChannel.Logging (line 1) | defmodule RealtimeWeb.RealtimeChannel.Logging
method log_error (line 21) | def log_error(socket, code, msg) do
method log_warning (line 33) | def log_warning(socket, code, msg) do
method maybe_log_error (line 43) | def maybe_log_error(socket, code, msg), do: maybe_log(socket, :error, ...
method maybe_log_warning (line 49) | def maybe_log_warning(socket, code, msg), do: maybe_log(socket, :warni...
method maybe_log_info (line 55) | def maybe_log_info(socket, msg), do: maybe_log(socket, :info, nil, msg)
method build_msg (line 57) | defp build_msg(code, msg) do
method log (line 62) | defp log(%{assigns: %{tenant: tenant, access_token: access_token}}, le...
method maybe_log (line 68) | defp maybe_log(%{assigns: %{log_level: log_level}} = socket, level, co...
method system_errors (line 82) | def system_errors, do: @system_errors
method emit_system_error (line 87) | defp emit_system_error(_, _), do: nil
method stringify! (line 90) | defp stringify!(msg), do: inspect(msg, pretty: true)
method update_metadata_with_token_claims (line 92) | defp update_metadata_with_token_claims(nil), do: nil
method update_metadata_with_token_claims (line 94) | defp update_metadata_with_token_claims(token) do
FILE: lib/realtime_web/channels/realtime_channel/message_dispatcher.ex
class RealtimeWeb.RealtimeChannel.MessageDispatcher (line 1) | defmodule RealtimeWeb.RealtimeChannel.MessageDispatcher
method fastlane_metadata (line 10) | def fastlane_metadata(fastlane_pid, serializer, topic, log_level, tena...
method dispatch (line 23) | def dispatch(subscribers, from, %Broadcast{event: @presence_diff} = ms...
method dispatch (line 47) | def dispatch(subscribers, from, msg) do
method maybe_log (line 86) | defp maybe_log(_level, _join_topic, _msg, _tenant_id), do: :ok
method do_dispatch (line 88) | defp do_dispatch(msg, fastlane_pid, serializer, join_topic, cache, ten...
method fastlane! (line 118) | defp fastlane!(Phoenix.Socket.V1.JSONSerializer = serializer, %UserBro...
method fastlane! (line 124) | defp fastlane!(serializer, msg), do: {:ok, serializer.fastlane!(msg)}
method tenant_id (line 126) | defp tenant_id([{_pid, {:rc_fastlane, _, _, _, _, tenant_id, _}} | _])...
method tenant_id (line 127) | defp tenant_id(_), do: nil
method increment_presence_counter (line 135) | defp increment_presence_counter(_tenant_id, _event, _count), do: :ok
method message_id (line 137) | defp message_id(%Broadcast{payload: %{"meta" => %{"id" => id}}}), do: id
method message_id (line 138) | defp message_id(_), do: nil
method already_replayed? (line 140) | defp already_replayed?(nil, _replayed_message_ids), do: false
method already_replayed? (line 141) | defp already_replayed?(message_id, replayed_message_ids), do: MapSet.m...
FILE: lib/realtime_web/channels/realtime_channel/presence_handler.ex
class RealtimeWeb.RealtimeChannel.PresenceHandler (line 1) | defmodule RealtimeWeb.RealtimeChannel.PresenceHandler
method sync (line 29) | def sync(%{assigns: %{presence_enabled?: false}}), do: :ok
method handle (line 64) | def handle(%{"event" => event} = payload, db_conn, socket) do
method handle (line 74) | def handle(_, _, socket), do: {:ok, socket}
method handle_presence_event (line 107) | defp handle_presence_event("untrack", _, _, socket) do
method handle_presence_event (line 113) | defp handle_presence_event(event, _, _, _) do
method track (line 118) | defp track(socket, payload) do
method check_track_payload (line 161) | defp check_track_payload(assigns, new_payload) do
method presence_dirty_list (line 169) | defp presence_dirty_list(topic) do
method limit_presence_event (line 178) | defp limit_presence_event(socket) do
method limit_client_presence_event (line 191) | defp limit_client_presence_event(socket) do
method validate_payload_size (line 215) | defp validate_payload_size(tenant, payload), do: Tenants.validate_payl...
FILE: lib/realtime_web/channels/realtime_channel/tracker.ex
class RealtimeWeb.RealtimeChannel.Tracker (line 1) | defmodule RealtimeWeb.RealtimeChannel.Tracker
method track (line 19) | def track(pid), do: :ets.update_counter(@table, pid, 1, {pid, 0})
method untrack (line 25) | def untrack(pid), do: :ets.update_counter(@table, pid, -1, {pid, 0})
method count (line 31) | def count(pid) do
method list_pids (line 42) | def list_pids, do: :ets.tab2list(@table)
method start_link (line 44) | def start_link(opts) do
method init (line 59) | def init(opts) do
method handle_info (line 66) | def handle_info(:check_channels, state) do
method chunked_killing (line 73) | defp chunked_killing(cont \\ nil) do
method table_name (line 87) | def table_name, do: @table
FILE: lib/realtime_web/channels/socket_disconnect.ex
class RealtimeWeb.SocketDisconnect (line 1) | defmodule RealtimeWeb.SocketDisconnect
method distributed_disconnect (line 37) | def distributed_disconnect(%Tenant{external_id: external_id}), do: dis...
method distributed_disconnect (line 39) | def distributed_disconnect(external_id) do
method disconnect (line 49) | def disconnect(%Tenant{external_id: external_id}), do: disconnect(exte...
method disconnect (line 51) | def disconnect(tenant_external_id) do
FILE: lib/realtime_web/channels/tenant_rate_limiters.ex
class RealtimeWeb.TenantRateLimiters (line 1) | defmodule RealtimeWeb.TenantRateLimiters
method check_tenant (line 12) | def check_tenant(tenant) do
method max_concurrent_users_check (line 18) | defp max_concurrent_users_check(%Tenant{max_concurrent_users: max_conn...
method max_joins_per_second_check (line 26) | defp max_joins_per_second_check(%Tenant{max_joins_per_second: max_join...
FILE: lib/realtime_web/channels/user_socket.ex
class RealtimeWeb.UserSocket (line 1) | defmodule RealtimeWeb.UserSocket
method handle_in (line 5) | def handle_in({payload, opts}, {_state, socket} = full_state) do
method handle_info (line 29) | def handle_info(
method id (line 61) | def id(%{assigns: %{tenant: tenant}}), do: subscribers_id(tenant)
method subscribers_id (line 64) | def subscribers_id(tenant), do: "user_socket:" <> tenant
method connect (line 67) | def connect(params, socket, opts) do
method access_token (line 143) | defp access_token(params, headers) do
method log_level (line 150) | defp log_level(params) do
method max_heap_size (line 157) | defp max_heap_size(), do: Application.fetch_env!(:realtime, :websocket...
method measure_traffic_interval_in_ms (line 158) | defp measure_traffic_interval_in_ms(), do: Application.fetch_env!(:rea...
method collect_traffic_telemetry (line 160) | defp collect_traffic_telemetry(nil, _tenant_external_id, previous_recv...
method collect_traffic_telemetry (line 163) | defp collect_traffic_telemetry(transport_pid, tenant_external_id, prev...
FILE: lib/realtime_web/controllers/broadcast_controller.ex
class RealtimeWeb.BroadcastController (line 1) | defmodule RealtimeWeb.BroadcastController
method broadcast (line 35) | def broadcast(%{assigns: %{tenant: tenant}} = conn, attrs) do
FILE: lib/realtime_web/controllers/fallback_controller.ex
class RealtimeWeb.FallbackController (line 1) | defmodule RealtimeWeb.FallbackController
method call (line 13) | def call(conn, {:error, :not_found}) do
method call (line 22) | def call(conn, {:error, %Ecto.Changeset{} = changeset}) do
method call (line 43) | def call(conn, {:error, %Ecto.Changeset{valid?: false} = changeset}) do
method call (line 55) | def call(conn, {:error, _}) do
method call (line 62) | def call(conn, %Ecto.Changeset{valid?: false} = changeset) do
method call (line 74) | def call(conn, response) do
FILE: lib/realtime_web/controllers/legacy_metrics_controller.ex
class RealtimeWeb.LegacyMetricsController (line 1) | defmodule RealtimeWeb.LegacyMetricsController
method index (line 8) | def index(conn, _) do
method region (line 12) | def region(conn, %{"region" => region}) do
method get_combined_metrics (line 16) | def get_combined_metrics do
method serve_metrics (line 21) | defp serve_metrics(conn, nodes, label) do
method collect_metrics (line 33) | defp collect_metrics(nodes, conn) do
method bump_max_heap_size (line 66) | defp bump_max_heap_size do
FILE: lib/realtime_web/controllers/metrics_controller.ex
class RealtimeWeb.MetricsController (line 1) | defmodule RealtimeWeb.MetricsController
method index (line 8) | def index(conn, _) do
method tenant (line 12) | def tenant(conn, _) do
method region (line 16) | def region(conn, %{"region" => region}) do
method region_tenant (line 20) | def region_tenant(conn, %{"region" => region}) do
method serve_metrics (line 24) | defp serve_metrics(conn, nodes, metrics_fun, label) do
method collect_metrics (line 36) | defp collect_metrics(nodes, metrics_fun, conn) do
method get_global_metrics (line 61) | def get_global_metrics do
method get_tenant_metrics (line 66) | def get_tenant_metrics do
method get_metrics (line 72) | def get_metrics, do: get_global_metrics()
method bump_max_heap_size (line 74) | defp bump_max_heap_size do
FILE: lib/realtime_web/controllers/page_controller.ex
class RealtimeWeb.PageController (line 1) | defmodule RealtimeWeb.PageController
method index (line 4) | def index(conn, _params) do
method healthcheck (line 8) | def healthcheck(conn, _params) do
FILE: lib/realtime_web/controllers/ping_controller.ex
class RealtimeWeb.PingController (line 1) | defmodule RealtimeWeb.PingController
method ping (line 4) | def ping(conn, _params) do
FILE: lib/realtime_web/controllers/tenant_controller.ex
class RealtimeWeb.TenantController (line 1) | defmodule RealtimeWeb.TenantController
method index (line 51) | def index(conn, _params) do
method show (line 76) | def show(conn, %{"tenant_id" => id}) do
method create (line 105) | def create(conn, %{"tenant" => params}) do
method update (line 134) | def update(conn, %{"tenant_id" => external_id, "tenant" => tenant_para...
method delete (line 187) | def delete(conn, %{"tenant_id" => tenant_id}) do
method reload (line 227) | def reload(conn, %{"tenant_id" => tenant_id}) do
method shutdown (line 260) | def shutdown(conn, %{"tenant_id" => tenant_id}) do
method health (line 291) | def health(conn, %{"tenant_id" => tenant_id}) do
method set_observability_attributes (line 299) | defp set_observability_attributes(conn, _opts) do
FILE: lib/realtime_web/dashboard/process_dump.ex
class Realtime.Dashboard.ProcessDump (line 1) | defmodule Realtime.Dashboard.ProcessDump
method menu_link (line 8) | def menu_link(_, _) do
method mount (line 13) | def mount(_, _, socket) do
method render (line 21) | def render(assigns) do
method dump_processes (line 33) | defp dump_processes(name) do
FILE: lib/realtime_web/dashboard/tenant_info.ex
class Realtime.Dashboard.TenantInfo (line 1) | defmodule Realtime.Dashboard.TenantInfo
method menu_link (line 12) | def menu_link(_, _), do: {:ok, "Tenant Info"}
method mount (line 15) | def mount(_, _, socket) do
method handle_event (line 20) | def handle_event("lookup", %{"project_ref" => ref}, socket) do
method render (line 30) | def render(assigns) do
method prepare_tenant (line 97) | defp prepare_tenant(tenant) do
method prepare_extension (line 101) | defp prepare_extension(ext) do
FILE: lib/realtime_web/endpoint.ex
class RealtimeWeb.Endpoint (line 1) | defmodule RealtimeWeb.Endpoint
method log_level (line 76) | def log_level(%{path_info: ["healthcheck"]}) do
method log_level (line 80) | def log_level(%{path_info: ["api", "tenants", _, "health"]}) do
method log_level (line 84) | def log_level(_), do: :info
FILE: lib/realtime_web/gettext.ex
class RealtimeWeb.Gettext (line 1) | defmodule RealtimeWeb.Gettext
FILE: lib/realtime_web/live/components.ex
class RealtimeWeb.Components (line 1) | defmodule RealtimeWeb.Components
method h1 (line 17) | def h1(assigns) do
method h2 (line 32) | def h2(assigns) do
method h3 (line 47) | def h3(assigns) do
method button (line 67) | def button(assigns) do
method link_button (line 93) | def link_button(assigns) do
method gray_link_button (line 118) | def gray_link_button(assigns) do
method patch_button (line 144) | def patch_button(assigns) do
method modal (line 186) | def modal(assigns) do
method badge (line 259) | def badge(assigns) do
method select (line 284) | def select(assigns) do
method text_input (line 308) | def text_input(assigns) do
method label (line 337) | def label(assigns) do
method show (line 345) | def show(js \\ %JS{}, selector) do
method hide (line 355) | def hide(js \\ %JS{}, selector) do
method hide_modal (line 376) | def hide_modal(js \\ %JS{}, id) do
FILE: lib/realtime_web/live/inspector_live/conn_component.ex
class RealtimeWeb.InspectorLive.ConnComponent (line 1) | defmodule RealtimeWeb.InspectorLive.ConnComponent
method mount (line 46) | def mount(socket) do
method update (line 59) | def update(assigns, socket) do
method handle_event (line 66) | def handle_event(
method handle_event (line 86) | def handle_event(
method handle_event (line 108) | def handle_event("validate", %{"connection" => conn}, socket) do
method handle_event (line 122) | def handle_event("connect", %{"connection" => conn} = params, socket) do
method handle_event (line 133) | def handle_event("disconnect", _params, socket) do
method handle_event (line 142) | def handle_event("clear_local_storage", _params, socket) do
method handle_event (line 159) | def handle_event(
method handle_event (line 178) | def handle_event("local_storage", %{"log_level" => nil} = params, sock...
method handle_event (line 193) | def handle_event("local_storage", params, socket) do
method handle_event (line 207) | def handle_event("cancel", params, socket) do
method send_share_url (line 213) | defp send_share_url(conn) do
class Connection (line 4) | defmodule Connection
method changeset (line 24) | def changeset(form, params \\ %{}) do
FILE: lib/realtime_web/live/inspector_live/index.ex
class RealtimeWeb.InspectorLive.Index (line 1) | defmodule RealtimeWeb.InspectorLive.Index
method mount (line 21) | def mount(_params, _session, socket) do
method handle_params (line 39) | def handle_params(params, _url, socket) do
method handle_event (line 53) | def handle_event("send_message", params, socket) do
method handle_event (line 57) | def handle_event("postgres_subscribed", _params, socket) do
method handle_event (line 63) | def handle_event("presence_subscribed", _params, socket) do
method handle_event (line 69) | def handle_event("broadcast_subscribed", %{"host" => host}, socket) do
method handle_info (line 81) | def handle_info({:share_url, url}, socket) do
class Message (line 6) | defmodule Message
method changeset (line 15) | def changeset(form, params \\ %{}) do
FILE: lib/realtime_web/live/page_live/index.ex
class RealtimeWeb.PageLive.Index (line 1) | defmodule RealtimeWeb.PageLive.Index
method mount (line 5) | def mount(_params, _session, socket) do
method handle_params (line 10) | def handle_params(params, _url, socket) do
method apply_action (line 14) | defp apply_action(socket, :index, _params) do
FILE: lib/realtime_web/live/ping_live.ex
class RealtimeWeb.PingLive (line 1) | defmodule RealtimeWeb.PingLive
method mount (line 4) | def mount(_params, _session, socket) do
method render (line 9) | def render(assigns) do
method handle_info (line 15) | def handle_info(:ping, socket) do
method handle_event (line 21) | def handle_event("pong", %{"ping" => ping}, socket) do
method ping (line 33) | defp ping do
FILE: lib/realtime_web/live/status_live/index.ex
class RealtimeWeb.StatusLive.Index (line 1) | defmodule RealtimeWeb.StatusLive.Index
method mount (line 9) | def mount(_params, _session, socket) do
method handle_params (line 21) | def handle_params(params, _url, socket) do
method handle_info (line 26) | def handle_info(%Phoenix.Socket.Broadcast{payload: %Payload{} = payloa...
method apply_action (line 32) | defp apply_action(socket, :index, _params) do
method all_nodes (line 37) | defp all_nodes do
method default_pings (line 41) | defp default_pings do
method pair_id (line 49) | defp pair_id(from, to) do
FILE: lib/realtime_web/live/tenants_live/index.ex
class RealtimeWeb.TenantsLive.Index (line 1) | defmodule RealtimeWeb.TenantsLive.Index
method mount (line 33) | def mount(_params, _session, socket) do
method handle_params (line 53) | def handle_params(params, _url, socket) do
method handle_event (line 74) | def handle_event("validate", %{"filter" => filter}, socket) do
method list_tenants (line 88) | defp list_tenants(%Filter{} = filter) do
class Socket (line 7) | defmodule Socket
class Filter (line 11) | defmodule Filter
method changeset (line 22) | def changeset(form, params \\ %{}) do
method apply_changes_form (line 27) | def apply_changes_form(changeset) do
FILE: lib/realtime_web/live/time_live.ex
class RealtimeWeb.TimeLive (line 1) | defmodule RealtimeWeb.TimeLive
method mount (line 4) | def mount(_params, _session, socket) do
method render (line 8) | def render(assigns) do
method handle_info (line 14) | def handle_info(:time, socket) do
method assign_time (line 18) | defp assign_time(socket) do
FILE: lib/realtime_web/open_api_schemas.ex
class RealtimeWeb.OpenApiSchemas (line 1) | defmodule RealtimeWeb.OpenApiSchemas
class ChannelParams (line 8) | defmodule ChannelParams
method params (line 23) | def params, do: {"Channel Params", "application/json", __MODULE__}
class TenantBatchParams (line 26) | defmodule TenantBatchParams
method params (line 55) | def params, do: {"Tenant Batch Params", "application/json", __MODULE__}
class TenantParams (line 58) | defmodule TenantParams
method params (line 188) | def params, do: {"Tenant Params", "application/json", __MODULE__}
class TenantResponseValue (line 191) | defmodule TenantResponseValue
class ChannelResponseValue (line 306) | defmodule ChannelResponseValue
class TenantHealthResponseValue (line 328) | defmodule TenantHealthResponseValue
class TenantHealthResponse (line 364) | defmodule TenantHealthResponse
method response (line 373) | def response, do: {"Tenant Response", "application/json", __MODULE__}
class TenantResponse (line 376) | defmodule TenantResponse
method response (line 385) | def response, do: {"Tenant Response", "application/json", __MODULE__}
class TenantResponseList (line 388) | defmodule TenantResponseList
method response (line 397) | def response, do: {"Tenant List Response", "application/json", __MODUL...
class ChannelResponse (line 400) | defmodule ChannelResponse
method response (line 409) | def response, do: {"Tenant Response", "application/json", __MODULE__}
class ChannelResponseList (line 412) | defmodule ChannelResponseList
method response (line 421) | def response, do: {"Tenant List Response", "application/json", __MODUL...
class EmptyResponse (line 424) | defmodule EmptyResponse
method response (line 433) | def response, do: {"Empty Response", "application/json", __MODULE__}
class NotFoundResponse (line 436) | defmodule NotFoundResponse
method response (line 447) | def response, do: {"Not Found", "application/json", __MODULE__}
class ErrorResponse (line 450) | defmodule ErrorResponse
method response (line 461) | def response, do: {"Error", "application/json", __MODULE__}
class UnauthorizedResponse (line 464) | defmodule UnauthorizedResponse
method response (line 475) | def response, do: {"Unauthorized", "application/json", __MODULE__}
class UnprocessableEntityResponse (line 478) | defmodule UnprocessableEntityResponse
method response (line 497) | def response, do: {"Unprocessable Entity", "application/json", __MODUL...
class TooManyRequestsResponse (line 500) | defmodule TooManyRequestsResponse
method response (line 519) | def response, do: {"Too Many Requests", "application/json", __MODULE__}
FILE: lib/realtime_web/plugs/assign_tenant.ex
class RealtimeWeb.Plugs.AssignTenant (line 1) | defmodule RealtimeWeb.Plugs.AssignTenant
method init (line 17) | def init(opts) do
method call (line 21) | def call(%Plug.Conn{host: host} = conn, _opts) do
method error_response (line 39) | defp error_response(conn, message) do
method initialize_counters (line 46) | defp initialize_counters(tenant) do
FILE: lib/realtime_web/plugs/auth_tenant.ex
class RealtimeWeb.AuthTenant (line 1) | defmodule RealtimeWeb.AuthTenant
method init (line 15) | def init(opts), do: opts
method call (line 17) | def call(%{assigns: %{tenant: tenant}} = conn, _opts) do
method call (line 34) | def call(conn, _opts), do: unauthorized(conn)
method access_token (line 36) | defp access_token(conn) do
method unauthorized (line 67) | defp unauthorized(conn),
FILE: lib/realtime_web/plugs/baggage_request_id.ex
class RealtimeWeb.Plugs.BaggageRequestId (line 1) | defmodule RealtimeWeb.Plugs.BaggageRequestId
method baggage_key (line 9) | def baggage_key, do: Application.get_env(:realtime, :request_id_baggag...
method init (line 17) | def init(opts) do
method call (line 24) | def call(conn, baggage_key) do
method valid_request_id? (line 38) | defp valid_request_id?(s), do: byte_size(s) in 10..200
FILE: lib/realtime_web/plugs/metrics_mode.ex
class RealtimeWeb.Plugs.MetricsMode (line 1) | defmodule RealtimeWeb.Plugs.MetricsMode
method init (line 7) | def init(opts), do: opts
method call (line 9) | def call(conn, _opts) do
method dispatch_legacy (line 13) | defp dispatch_legacy(%{path_info: ["tenant-metrics" | _]} = conn) do
method dispatch_legacy (line 17) | defp dispatch_legacy(conn) do
FILE: lib/realtime_web/plugs/rate_limiter.ex
class RealtimeWeb.Plugs.RateLimiter (line 1) | defmodule RealtimeWeb.Plugs.RateLimiter
method init (line 11) | def init(opts) do
method call (line 15) | def call(
method call (line 45) | def call(conn, _opts) do
FILE: lib/realtime_web/router.ex
class RealtimeWeb.Router (line 1) | defmodule RealtimeWeb.Router
method check_auth (line 144) | defp check_auth(conn, [secret_key, blocklist_key]) do
method dashboard_auth (line 161) | defp dashboard_auth(conn, _opts) do
method set_span_request_id (line 177) | defp set_span_request_id(conn, _) do
FILE: lib/realtime_web/socket/user_broadcast.ex
class RealtimeWeb.Socket.UserBroadcast (line 1) | defmodule RealtimeWeb.Socket.UserBroadcast
method convert_to_json_broadcast (line 21) | def convert_to_json_broadcast(%__MODULE__{user_payload_encoding: :json...
method convert_to_json_broadcast (line 38) | def convert_to_json_broadcast(%__MODULE__{}), do: {:error, "User paylo...
FILE: lib/realtime_web/socket/v2_serializer.ex
class RealtimeWeb.Socket.V2Serializer (line 1) | defmodule RealtimeWeb.Socket.V2Serializer
method fastlane! (line 19) | def fastlane!(%UserBroadcast{} = msg) do
method fastlane! (line 47) | def fastlane!(%Broadcast{payload: {:binary, data}} = msg) do
method fastlane! (line 63) | def fastlane!(%Broadcast{payload: %{}} = msg) do
method fastlane! (line 68) | def fastlane!(%Broadcast{payload: invalid}) do
method encode! (line 73) | def encode!(%Reply{payload: {:binary, data}} = reply) do
method encode! (line 98) | def encode!(%Reply{} = reply) do
method encode! (line 110) | def encode!(%Message{payload: {:binary, data}} = msg) do
method encode! (line 130) | def encode!(%Message{payload: %{}} = msg) do
method encode! (line 135) | def encode!(%Message{payload: invalid}) do
method decode! (line 140) | def decode!(raw_message, opts) do
method decode_text (line 147) | defp decode_text(raw_message) do
method decode_binary (line 158) | defp decode_binary(<<
method decode_binary (line 179) | defp decode_binary(<<
method byte_size! (line 214) | defp byte_size!(nil, _kind, _max), do: 0
method byte_size! (line 216) | defp byte_size!(bin, kind, max) do
FILE: lib/realtime_web/telemetry.ex
class RealtimeWeb.Telemetry (line 1) | defmodule RealtimeWeb.Telemetry
method start_link (line 7) | def start_link(arg) do
method init (line 12) | def init(_arg) do
method metrics (line 24) | def metrics do
method periodic_measurements (line 50) | defp periodic_measurements do
FILE: lib/realtime_web/tenant_broadcaster.ex
class RealtimeWeb.TenantBroadcaster (line 1) | defmodule RealtimeWeb.TenantBroadcaster
method pubsub_direct_broadcast (line 19) | def pubsub_direct_broadcast(node, tenant_id, topic, message, dispatche...
method do_direct_broadcast (line 37) | defp do_direct_broadcast(_node, topic, message, dispatcher) do
method pubsub_broadcast (line 43) | def pubsub_broadcast(tenant_id, topic, message, dispatcher, message_ty...
method pubsub_broadcast_from (line 64) | def pubsub_broadcast_from(tenant_id, from, topic, message, dispatcher,...
method collect_payload_size (line 89) | def collect_payload_size(tenant_id, payload, message_type) do
method pubsub_adapter (line 96) | defp pubsub_adapter, do: Application.fetch_env!(:realtime, :pubsub_ada...
FILE: lib/realtime_web/views/changeset_view.ex
class RealtimeWeb.ChangesetView (line 1) | defmodule RealtimeWeb.ChangesetView
method translate_errors (line 10) | def translate_errors(changeset) do
method render (line 14) | def render("error.json", %{changeset: changeset}) do
FILE: lib/realtime_web/views/error_helpers.ex
class RealtimeWeb.ErrorHelpers (line 1) | defmodule RealtimeWeb.ErrorHelpers
method error_tag (line 11) | def error_tag(form, field) do
method translate_error (line 23) | def translate_error({msg, opts}) do
FILE: lib/realtime_web/views/error_view.ex
class RealtimeWeb.ErrorView (line 1) | defmodule RealtimeWeb.ErrorView
method render (line 4) | def render("error.json", %{conn: %{assigns: %{message: message}}}), do...
method template_not_found (line 6) | def template_not_found(template, _assigns), do: Phoenix.Controller.sta...
FILE: lib/realtime_web/views/layout_view.ex
class RealtimeWeb.LayoutView (line 1) | defmodule RealtimeWeb.LayoutView
FILE: lib/realtime_web/views/tenant_view.ex
class RealtimeWeb.TenantView (line 1) | defmodule RealtimeWeb.TenantView
method render (line 5) | def render("index.json", %{tenants: tenants}) do
method render (line 9) | def render("show.json", %{tenant: tenant}) do
method render (line 13) | def render("not_found.json", %{tenant: nil}) do
method render (line 17) | def render("tenant.json", %{tenant: tenant}) do
FILE: mix.exs
class Realtime.MixProject (line 1) | defmodule Realtime.MixProject
method project (line 4) | def project do
method dialyzer (line 27) | defp dialyzer do
method application (line 40) | def application do
method elixirc_paths (line 48) | defp elixirc_paths(:test), do: ["lib", "test/support"]
method elixirc_paths (line 49) | defp elixirc_paths(_), do: ["lib"]
method deps (line 54) | defp deps do
method aliases (line 121) | defp aliases do
FILE: priv/repo/migrations/20210706140551_create_tenant.exs
class Realtime.Repo.Migrations.CreateTenants (line 1) | defmodule Realtime.Repo.Migrations.CreateTenants
method change (line 4) | def change do
FILE: priv/repo/migrations/20220329161857_add_extensions_table.exs
class Realtime.Repo.Migrations.AddExtensionsTable (line 1) | defmodule Realtime.Repo.Migrations.AddExtensionsTable
method change (line 4) | def change do
FILE: priv/repo/migrations/20220410212326_add_tenant_max_eps.exs
class Realtime.Repo.Migrations.AddTenantMaxEps (line 1) | defmodule Realtime.Repo.Migrations.AddTenantMaxEps
method up (line 4) | def up do
method down (line 10) | def down do
FILE: priv/repo/migrations/20220506102948_rename_poll_interval_to_poll_interval_ms.exs
class Realtime.Repo.Migrations.RenamePollIntervalToPollIntervalMs (line 1) | defmodule Realtime.Repo.Migrations.RenamePollIntervalToPollIntervalMs
method up (line 5) | def up do
method down (line 9) | def down do
FILE: priv/repo/migrations/20220527210857_add_external_id_uniq_index.exs
class Realtime.Repo.Migrations.AddExternalIdUniqIndex (line 1) | defmodule Realtime.Repo.Migrations.AddExternalIdUniqIndex
method change (line 4) | def change do
FILE: priv/repo/migrations/20220815211129_new_max_events_per_second_default.exs
class Realtime.Repo.Migrations.NewMaxEventsPerSecondDefault (line 1) | defmodule Realtime.Repo.Migrations.NewMaxEventsPerSecondDefault
method change (line 4) | def change do
FILE: priv/repo/migrations/20220815215024_set_current_max_events_per_second.exs
class Realtime.Repo.Migrations.SetCurrentMaxEventsPerSecond (line 1) | defmodule Realtime.Repo.Migrations.SetCurrentMaxEventsPerSecond
method change (line 4) | def change do
FILE: priv/repo/migrations/20220818141501_change_limits_defaults.exs
class Realtime.Repo.Migrations.ChangeLimitsDefaults (line 1) | defmodule Realtime.Repo.Migrations.ChangeLimitsDefaults
method change (line 4) | def change do
FILE: priv/repo/migrations/20221018173709_add_cdc_default.exs
class Realtime.Repo.Migrations.AddCdcDefault (line 1) | defmodule Realtime.Repo.Migrations.AddCdcDefault
method up (line 4) | def up do
method down (line 10) | def down do
FILE: priv/repo/migrations/20221102172703_rename_pg_type.exs
class Realtime.Repo.Migrations.RenamePgType (line 1) | defmodule Realtime.Repo.Migrations.RenamePgType
method up (line 4) | def up do
method down (line 8) | def down do
FILE: priv/repo/migrations/20221223010058_drop_tenants_uniq_external_id_index.exs
class Realtime.Repo.Migrations.DropTenantsUniqExternalIdIndex (line 1) | defmodule Realtime.Repo.Migrations.DropTenantsUniqExternalIdIndex
method change (line 4) | def change do
FILE: priv/repo/migrations/20230110180046_add_limits_fields_to_tenants.exs
class Realtime.Repo.Migrations.AddLimitsFieldsToTenants (line 1) | defmodule Realtime.Repo.Migrations.AddLimitsFieldsToTenants
method change (line 4) | def change do
FILE: priv/repo/migrations/20230810220907_alter_tenants_table_columns_to_text.exs
class Realtime.Repo.Migrations.AlterTenantsTableColumnsToText (line 1) | defmodule Realtime.Repo.Migrations.AlterTenantsTableColumnsToText
method change (line 4) | def change do
FILE: priv/repo/migrations/20230810220924_alter_extensions_table_columns_to_text.exs
class Realtime.Repo.Migrations.AlterExtensionsTableColumnsToText (line 1) | defmodule Realtime.Repo.Migrations.AlterExtensionsTableColumnsToText
method change (line 4) | def change do
FILE: priv/repo/migrations/20240306114423_add_tenant_jwt_jwks.exs
class Realtime.Repo.Migrations.AdddTenantJwtJwksColumn (line 1) | defmodule Realtime.Repo.Migrations.AdddTenantJwtJwksColumn
method change (line 4) | def change do
FILE: priv/repo/migrations/20240418082835_add_authorization_flag.exs
class Realtime.Repo.Migrations.AddAuthorizationFlag (line 1) | defmodule Realtime.Repo.Migrations.AddAuthorizationFlag
method change (line 4) | def change do
FILE: priv/repo/migrations/20240625211759_remove_enable_authorization_flag.exs
class Realtime.Repo.Migrations.RemoveEnableAuthorizationFlag (line 1) | defmodule Realtime.Repo.Migrations.RemoveEnableAuthorizationFlag
method change (line 4) | def change do
FILE: priv/repo/migrations/20240902173232_add_extension_external_id_index.exs
class Realtime.Repo.Migrations.AddExtensionExternalIdIndex (line 1) | defmodule Realtime.Repo.Migrations.AddExtensionExternalIdIndex
method change (line 4) | def change do
FILE: priv/repo/migrations/20241106103258_add_private_only_flag_column_to_tenant.exs
class Realtime.Repo.Migrations.AddPrivateOnlyFlagColumnToTenant (line 1) | defmodule Realtime.Repo.Migrations.AddPrivateOnlyFlagColumnToTenant
method change (line 4) | def change do
FILE: priv/repo/migrations/20250424203323_add_migrations_ran_to_tenant.exs
class Realtime.Repo.Migrations.AddMigrationsRanToTenant (line 1) | defmodule Realtime.Repo.Migrations.AddMigrationsRanToTenant
method change (line 4) | def change do
FILE: priv/repo/migrations/20250613072131_add_tenant_broadcast_adapter.exs
class Realtime.Repo.Migrations.AddTenantBroadcastAdapter (line 1) | defmodule Realtime.Repo.Migrations.AddTenantBroadcastAdapter
method change (line 4) | def change do
FILE: priv/repo/migrations/20250711044927_change_default_broadcast_adapter_to_gen_rpc.exs
class Realtime.Repo.Migrations.ChangeDefaultBroadcastAdapterToGenRpc (line 1) | defmodule Realtime.Repo.Migrations.ChangeDefaultBroadcastAdapterToGenRpc
method change (line 4) | def change do
FILE: priv/repo/migrations/20250811121559_add_max_presence_events_per_second.exs
class Realtime.Repo.Migrations.AddMaxPresenceEventsPerSecond (line 1) | defmodule Realtime.Repo.Migrations.AddMaxPresenceEventsPerSecond
method change (line 4) | def change do
FILE: priv/repo/migrations/20250926223044_set_default_presence_value.exs
class Realtime.Repo.Migrations.SetDefaultPresenceValue (line 1) | defmodule Realtime.Repo.Migrations.SetDefaultPresenceValue
method change (line 5) | def change do
FILE: priv/repo/migrations/20251204170944_nullable_jwt_secrets.exs
class Realtime.Repo.Migrations.NullableJwtSecrets (line 1) | defmodule Realtime.Repo.Migrations.NullableJwtSecrets
method change (line 4) | def change do
FILE: priv/repo/migrations/20251218000543_ensure_jwt_secret_is_text.exs
class Realtime.Repo.Migrations.EnsureJwtSecretIsText (line 1) | defmodule Realtime.Repo.Migrations.EnsureJwtSecretIsText
method change (line 4) | def change do
FILE: priv/repo/migrations/20260209232800_add_max_client_presence_events_per_second.exs
class Realtime.Repo.Migrations.AddMaxClientPresenceEventsPerSecond (line 1) | defmodule Realtime.Repo.Migrations.AddMaxClientPresenceEventsPerSecond
method change (line 4) | def change do
FILE: priv/repo/migrations/20260304000000_add_presence_enabled_to_tenants.exs
class Realtime.Repo.Migrations.AddPresenceEnabledToTenants (line 1) | defmodule Realtime.Repo.Migrations.AddPresenceEnabledToTenants
method change (line 4) | def change do
FILE: test/api_jwt_secret_test.exs
class RealtimeWeb.ApiJwtSecretTest (line 1) | defmodule RealtimeWeb.ApiJwtSecretTest
FILE: test/e2e/legacy/tests.ts
function signInUser (line 381) | async function signInUser(
function stopClient (line 394) | async function stopClient(supabase: SupabaseClient) {
function executeInsert (line 400) | async function executeInsert(
function executeUpdate (line 413) | async function executeUpdate(
function executeDelete (line 427) | async function executeDelete(
function generateJwtToken (line 439) | async function generateJwtToken(payload: JWTPayload) {
FILE: test/e2e/realtime-check.ts
constant ANON_KEY (line 24) | const ANON_KEY: string = opts.publishableKey ?? process.env.SUPABASE_ANO...
constant SERVICE_KEY (line 25) | const SERVICE_KEY: string = opts.secretKey ?? process.env.SUPABASE_SERVI...
constant TEST_CATEGORIES (line 29) | const TEST_CATEGORIES = TEST_FILTER
constant PROJECT_URL (line 50) | const PROJECT_URL = (() => {
constant DB_URL (line 56) | const DB_URL = (() => {
constant DB_SSL (line 63) | const DB_SSL = env !== "local" ? { rejectUnauthorized: false } : false;
constant REALTIME_OPTS (line 65) | const REALTIME_OPTS = { heartbeatIntervalMs: 5000, timeout: 5000 };
constant BROADCAST_CONFIG (line 66) | const BROADCAST_CONFIG = { config: { broadcast: { self: true } } };
constant EVENT_TIMEOUT_MS (line 67) | const EVENT_TIMEOUT_MS = 8000;
constant RATE_LIMIT_PAUSE_MS (line 68) | const RATE_LIMIT_PAUSE_MS = 2000;
constant BROADCAST_API_HEADERS (line 69) | const BROADCAST_API_HEADERS = {
constant LOAD_MESSAGES (line 74) | const LOAD_MESSAGES = 20;
constant LOAD_SETTLE_MS (line 75) | const LOAD_SETTLE_MS = 5000;
constant LOAD_DELIVERY_SLO (line 76) | const LOAD_DELIVERY_SLO = 99;
function measureThroughput (line 82) | function measureThroughput(latencies: number[], total: number, label: st...
type Metric (line 96) | type Metric = { label: string; value: number; unit: string };
type TestResult (line 97) | type TestResult = { suite: string; name: string; passed: boolean; durati...
function test (line 102) | async function test(name: string, fn: () => Promise<Metric[]>) {
function suite (line 119) | function suite(name: string) {
function waitFor (line 124) | async function waitFor<T>(getter: () => T | null, label: string): Promis...
function stopClient (line 133) | async function stopClient(supabase: SupabaseClient) {
function signInUser (line 138) | async function signInUser(supabase: SupabaseClient, email: string, passw...
function waitForSubscribed (line 144) | async function waitForSubscribed(channel: ReturnType<SupabaseClient["cha...
function waitForPostgresChannel (line 152) | async function waitForPostgresChannel(channel: ReturnType<SupabaseClient...
type TableName (line 161) | type TableName = "pg_changes" | "dummy" | "authorization" | "broadcast_c...
function executeInsert (line 163) | async function executeInsert(supabase: SupabaseClient, table: TableName)...
function executeUpdate (line 169) | async function executeUpdate(supabase: SupabaseClient, table: TableName,...
function executeDelete (line 174) | async function executeDelete(supabase: SupabaseClient, table: TableName,...
function setup (line 179) | async function setup(): Promise<{ userId: string; testUser: { email: str...
function cleanup (line 310) | async function cleanup(userId: string) {
function runConnectionTest (line 320) | async function runConnectionTest() {
function runLoadPostgresChangesTests (line 369) | async function runLoadPostgresChangesTests(testUser: { email: string; pa...
function runLoadPresenceTests (line 487) | async function runLoadPresenceTests() {
function runLoadBroadcastFromDbTests (line 537) | async function runLoadBroadcastFromDbTests(testUser: { email: string; pa...
function runLoadBroadcastTests (line 575) | async function runLoadBroadcastTests() {
function runLoadBroadcastReplayTests (line 648) | async function runLoadBroadcastReplayTests(testUser: { email: string; pa...
function runBroadcastTests (line 685) | async function runBroadcastTests() {
function runPresenceTests (line 743) | async function runPresenceTests(testUser: { email: string; password: str...
function runAuthorizationTests (line 804) | async function runAuthorizationTests(testUser: { email: string; password...
function runBroadcastChangesTests (line 850) | async function runBroadcastChangesTests(testUser: { email: string; passw...
function runPostgresChangesTests (line 948) | async function runPostgresChangesTests(testUser: { email: string; passwo...
function runBroadcastReplayTests (line 1096) | async function runBroadcastReplayTests(testUser: { email: string; passwo...
function printSummary (line 1182) | function printSummary(totalMs: number) {
constant SUITES (line 1256) | const SUITES: Record<string, (testUser: { email: string; password: strin...
constant LOAD_SUITES (line 1271) | const LOAD_SUITES = Object.keys(SUITES).filter((k) => k.startsWith("load...
constant FUNCTIONAL_SUITES (line 1272) | const FUNCTIONAL_SUITES = Object.keys(SUITES).filter((k) => !k.startsWit...
function main (line 1274) | async function main() {
FILE: test/extensions/extensions_test.exs
class Realtime.ExtensionsTest (line 1) | defmodule Realtime.ExtensionsTest
FILE: test/extensions/postgres_cdc_rls/db_settings_test.exs
class Extensions.PostgresCdcRls.DbSettingsTest (line 1) | defmodule Extensions.PostgresCdcRls.DbSettingsTest
FILE: test/extensions/postgres_cdc_rls/message_dispatcher_test.exs
class Extensions.PostgresCdcRls.MessageDispatcherTest (line 1) | defmodule Extensions.PostgresCdcRls.MessageDispatcherTest
class FakeSerializer (line 7) | defmodule FakeSerializer
method fastlane! (line 8) | def fastlane!(msg), do: {:encoded, msg}
FILE: test/extensions/postgres_cdc_rls/replications_test.exs
class Extensions.PostgresCdcRls.ReplicationsTest (line 1) | defmodule Extensions.PostgresCdcRls.ReplicationsTest
method drop_slot_on_exit (line 14) | defp drop_slot_on_exit(tenant, slot_name) do
FILE: test/extensions/postgres_cdc_rls/worker_supervisor_test.exs
class Extensions.PostgresCdcRls.WorkerSupervisorTest (line 1) | defmodule Extensions.PostgresCdcRls.WorkerSupervisorTest
FILE: test/integration/distributed_realtime_channel_test.exs
class Realtime.Integration.DistributedRealtimeChannelTest (line 1) | defmodule Realtime.Integration.DistributedRealtimeChannelTest
FILE: test/integration/measure_traffic_test.exs
class Realtime.Integration.MeasureTrafficTest (line 1) | defmodule Realtime.Integration.MeasureTrafficTest
method handle_telemetry (line 10) | def handle_telemetry(event, measurements, metadata, name) do
method get_count (line 34) | defp get_count(event, tenant) do
FILE: test/integration/region_aware_migrations_test.exs
class Realtime.Integration.RegionAwareMigrationsTest (line 1) | defmodule Realtime.Integration.RegionAwareMigrationsTest
FILE: test/integration/region_aware_routing_test.exs
class Realtime.Integration.RegionAwareRoutingTest (line 1) | defmodule Realtime.Integration.RegionAwareRoutingTest
FILE: test/integration/rt_channel/authorization_test.exs
class Realtime.Integration.RtChannel.AuthorizationTest (line 1) | defmodule Realtime.Integration.RtChannel.AuthorizationTest
FILE: test/integration/rt_channel/billable_events_test.exs
class Realtime.Integration.RtChannel.BillableEventsTest (line 1) | defmodule Realtime.Integration.RtChannel.BillableEventsTest
method handle_telemetry (line 44) | def handle_telemetry(event, measurements, metadata, name) do
method get_count (line 268) | defp get_count(event, tenant) do
FILE: test/integration/rt_channel/broadcast_test.exs
class Realtime.Integration.RtChannel.BroadcastTest (line 1) | defmodule Realtime.Integration.RtChannel.BroadcastTest
method setup_trigger (line 424) | defp setup_trigger(%{tenant: tenant, topic: topic}) do
FILE: test/integration/rt_channel/connection_lifecycle_test.exs
class Realtime.Integration.RtChannel.ConnectionLifecycleTest (line 1) | defmodule Realtime.Integration.RtChannel.ConnectionLifecycleTest
FILE: test/integration/rt_channel/postgres_changes_test.exs
class Realtime.Integration.RtChannel.PostgresChangesTest (line 1) | defmodule Realtime.Integration.RtChannel.PostgresChangesTest
FILE: test/integration/rt_channel/presence_test.exs
class Realtime.Integration.RtChannel.PresenceTest (line 1) | defmodule Realtime.Integration.RtChannel.PresenceTest
FILE: test/integration/rt_channel/token_handling_test.exs
class Realtime.Integration.RtChannel.TokenHandlingTest (line 1) | defmodule Realtime.Integration.RtChannel.TokenHandlingTest
FILE: test/integration/rt_channel/wal_bloat_test.exs
class Realtime.Integration.RtChannel.WalBloatTest (line 1) | defmodule Realtime.Integration.RtChannel.WalBloatTest
method active_replication_slot_pid! (line 126) | defp active_replication_slot_pid!(db_conn) do
method await_replication_slot_active (line 137) | defp await_replication_slot_active(db_conn, retries, interval_ms) do
method generate_wal_bloat (line 158) | defp generate_wal_bloat(tenant) do
method terminate_bloat_connections (line 175) | defp terminate_bloat_connections(db_conn) do
FILE: test/integration/tests.ts
function stopClient (line 200) | async function stopClient(client: RealtimeClient | null) {
FILE: test/integration/tracker_test.exs
class Integration.TrackerTest (line 1) | defmodule Integration.TrackerTest
FILE: test/realtime/adapters/postgres/protocol_test.exs
class Realtime.Adapters.Postgres.ProtocolTest (line 1) | defmodule Realtime.Adapters.Postgres.ProtocolTest
FILE: test/realtime/api/extensions_test.exs
class Realtime.Api.ExtensionsTest (line 1) | defmodule Realtime.Api.ExtensionsTest
FILE: test/realtime/api_test.exs
class Realtime.ApiTest (line 1) | defmodule Realtime.ApiTest
method create_tenants (line 17) | defp create_tenants(_) do
FILE: test/realtime/database_distributed_test.exs
class Realtime.DatabaseDistributedTest (line 1) | defmodule Realtime.DatabaseDistributedTest
method handle_telemetry (line 12) | def handle_telemetry(event, metadata, content, pid: pid), do: send(pid...
FILE: test/realtime/database_test.exs
class Realtime.DatabaseTest (line 1) | defmodule Realtime.DatabaseTest
method handle_telemetry (line 9) | def handle_telemetry(event, metadata, content, pid: pid), do: send(pid...
method update_extension (line 398) | defp update_extension(tenant, extension) do
FILE: test/realtime/extensions/cdc_rls/cdc_rls_test.exs
class Realtime.Extensions.CdcRlsTest (line 1) | defmodule Realtime.Extensions.CdcRlsTest
method integration (line 488) | defp integration(_) do
method distributed_integration (line 508) | defp distributed_integration(_) do
method pubsub_subscribe (line 528) | defp pubsub_subscribe(external_id, event \\ "*") do
method handle_telemetry (line 557) | def handle_telemetry(event, measures, metadata, pid: pid), do: send(pi...
FILE: test/realtime/extensions/cdc_rls/replication_poller_test.exs
class Realtime.Extensions.PostgresCdcRls.ReplicationPollerTest (line 1) | defmodule Realtime.Extensions.PostgresCdcRls.ReplicationPollerTest
method handle_telemetry (line 733) | def handle_telemetry(event, measures, metadata, pid: pid), do: send(pi...
method build_result (line 735) | defp build_result(subscription_ids) do
FILE: test/realtime/extensions/cdc_rls/replications_test.exs
class Realtime.Extensions.PostgresCdcRls.ReplicationsTest (line 1) | defmodule Realtime.Extensions.PostgresCdcRls.ReplicationsTest
FILE: test/realtime/extensions/cdc_rls/subscription_manager_test.exs
class Realtime.Extensions.CdcRls.SubscriptionManagerTest (line 1) | defmodule Realtime.Extensions.CdcRls.SubscriptionManagerTest
method pg_change_params (line 249) | defp pg_change_params do
FILE: test/realtime/extensions/cdc_rls/subscriptions_checker_distributed_test.exs
class Realtime.Extensions.CdcRls.SubscriptionsCheckerDistributedTest (line 1) | defmodule Realtime.Extensions.CdcRls.SubscriptionsCheckerDistributedTest
FILE: test/realtime/extensions/cdc_rls/subscriptions_checker_test.exs
class Realtime.Extensions.PostgresCdcRl.SubscriptionsCheckerTest (line 1) | defmodule Realtime.Extensions.PostgresCdcRl.SubscriptionsCheckerTest
FILE: test/realtime/extensions/cdc_rls/subscriptions_test.exs
class Realtime.Extensions.PostgresCdcRls.SubscriptionsTest (line 1) | defmodule Realtime.Extensions.PostgresCdcRls.SubscriptionsTest
method create_subscriptions (line 312) | defp create_subscriptions(conn, num) do
FILE: test/realtime/gen_counter/gen_counter_test.exs
class Realtime.GenCounterTest (line 1) | defmodule Realtime.GenCounterTest
FILE: test/realtime/gen_rpc_pub_sub/worker_test.exs
class Realtime.GenRpcPubSub.WorkerTest (line 1) | defmodule Realtime.GenRpcPubSub.WorkerTest
FILE: test/realtime/gen_rpc_pub_sub_test.exs
class Realtime.GenRpcPubSubTest (line 4) | defmodule Realtime.GenRpcPubSubTest
FILE: test/realtime/gen_rpc_test.exs
class Realtime.GenRpcTest (line 1) | defmodule Realtime.GenRpcTest
method handle_telemetry (line 388) | def handle_telemetry(event, measurements, metadata, pid: pid), do: sen...
FILE: test/realtime/helpers_test.exs
class Realtime.HelpersTest (line 1) | defmodule Realtime.HelpersTest
FILE: test/realtime/log_filter_test.exs
class Realtime.LogFilterTest (line 1) | defmodule Realtime.LogFilterTest
method gen_statem_event (line 65) | defp gen_statem_event(reason) do
method ranch_event (line 74) | defp ranch_event(ref, protocol, pid, reason) do
method db_connection_log_event (line 78) | defp db_connection_log_event(message) do
FILE: test/realtime/logs_test.exs
class Realtime.LogsTest (line 1) | defmodule Realtime.LogsTest
FILE: test/realtime/messages_test.exs
class Realtime.MessagesTest (line 1) | defmodule Realtime.MessagesTest
method handle_telemetry (line 260) | def handle_telemetry(event, measures, metadata, pid: pid), do: send(pi...
FILE: test/realtime/metrics_cleaner_test.exs
class Realtime.MetricsCleanerTest (line 1) | defmodule Realtime.MetricsCleanerTest
FILE: test/realtime/metrics_pusher_test.exs
class Realtime.MetricsPusherTest (line 1) | defmodule Realtime.MetricsPusherTest
method start_and_allow_pusher (line 11) | defp start_and_allow_pusher(opts) do
FILE: test/realtime/monitoring/distributed_metrics_test.exs
class Realtime.DistributedMetricsTest (line 1) | defmodule Realtime.DistributedMetricsTest
FILE: test/realtime/monitoring/erl_sys_mon_test.exs
class Realtime.Monitoring.ErlSysMonTest (line 1) | defmodule Realtime.Monitoring.ErlSysMonTest
FILE: test/realtime/monitoring/gen_rpc_metrics_test.exs
class Realtime.GenRpcMetricsTest (line 1) | defmodule Realtime.GenRpcMetricsTest
FILE: test/realtime/monitoring/latency_test.exs
class Realtime.LatencyTest (line 1) | defmodule Realtime.LatencyTest
FILE: test/realtime/monitoring/prom_ex/plugins/distributed_test.exs
class Realtime.PromEx.Plugins.DistributedTest (line 1) | defmodule Realtime.PromEx.Plugins.DistributedTest
method metric_value (line 62) | defp metric_value(metrics, metric, expected_tags), do: MetricsHelper.s...
class MetricsTest (line 6) | defmodule MetricsTest
method plugins (line 9) | def plugins do
FILE: test/realtime/monitoring/prom_ex/plugins/gen_rpc_test.exs
class Realtime.PromEx.Plugins.GenRpcTest (line 1) | defmodule Realtime.PromEx.Plugins.GenRpcTest
method metric_value (line 63) | defp metric_value(metrics, metric, expected_tags), do: MetricsHelper.s...
class MetricsTest (line 6) | defmodule MetricsTest
method plugins (line 9) | def plugins do
FILE: test/realtime/monitoring/prom_ex/plugins/phoenix_test.exs
class Realtime.PromEx.Plugins.PhoenixTest (line 1) | defmodule Realtime.PromEx.Plugins.PhoenixTest
method metric_value (line 86) | defp metric_value(metric, expected_tags \\ nil) do
class MetricsTest (line 6) | defmodule MetricsTest
method plugins (line 9) | def plugins do
FILE: test/realtime/monitoring/prom_ex/plugins/tenant_test.exs
class Realtime.PromEx.Plugins.TenantTest (line 1) | defmodule Realtime.PromEx.Plugins.TenantTest
method handle_telemetry (line 25) | def handle_telemetry(event, metadata, content, pid: pid), do: send(pid...
method metric_value (line 338) | defp metric_value(metric, expected_tags \\ nil) do
class MetricsTest (line 13) | defmodule MetricsTest
method plugins (line 17) | def plugins, do: [{Tenant, poll_rate: 50}, {TenantGlobal, poll_rate: 50}]
FILE: test/realtime/monitoring/prom_ex/plugins/tenants_test.exs
class Realtime.PromEx.Plugins.TenantsTest (line 1) | defmodule Realtime.PromEx.Plugins.TenantsTest
method metric_value (line 123) | defp metric_value(metric, expected_tags \\ nil) do
class MetricsTest (line 9) | defmodule MetricsTest
method plugins (line 12) | def plugins do
class Test (line 17) | defmodule Test
method success (line 18) | def success, do: {:ok, "success"}
method failure (line 19) | def failure, do: {:error, "failure"}
method exception (line 20) | def exception, do: raise(RuntimeError)
FILE: test/realtime/monitoring/prom_ex_test.exs
class Realtime.PromExTest (line 1) | defmodule Realtime.PromExTest
FILE: test/realtime/monitoring/prometheus_test.exs
class Realtime.Monitoring.PrometheusTest (line 2) | defmodule Realtime.Monitoring.PrometheusTest
method export (line 422) | defp export(name) do
method lines_to_string (line 428) | defp lines_to_string(lines) do
class StorageCounter (line 8) | defmodule StorageCounter
method start (line 12) | def start() do
method fresh_id (line 16) | def fresh_id() do
class TestError (line 22) | defmodule TestError
FILE: test/realtime/nodes_test.exs
class Realtime.NodesTest (line 1) | defmodule Realtime.NodesTest
method spawn_fake_node (line 7) | defp spawn_fake_node(region, node) do
FILE: test/realtime/oid_test.exs
class Realtime.OidTest (line 1) | defmodule Realtime.OidTest
FILE: test/realtime/postgres_decoder_test.exs
class Realtime.PostgresDecoderTest (line 1) | defmodule Realtime.PostgresDecoderTest
FILE: test/realtime/rate_counter/rate_counter_test.exs
class Realtime.RateCounterTest (line 1) | defmodule Realtime.RateCounterTest
method handle_telemetry (line 331) | def handle_telemetry(event, measures, metadata, pid: pid), do: send(pi...
FILE: test/realtime/repo_replica_test.exs
class Realtime.Repo.ReplicaTest (line 1) | defmodule Realtime.Repo.ReplicaTest
method replica_asserts (line 117) | defp replica_asserts(mod, replica) do
FILE: test/realtime/rpc_test.exs
class Realtime.RpcTest (line 1) | defmodule Realtime.RpcTest
method handle_telemetry (line 18) | def handle_telemetry(event, measurements, metadata, pid: pid), do: sen...
FILE: test/realtime/signal_handler_test.exs
class Realtime.SignalHandlerTest (line 1) | defmodule Realtime.SignalHandlerTest
class FakeHandler (line 6) | defmodule FakeHandler
method handle_event (line 7) | def handle_event(signal, _state), do: send(self(), signal)
FILE: test/realtime/syn_handler_test.exs
class Realtime.SynHandlerTest (line 1) | defmodule Realtime.SynHandlerTest
method assert_process_down (line 331) | defp assert_process_down(pid, reason, timeout) do
FILE: test/realtime/telemetry/logger_test.exs
class Realtime.Telemetry.LoggerTest (line 1) | defmodule Realtime.Telemetry.LoggerTest
FILE: test/realtime/tenants/authorization_remote_test.exs
class Realtime.Tenants.AuthorizationRemoteTest (line 1) | defmodule Realtime.Tenants.AuthorizationRemoteTest
method remote_rls_context (line 230) | defp remote_rls_context(context) do
FILE: test/realtime/tenants/authorization_test.exs
class Realtime.Tenants.AuthorizationTest (line 1) | defmodule Realtime.Tenants.AuthorizationTest
method update_db_pool_size (line 287) | defp update_db_pool_size(tenant, db_pool) do
FILE: test/realtime/tenants/batch_broadcast_test.exs
class Realtime.Tenants.BatchBroadcastTest (line 1) | defmodule Realtime.Tenants.BatchBroadcastTest
FILE: test/realtime/tenants/cache_test.exs
class Realtime.Tenants.CacheTest (line 1) | defmodule Realtime.Tenants.CacheTest
method seed_remote_cache (line 145) | defp seed_remote_cache(node, external_id, tenant, attempts \\ 20) do
method assert_eventually (line 161) | defp assert_eventually(fun, attempts \\ 50, interval \\ 100)
method assert_eventually (line 163) | defp assert_eventually(fun, 0, _interval) do
method assert_eventually (line 167) | defp assert_eventually(fun, attempts, interval) do
FILE: test/realtime/tenants/connect/get_tenant_test.exs
class Realtime.Tenants.Connect.GetTenantTest (line 1) | defmodule Realtime.Tenants.Connect.GetTenantTest
FILE: test/realtime/tenants/connect/piper_test.exs
class Realtime.Tenants.Connect.PiperTest (line 1) | defmodule Realtime.Tenants.Connect.PiperTest
class Piper1 (line 8) | defmodule Piper1
method run (line 10) | def run(acc), do: {:ok, Map.put(acc, :piper1, "Piper1")}
class Piper2 (line 13) | defmodule Piper2
method run (line 15) | def run(acc), do: Map.get(acc, :piper1) && {:ok, Map.put(acc, :piper2,...
class Piper3 (line 18) | defmodule Piper3
method run (line 20) | def run(acc), do: Map.get(acc, :piper2) && {:ok, Map.put(acc, :piper3,...
class PiperErr (line 23) | defmodule PiperErr
method run (line 25) | def run(_acc), do: {:error, "PiperErr"}
class PiperBadReturn (line 28) | defmodule PiperBadReturn
method run (line 30) | def run(_acc), do: nil
class PiperException (line 33) | defmodule PiperException
method run (line 35) | def run(_acc), do: raise("PiperException")
FILE: test/realtime/tenants/connect/reconcile_migrations_test.exs
class Realtime.Tenants.Connect.ReconcileMigrationsTest (line 1) | defmodule Realtime.Tenants.Connect.ReconcileMigrationsTest
FILE: test/realtime/tenants/connect/register_process_test.exs
class Realtime.Tenants.Connect.RegisterProcessTest (line 1) | defmodule Realtime.Tenants.Connect.RegisterProcessTest
FILE: test/realtime/tenants/connect_test.exs
class Realtime.Tenants.ConnectTest (line 1) | defmodule Realtime.Tenants.ConnectTest
method assert_process_down (line 23) | defp assert_process_down(pid, timeout \\ 100, reason \\ nil) do
method refute_process_down (line 33) | defp refute_process_down(pid, timeout \\ 500) do
method check_db_connections_created (line 818) | defp check_db_connections_created(test_pid, tenant_id) do
method update_extension (line 840) | defp update_extension(tenant, extension) do
method assert_pid (line 850) | defp assert_pid(call, attempts \\ 10)
method assert_pid (line 852) | defp assert_pid(_call, 0) do
method assert_pid (line 856) | defp assert_pid(call, attempts) do
method assert_replication_status (line 867) | defp assert_replication_status(tenant_id, attempts \\ 20)
method assert_replication_status (line 869) | defp assert_replication_status(tenant_id, 0) do
method assert_replication_status (line 873) | defp assert_replication_status(tenant_id, attempts) do
FILE: test/realtime/tenants/janitor/maintenance_task_test.exs
class Realtime.Tenants.Janitor.MaintenanceTaskTest (line 1) | defmodule Realtime.Tenants.Janitor.MaintenanceTaskTest
method verify_partitions (line 86) | defp verify_partitions(conn) do
FILE: test/realtime/tenants/janitor_test.exs
class Realtime.Tenants.JanitorTest (line 1) | defmodule Realtime.Tenants.JanitorTest
method verify_partitions (line 171) | defp verify_partitions(conn) do
FILE: test/realtime/tenants/migrations_test.exs
class Realtime.Tenants.MigrationsTest (line 1) | defmodule Realtime.Tenants.MigrationsTest
FILE: test/realtime/tenants/rebalancer_test.exs
class Realtime.Tenants.RebalancerTest (line 1) | defmodule Realtime.Tenants.RebalancerTest
FILE: test/realtime/tenants/replication_connection/watchdog_test.exs
class Realtime.Tenants.ReplicationConnection.WatchdogTest (line 1) | defmodule Realtime.Tenants.ReplicationConnection.WatchdogTest
class FakeReplicationConnection (line 8) | defmodule FakeReplicationConnection
method child_spec (line 9) | def child_spec(opts) do
method start_link (line 13) | def start_link(opts \\ []), do: :gen_statem.start_link(__MODULE__, opt...
method callback_mode (line 15) | def callback_mode, do: :state_functions
method init (line 17) | def init(opts) do
method idle (line 30) | def idle({:call, from}, :health_check, %{respond_to_health_checks: tru...
method idle (line 39) | def idle({:call, _from}, :health_check, %{respond_to_health_checks: fa...
method idle (line 44) | def idle({:call, from}, :get_health_check_count, data) do
method idle (line 49) | def idle({:call, from}, :set_no_respond, data) do
method get_health_check_count (line 54) | def get_health_check_count(pid), do: :gen_statem.call(pid, :get_health...
method set_no_respond (line 56) | def set_no_respond(pid), do: :gen_statem.call(pid, :set_no_respond)
FILE: test/realtime/tenants/replication_connection_test.exs
class Realtime.Tenants.ReplicationConnectionTest (line 1) | defmodule Realtime.Tenants.ReplicationConnectionTest
method handle_telemetry (line 723) | def handle_telemetry(event, measures, metadata, pid: pid), do: send(pi...
method subscribe (line 768) | defp subscribe(tenant_topic, topic) do
method assert_process_down (line 781) | defp assert_process_down(pid, timeout \\ 100) do
method message_fixture_with_conn (line 786) | defp message_fixture_with_conn(_tenant, conn, override) do
method assert_publication_contains_only_messages (line 802) | defp assert_publication_contains_only_messages(db_conn, publication_na...
method assert_replication_started (line 818) | defp assert_replication_started(db_conn, slot_name, retries \\ 10, int...
method check_replication_status (line 825) | defp check_replication_status(_db_conn, _slot_name, 0, _interval_ms), ...
method check_replication_status (line 827) | defp check_replication_status(db_conn, slot_name, retries_remaining, i...
FILE: test/realtime/tenants/repo_test.exs
class Realtime.Tenants.RepoTest (line 1) | defmodule Realtime.Tenants.RepoTest
FILE: test/realtime/tenants_test.exs
class Realtime.TenantsTest (line 1) | defmodule Realtime.TenantsTest
FILE: test/realtime/users_counter_test.exs
class Realtime.UsersCounterTest (line 1) | defmodule Realtime.UsersCounterTest
method generate_load (line 94) | defp generate_load(tenant_id) do
FILE: test/realtime_web/channels/auth/channels_authorization_test.exs
class RealtimeWeb.ChannelsAuthorizationTest (line 1) | defmodule RealtimeWeb.ChannelsAuthorizationTest
FILE: test/realtime_web/channels/auth/jwt_verification_test.exs
class RealtimeWeb.JwtVerificationTest (line 1) | defmodule RealtimeWeb.JwtVerificationTest
FILE: test/realtime_web/channels/payloads/flexible_boolean_test.exs
class RealtimeWeb.Channels.Payloads.FlexibleBooleanTest (line 1) | defmodule RealtimeWeb.Channels.Payloads.FlexibleBooleanTest
FILE: test/realtime_web/channels/payloads/join_test.exs
class RealtimeWeb.Channels.Payloads.JoinTest (line 1) | defmodule RealtimeWeb.Channels.Payloads.JoinTest
FILE: test/realtime_web/channels/realtime_channel/broadcast_handler_test.exs
class RealtimeWeb.RealtimeChannel.BroadcastHandlerTest (line 1) | defmodule RealtimeWeb.RealtimeChannel.BroadcastHandlerTest
method initiate_tenant (line 444) | defp initiate_tenant(context) do
method socket_fixture (line 478) | defp socket_fixture(tenant, topic, opts \\ []) do
method message (line 520) | defp message(RealtimeWeb.Socket.V2Serializer, topic, payload), do: [ni...
method message (line 522) | defp message(Phoenix.Socket.V1.JSONSerializer, topic, payload) do
FILE: test/realtime_web/channels/realtime_channel/logging_test.exs
class RealtimeWeb.RealtimeChannel.LoggingTest (line 1) | defmodule RealtimeWeb.RealtimeChannel.LoggingTest
method handle_telemetry (line 7) | def handle_telemetry(event, measures, metadata, pid: pid), do: send(pi...
FILE: test/realtime_web/channels/realtime_channel/message_dispatcher_test.exs
class RealtimeWeb.RealtimeChannel.MessageDispatcherTest (line 1) | defmodule RealtimeWeb.RealtimeChannel.MessageDispatcherTest
class TestSerializer (line 12) | defmodule TestSerializer
method fastlane! (line 13) | def fastlane!(msg) do
FILE: test/realtime_web/channels/realtime_channel/presence_handler_test.exs
class RealtimeWeb.RealtimeChannel.PresenceHandlerTest (line 1) | defmodule RealtimeWeb.RealtimeChannel.PresenceHandlerTest
method initiate_tenant (line 742) | defp initiate_tenant(context) do
method socket_fixture (line 756) | defp socket_fixture(tenant, topic, presence_key, opts \\ []) do
method handle_telemetry (line 838) | def handle_telemetry(event, measures, metadata, %{pid: pid, tenant: te...
FILE: test/realtime_web/channels/realtime_channel/tracker_test.exs
class RealtimeWeb.RealtimeChannel.TrackerTest (line 1) | defmodule RealtimeWeb.RealtimeChannel.TrackerTest
FILE: test/realtime_web/channels/realtime_channel_test.exs
class RealtimeWeb.RealtimeChannelTest (line 1) | defmodule RealtimeWeb.RealtimeChannelTest
method conn_opts (line 1397) | defp conn_opts(tenant, token) do
method update_extension (line 1406) | defp update_extension(tenant, extension) do
method assert_process_down (line 1419) | defp assert_process_down(pid) do
method rls_context (line 1424) | defp rls_context(%{tenant: tenant, policies: policies}) do
method rls_context (line 1430) | defp rls_context(_), do: :ok
FILE: test/realtime_web/channels/socket_disconnect_test.exs
class RealtimeWeb.SocketDisconnectTest (line 1) | defmodule RealtimeWeb.SocketDisconnectTest
FILE: test/realtime_web/channels/tenant_rate_limiters_test.exs
class RealtimeWeb.TenantRateLimitersTest (line 1) | defmodule RealtimeWeb.TenantRateLimitersTest
FILE: test/realtime_web/channels/user_socket_test.exs
class RealtimeWeb.UserSocketTest (line 1) | defmodule RealtimeWeb.UserSocketTest
FILE: test/realtime_web/controllers/broadcast_controller_test.exs
class RealtimeWeb.BroadcastControllerTest (line 1) | defmodule RealtimeWeb.BroadcastControllerTest
method subscribe (line 28) | defp subscribe(tenant_topic, topic) do
method assert_receive_messages (line 41) | defp assert_receive_messages(count) do
method generate_message_with_policies (line 517) | defp generate_message_with_policies(db_conn, tenant) do
method generate_conn (line 523) | defp generate_conn(conn, tenant) do
FILE: test/realtime_web/controllers/fallback_controller_test.exs
class RealtimeWeb.FallbackControllerTest (line 1) | defmodule RealtimeWeb.FallbackControllerTest
FILE: test/realtime_web/controllers/legacy_metrics_controller_test.exs
class RealtimeWeb.LegacyMetricsControllerTest (line 1) | defmodule RealtimeWeb.LegacyMetricsControllerTest
method fire_all_tenant_events (line 44) | defp fire_all_tenant_events do
FILE: test/realtime_web/controllers/live_dasboard_test.exs
class RealtimeWeb.LiveDashboardTest (line 1) | defmodule RealtimeWeb.LiveDashboardTest
method using_basic_auth (line 72) | defp using_basic_auth(conn, username, password) do
FILE: test/realtime_web/controllers/metrics_controller_test.exs
class RealtimeWeb.MetricsControllerTest (line 1) | defmodule RealtimeWeb.MetricsControllerTest
method fire_all_tenant_events (line 57) | defp fire_all_tenant_events do
FILE: test/realtime_web/controllers/openapi_controller_test.exs
class RealtimeWeb.Controllers.OpenapiControllerTest (line 1) | defmodule RealtimeWeb.Controllers.OpenapiControllerTest
FILE: test/realtime_web/controllers/page_controller_test.exs
class RealtimeWeb.PageControllerTest (line 1) | defmodule RealtimeWeb.PageControllerTest
FILE: test/realtime_web/controllers/tenant_controller_test.exs
class RealtimeWeb.TenantControllerTest (line 1) | defmodule RealtimeWeb.TenantControllerTest
method with_tenant (line 35) | defp with_tenant(_context) do
method default_tenant_attrs (line 651) | defp default_tenant_attrs(port) do
method wait_on_postgres_cdc_rls (line 675) | defp wait_on_postgres_cdc_rls(external_id, attempt \\ 10)
method wait_on_postgres_cdc_rls (line 677) | defp wait_on_postgres_cdc_rls(external_id, 0) do
method wait_on_postgres_cdc_rls (line 681) | defp wait_on_postgres_cdc_rls(external_id, attempt) do
FILE: test/realtime_web/dashboard/tenant_info_test.exs
class Realtime.Dashboard.TenantInfoTest (line 1) | defmodule Realtime.Dashboard.TenantInfoTest
method using_basic_auth (line 62) | defp using_basic_auth(conn, username, password) do
FILE: test/realtime_web/integration/tracing_test.exs
class Realtime.Integration.TracingTest (line 1) | defmodule Realtime.Integration.TracingTest
FILE: test/realtime_web/live/inspector_live/index_test.exs
class RealtimeWeb.InspectorLive.IndexTest (line 1) | defmodule RealtimeWeb.InspectorLive.IndexTest
FILE: test/realtime_web/live/page_live/index_test.exs
class RealtimeWeb.PageLive.IndexTest (line 1) | defmodule RealtimeWeb.PageLive.IndexTest
FILE: test/realtime_web/live/status_live/index_test.exs
class RealtimeWeb.StatusLive.IndexTest (line 1) | defmodule RealtimeWeb.StatusLive.IndexTest
FILE: test/realtime_web/live/tenants_live/index_test.exs
class RealtimeWeb.TenantsLive.IndexTest (line 1) | defmodule RealtimeWeb.TenantsLive.IndexTest
method using_basic_auth (line 67) | defp using_basic_auth(conn, username, password) do
FILE: test/realtime_web/plugs/assign_tenant_test.exs
class RealtimeWeb.Plugs.AssignTenantTest (line 1) | defmodule RealtimeWeb.Plugs.AssignTenantTest
FILE: test/realtime_web/plugs/auth_tenant_test.exs
class RealtimeWeb.AuthTenantTest (line 1) | defmodule RealtimeWeb.AuthTenantTest
FILE: test/realtime_web/plugs/baggage_request_id_test.exs
class RealtimeWeb.Plugs.BaggageRequestIdTest (line 1) | defmodule RealtimeWeb.Plugs.BaggageRequestIdTest
method call (line 8) | defp call(conn, opts) do
method generated_request_id? (line 81) | defp generated_request_id?(request_id) do
FILE: test/realtime_web/plugs/metrics_mode_test.exs
class RealtimeWeb.Plugs.MetricsModeTest (line 1) | defmodule RealtimeWeb.Plugs.MetricsModeTest
FILE: test/realtime_web/plugs/rate_limiter_test.exs
class RealtimeWeb.Plugs.RateLimiterTest (line 1) | defmodule RealtimeWeb.Plugs.RateLimiterTest
FILE: test/realtime_web/socket/v2_serializer_test.exs
class RealtimeWeb.Socket.V2SerializerTest (line 1) | defmodule RealtimeWeb.Socket.V2SerializerTest
method encode! (line 271) | defp encode!(serializer, msg) do
method decode! (line 283) | defp decode!(serializer, msg, opts), do: serializer.decode!(msg, opts)
method fastlane! (line 285) | defp fastlane!(serializer, msg) do
FILE: test/realtime_web/tenant_broadcaster_test.exs
class RealtimeWeb.TenantBroadcasterTest (line 1) | defmodule RealtimeWeb.TenantBroadcasterTest
method handle_telemetry (line 247) | def handle_telemetry(event, measures, metadata, %{pid: pid, tenant: te...
FILE: test/realtime_web/views/error_view_test.exs
class RealtimeWeb.ErrorViewTest (line 1) | defmodule RealtimeWeb.ErrorViewTest
FILE: test/realtime_web/views/layout_view_test.exs
class RealtimeWeb.LayoutViewTest (line 1) | defmodule RealtimeWeb.LayoutViewTest
FILE: test/realtime_web/views/page_view_test.exs
class RealtimeWeb.PageViewTest (line 1) | defmodule RealtimeWeb.PageViewTest
FILE: test/support/channel_case.ex
class RealtimeWeb.ChannelCase (line 1) | defmodule RealtimeWeb.ChannelCase
FILE: test/support/cleanup.ex
class Cleanup (line 1) | defmodule Cleanup
method ensure_no_replication_slot (line 3) | def ensure_no_replication_slot(attempts \\ 5)
method ensure_no_replication_slot (line 4) | def ensure_no_replication_slot(0), do: raise("Replication slot teardow...
method ensure_no_replication_slot (line 7) | def ensure_no_replication_slot(attempts) do
FILE: test/support/clustered.ex
class Clustered (line 1) | defmodule Clustered
method start (line 27) | def start(aux_mod \\ nil, opts \\ []) do
method start_disconnected (line 47) | def start_disconnected(aux_mod \\ nil, opts \\ []) do
method wait_for_gen_rpc (line 137) | defp wait_for_gen_rpc(pid) do
method wait_for_port_free (line 146) | defp wait_for_port_free(port, attempts \\ 50, delay_ms \\ 100)
method wait_for_port_free (line 147) | defp wait_for_port_free(_port, 0, _delay_ms), do: :ok
method wait_for_port_free (line 149) | defp wait_for_port_free(port, attempts, delay_ms) do
method wait_for_port (line 161) | defp wait_for_port(_host, _port, 0, _delay_ms), do: raise("gen_rpc tcp...
method wait_for_port (line 163) | defp wait_for_port(host, port, attempts, delay_ms) do
FILE: test/support/conn_case.ex
class RealtimeWeb.ConnCase (line 1) | defmodule RealtimeWeb.ConnCase
FILE: test/support/containers.ex
class Containers (line 1) | defmodule Containers
method pull (line 12) | def pull do
method start_container (line 23) | def start_container(), do: GenServer.call(__MODULE__, :start_container...
method port (line 24) | def port(), do: GenServer.call(__MODULE__, :port, 10_000)
method start_link (line 26) | def start_link(max_cases), do: GenServer.start_link(__MODULE__, max_ca...
method init (line 28) | def init(max_cases) do
method handle_continue (line 36) | def handle_continue({:pool, max_cases}, state) do
method handle_call (line 52) | def handle_call(:port, _from, state) do
method handle_call (line 57) | def handle_call(:start_container, _from, state) do
method random_string (line 72) | defp random_string(length) do
method initialize (line 78) | def initialize(external_id) do
method checkout (line 106) | def checkout() do
method storage_up! (line 118) | defp storage_up!(tenant) do
method checkout_tenant (line 131) | def checkout_tenant(opts \\ []), do: do_checkout_tenant(opts, :sandbox)
method checkout_tenant_unboxed (line 132) | def checkout_tenant_unboxed(opts \\ []), do: do_checkout_tenant(opts, ...
method do_checkout_tenant (line 134) | defp do_checkout_tenant(opts, mode) do
method repo_run (line 203) | defp repo_run(:unboxed, fun), do: Ecto.Adapters.SQL.Sandbox.unboxed_ru...
method repo_run (line 204) | defp repo_run(:sandbox, fun), do: fun.()
method stop_containers (line 206) | def stop_containers() do
method stop_container (line 215) | def stop_container(external_id) do
method existing_containers (line 220) | defp existing_containers(pattern) do
method check_container_ready (line 239) | defp check_container_ready(name, attempts \\ 50)
method check_container_ready (line 240) | defp check_container_ready(name, 0), do: raise("Container #{name} is n...
method check_container_ready (line 242) | defp check_container_ready(name, attempts) do
method run_migrations (line 254) | defp run_migrations(tenant) do
method docker_run! (line 284) | defp docker_run!(name, port) do
FILE: test/support/containers/container.ex
class Containers.Container (line 1) | defmodule Containers.Container
method start_link (line 4) | def start_link(args \\ [], opts \\ []) do
method port (line 8) | def port(pid), do: GenServer.call(pid, :port, 15_000)
method name (line 9) | def name(pid), do: GenServer.call(pid, :name, 15_000)
method handle_call (line 12) | def handle_call(:port, _from, state) do
method handle_call (line 17) | def handle_call(:name, _from, state) do
method init (line 22) | def init(_args) do
method handle_continue (line 27) | def handle_continue(:start_container, _state) do
method handle_continue (line 34) | def handle_continue(:check_container_ready, state) do
method check_container_ready (line 39) | defp check_container_ready(name, attempts \\ 100)
method check_container_ready (line 40) | defp check_container_ready(name, 0), do: raise("Container #{name} is n...
method check_container_ready (line 42) | defp check_container_ready(name, attempts) do
FILE: test/support/data_case.ex
class Realtime.DataCase (line 1) | defmodule Realtime.DataCase
method setup_sandbox (line 31) | def setup_sandbox(tags) do
method errors_on (line 48) | def errors_on(changeset) do
FILE: test/support/generators.ex
class Generators (line 1) | defmodule Generators
method port (line 10) | def port(), do: Containers.port()
method tenant_fixture (line 13) | def tenant_fixture(override \\ %{}) do
method message_fixture (line 49) | def message_fixture(tenant, override \\ %{}) do
method random_string (line 69) | def random_string(length \\ 20) do
method clean_table (line 73) | def clean_table(db_conn, schema, table) do
method create_messages_partitions (line 96) | def create_messages_partitions(db_conn, start_date, end_date) do
method create_rls_policies (line 124) | def create_rls_policies(conn, policies, params) do
method policy_query (line 141) | def policy_query(query, params \\ nil)
method policy_query (line 143) | def policy_query(:authenticated_all_topic_read, _) do
method policy_query (line 152) | def policy_query(:authenticated_all_topic_insert, _) do
method policy_query (line 161) | def policy_query(:authenticated_read_matching_user_sub, %{sub: sub}) do
method policy_query (line 170) | def policy_query(:read_matching_user_role, %{role: role}) do
method policy_query (line 178) | def policy_query(:authenticated_write_matching_user_sub, %{sub: sub}) do
method policy_query (line 187) | def policy_query(:write_matching_user_role, %{role: role}) do
method policy_query (line 195) | def policy_query(:authenticated_read_broadcast, %{topic: name}) do
method policy_query (line 204) | def policy_query(:authenticated_write_broadcast, %{topic: name}) do
method policy_query (line 213) | def policy_query(:authenticated_read_presence, %{topic: name}) do
method policy_query (line 222) | def policy_query(:authenticated_write_presence, %{topic: name}) do
method policy_query (line 231) | def policy_query(:authenticated_read_broadcast_and_presence, %{topic: ...
method policy_query (line 240) | def policy_query(:authenticated_write_broadcast_and_presence, %{topic:...
method policy_query (line 249) | def policy_query(:broken_read_presence, _) do
method policy_query (line 258) | def policy_query(:broken_write_presence, _) do
method generate_jwt_token (line 268) | def generate_jwt_token(secret_or_tenant) do
method generate_jwt_token (line 274) | def generate_jwt_token(%Tenant{} = tenant, claims) do
method test_port (line 286) | defp test_port do
method get_connection (line 292) | def get_connection(tenant, serializer \\ Phoenix.Socket.V1.JSONSeriali...
method uri (line 306) | def uri(tenant, serializer, port \\ nil),
method vsn (line 309) | defp vsn(Phoenix.Socket.V1.JSONSerializer), do: "1.0.0"
method vsn (line 310) | defp vsn(RealtimeWeb.Socket.V2Serializer), do: "2.0.0"
method token_valid (line 313) | def token_valid(tenant, role, claims \\ %{}), do: generate_token(tenan...
method token_no_role (line 315) | def token_no_role(tenant), do: generate_token(tenant)
method generate_token (line 318) | def generate_token(tenant, claims \\ %{}) do
FILE: test/support/integrations.ex
class Integrations (line 1) | defmodule Integrations
method checkout_tenant_and_connect (line 10) | def checkout_tenant_and_connect(_context \\ %{}) do
method rls_context (line 17) | def rls_context(%{tenant: tenant} = context) do
method change_tenant_configuration (line 50) | def change_tenant_configuration(%Tenant{external_id: external_id}, lim...
method checkout_tenant_connect_and_setup_postgres_changes (line 60) | def checkout_tenant_connect_and_setup_postgres_changes(_context \\ %{}...
method setup_postgres_changes (line 66) | def setup_postgres_changes(conn) do
method assert_process_down (line 120) | def assert_process_down(pid, timeout \\ 1000) do
FILE: test/support/joken_current_time_mock.ex
class RealtimeWeb.Joken.CurrentTime.Mock (line 1) | defmodule RealtimeWeb.Joken.CurrentTime.Mock
method start_link (line 12) | def start_link do
method child_spec (line 21) | def child_spec(_args) do
method current_time (line 28) | def current_time do
method freeze (line 38) | def freeze do
method freeze (line 42) | def freeze(timestamp) do
method unique_name_per_process (line 48) | def unique_name_per_process do
FILE: test/support/metrics_helper.ex
class MetricsHelper (line 1) | defmodule MetricsHelper
method search (line 4) | def search(prometheus_metrics, metric_name, expected_tags \\ nil) do
method parse (line 27) | defp parse(metric_string, regex, expected_tags) do
method parse_tags (line 43) | defp parse_tags(tags_string) do
method matching_tags (line 50) | defp matching_tags(tags, expected_tags) do
FILE: test/support/rate_counter_helper.ex
class RateCounterHelper (line 1) | defmodule RateCounterHelper
method stop (line 5) | def stop(tenant_id) do
method tick! (line 21) | def tick!(args) do
method tick_tenant_rate_counters! (line 27) | def tick_tenant_rate_counters!(tenant_id) do
FILE: test/support/replication_test_handler.ex
class Replication.TestHandler (line 1) | defmodule Replication.TestHandler
method call (line 25) | def call(_, _), do: :noreply
FILE: test/support/tenant_connection.ex
class TenantConnection (line 1) | defmodule TenantConnection
method create_message (line 11) | def create_message(attrs, conn, opts \\ [mode: :savepoint]) do
method ensure_connect_down (line 28) | def ensure_connect_down(tenant_id) do
FILE: test/support/tracing.ex
class Realtime.Tracing (line 1) | defmodule Realtime.Tracing
FILE: test/support/websocket_client.ex
class Realtime.Integration.WebsocketClient (line 4) | defmodule Realtime.Integration.WebsocketClient
method connect (line 35) | def connect(sender, url, serializer, headers \\ []) do
method close (line 45) | def close(socket), do: GenServer.cast(socket, :close)
method send_event (line 50) | def send_event(socket, topic, event, msg) do
method send (line 57) | def send(socket, msg), do: GenServer.call(socket, {:send, msg})
method send_heartbeat (line 62) | def send_heartbeat(socket), do: send_event(socket, "phoenix", "heartbe...
method join (line 67) | def join(socket, topic, msg), do: send_event(socket, topic, "phx_join"...
method leave (line 72) | def leave(socket, topic, msg), do: send_event(socket, topic, "phx_leav...
method init (line 77) | def init({sender, serializer}) do
method handle_call (line 83) | def handle_call({:connect, url, headers}, from, state) do
method handle_call (line 116) | def handle_call({:send, msg}, _from, state) do
method handle_cast (line 126) | def handle_cast(:close, state), do: do_close(state)
method do_close (line 128) | defp do_close(state) do
method handle_info (line 137) | def handle_info(message, state) do
method handle_responses (line 155) | defp handle_responses(state, responses)
method handle_responses (line 157) | defp handle_responses(%{request_ref: ref} = state, [{:status, ref, sta...
method handle_responses (line 163) | defp handle_responses(%{request_ref: ref} = state, [{:headers, ref, re...
method handle_responses (line 169) | defp handle_responses(%{request_ref: ref} = state, [{:done, ref} | res...
method handle_responses (line 199) | defp handle_responses(state, [_response | rest]), do: handle_responses...
method handle_responses (line 201) | defp handle_responses(state, []), do: state
method handle_frames (line 203) | defp handle_frames(state, frames) do
method stream_frame (line 240) | defp stream_frame(state, frame) do
method reply (line 255) | defp reply(state, response) do
method serialize_msg (line 260) | defp serialize_msg(msg, %{serializer: :noop} = state), do: {msg, state}
method serialize_msg (line 262) | defp serialize_msg(%Message{payload: {:binary, _}} = msg, %{ref: ref} ...
method serialize_msg (line 268) | defp serialize_msg(%Message{} = msg, %{ref: ref} = state) do
method serialize_msg (line 274) | defp serialize_msg(msg, state), do: {msg, state}
method join_ref_for (line 276) | defp join_ref_for(
method join_ref_for (line 284) | defp join_ref_for(%{topic: topic}, %{topics: topics} = state) do
method encode! (line 288) | defp encode!(map, state) do
method binary_encode_push! (line 293) | defp binary_encode_push!(%Message{payload: {:binary, data}} = msg) do
method binary_decode (line 316) | defp binary_decode(<<
method binary_decode (line 330) | defp binary_decode(<<
Condensed preview — 475 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,175K chars).
[
{
"path": ".credo.exs",
"chars": 730,
"preview": "%{\n configs: [\n %{\n name: \"default\",\n files: %{\n included: [\"lib/\", \"src/\", \"web/\", \"apps/\"],\n "
},
{
"path": ".dockerignore",
"chars": 1270,
"preview": "# This file excludes paths from the Docker build context.\n#\n# By default, Docker's build context includes all files (and"
},
{
"path": ".formatter.exs",
"chars": 233,
"preview": "[\n import_deps: [:ecto, :ecto_sql, :phoenix, :open_api_spex],\n subdirectories: [\"priv/*/migrations\"],\n plugins: [],\n "
},
{
"path": ".github/actionlint.yaml",
"chars": 100,
"preview": "self-hosted-runner:\n labels:\n - blacksmith-4vcpu-ubuntu-2404\n - blacksmith-8vcpu-ubuntu-2404\n"
},
{
"path": ".github/workflows/beacon_tests.yml",
"chars": 1589,
"preview": "name: Beacon Tests\ndefaults:\n run:\n shell: bash\n working-directory: ./beacon\non:\n pull_request:\n paths:\n "
},
{
"path": ".github/workflows/docker-build.yml",
"chars": 326,
"preview": "name: Docker Build\n\non:\n pull_request:\n branches:\n - main\n\njobs:\n build:\n runs-on: ubuntu-latest\n\n steps"
},
{
"path": ".github/workflows/integration_tests.yml",
"chars": 1998,
"preview": "name: Integration Tests\non:\n pull_request:\n paths:\n - \"lib/**\"\n - \"test/**\"\n - \"config/**\"\n - \"p"
},
{
"path": ".github/workflows/lint.yml",
"chars": 2091,
"preview": "name: Lint\non:\n pull_request:\n paths:\n - \"lib/**\"\n - \"test/**\"\n - \"config/**\"\n - \"priv/**\"\n "
},
{
"path": ".github/workflows/manual_prod_build.yml",
"chars": 3690,
"preview": "name: Manual Build Production\non:\n workflow_dispatch:\n inputs:\n branch:\n description: \"Branch to run the"
},
{
"path": ".github/workflows/mirror.yml",
"chars": 1019,
"preview": "name: Mirror Image\n\non:\n workflow_dispatch:\n inputs:\n version:\n description: \"Image tag\"\n require"
},
{
"path": ".github/workflows/prod_build.yml",
"chars": 5491,
"preview": "name: Build Production\non:\n push:\n branches:\n - \"main\"\n paths:\n - \"lib/**\"\n - \"config/**\"\n - "
},
{
"path": ".github/workflows/prod_linter.yml",
"chars": 1631,
"preview": "name: Production Formatting Checks\non:\n pull_request:\n branches:\n - release\n\njobs:\n format:\n name: Formatti"
},
{
"path": ".github/workflows/tests.yml",
"chars": 3244,
"preview": "name: Tests\non:\n pull_request:\n paths:\n - \"lib/**\"\n - \"test/**\"\n - \"config/**\"\n - \"priv/**\"\n "
},
{
"path": ".github/workflows/update-supabase-js.yml",
"chars": 2142,
"preview": "name: Update @supabase/supabase-js\n\non:\n workflow_dispatch:\n inputs:\n version:\n description: \"Version to"
},
{
"path": ".gitignore",
"chars": 759,
"preview": "# The directory Mix will write compiled artifacts to.\n/_build/\n\n# If you run \"mix test --cover\", coverage assets end up "
},
{
"path": ".releaserc",
"chars": 490,
"preview": "{\n \"branches\": [\"main\"],\n \"plugins\": [\n \"@semantic-release/commit-analyzer\",\n \"@semantic-release/release-notes-g"
},
{
"path": ".sobelow-conf",
"chars": 213,
"preview": "[\n verbose: true,\n private: false,\n skip: false,\n router: nil,\n exit: :low,\n format: \"txt\",\n out: nil,\n threshol"
},
{
"path": ".tool-versions",
"chars": 41,
"preview": "elixir 1.18.4-otp-27\nnodejs 24\nerlang 27\n"
},
{
"path": "Dockerfile",
"chars": 2591,
"preview": "ARG ELIXIR_VERSION=1.18\nARG OTP_VERSION=27.3\nARG DEBIAN_VERSION=bookworm-20250929-slim\nARG BUILDER_IMAGE=\"hexpm/elixir:$"
},
{
"path": "LICENSE",
"chars": 11338,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "Makefile",
"chars": 3443,
"preview": "CLUSTER_STRATEGIES ?= EPMD\nNODE_NAME ?= pink\nPORT ?= 4000\n\n.PHONY: dev dev.orange seed prod bench.% dev_db start start.%"
},
{
"path": "README.md",
"chars": 85842,
"preview": "<br />\n<p align=\"center\">\n <a href=\"https://supabase.io\">\n <picture>\n <source media=\"(prefers-color-scheme:"
},
{
"path": "assets/css/app.css",
"chars": 58,
"preview": "@tailwind base;\n@tailwind components;\n@tailwind utilities;"
},
{
"path": "assets/css/phoenix.css",
"chars": 10310,
"preview": "/* Includes some default style for the starter application.\n * This can be safely deleted to start fresh.\n */\n\n/* Millig"
},
{
"path": "assets/js/app.js",
"chars": 9646,
"preview": "import \"../css/app.css\";\nimport \"phoenix_html\";\nimport { Socket } from \"phoenix\";\nimport { LiveSocket } from \"phoenix_li"
},
{
"path": "assets/package.json",
"chars": 76,
"preview": "{\n \"dependencies\": {\n \"@supabase/supabase-js\": \"2.100.0-canary.0\"\n }\n}\n"
},
{
"path": "assets/tailwind.config.js",
"chars": 1201,
"preview": "const plugin = require(\"tailwindcss/plugin\")\nconst colors = require('tailwindcss/colors')\n\nmodule.exports = {\n content:"
},
{
"path": "assets/vendor/topbar.js",
"chars": 5087,
"preview": "/**\n * @license MIT\n * topbar 1.0.0, 2021-01-06\n * https://buunguyen.github.io/topbar\n * Copyright (c) 2021 Buu Nguyen\n "
},
{
"path": "beacon/.formatter.exs",
"chars": 97,
"preview": "# Used by \"mix format\"\n[\n inputs: [\"{mix,.formatter}.exs\", \"{config,lib,test}/**/*.{ex,exs}\"]\n]\n"
},
{
"path": "beacon/.gitignore",
"chars": 545,
"preview": "# The directory Mix will write compiled artifacts to.\n/_build/\n\n# If you run \"mix test --cover\", coverage assets end up "
},
{
"path": "beacon/README.md",
"chars": 1665,
"preview": "# Beacon\n\nBeacon is a scalable process group manager. The main use case for this library is to have membership counts av"
},
{
"path": "beacon/config/config.exs",
"chars": 130,
"preview": "import Config\n\n# Print nothing during tests unless captured or a test failure happens\nconfig :logger, backends: [], leve"
},
{
"path": "beacon/lib/beacon/adapter/erl_dist.ex",
"chars": 751,
"preview": "defmodule Beacon.Adapter.ErlDist do\n @moduledoc false\n\n import Kernel, except: [send: 2]\n\n @behaviour Beacon.Adapter\n"
},
{
"path": "beacon/lib/beacon/adapter.ex",
"chars": 611,
"preview": "defmodule Beacon.Adapter do\n @moduledoc \"\"\"\n Behaviour module for Beacon messaging adapters.\n \"\"\"\n\n @doc \"Register t"
},
{
"path": "beacon/lib/beacon/partition.ex",
"chars": 5029,
"preview": "defmodule Beacon.Partition do\n @moduledoc false\n\n use GenServer\n require Logger\n\n defmodule State do\n @moduledoc "
},
{
"path": "beacon/lib/beacon/scope.ex",
"chars": 6236,
"preview": "defmodule Beacon.Scope do\n @moduledoc false\n # Responsible to discover and keep track of all Beacon peers in the clust"
},
{
"path": "beacon/lib/beacon/supervisor.ex",
"chars": 2116,
"preview": "defmodule Beacon.Supervisor do\n @moduledoc false\n use Supervisor\n\n def name(scope), do: :\"#{scope}_beacon\"\n def supe"
},
{
"path": "beacon/lib/beacon.ex",
"chars": 5488,
"preview": "defmodule Beacon do\n @moduledoc \"\"\"\n Distributed process group membership tracking.\n \"\"\"\n\n alias Beacon.Partition\n "
},
{
"path": "beacon/mix.exs",
"chars": 828,
"preview": "defmodule Beacon.MixProject do\n use Mix.Project\n\n def project do\n [\n app: :beacon,\n version: \"1.0.0\",\n "
},
{
"path": "beacon/test/beacon/partition_test.exs",
"chars": 8408,
"preview": "defmodule Beacon.PartitionTest do\n use ExUnit.Case, async: true\n alias Beacon.Partition\n\n @scope __MODULE__\n\n setup "
},
{
"path": "beacon/test/beacon_test.exs",
"chars": 15580,
"preview": "defmodule BeaconTest do\n use ExUnit.Case, async: true\n\n setup do\n scope = :\"test_scope#{System.unique_integer([:pos"
},
{
"path": "beacon/test/support/peer.ex",
"chars": 2533,
"preview": "defmodule Peer do\n @moduledoc \"\"\"\n Uses the gist https://gist.github.com/ityonemo/177cbc96f8c8722bfc4d127ff9baec62 to "
},
{
"path": "beacon/test/test_helper.exs",
"chars": 74,
"preview": "ExUnit.start(capture_log: true)\n\n:net_kernel.start([:\"beacon@127.0.0.1\"])\n"
},
{
"path": "bench/gen_counter.exs",
"chars": 265,
"preview": "alias Realtime.GenCounter\n\ncounter = :counters.new(1, [:write_concurrency])\n_gen_counter = GenCounter.new(:any_term)\n\nBe"
},
{
"path": "bench/secrets.exs",
"chars": 784,
"preview": "alias RealtimeWeb.ChannelsAuthorization\nalias Realtime.Helpers, as: H\n\njwt =\n \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ"
},
{
"path": "config/config.exs",
"chars": 3072,
"preview": "# This file is responsible for configuring your application\n# and its dependencies with the aid of the Mix.Config module"
},
{
"path": "config/dev.exs",
"chars": 2967,
"preview": "import Config\n\n# For development, we disable any cache and enable\n# debugging and code reloading.\n#\n# The watchers confi"
},
{
"path": "config/prod.exs",
"chars": 2160,
"preview": "import Config\n\n# For production, don't forget to configure the url host\n# to something meaningful, Phoenix uses this inf"
},
{
"path": "config/runtime.exs",
"chars": 17027,
"preview": "import Config\n\ndefmodule Env do\n def get_integer(env, default) do\n value = System.get_env(env)\n if value, do: Str"
},
{
"path": "config/test.exs",
"chars": 2277,
"preview": "import Config\n\npartition = System.get_env(\"MIX_TEST_PARTITION\")\n\nfor repo <- [\n Realtime.Repo,\n Realtime.Repo."
},
{
"path": "coveralls.json",
"chars": 1132,
"preview": "{\n \"skip_files\": [\n \"lib/realtime_web/api_spec.ex\",\n \"lib/realtime_web/channels/presence.ex\",\n \""
},
{
"path": "deploy/fly/prod.toml",
"chars": 1107,
"preview": "# fly.toml app configuration file generated for realtime-prod on 2023-08-08T09:07:09-07:00\n#\n# See https://fly.io/docs/r"
},
{
"path": "deploy/fly/qa.toml",
"chars": 1026,
"preview": "app = \"realtime-qa\"\nkill_signal = \"SIGTERM\"\nkill_timeout = 5\nprocesses = []\n\n[deploy]\n release_command = \"/app/bin/migr"
},
{
"path": "deploy/fly/staging.toml",
"chars": 1218,
"preview": "# fly.toml app configuration file generated for realtime-staging on 2023-06-27T07:39:20-07:00\n#\n# See https://fly.io/doc"
},
{
"path": "dev/postgres/00-supabase-schema.sql",
"chars": 77,
"preview": "create schema if not exists _realtime;\ncreate schema if not exists realtime;\n"
},
{
"path": "docker-compose.dbs.yml",
"chars": 679,
"preview": "version: '3'\n\nservices:\n db:\n image: supabase/postgres:17.6.1.074\n container_name: realtime-db\n ports:\n -"
},
{
"path": "docker-compose.tests.yml",
"chars": 2399,
"preview": "services:\n # Supabase Realtime service\n test_db:\n image: supabase/postgres:17.6.1.074\n container_name: test-real"
},
{
"path": "docker-compose.yml",
"chars": 1604,
"preview": "services:\n db:\n image: supabase/postgres:17.6.1.074\n container_name: realtime-db\n ports:\n - \"5432:5432\"\n "
},
{
"path": "lib/extensions/extensions.ex",
"chars": 526,
"preview": "defmodule Realtime.Extensions do\n @moduledoc \"\"\"\n This module provides functions to get extension settings.\n \"\"\"\n de"
},
{
"path": "lib/extensions/postgres_cdc_rls/cdc_rls.ex",
"chars": 6561,
"preview": "defmodule Extensions.PostgresCdcRls do\n @moduledoc \"\"\"\n Callbacks for initiating a Postgres connection and creating a "
},
{
"path": "lib/extensions/postgres_cdc_rls/db_settings.ex",
"chars": 647,
"preview": "defmodule Extensions.PostgresCdcRls.DbSettings do\n @moduledoc \"\"\"\n Schema callbacks for CDC RLS implementation.\n \"\"\"\n"
},
{
"path": "lib/extensions/postgres_cdc_rls/message_dispatcher.ex",
"chars": 1837,
"preview": "# This file draws from https://github.com/phoenixframework/phoenix/blob/9941711736c8464b27b40914a4d954ed2b4f5958/lib/pho"
},
{
"path": "lib/extensions/postgres_cdc_rls/replication_poller.ex",
"chars": 10870,
"preview": "defmodule Extensions.PostgresCdcRls.ReplicationPoller do\n @moduledoc \"\"\"\n Polls the write ahead log, applies row level"
},
{
"path": "lib/extensions/postgres_cdc_rls/replications.ex",
"chars": 2750,
"preview": "defmodule Extensions.PostgresCdcRls.Replications do\n @moduledoc \"\"\"\n SQL queries that use PostgresCdcRls.ReplicationPo"
},
{
"path": "lib/extensions/postgres_cdc_rls/subscription_manager.ex",
"chars": 8000,
"preview": "defmodule Extensions.PostgresCdcRls.SubscriptionManager do\n @moduledoc \"\"\"\n Handles subscriptions from tenant's databa"
},
{
"path": "lib/extensions/postgres_cdc_rls/subscriptions.ex",
"chars": 10339,
"preview": "defmodule Extensions.PostgresCdcRls.Subscriptions do\n @moduledoc \"\"\"\n This module consolidates subscriptions handling\n"
},
{
"path": "lib/extensions/postgres_cdc_rls/subscriptions_checker.ex",
"chars": 5885,
"preview": "defmodule Extensions.PostgresCdcRls.SubscriptionsChecker do\n @moduledoc false\n use GenServer\n use Realtime.Logs\n\n al"
},
{
"path": "lib/extensions/postgres_cdc_rls/supervisor.ex",
"chars": 955,
"preview": "defmodule Extensions.PostgresCdcRls.Supervisor do\n @moduledoc \"\"\"\n Supervisor to spin up the Postgres CDC RLS tree.\n "
},
{
"path": "lib/extensions/postgres_cdc_rls/worker_supervisor.ex",
"chars": 1559,
"preview": "defmodule Extensions.PostgresCdcRls.WorkerSupervisor do\n @moduledoc false\n use Supervisor\n\n alias Extensions.Postgres"
},
{
"path": "lib/realtime/adapters/changes.ex",
"chars": 1573,
"preview": "# This file draws heavily from https://github.com/cainophile/cainophile\n# License: https://github.com/cainophile/cainoph"
},
{
"path": "lib/realtime/adapters/postgres/decoder.ex",
"chars": 10244,
"preview": "# This file draws heavily from https://github.com/cainophile/pgoutput_decoder\n# License: https://github.com/cainophile/p"
},
{
"path": "lib/realtime/adapters/postgres/oid_database.ex",
"chars": 4560,
"preview": "# CREDITS\n# This file draws heavily from https://github.com/cainophile/pgoutput_decoder\n# License: https://github.com/ca"
},
{
"path": "lib/realtime/adapters/postgres/protocol/keep_alive.ex",
"chars": 795,
"preview": "defmodule Realtime.Adapters.Postgres.Protocol.KeepAlive do\n @moduledoc \"\"\"\n Primary keepalive message (B)\n Byte1('k')"
},
{
"path": "lib/realtime/adapters/postgres/protocol/write.ex",
"chars": 825,
"preview": "defmodule Realtime.Adapters.Postgres.Protocol.Write do\n @moduledoc \"\"\"\n XLogData (B)\n Byte1('w')\n Identifies the mes"
},
{
"path": "lib/realtime/adapters/postgres/protocol.ex",
"chars": 2055,
"preview": "defmodule Realtime.Adapters.Postgres.Protocol do\n @moduledoc \"\"\"\n This module is responsible for parsing the Postgres "
},
{
"path": "lib/realtime/api/extensions.ex",
"chars": 2027,
"preview": "defmodule Realtime.Api.Extensions do\n @moduledoc \"\"\"\n Schema for Realtime Extension settings.\n \"\"\"\n\n use Ecto.Schema"
},
{
"path": "lib/realtime/api/message.ex",
"chars": 1167,
"preview": "defmodule Realtime.Api.Message do\n @moduledoc \"\"\"\n Defines the Message schema to be used to check RLS authorization po"
},
{
"path": "lib/realtime/api/tenant.ex",
"chars": 3816,
"preview": "defmodule Realtime.Api.Tenant do\n @moduledoc \"\"\"\n Describes a database/tenant which makes use of the realtime service."
},
{
"path": "lib/realtime/api.ex",
"chars": 9621,
"preview": "defmodule Realtime.Api do\n @moduledoc \"\"\"\n The Api context.\n \"\"\"\n require Logger\n\n import Ecto.Query\n\n alias Ecto."
},
{
"path": "lib/realtime/application.ex",
"chars": 8097,
"preview": "defmodule Realtime.Application do\n # See https://hexdocs.pm/elixir/Application.html\n # for more information on OTP App"
},
{
"path": "lib/realtime/beacon_pub_sub_adapter.ex",
"chars": 943,
"preview": "defmodule Realtime.BeaconPubSubAdapter do\n @moduledoc \"Beacon adapter to use PubSub\"\n\n import Kernel, except: [send: 2"
},
{
"path": "lib/realtime/crypto.ex",
"chars": 1028,
"preview": "defmodule Realtime.Crypto do\n @moduledoc \"\"\"\n Encrypt and decrypt operations required by Realtime. It uses the secret "
},
{
"path": "lib/realtime/database.ex",
"chars": 12938,
"preview": "defmodule Realtime.Database do\n @moduledoc \"\"\"\n Handles tenant database operations\n \"\"\"\n use Realtime.Logs\n\n alias "
},
{
"path": "lib/realtime/gen_counter/gen_counter.ex",
"chars": 1323,
"preview": "defmodule Realtime.GenCounter do\n @moduledoc \"\"\"\n Process holds an ETS table where each row is a key and a counter\n \""
},
{
"path": "lib/realtime/gen_rpc/pub_sub.ex",
"chars": 4149,
"preview": "defmodule Realtime.GenRpcPubSub do\n @moduledoc \"\"\"\n gen_rpc Phoenix.PubSub adapter\n \"\"\"\n\n @behaviour Phoenix.PubSub."
},
{
"path": "lib/realtime/gen_rpc.ex",
"chars": 8311,
"preview": "defmodule Realtime.GenRpc do\n @moduledoc \"\"\"\n RPC module for Realtime using :gen_rpc\n\n :max_gen_rpc_clients is the ma"
},
{
"path": "lib/realtime/helpers.ex",
"chars": 1033,
"preview": "defmodule Realtime.Helpers do\n @moduledoc \"\"\"\n This module includes helper functions for different contexts that can't"
},
{
"path": "lib/realtime/log_filter.ex",
"chars": 1071,
"preview": "defmodule Realtime.LogFilter do\n @moduledoc \"\"\"\n Primary logger filter that suppresses noisy errors from dependencies."
},
{
"path": "lib/realtime/logs.ex",
"chars": 1735,
"preview": "defmodule Realtime.Logs do\n @moduledoc \"\"\"\n Logging operations for Realtime\n \"\"\"\n require Logger\n\n defmacro __using"
},
{
"path": "lib/realtime/messages.ex",
"chars": 3427,
"preview": "defmodule Realtime.Messages do\n @moduledoc \"\"\"\n Handles `realtime.messages` table operations\n \"\"\"\n\n alias Realtime.A"
},
{
"path": "lib/realtime/metrics_cleaner.ex",
"chars": 4358,
"preview": "defmodule Realtime.MetricsCleaner do\n @moduledoc false\n\n use GenServer\n require Logger\n\n defstruct [:check_ref, :int"
},
{
"path": "lib/realtime/metrics_pusher.ex",
"chars": 3516,
"preview": "defmodule Realtime.MetricsPusher do\n @moduledoc \"\"\"\n GenServer that periodically pushes Prometheus metrics to an endpo"
},
{
"path": "lib/realtime/monitoring/distributed_metrics.ex",
"chars": 2240,
"preview": "defmodule Realtime.DistributedMetrics do\n @moduledoc \"\"\"\n Gather stats for each connected node\n \"\"\"\n\n require Record"
},
{
"path": "lib/realtime/monitoring/erl_sys_mon.ex",
"chars": 1374,
"preview": "defmodule Realtime.ErlSysMon do\n @moduledoc \"\"\"\n Logs Erlang System Monitor events.\n \"\"\"\n\n use GenServer\n\n require "
},
{
"path": "lib/realtime/monitoring/gen_rpc_metrics.ex",
"chars": 3699,
"preview": "defmodule Realtime.GenRpcMetrics do\n @moduledoc \"\"\"\n Gather stats for gen_rpc TCP sockets\n \"\"\"\n\n require Record\n Re"
},
{
"path": "lib/realtime/monitoring/latency.ex",
"chars": 4214,
"preview": "defmodule Realtime.Latency do\n @moduledoc \"\"\"\n Measures the latency of the cluster from each node and broadcasts it "
},
{
"path": "lib/realtime/monitoring/os_metrics.ex",
"chars": 666,
"preview": "defmodule Realtime.OsMetrics do\n @moduledoc \"\"\"\n This module provides functions to get CPU and RAM usage.\n \"\"\"\n\n @sp"
},
{
"path": "lib/realtime/monitoring/peep/partitioned.ex",
"chars": 4511,
"preview": "defmodule Realtime.Monitoring.Peep.Partitioned do\n @moduledoc \"\"\"\n Peep.Storage implementation using a single ETS tabl"
},
{
"path": "lib/realtime/monitoring/prom_ex/plugins/channels.ex",
"chars": 488,
"preview": "defmodule Realtime.PromEx.Plugins.Channels do\n @moduledoc \"\"\"\n Realtime channels monitoring plugin for PromEx\n \"\"\"\n "
},
{
"path": "lib/realtime/monitoring/prom_ex/plugins/distributed.ex",
"chars": 3494,
"preview": "defmodule Realtime.PromEx.Plugins.Distributed do\n @moduledoc \"\"\"\n Distributed erlang metrics\n \"\"\"\n\n use PromEx.Plugi"
},
{
"path": "lib/realtime/monitoring/prom_ex/plugins/gen_rpc.ex",
"chars": 3518,
"preview": "defmodule Realtime.PromEx.Plugins.GenRpc do\n @moduledoc \"\"\"\n GenRpc metrics\n \"\"\"\n\n use PromEx.Plugin\n\n alias Realti"
},
{
"path": "lib/realtime/monitoring/prom_ex/plugins/osmon.ex",
"chars": 2065,
"preview": "defmodule Realtime.PromEx.Plugins.OsMon do\n @moduledoc \"\"\"\n Polls os_mon metrics.\n \"\"\"\n\n use PromEx.Plugin\n require"
},
{
"path": "lib/realtime/monitoring/prom_ex/plugins/phoenix.ex",
"chars": 7070,
"preview": "# copied from https://github.com/akoutmos/prom_ex/blob/master/lib/prom_ex/plugins/phoenix.ex\n\nif Code.ensure_loaded?(Pho"
},
{
"path": "lib/realtime/monitoring/prom_ex/plugins/tenant.ex",
"chars": 7475,
"preview": "defmodule Realtime.PromEx.Plugins.Tenant do\n @moduledoc false\n\n use PromEx.Plugin\n require Logger\n alias Realtime.Te"
},
{
"path": "lib/realtime/monitoring/prom_ex/plugins/tenant_global.ex",
"chars": 4378,
"preview": "defmodule Realtime.PromEx.Plugins.TenantGlobal do\n @moduledoc \"\"\"\n Global aggregated variants of per-tenant metrics.\n\n"
},
{
"path": "lib/realtime/monitoring/prom_ex/plugins/tenants.ex",
"chars": 1605,
"preview": "defmodule Realtime.PromEx.Plugins.Tenants do\n @moduledoc false\n\n use PromEx.Plugin\n\n alias PromEx.MetricTypes.Event\n "
},
{
"path": "lib/realtime/monitoring/prom_ex.ex",
"chars": 4317,
"preview": "defmodule Realtime.PromEx do\n alias Realtime.PromEx.Plugins.Distributed\n alias Realtime.PromEx.Plugins.GenRpc\n alias "
},
{
"path": "lib/realtime/monitoring/prometheus.ex",
"chars": 5956,
"preview": "# Based on https://github.com/rkallos/peep/blob/708546ed069aebdf78ac1f581130332bd2e8b5b1/lib/peep/prometheus.ex\ndefmodul"
},
{
"path": "lib/realtime/monitoring/tenant_prom_ex.ex",
"chars": 879,
"preview": "defmodule Realtime.TenantPromEx do\n alias Realtime.PromEx.Plugins.Channels\n alias Realtime.PromEx.Plugins.Tenant\n\n @m"
},
{
"path": "lib/realtime/nodes.ex",
"chars": 8083,
"preview": "defmodule Realtime.Nodes do\n @moduledoc \"\"\"\n Handles common needs for :syn module operations\n \"\"\"\n require Logger\n "
},
{
"path": "lib/realtime/operations.ex",
"chars": 3146,
"preview": "defmodule Realtime.Operations do\n @moduledoc \"\"\"\n Support operations for Realtime.\n \"\"\"\n alias Realtime.Rpc\n\n @doc "
},
{
"path": "lib/realtime/postgres_cdc.ex",
"chars": 2567,
"preview": "defmodule Realtime.PostgresCdc do\n @moduledoc false\n\n require Logger\n\n alias Realtime.Api.Tenant\n\n @timeout 10_000\n "
},
{
"path": "lib/realtime/rate_counter/dynamic_supervisor.ex",
"chars": 413,
"preview": "defmodule Realtime.RateCounter.DynamicSupervisor do\n @moduledoc \"\"\"\n Dynamic Supervisor to spin up `RateCounter`s as n"
},
{
"path": "lib/realtime/rate_counter/rate_counter.ex",
"chars": 8420,
"preview": "defmodule Realtime.RateCounter do\n @moduledoc \"\"\"\n Start a RateCounter for any Erlang term.\n\n These rate counters use"
},
{
"path": "lib/realtime/release.ex",
"chars": 995,
"preview": "defmodule Realtime.Release do\n @moduledoc \"\"\"\n Used for executing DB release tasks when run in production without Mix\n"
},
{
"path": "lib/realtime/repo.ex",
"chars": 459,
"preview": "defmodule Realtime.Repo do\n use Ecto.Repo,\n otp_app: :realtime,\n adapter: Ecto.Adapters.Postgres\n\n def with_dyna"
},
{
"path": "lib/realtime/repo_replica.ex",
"chars": 2642,
"preview": "defmodule Realtime.Repo.Replica do\n @moduledoc \"\"\"\n Generates a read-only replica repo for the region specified in con"
},
{
"path": "lib/realtime/rpc.ex",
"chars": 2786,
"preview": "defmodule Realtime.Rpc do\n @moduledoc \"\"\"\n RPC module for Realtime with the intent of standardizing the RPC interface "
},
{
"path": "lib/realtime/signal_handler.ex",
"chars": 891,
"preview": "defmodule Realtime.SignalHandler do\n @moduledoc false\n @behaviour :gen_event\n require Logger\n\n @spec shutdown_in_pro"
},
{
"path": "lib/realtime/syn/postgres_cdc.ex",
"chars": 694,
"preview": "defmodule Realtime.Syn.PostgresCdc do\n @moduledoc \"\"\"\n Scope for the PostgresCdc module.\n \"\"\"\n\n @doc \"\"\"\n Returns t"
},
{
"path": "lib/realtime/syn_handler.ex",
"chars": 4498,
"preview": "defmodule Realtime.SynHandler do\n @moduledoc \"\"\"\n Custom defined Syn's callbacks\n \"\"\"\n require Logger\n alias Realti"
},
{
"path": "lib/realtime/telemetry/logger.ex",
"chars": 1101,
"preview": "defmodule Realtime.Telemetry.Logger do\n @moduledoc \"\"\"\n We can log less frequent Telemetry events to get data into Big"
},
{
"path": "lib/realtime/telemetry/telemetry.ex",
"chars": 290,
"preview": "defmodule Realtime.Telemetry do\n @moduledoc \"\"\"\n Telemetry wrapper\n \"\"\"\n\n @doc \"\"\"\n Dispatches Telemetry events.\n "
},
{
"path": "lib/realtime/tenants/authorization/policies/broadcast_policies.ex",
"chars": 418,
"preview": "defmodule Realtime.Tenants.Authorization.Policies.BroadcastPolicies do\n @moduledoc \"\"\"\n BroadcastPolicies structure th"
},
{
"path": "lib/realtime/tenants/authorization/policies/presence_policies.ex",
"chars": 417,
"preview": "defmodule Realtime.Tenants.Authorization.Policies.PresencePolicies do\n @moduledoc \"\"\"\n PresencePolicies structure th"
},
{
"path": "lib/realtime/tenants/authorization/policies.ex",
"chars": 1087,
"preview": "defmodule Realtime.Tenants.Authorization.Policies do\n @moduledoc \"\"\"\n Policies structure that holds the required autho"
},
{
"path": "lib/realtime/tenants/authorization.ex",
"chars": 10744,
"preview": "defmodule Realtime.Tenants.Authorization do\n @moduledoc \"\"\"\n Runs validations based on RLS policies to return policies"
},
{
"path": "lib/realtime/tenants/batch_broadcast.ex",
"chars": 5910,
"preview": "defmodule Realtime.Tenants.BatchBroadcast do\n @moduledoc \"\"\"\n Virtual schema with a representation of a batched broadc"
},
{
"path": "lib/realtime/tenants/cache.ex",
"chars": 1614,
"preview": "defmodule Realtime.Tenants.Cache do\n @moduledoc \"\"\"\n Cache for Tenants.\n \"\"\"\n require Cachex.Spec\n require Logger\n\n"
},
{
"path": "lib/realtime/tenants/connect/check_connection.ex",
"chars": 628,
"preview": "defmodule Realtime.Tenants.Connect.CheckConnection do\n @moduledoc \"\"\"\n Check tenant database connection.\n \"\"\"\n\n @beh"
},
{
"path": "lib/realtime/tenants/connect/get_tenant.ex",
"chars": 481,
"preview": "defmodule Realtime.Tenants.Connect.GetTenant do\n @moduledoc \"\"\"\n Get tenant database connection.\n \"\"\"\n\n alias Realti"
},
{
"path": "lib/realtime/tenants/connect/piper.ex",
"chars": 786,
"preview": "defmodule Realtime.Tenants.Connect.Piper do\n @moduledoc \"\"\"\n Pipes different commands to execute specific actions duri"
},
{
"path": "lib/realtime/tenants/connect/reconcile_migrations.ex",
"chars": 1031,
"preview": "defmodule Realtime.Tenants.Connect.ReconcileMigrations do\n @moduledoc \"\"\"\n Reconciles the tenant's cached migrations_r"
},
{
"path": "lib/realtime/tenants/connect/register_process.ex",
"chars": 683,
"preview": "defmodule Realtime.Tenants.Connect.RegisterProcess do\n @moduledoc \"\"\"\n Registers the database process in :syn\n \"\"\"\n "
},
{
"path": "lib/realtime/tenants/connect.ex",
"chars": 19351,
"preview": "defmodule Realtime.Tenants.Connect do\n @moduledoc \"\"\"\n This module is responsible for attempting to connect to a tenan"
},
{
"path": "lib/realtime/tenants/janitor/maintenance_task.ex",
"chars": 633,
"preview": "defmodule Realtime.Tenants.Janitor.MaintenanceTask do\n @moduledoc \"\"\"\n Perform maintenance on the messages table.\n * "
},
{
"path": "lib/realtime/tenants/janitor.ex",
"chars": 3789,
"preview": "defmodule Realtime.Tenants.Janitor do\n @moduledoc \"\"\"\n Scheduled tasks for the Tenants.\n \"\"\"\n\n use GenServer\n use R"
},
{
"path": "lib/realtime/tenants/migrations.ex",
"chars": 11012,
"preview": "defmodule Realtime.Tenants.Migrations do\n @moduledoc \"\"\"\n Run Realtime database migrations for tenant's database.\n \"\""
},
{
"path": "lib/realtime/tenants/rebalancer.ex",
"chars": 1274,
"preview": "defmodule Realtime.Tenants.Rebalancer do\n @moduledoc \"\"\"\n Responsible to tell if the executing node is in the correct "
},
{
"path": "lib/realtime/tenants/replication_connection/watchdog.ex",
"chars": 1863,
"preview": "defmodule Realtime.Tenants.ReplicationConnection.Watchdog do\n @moduledoc \"\"\"\n Monitors ReplicationConnection health by"
},
{
"path": "lib/realtime/tenants/replication_connection.ex",
"chars": 16608,
"preview": "defmodule Realtime.Tenants.ReplicationConnection do\n @moduledoc \"\"\"\n ReplicationConnection it's the module that provid"
},
{
"path": "lib/realtime/tenants/repo/migrations/20211116024918_create_realtime_subscription_table.ex",
"chars": 1443,
"preview": "defmodule Realtime.Tenants.Migrations.CreateRealtimeSubscriptionTable do\n @moduledoc false\n\n use Ecto.Migration\n\n def"
},
{
"path": "lib/realtime/tenants/repo/migrations/20211116045059_create_realtime_check_filters_trigger.ex",
"chars": 2237,
"preview": "defmodule Realtime.Tenants.Migrations.CreateRealtimeCheckFiltersTrigger do\n @moduledoc false\n\n use Ecto.Migration\n\n d"
},
{
"path": "lib/realtime/tenants/repo/migrations/20211116050929_create_realtime_quote_wal2json_function.ex",
"chars": 1199,
"preview": "defmodule Realtime.Tenants.Migrations.CreateRealtimeQuoteWal2jsonFunction do\n @moduledoc false\n\n use Ecto.Migration\n\n "
},
{
"path": "lib/realtime/tenants/repo/migrations/20211116051442_create_realtime_check_equality_op_function.ex",
"chars": 974,
"preview": "defmodule Realtime.Tenants.Migrations.CreateRealtimeCheckEqualityOpFunction do\n @moduledoc false\n\n use Ecto.Migration\n"
},
{
"path": "lib/realtime/tenants/repo/migrations/20211116212300_create_realtime_build_prepared_statement_sql_function.ex",
"chars": 1952,
"preview": "defmodule Realtime.Tenants.Migrations.CreateRealtimeBuildPreparedStatementSqlFunction do\n @moduledoc false\n\n use Ecto."
},
{
"path": "lib/realtime/tenants/repo/migrations/20211116213355_create_realtime_cast_function.ex",
"chars": 441,
"preview": "defmodule Realtime.Tenants.Migrations.CreateRealtimeCastFunction do\n @moduledoc false\n\n use Ecto.Migration\n\n def chan"
},
{
"path": "lib/realtime/tenants/repo/migrations/20211116213934_create_realtime_is_visible_through_filters_function.ex",
"chars": 947,
"preview": "defmodule Realtime.Tenants.Migrations.CreateRealtimeIsVisibleThroughFiltersFunction do\n @moduledoc false\n\n use Ecto.Mi"
},
{
"path": "lib/realtime/tenants/repo/migrations/20211116214523_create_realtime_apply_rls_function.ex",
"chars": 7436,
"preview": "defmodule Realtime.Tenants.Migrations.CreateRealtimeApplyRlsFunction do\n @moduledoc false\n\n use Ecto.Migration\n\n def "
},
{
"path": "lib/realtime/tenants/repo/migrations/20211122062447_grant_realtime_usage_to_authenticated_role.ex",
"chars": 211,
"preview": "defmodule Realtime.Tenants.Migrations.GrantRealtimeUsageToAuthenticatedRole do\n @moduledoc false\n\n use Ecto.Migration\n"
},
{
"path": "lib/realtime/tenants/repo/migrations/20211124070109_enable_realtime_apply_rls_function_postgrest_9_compatibility.ex",
"chars": 6920,
"preview": "defmodule Realtime.Tenants.Migrations.EnableRealtimeApplyRlsFunctionPostgrest9Compatibility do\n @moduledoc false\n\n use"
},
{
"path": "lib/realtime/tenants/repo/migrations/20211202204204_update_realtime_subscription_check_filters_function_security.ex",
"chars": 2067,
"preview": "defmodule Realtime.Tenants.Migrations.UpdateRealtimeSubscriptionCheckFiltersFunctionSecurity do\n @moduledoc false\n\n us"
},
{
"path": "lib/realtime/tenants/repo/migrations/20211202204605_update_realtime_build_prepared_statement_sql_function_for_compatibility_with_all_types.ex",
"chars": 1107,
"preview": "defmodule Realtime.Tenants.Migrations.UpdateRealtimeBuildPreparedStatementSqlFunctionForCompatibilityWithAllTypes do\n @"
},
{
"path": "lib/realtime/tenants/repo/migrations/20211210212804_enable_generic_subscription_claims.ex",
"chars": 11815,
"preview": "defmodule Realtime.Tenants.Migrations.EnableGenericSubscriptionClaims do\n @moduledoc false\n\n use Ecto.Migration\n\n def"
},
{
"path": "lib/realtime/tenants/repo/migrations/20211228014915_add_wal_payload_on_errors_in_apply_rls_function.ex",
"chars": 8165,
"preview": "defmodule Realtime.Tenants.Migrations.AddWalPayloadOnErrorsInApplyRlsFunction do\n @moduledoc false\n\n use Ecto.Migratio"
},
{
"path": "lib/realtime/tenants/repo/migrations/20220107221237_update_change_timestamp_to_iso_8601_zulu_format.ex",
"chars": 8566,
"preview": "defmodule Realtime.Tenants.Migrations.UpdateChangeTimestampToIso8601ZuluFormat do\n @moduledoc false\n\n use Ecto.Migrati"
},
{
"path": "lib/realtime/tenants/repo/migrations/20220228202821_update_subscription_check_filters_function_dynamic_table_name.ex",
"chars": 2143,
"preview": "defmodule Realtime.Tenants.Migrations.UpdateSubscriptionCheckFiltersFunctionDynamicTableName do\n @moduledoc false\n\n us"
},
{
"path": "lib/realtime/tenants/repo/migrations/20220312004840_update_apply_rls_function_to_apply_iso_8601.ex",
"chars": 8562,
"preview": "defmodule Realtime.Tenants.Migrations.UpdateApplyRlsFunctionToApplyIso8601 do\n @moduledoc false\n\n use Ecto.Migration\n\n"
},
{
"path": "lib/realtime/tenants/repo/migrations/20220603231003_add_quoted_regtypes_support.ex",
"chars": 12602,
"preview": "defmodule Realtime.Tenants.Migrations.AddQuotedRegtypesSupport do\n @moduledoc false\n\n use Ecto.Migration\n\n def change"
},
{
"path": "lib/realtime/tenants/repo/migrations/20220603232444_add_output_for_data_less_than_equal_64_bytes_when_payload_too_large.ex",
"chars": 11860,
"preview": "defmodule Realtime.Tenants.Migrations.AddOutputForDataLessThanEqual64BytesWhenPayloadTooLarge do\n @moduledoc false\n\n u"
},
{
"path": "lib/realtime/tenants/repo/migrations/20220615214548_add_quoted_regtypes_backward_compatibility_support.ex",
"chars": 13389,
"preview": "defmodule Realtime.Tenants.Migrations.AddQuotedRegtypesBackwardCompatibilitySupport do\n @moduledoc false\n\n use Ecto.Mi"
},
{
"path": "lib/realtime/tenants/repo/migrations/20220712093339_recreate_realtime_build_prepared_statement_sql_function.ex",
"chars": 1239,
"preview": "defmodule Realtime.Tenants.Migrations.RecreateRealtimeBuildPreparedStatementSqlFunction do\n @moduledoc false\n\n use Ect"
},
{
"path": "lib/realtime/tenants/repo/migrations/20220908172859_null_passes_filters_recreate_is_visible_through_filters.ex",
"chars": 1466,
"preview": "defmodule Realtime.Tenants.Migrations.NullPassesFiltersRecreateIsVisibleThroughFilters do\n @moduledoc false\n\n use Ecto"
},
{
"path": "lib/realtime/tenants/repo/migrations/20220916233421_update_apply_rls_function_to_pass_through_delete_events_on_filter.ex",
"chars": 12306,
"preview": "defmodule Realtime.Tenants.Migrations.UpdateApplyRlsFunctionToPassThroughDeleteEventsOnFilter do\n @moduledoc false\n\n u"
},
{
"path": "lib/realtime/tenants/repo/migrations/20230119133233_millisecond_precision_for_walrus.ex",
"chars": 12259,
"preview": "defmodule Realtime.Tenants.Migrations.MillisecondPrecisionForWalrus do\n @moduledoc false\n\n use Ecto.Migration\n\n def c"
},
{
"path": "lib/realtime/tenants/repo/migrations/20230128025114_add_in_op_to_filters.ex",
"chars": 4178,
"preview": "defmodule Realtime.Tenants.Migrations.AddInOpToFilters do\n @moduledoc false\n\n use Ecto.Migration\n\n def change do\n "
},
{
"path": "lib/realtime/tenants/repo/migrations/20230128025212_enable_filtering_on_delete_record.ex",
"chars": 12256,
"preview": "defmodule Realtime.Tenants.Migrations.EnableFilteringOnDeleteRecord do\n @moduledoc false\n\n use Ecto.Migration\n\n def c"
},
{
"path": "lib/realtime/tenants/repo/migrations/20230227211149_update_subscription_check_filters_for_in_filter_non_text_types.ex",
"chars": 2831,
"preview": "defmodule Realtime.Tenants.Migrations.UpdateSubscriptionCheckFiltersForInFilterNonTextTypes do\n @moduledoc false\n\n use"
},
{
"path": "lib/realtime/tenants/repo/migrations/20230228184745_convert_commit_timestamp_to_utc.ex",
"chars": 11764,
"preview": "defmodule Realtime.Tenants.Migrations.ConvertCommitTimestampToUtc do\n @moduledoc false\n\n use Ecto.Migration\n\n def cha"
},
{
"path": "lib/realtime/tenants/repo/migrations/20230308225145_output_full_record_when_unchanged_toast.ex",
"chars": 12436,
"preview": "defmodule Realtime.Tenants.Migrations.OutputFullRecordWhenUnchangedToast do\n @moduledoc false\n\n use Ecto.Migration\n\n "
},
{
"path": "lib/realtime/tenants/repo/migrations/20230328144023_create_list_changes_function.ex",
"chars": 2096,
"preview": "defmodule Realtime.Tenants.Migrations.CreateListChangesFunction do\n @moduledoc false\n\n use Ecto.Migration\n\n def chang"
},
{
"path": "lib/realtime/tenants/repo/migrations/20231018144023_create_channels.ex",
"chars": 306,
"preview": "defmodule Realtime.Tenants.Migrations.CreateChannels do\n @moduledoc false\n\n use Ecto.Migration\n\n def change do\n cr"
},
{
"path": "lib/realtime/tenants/repo/migrations/20231204144023_set_required_grants.ex",
"chars": 609,
"preview": "defmodule Realtime.Tenants.Migrations.SetRequiredGrants do\n @moduledoc false\n\n use Ecto.Migration\n\n def change do\n "
},
{
"path": "lib/realtime/tenants/repo/migrations/20231204144024_create_rls_helper_functions.ex",
"chars": 338,
"preview": "defmodule Realtime.Tenants.Migrations.CreateRlsHelperFunctions do\n @moduledoc false\n\n use Ecto.Migration\n\n def change"
},
{
"path": "lib/realtime/tenants/repo/migrations/20231204144025_enable_channels_rls.ex",
"chars": 198,
"preview": "defmodule Realtime.Tenants.Migrations.EnableChannelsRls do\n @moduledoc false\n\n use Ecto.Migration\n\n def change do\n "
},
{
"path": "lib/realtime/tenants/repo/migrations/20240108234812_add_channels_column_for_write_check.ex",
"chars": 241,
"preview": "defmodule Realtime.Tenants.Migrations.AddChannelsColumnForWriteCheck do\n @moduledoc false\n\n use Ecto.Migration\n\n def "
},
{
"path": "lib/realtime/tenants/repo/migrations/20240109165339_add_update_grant_to_channels.ex",
"chars": 244,
"preview": "defmodule Realtime.Tenants.Migrations.AddUpdateGrantToChannels do\n @moduledoc false\n\n use Ecto.Migration\n\n def change"
},
{
"path": "lib/realtime/tenants/repo/migrations/20240227174441_add_broadcast_permissions_table.ex",
"chars": 655,
"preview": "defmodule Realtime.Tenants.Migrations.AddBroadcastsPoliciesTable do\n @moduledoc false\n\n use Ecto.Migration\n\n def chan"
},
{
"path": "lib/realtime/tenants/repo/migrations/20240311171622_add_insert_and_delete_grant_to_channels.ex",
"chars": 502,
"preview": "defmodule Realtime.Tenants.Migrations.AddInsertAndDeleteGrantToChannels do\n @moduledoc false\n\n use Ecto.Migration\n\n d"
},
{
"path": "lib/realtime/tenants/repo/migrations/20240321100241_add_presences_permissions_table.ex",
"chars": 888,
"preview": "defmodule Realtime.Tenants.Migrations.AddPresencesPoliciesTable do\n @moduledoc false\n\n use Ecto.Migration\n\n def chang"
},
{
"path": "lib/realtime/tenants/repo/migrations/20240401105812_create_realtime_admin_and_move_ownership.ex",
"chars": 1289,
"preview": "defmodule Realtime.Tenants.Migrations.CreateRealtimeAdminAndMoveOwnership do\n @moduledoc false\n\n use Ecto.Migration\n\n "
},
{
"path": "lib/realtime/tenants/repo/migrations/20240418121054_remove_check_columns.ex",
"chars": 307,
"preview": "defmodule Realtime.Tenants.Migrations.RemoveCheckColumns do\n @moduledoc false\n\n use Ecto.Migration\n\n def change do\n "
},
{
"path": "lib/realtime/tenants/repo/migrations/20240523004032_redefine_authorization_tables.ex",
"chars": 1388,
"preview": "defmodule Realtime.Tenants.Migrations.RedefineAuthorizationTables do\n @moduledoc false\n\n use Ecto.Migration\n\n def cha"
},
{
"path": "lib/realtime/tenants/repo/migrations/20240618124746_fix_walrus_role_handling.ex",
"chars": 11576,
"preview": "defmodule Realtime.Tenants.Migrations.FixWalrusRoleHandling do\n @moduledoc false\n use Ecto.Migration\n\n def change do\n"
},
{
"path": "lib/realtime/tenants/repo/migrations/20240801235015_unlogged_messages_table.ex",
"chars": 251,
"preview": "defmodule Realtime.Tenants.Migrations.UnloggedMessagesTable do\n @moduledoc false\n use Ecto.Migration\n\n def change do\n"
},
{
"path": "lib/realtime/tenants/repo/migrations/20240805133720_logged_messages_table.ex",
"chars": 247,
"preview": "defmodule Realtime.Tenants.Migrations.LoggedMessagesTable do\n @moduledoc false\n use Ecto.Migration\n\n def change do\n "
},
{
"path": "lib/realtime/tenants/repo/migrations/20240827160934_filter_delete_postgres_changes.ex",
"chars": 11741,
"preview": "defmodule Realtime.Tenants.Migrations.FilterDeletePostgresChanges do\n @moduledoc false\n use Ecto.Migration\n\n def chan"
},
{
"path": "lib/realtime/tenants/repo/migrations/20240919163303_add_payload_to_messages.ex",
"chars": 1985,
"preview": "defmodule Realtime.Tenants.Migrations.AddPayloadToMessages do\n @moduledoc false\n use Ecto.Migration\n\n def change do\n "
},
{
"path": "lib/realtime/tenants/repo/migrations/20240919163305_change_messages_id_type.ex",
"chars": 204,
"preview": "defmodule Realtime.Tenants.Migrations.ChangeMessagesIdType do\n @moduledoc false\n use Ecto.Migration\n\n def change do\n "
},
{
"path": "lib/realtime/tenants/repo/migrations/20241019105805_uuid_auto_generation.ex",
"chars": 244,
"preview": "defmodule Realtime.Tenants.Migrations.UuidAutoGeneration do\n @moduledoc false\n use Ecto.Migration\n\n def change do\n "
},
{
"path": "lib/realtime/tenants/repo/migrations/20241030150047_messages_partitioning.ex",
"chars": 4001,
"preview": "defmodule Realtime.Tenants.Migrations.MessagesPartitioning do\n @moduledoc false\n use Ecto.Migration\n\n def change do\n "
},
{
"path": "lib/realtime/tenants/repo/migrations/20241108114728_messages_using_uuid.ex",
"chars": 411,
"preview": "defmodule Realtime.Tenants.Migrations.MessagesUsingUuid do\n @moduledoc false\n use Ecto.Migration\n\n def change do\n "
},
{
"path": "lib/realtime/tenants/repo/migrations/20241121104152_fix_send_function_.ex",
"chars": 1119,
"preview": "defmodule Realtime.Tenants.Migrations.FixSendFunction do\n @moduledoc false\n use Ecto.Migration\n\n # We missed the sche"
},
{
"path": "lib/realtime/tenants/repo/migrations/20241130184212_recreate_entity_index_using_btree.ex",
"chars": 556,
"preview": "defmodule Realtime.Tenants.Migrations.RecreateEntityIndexUsingBtree do\n @moduledoc false\n use Ecto.Migration\n\n def ch"
},
{
"path": "lib/realtime/tenants/repo/migrations/20241220035512_fix_send_function_partition_creation.ex",
"chars": 1201,
"preview": "defmodule Realtime.Tenants.Migrations.FixSendFunctionPartitionCreation do\n @moduledoc false\n use Ecto.Migration\n\n # W"
},
{
"path": "lib/realtime/tenants/repo/migrations/20241220123912_realtime_send_handle_exceptions_remove_partition_creation.ex",
"chars": 1068,
"preview": "defmodule Realtime.Tenants.Migrations.RealtimeSendHandleExceptionsRemovePartitionCreation do\n @moduledoc false\n use Ec"
},
{
"path": "lib/realtime/tenants/repo/migrations/20241224161212_realtime_send_sets_config.ex",
"chars": 1122,
"preview": "defmodule Realtime.Tenants.Migrations.RealtimeSendSetsConfig do\n @moduledoc false\n use Ecto.Migration\n\n # We missed t"
},
{
"path": "lib/realtime/tenants/repo/migrations/20250107150512_realtime_subscription_unlogged.ex",
"chars": 263,
"preview": "defmodule Realtime.Tenants.Migrations.RealtimeSubscriptionUnlogged do\n @moduledoc false\n use Ecto.Migration\n\n def cha"
},
{
"path": "lib/realtime/tenants/repo/migrations/20250110162412_realtime_subscription_logged.ex",
"chars": 377,
"preview": "defmodule Realtime.Tenants.Migrations.RealtimeSubscriptionLogged do\n @moduledoc false\n use Ecto.Migration\n\n # PG Upda"
},
{
"path": "lib/realtime/tenants/repo/migrations/20250123174212_remove_unused_publications.ex",
"chars": 464,
"preview": "defmodule Realtime.Tenants.Migrations.RemoveUnusedPublications do\n @moduledoc false\n use Ecto.Migration\n\n def change "
}
]
// ... and 275 more files (download for full content)
About this extraction
This page contains the full source code of the supabase/realtime GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 475 files (2.0 MB), approximately 539.3k tokens, and a symbol index with 1913 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.