mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
Closes [DOCS-1289](https://linear.app/supabase/issue/DOCS-1289/get-the-linter-to-fix-what-it-flags-or-retirereplace-the-linter) Stacked on #50600, which points contributors at the authoring skills. Merge that one first. ## Problem Contributors experienced friction with the linter. They felt nickle and dimed for tiny nits and felt detracted from the work itself. PRs would become noisy with tiny one-word suggestions. Additionally, our homegrown linter is not very intelligent, causing frequent overrides. ## Solution This removes the linter entirely in favor of directing contributors to use SKILLS instead. The removal entails... - **CI.** Delete the three `docs_lint` workflows: the PR check, the external-PR comment companion, and the nightly `--fix` bot. Drop the stale `zizmor.yml` ignore entry for the deleted workflow. - **Tooling.** Delete `supa-mdx-lint.config.toml` and the 14 rule files. Drop the `lint:mdx` script and the `@supabase/supa-mdx-lint` dependency from docs, learn, and ui-library, and regenerate the lockfile. - **Content.** Remove the 181 directives. A separate commit carries Prettier's reformatting of the tables and blank lines those comments had suppressed, so the deletion commit stays readable. No prose changes. - **Style guide.** The word list states each rule directly instead of describing what the linter flagged. Every term survives, including the phrase groups that mirrored `Rule004ExcludeWords`. - **Skills.** `write-the-docs`, `edit-the-docs`, and `review-the-docs` drop `pnpm lint:mdx` from their self-review commands and check the word list directly. `ask-the-docs`'s CI reference drops both workflows. ## Manual testing 1. Run `git grep -i supa-mdx-lint -- . ':!pnpm-lock.yaml'`. No matches. 2. Run `pnpm install --frozen-lockfile --lockfile-only`. It passes, so the lockfile matches the three trimmed manifests. 3. Run `git diff master...HEAD --name-only --diff-filter=ACMR | grep -E '\.(md|mdx)$' | xargs npx prettier --config prettier.config.mjs --check`. All changed markdown passes. 4. Open the [reformatted filter table](https://docs-git-docs-retire-mdx-linter-supabase.vercel.app/docs/guides/observability/logs#filter-events) on the preview and compare it with [production](https://supabase.com/docs/guides/observability/logs#filter-events). The table renders the same. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Documentation guidance now uses manual prose and terminology review with the shared word list. * Clarified storage configuration and common Realtime channel mistakes. * Improved table formatting, text wrapping, and selected reference links. * Updated documentation authoring and review guidance. * **Chores** * Retired automated MDX linting from workflows and local validation commands. * Removed lint-suppression markers throughout documentation without changing instructions. * Added targeted documentation review guidance for pull requests. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
76 lines
4.4 KiB
Plaintext
76 lines
4.4 KiB
Plaintext
---
|
|
id: 'architecture'
|
|
title: 'Realtime Architecture'
|
|
description: 'Architecture of the Supabase Realtime service'
|
|
sidebar_label: 'Architecture'
|
|
---
|
|
|
|
Realtime is a globally distributed Elixir cluster. Clients can connect to any node in the cluster via WebSockets and send messages to any other client connected to the cluster.
|
|
|
|
Realtime is written in [Elixir](https://elixir-lang.org/), which compiles to [Erlang](https://www.erlang.org/), and uses many tools the [Phoenix Framework](https://www.phoenixframework.org/) provides out of the box.
|
|
|
|
<Image
|
|
alt="Architecture"
|
|
src={{
|
|
light: '/docs/img/guides/platform/realtime/architecture--light.png',
|
|
dark: '/docs/img/guides/platform/realtime/architecture--dark.png',
|
|
}}
|
|
|
|
width={1990}
|
|
height={2226}
|
|
/>
|
|
|
|
## Elixir & Phoenix
|
|
|
|
Phoenix is fast and able to handle millions of concurrent connections.
|
|
|
|
Phoenix can handle many concurrent connections because Elixir provides lightweight processes (not OS processes) to work with.
|
|
|
|
Client-facing WebSocket servers need to handle many concurrent connections. Elixir & Phoenix let the Supabase Realtime cluster do this easily.
|
|
|
|
## Channels
|
|
|
|
Channels are implemented using [Phoenix Channels](https://hexdocs.pm/phoenix/channels.html) which uses [Phoenix.PubSub](https://hexdocs.pm/phoenix_pubsub/Phoenix.PubSub.html) with the default `Phoenix.PubSub.PG2` adapter.
|
|
|
|
The PG2 adapter uses Erlang [process groups](https://www.erlang.org/docs/18/man/pg2.html) to implement the PubSub model where a publisher can send messages to many subscribers.
|
|
|
|
## Global cluster
|
|
|
|
Presence is an in-memory key-value store backed by a CRDT. When a user is connected to the cluster the state of that user is sent to all connected Realtime nodes.
|
|
|
|
Broadcast lets you send a message from any connected client to a Channel. Any other client connected to that same Channel will receive that message.
|
|
|
|
This works globally. A client connected to a Realtime node in the United States can send a message to another client connected to a node in Singapore. Connect two clients to the same Realtime Channel and they'll all receive the same messages.
|
|
|
|
Broadcast is useful for getting messages to users in the same location rapidly. If a group of clients are connected to a node in Singapore, the message only needs to go to that Realtime node in Singapore and back down. If users are close to a Realtime node they'll get Broadcast messages in the time it takes to ping the cluster.
|
|
|
|
Thanks to the Realtime cluster, you (an amazing Supabase user) don't have to think about which regions your clients are connected to.
|
|
|
|
If you're using Broadcast, Presence, or streaming database changes, messages will always get to your users via the shortest path possible.
|
|
|
|
## Connecting to a database
|
|
|
|
Realtime allows you to listen to changes from your Postgres database. When a new client connects to Realtime and initializes the `postgres_changes` Realtime Extension the cluster will connect to your Postgres database and start streaming changes from a replication slot.
|
|
|
|
Realtime knows the region your database is in, and connects to it from the closest region possible.
|
|
|
|
Every Realtime region has at least two nodes so if one node goes offline the other node should reconnect and start streaming changes again.
|
|
|
|
## Broadcast from Postgres
|
|
|
|
Realtime Broadcast sends messages when changes happen in your database. Behind the scenes, Realtime creates a publication on the `realtime.messages` table. It then reads the Write-Ahead Log (WAL) file for this table, and sends a message whenever an insert happens. Messages are sent as JSON packages over WebSockets.
|
|
|
|
The `realtime.messages` table is partitioned by day. This allows old messages to be deleted performantly, by dropping old partitions. Partitions are retained for 3 days before being deleted.
|
|
|
|
Broadcast uses [Realtime Authorization](/docs/guides/realtime/authorization) by default to protect your data.
|
|
|
|
## Streaming the Write-Ahead Log
|
|
|
|
A Postgres logical replication slot is acquired when connecting to your database.
|
|
|
|
Realtime delivers changes by polling the replication slot and appending channel subscription IDs to each wal record.
|
|
|
|
Subscription IDs are Erlang processes representing underlying sockets on the cluster. These IDs are globally unique and messages to processes are routed automatically by the Erlang virtual machine.
|
|
|
|
After receiving results from the polling query, with subscription IDs appended, Realtime delivers records to those clients.
|