Files
supabase/apps/studio/data/realtime/realtime-config-mutation.ts
Filipe CabaçoandIvan Vasilov 29e47821f5 fix(realtime): add pg changes pool to realtime settings (#49256)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Feature — adds a new Realtime setting to configure the Postgres Changes
connection pool size.

## What is the current behavior?

The Realtime settings page only exposes the connection pool used for
Realtime Authorization (`connection_pool`). The pool that Realtime uses
for Postgres Changes is not surfaced anywhere in the dashboard, so
projects that need to tune it have no self-serve way to do so — the only
option is to contact support.

## What is the new behavior?

The Realtime settings page now includes a **Postgres Changes connection
pool size** field:

- Reads `postgres_changes_pool` from the project's Realtime config,
falling back to a default of `2` when no override is stored.
- Validates input from `1` through `20` (`MAX_POSTGRES_CHANGES_POOL`),
and submits the value as a number in the config `PATCH` payload.
- Docs (`apps/docs/content/guides/realtime/settings.mdx`) are expanded
with sizing guidance for both connection pools, plus limits,
resource-usage notes, and the operational error codes to look for.

<img width="1160" height="166" alt="Screenshot 2026-08-19 at 13 59 04"
src="https://github.com/user-attachments/assets/fd3ee29e-e9bf-438b-970f-8008ec57020f"
/>

## Additional context

The named `RealtimeConfigResponse` / `UpdateRealtimeConfigBody` schemas
in the generated `api-types` package do not carry
`postgres_changes_pool` yet, so both the query and mutation types extend
the generated schema locally — the same pattern already used elsewhere
in `apps/studio/data/`. Once the platform OpenAPI spec ships the field
and `api-types` is regenerated, those two local intersections can be
dropped.

Covered by component tests in `RealtimeSettings.test.tsx` for both the
fetch and save paths.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added a Realtime setting to configure the Postgres Changes connection
pool size.
  * Connection pools support 1–20 connections, with a default of 2.
  * Saving the setting now applies the configured value correctly.

* **Documentation**
* Expanded Realtime Settings guidance with configuration limits,
resource usage, channel access, payload and presence limits, plan
ceilings, spend-cap restrictions, and operational error codes.
* Added guidance for sizing authorization and Postgres Changes
connection pools.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
2026-08-21 09:08:28 +01:00

91 lines
2.5 KiB
TypeScript

import { useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
import { realtimeKeys } from './keys'
import type { components } from '@/data/api'
import { handleError, patch } from '@/data/fetchers'
import type { ResponseError, UseCustomMutationOptions } from '@/types'
export type RealtimeConfigurationUpdateBody = components['schemas']['UpdateRealtimeConfigBody'] & {
postgres_changes_pool?: number
}
export type RealtimeConfigurationUpdateVariables = {
ref: string
} & RealtimeConfigurationUpdateBody
export async function updateRealtimeConfiguration({
ref,
private_only,
connection_pool,
postgres_changes_pool,
max_concurrent_users,
max_events_per_second,
max_bytes_per_second,
max_channels_per_client,
max_joins_per_second,
max_presence_events_per_second,
max_payload_size_in_kb,
suspend,
}: RealtimeConfigurationUpdateVariables) {
if (!ref) return console.error('Project ref is required')
const { data, error } = await patch('/platform/projects/{ref}/config/realtime', {
params: { path: { ref } },
body: {
private_only,
connection_pool,
postgres_changes_pool,
max_concurrent_users,
max_events_per_second,
max_bytes_per_second,
max_channels_per_client,
max_joins_per_second,
max_presence_events_per_second,
max_payload_size_in_kb,
suspend,
},
})
if (error) handleError(error)
return data
}
type RealtimeConfigurationUpdateData = Awaited<ReturnType<typeof updateRealtimeConfiguration>>
export const useRealtimeConfigurationUpdateMutation = ({
onSuccess,
onError,
...options
}: Omit<
UseCustomMutationOptions<
RealtimeConfigurationUpdateData,
ResponseError,
RealtimeConfigurationUpdateVariables
>,
'mutationFn'
> = {}) => {
const queryClient = useQueryClient()
return useMutation<
RealtimeConfigurationUpdateData,
ResponseError,
RealtimeConfigurationUpdateVariables
>({
mutationFn: (vars) => updateRealtimeConfiguration(vars),
async onSuccess(data, variables, context) {
const { ref } = variables
await queryClient.invalidateQueries({ queryKey: realtimeKeys.configuration(ref) })
await onSuccess?.(data, variables, context)
},
async onError(data, variables, context) {
if (onError === undefined) {
toast.error(`Failed to update realtime configuration: ${data.message}`)
} else {
onError(data, variables, context)
}
},
...options,
})
}