Files
Danny WhiteandJoshen Lim 05a45dd1ed feat(studio): rename Replication to Pipelines (#50637)
## What kind of change does this PR introduce?

Feature and docs update.

## What is the current behavior?

The Dashboard lists Pipelines destinations under Database > Replication.
Read replicas have moved to Infrastructure, but the temporary notices
remain on the destinations page and new destination sheet.

Closes PIPE-1021.

## What is the new behavior?

The canonical Dashboard routes are Database > Pipelines, while legacy
Replication list and detail URLs permanently redirect to the equivalent
Pipelines routes. Navigation, command palette, shortcuts, pipeline
links, docs, and current marketing copy use Pipelines. Read-replica
notices and their obsolete dismissal state are removed.

| Before | After |
| --- | --- |
| <img width="1024" height="759" alt="Replication Database Agua Basket
Supabase"
src="https://github.com/user-attachments/assets/53f9f565-1ed1-43e9-a7d9-b66b2a47e948"
/> | <img width="1024" height="759" alt="2540"
src="https://github.com/user-attachments/assets/14ab2d61-d01c-483f-9d4f-0ac286dae159"
/> |

The Management API, pipeline behaviour, replication logs, and Postgres
replication terminology remain unchanged.

## To test

- Open `/project/<ref>/database/pipelines` and confirm the Database
navigation, page header, and pipeline breadcrumb say Pipelines.
- Open
`/project/<ref>/database/replication?source=bookmark#destinations` and a
legacy pipeline detail URL. Confirm each redirects to the matching
Pipelines URL while preserving parameters and fragments.
- From the Pipelines page, open Add destination. Confirm no read-replica
migration notice appears.
- Open the Pipelines guide and confirm its Dashboard steps lead to
Database > Pipelines.

## Before merge

- [ ] Get changelog entry reviewed
https://github.com/supabase/changelog/pull/262 and prepare to merge
simultaneously

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

- **New Features**
- Added dedicated **Database > Pipelines** pages for pipeline lists and
details.
- Added permanent redirects from legacy Replication URLs to their
corresponding Pipelines pages.
- Read replica management links now open **Settings > Infrastructure**.

- **Documentation**
- Updated Pipelines setup, monitoring, troubleshooting, and usage
guidance to reference the current dashboard locations.
  - Updated Realtime guidance to use **Database > Publications**.

- **Updates**
- Renamed dashboard navigation, breadcrumbs, commands, and keyboard
shortcuts from **Replication** to **Pipelines**.
  - Removed the “Read replicas have moved” notification.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-09-23 08:52:07 +10:00

220 lines
9.2 KiB
TypeScript

import { useParams } from 'common'
import Link from 'next/link'
import { Button } from 'ui'
import { Admonition } from 'ui-patterns/Admonition'
import { InlineLink } from '@/components/ui/InlineLink'
import {
ProjectUpgradeEligibilityValidationError,
ProjectUpgradeEligibilityWarning,
} from '@/data/config/project-upgrade-eligibility-query'
import { DOCS_URL } from '@/lib/constants'
export const ReadReplicasWarning = ({ latestPgVersion }: { latestPgVersion: string }) => {
const { ref } = useParams()
return (
<Admonition
type="note"
showIcon={false}
title="A newer version of Postgres is available"
description={`You will need to remove all read replicas prior to upgrading your Postgres version to the latest available (${latestPgVersion}).`}
actions={
<Button asChild>
<Link href={`/project/${ref}/settings/infrastructure`}>Manage read replicas</Link>
</Button>
}
/>
)
}
const getValidationErrorTitle = (error: ProjectUpgradeEligibilityValidationError): string => {
switch (error.type) {
case 'objects_depending_on_pg_cron':
return (error.dependents ?? []).join(', ') || 'Objects depending on pg_cron'
case 'indexes_referencing_ll_to_earth':
return `${error.schema_name}.${error.index_name}`
case 'function_using_obsolete_lang':
return `${error.schema_name}.${error.function_name}`
case 'unsupported_extension':
return error.extension_name
case 'unsupported_fdw_handler':
return error.fdw_name
case 'unlogged_table_with_persistent_sequence':
return `${error.schema_name}.${error.table_name}`
case 'user_defined_objects_in_internal_schemas':
return `${error.schema_name}.${error.obj_name}`
case 'active_replication_slot':
return error.slot_name
case 'project_hibernating':
return 'Project is hibernating'
case 'x86_architecture':
return 'Project is running on x86 architecture'
}
}
const getValidationErrorDescription = (error: ProjectUpgradeEligibilityValidationError): string => {
switch (error.type) {
case 'objects_depending_on_pg_cron':
return 'Remove objects that depend on pg_cron'
case 'indexes_referencing_ll_to_earth':
return `Drop or recreate the index on table ${error.schema_name}.${error.table_name} that references ll_to_earth()`
case 'function_using_obsolete_lang':
return `Update the function to use a supported language instead of ${error.lang_name}`
case 'unsupported_extension':
return 'Remove the unsupported extension'
case 'unsupported_fdw_handler':
return `Update or remove the FDW using the obsolete handler ${error.fdw_handler_name}`
case 'unlogged_table_with_persistent_sequence':
return `Convert the sequence ${error.sequence_name} to unlogged or convert the table to logged`
case 'user_defined_objects_in_internal_schemas':
return `Move the ${error.obj_type} to your own schema`
case 'active_replication_slot':
return 'Drop the active replication slot'
case 'project_hibernating':
return 'The project is currently hibernating and will wake on next supported request'
case 'x86_architecture':
return 'The project is running on x86 architecture and cannot be upgraded'
}
}
const ValidationErrorItem = ({ error }: { error: ProjectUpgradeEligibilityValidationError }) => {
const { ref: projectRef } = useParams()
const title = getValidationErrorTitle(error)
const description = getValidationErrorDescription(error)
const getManageLink = (): string | null => {
const encode = encodeURIComponent
switch (error.type) {
case 'function_using_obsolete_lang':
return `/project/${projectRef}/database/functions?schema=${encode(error.schema_name)}&search=${encode(error.function_name)}`
case 'unsupported_extension':
return `/project/${projectRef}/database/extensions?filter=${encode(error.extension_name)}`
case 'indexes_referencing_ll_to_earth':
return `/project/${projectRef}/database/indexes?schema=${encode(error.schema_name)}&search=${encode(error.index_name)}`
case 'unlogged_table_with_persistent_sequence':
return `/project/${projectRef}/database/tables?schema=${encode(error.schema_name)}&search=${encode(error.table_name)}`
case 'user_defined_objects_in_internal_schemas':
if (error.obj_type === 'function') {
return `/project/${projectRef}/database/functions?schema=${encode(error.schema_name)}&search=${encode(error.obj_name)}`
}
if (error.obj_type === 'table') {
return `/project/${projectRef}/database/tables?schema=${encode(error.schema_name)}&search=${encode(error.obj_name)}`
}
return null
case 'active_replication_slot':
return null
case 'unsupported_fdw_handler':
return `/project/${projectRef}/integrations`
case 'objects_depending_on_pg_cron':
return null
default:
return null
}
}
const manageLink = getManageLink()
return (
<li className="py-3 last:pb-0 flex flex-row gap-x-3 justify-between items-center">
<div className="flex flex-col gap-y-0.5 flex-1 min-w-0">
<h6 className="overflow-hidden text-ellipsis whitespace-nowrap min-w-0 text-sm font-normal text-foreground">
{title}
</h6>
<p className="text-foreground-lighter text-xs">{description}</p>
</div>
{manageLink && (
<Button size="tiny" asChild>
<Link href={manageLink}>Manage</Link>
</Button>
)}
</li>
)
}
export const ValidationErrorsWarning = ({
validationErrors,
}: {
validationErrors: ProjectUpgradeEligibilityValidationError[]
}) => {
if (validationErrors.length === 0) return null
return (
<Admonition type="note" showIcon={false} title="A newer version of Postgres is available">
<div className="flex flex-col gap-3">
<p>
The following issues must be resolved before upgrading.{' '}
<InlineLink href={`${DOCS_URL}/guides/platform/upgrading`}>Learn more</InlineLink>
</p>
<ul className="border-t border-border-muted flex flex-col divide-y divide-border-muted">
{validationErrors.map((error, idx) => (
<ValidationErrorItem key={`${error.type}-${idx}`} error={error} />
))}
</ul>
</div>
</Admonition>
)
}
const getWarningTitle = (warning: ProjectUpgradeEligibilityWarning): string => {
switch (warning.type) {
case 'pg_graphql_introspection_change':
return 'GraphQL introspection will be disabled by default after upgrade'
case 'ltree_reindex_required':
return 'ltree indexes must be reindexed after this upgrade'
case 'operator_estimator_gate':
return 'Custom operators may need attention after this upgrade'
case 'btree_gist_nan_reindex':
return 'btree_gist indexes on float columns must be reindexed after this upgrade'
}
}
const getWarningDescription = (warning: ProjectUpgradeEligibilityWarning): string => {
switch (warning.type) {
case 'pg_graphql_introspection_change':
return 'After upgrading, queries to `__schema` and `__type` will return an error unless introspection is explicitly re-enabled on the schema. Regular data queries are not affected.'
case 'ltree_reindex_required':
return 'After upgrading, ltree indexes on this database can return incomplete results until they are rebuilt. Run `REINDEX INDEX CONCURRENTLY` on the affected indexes — this runs online with no downtime.'
case 'operator_estimator_gate':
return 'After upgrading, recreating an operator that references a non-built-in selectivity estimator (for example during a restore or branch) requires superuser and may fail. Most projects are not affected.'
case 'btree_gist_nan_reindex':
return 'After upgrading, btree_gist indexes on float columns can return wrong results for rows containing NaN until they are rebuilt. If those columns may contain NaN values, run `REINDEX INDEX CONCURRENTLY` on the affected indexes — this runs online with no downtime.'
}
}
const getWarningLink = (warning: ProjectUpgradeEligibilityWarning): string => {
switch (warning.type) {
case 'pg_graphql_introspection_change':
return `${DOCS_URL}/guides/platform/upgrading#upgrading-to-pg_graphql-160`
case 'ltree_reindex_required':
return `${DOCS_URL}/guides/platform/upgrading#ltree-indexes-require-reindexing-after-upgrade`
case 'operator_estimator_gate':
return `${DOCS_URL}/guides/platform/upgrading#custom-operator-selectivity-estimators`
case 'btree_gist_nan_reindex':
return `${DOCS_URL}/guides/platform/upgrading#btree_gist-indexes-on-float-columns-require-reindexing-after-upgrade`
}
}
export const ValidationWarningsAdmonition = ({
warnings,
}: {
warnings: ProjectUpgradeEligibilityWarning[]
}) => {
if (warnings.length === 0) return null
return warnings.map((warning, idx) => (
<Admonition
key={`${warning.type}-${idx}`}
type="default"
title={getWarningTitle(warning)}
description={getWarningDescription(warning)}
>
<Button asChild className="mt-2">
<Link href={getWarningLink(warning)} target="_blank" rel="noreferrer">
Read upgrade notes
</Link>
</Button>
</Admonition>
))
}