diff --git a/apps/docs/components/Extensions.tsx b/apps/docs/components/Extensions.tsx index a3ab9bff99e..b7b7260cd64 100644 --- a/apps/docs/components/Extensions.tsx +++ b/apps/docs/components/Extensions.tsx @@ -94,8 +94,15 @@ export default function Extensions() { filters.length === 0 ? x : x.tags.some((item) => filters.includes(item)) ) .map((extension) => ( - - + +

{extension.comment.charAt(0).toUpperCase() + extension.comment.slice(1)} diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index 88c2d614951..2dcd089bbc1 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -637,7 +637,7 @@ export const platform = { url: undefined, items: [ { name: 'Access Control', url: '/guides/platform/access-control', items: [] }, - { name: 'Database Usage', url: '/guides/platform/database-usage', items: [] }, + { name: 'Database Size', url: '/guides/platform/database-size', items: [] }, { name: 'HTTP Status Codes', url: '/guides/platform/http-status-codes', items: [] }, { name: 'Logging', url: '/guides/platform/logs', items: [] }, { name: 'Metrics', url: '/guides/platform/metrics', items: [] }, diff --git a/apps/docs/pages/guides/auth/server-side-rendering.mdx b/apps/docs/pages/guides/auth/server-side-rendering.mdx index 635407cd986..346984a63fb 100644 --- a/apps/docs/pages/guides/auth/server-side-rendering.mdx +++ b/apps/docs/pages/guides/auth/server-side-rendering.mdx @@ -160,6 +160,12 @@ use this potentially stale information to render a page. ## Frequently Asked Questions +### No session on the server side with Next.js route prefetching? + +When you use route prefetching in Next.js using `` components or the `Router.push()` APIs can send server-side requests before the browser processes the access and refresh tokens. This means that those requests may not have any cookies set and your server code will render unauthenticated content. + +To improve experience for your users, we recommend redirecting users to one specific page after sign-in that does not include any route prefetching from Next.js. Once the Supabase client library running in the browser has obtained the access and refresh tokens from the URL fragment, you can send users to any pages that use prefetching. + ### How do I make the cookies `HttpOnly`? This is not necessary. Both the access token and refresh token are designed to diff --git a/apps/docs/pages/guides/getting-started/quickstarts/flutter.mdx b/apps/docs/pages/guides/getting-started/quickstarts/flutter.mdx index 339eb20dd2f..476c5813490 100644 --- a/apps/docs/pages/guides/getting-started/quickstarts/flutter.mdx +++ b/apps/docs/pages/guides/getting-started/quickstarts/flutter.mdx @@ -176,6 +176,7 @@ export const meta = { Run your app on a platform of your choosing! By default an app should launch in your web browser. Note that `supabase_flutter` is compatible with web, iOS, Android, macOS, and Windows apps. + Running the app on MacOS requires additional configuration to [set the entitlements](https://docs.flutter.dev/development/platform-integration/macos/building#setting-up-entitlements). diff --git a/apps/docs/pages/guides/integrations/illa.mdx b/apps/docs/pages/guides/integrations/illa.mdx index e30c07e88f3..3dbe3b53616 100644 --- a/apps/docs/pages/guides/integrations/illa.mdx +++ b/apps/docs/pages/guides/integrations/illa.mdx @@ -25,9 +25,9 @@ Supabase offers a variety of options for populating tables with data, including Fill out the info in the table. The database is now set up. -### Step 2: Build UI on ILLA Builder +### Step 2: Build UI on ILLA Cloud -On [ILLA Builder](https://fast-try.illacloud.com/), click Create New to create a new application. +On [ILLA Cloud](https://cloud.illacloud.com/), click Create New to create a new application. ![Create new project on ILLA Builder](/docs/img/guides/integrations/illa/supabase-illa-create-project.png) diff --git a/apps/docs/pages/guides/integrations/prisma.mdx b/apps/docs/pages/guides/integrations/prisma.mdx index c2f0d5a8f14..2951c44b9d6 100644 --- a/apps/docs/pages/guides/integrations/prisma.mdx +++ b/apps/docs/pages/guides/integrations/prisma.mdx @@ -29,22 +29,17 @@ In case you don’t have a Prisma project or this is your first time working wit ### Cloning the starter project -Navigate into a directory of your choice and run the following command in your terminal if you’re on a Windows machine: +Navigate into a directory of your choice and run the following command in your terminal: ```bash -curl https://pris.ly/quickstart -L -o quickstart-main.tar.gz && tar -zxvf quickstart-main.tar.gz quickstart-main/typescript/starter && move quickstart-main\typescript\starter starter && rmdir /S /Q quickstart-main && del /Q quickstart-main.tar.gz -``` - -And if you’re using Mac OS or Linux, run the following command: - -```bash -curl -L https://pris.ly/quickstart | tar -xz --strip=2 quickstart-main/typescript/starter +curl https://codeload.github.com/prisma/prisma-examples/tar.gz/latest | tar -xz --strip=2 prisma-examples-latest/databases/postgresql-supabase ``` You can now navigate into the directory and install the project’s dependencies: ```bash -cd starter && npm install +cd postgresql-supabase +npm install ``` ### A look at the project’s structure @@ -52,16 +47,17 @@ cd starter && npm install This project comes with TypeScript configured and has the following structure. - A `prisma` directory which contains: - - A `dev.db` file: This is a SQLite database. - - A `schema.prisma` file: Where we define the different database models and relations between them. -- A `.env` file: Contains the `DATABASE_URL` variable, which Prisma will use. -- A `script.ts` file: where we will run some queries using Prisma Client. - This starter also comes with the following packages installed: + - A `seed.ts` file: This is the data used to seed your database. + - A `schema.prisma` file: Where you define the different database models and relations between them. +- A `script.ts` file: where you will run some queries using Prisma Client. + +This starter also comes with the following packages installed: - [`@prisma/client`](https://www.npmjs.com/package/@prisma/client): An auto-generated and type-safe query builder that’s _tailored_ to your data. - [`prisma`](https://www.npmjs.com/package/prisma): Prisma’s command-line interface (CLI). It allows you to initialize new project assets, generate Prisma Client, and analyze existing database structures through introspection to automatically create your application models. - > Note: Prisma works with both JavaScript and TypeScript. However, to get the best possible development experience, using TypeScript is highly recommended. -### Configuring the project to use PostgreSQL +> Note: Prisma works with both JavaScript and TypeScript. However, to get the best possible development experience, using TypeScript is highly recommended. + +### Configuring the project By default, Prisma migrations will try to drop the `postgres` database, which can lead to conflicts with Supabase databases. For this scenario, use [Prisma Shadow Databases](https://www.prisma.io/docs/concepts/components/prisma-migrate/shadow-database#cloud-hosted-shadow-databases-must-be-created-manually). @@ -78,7 +74,6 @@ postgres=> CREATE DATABASE postgres_shadow; postgres=> exit ``` -Go ahead and delete the `prisma/dev.db` file because we will be switching to PostgreSQL. In the `.env` file, update `DATABASE_URL` and `SHADOW_DATABASE_URL` to the connection string from **step 1**. The `.env` file should look like: ```env @@ -87,18 +82,19 @@ DATABASE_URL="postgres://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT-REF].supabase SHADOW_DATABASE_URL="postgres://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT-REF].supabase.co:5432/postgres_shadow" ``` -In the `schema.prisma` file, change the `provider` from "sqlite" to `"postgresql"` and add the `shadowDatabaseUrl` property. This is what your `schema.prisma` file should look like: ```go datasource db { - provider = "postgresql" - url = env("DATABASE_URL") + provider = "postgresql" + url = env("DATABASE_URL") shadowDatabaseUrl = env("SHADOW_DATABASE_URL") } + generator client { provider = "prisma-client-js" } + model Post { id Int @id @default(autoincrement()) title String @@ -107,6 +103,7 @@ model Post { author User? @relation(fields: [authorId], references: [id]) authorId Int? } + model User { id Int @id @default(autoincrement()) email String @unique @@ -115,53 +112,83 @@ model User { } ``` + To test that everything works correctly, run the following command to create a migration: ```bash -prisma migrate dev --name init +npx prisma migrate dev --name init ``` -You can optionally give your migration a name, depending on the changes you made. Since this is the project’s first migration, you’re setting the `--name` flag to “init”. -If everything works correctly, you should get the following message in your terminal: +You can optionally give your migration a name, depending on the changes you made. Since this is the project’s first migration, you’re setting the `--name` flag to “init”. If everything works correctly, you should get the following message in your terminal: ```text Your database is now in sync with your schema. -:heavy_check_mark: Generated Prisma Client (2.x.x) to ./node_modules/@prisma/client in 111ms +:heavy_check_mark: Generated Prisma Client (4.x.x) to ./node_modules/@prisma/client in 111ms ``` This will create a `prisma/migrations` folder inside your `prisma` directory and synchronize your Prisma schema with your database schema. -> Note: if you want to skip the process of creating a migration history, you can use the [`db push`](https://www.prisma.io/docs/concepts/components/prisma-migrate/db-push) command instead of `migrate dev`. -> If you go to your Supabase project, in the table editor, you should see that two tables have been created, a `Post` and a `User` table. -> ![tables created in the UI](/docs/img/guides/integrations/prisma/7y4qq4wwvfrheti6r09u.png) -> That’s it! You have now successfully connected a Prisma project to a PostgreSQL database hosted on Supabase and ran your first migration. +> **Note**: If you want to skip the process of creating a migration history, you can use the [`prisma db push`](https://www.prisma.io/docs/concepts/components/prisma-migrate/db-push) command instead of `prisma migrate dev`. However, we recommend using `prisma migrate dev` to evolve your database schema in development. +> If you would like to get a conceptual overview of how Prisma Migrate works and which commands to use in what environment, refer to [this page in the Prisma documentation](https://www.prisma.io/docs/concepts/components/prisma-migrate/mental-model). + +If you go to your Supabase project, in the table editor, you should see that two tables have been created, a `Post`, `User`, and `_prisma_migrations` tables. The `_prisma_migrations` table is used to keep + +![tables created in the UI](/docs/img/guides/integrations/prisma/7y4qq4wwvfrheti6r09u.png) + +That’s it! You have now successfully connected a Prisma project to a PostgreSQL database hosted on Supabase and ran your first migration. ## Connection pooling with Supabase -If you’re working in a serverless environment (for example Node.js functions hosted on AWS Lambda, Vercel or Netlify Functions), you need to set up [connection pooling](https://www.prisma.io/docs/guides/performance-and-optimization/connection-management#serverless-environments-faas) using a tool like [PgBouncer](https://www.pgbouncer.org/). That’s because every function invocation may result in a [new connection to the database](https://www.prisma.io/docs/guides/performance-and-optimization/connection-management#the-serverless-challenge). Supabase [supports connection management using PgBouncer](https://supabase.io/blog/2021/04/02/supabase-pgbouncer#what-is-connection-pooling) and are enabled by default. -Go to the **Database** page from the sidebar in the Supabase dashboard and navigate to **connection pool** settings +If you’re working in a serverless environment (for example Node.js functions hosted on AWS Lambda, Vercel or Netlify Functions), you need to set up [connection pooling](https://www.prisma.io/docs/guides/performance-and-optimization/connection-management#serverless-environments-faas) using a tool like [PgBouncer](https://www.pgbouncer.org/). That’s because every function invocation may result in a [new connection to the database](https://www.prisma.io/docs/guides/performance-and-optimization/connection-management#the-serverless-challenge). + +Supabase [supports connection management using PgBouncer](/docs/guides/database/connecting-to-postgres#connection-pool). + +Go to the **Database** page from the sidebar in the Supabase dashboard and navigate to **Connection pool** settings: + ![Connection pool settings](/docs/img/guides/integrations/prisma/w0oowg8vq435ob5c3gf0.png) -When migrating, you need to use the non-pooled connection URL (like the one used in **step 1**). However, when deploying your app, use the pooled connection URL and add the `?pgbouncer=true` flag to the PostgreSQL connection URL. It's also recommended to minimize the number of concurrent connections by setting the `connection_limit` to `1`. The `.env` file should look like: + +When updating your database schema, you need to use the non-pooled connection URL (like the one used in **step 1**). You can configure the non-pooled connection string by using the `directUrl` property in the datasource block. + +Update your `.env` file with the following changes: +1. Rename the `DATABASE_URL` environment variable to `DIRECT_URL` +1. Create a `DATABASE_URL` environment variable and paste in the new connection string from the dashboard as its value + +It is recommended to minimize the number of concurrent connections by setting the `connection_limit` to `1`. You can set connection limit by appending `?connection_limit=1` to your connection string + +Your `.env` file should resemble the following: ```env # .env -DATABASE_URL="postgres://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT-REF].supabase.co:6543/postgres?pgbouncer=true&connection_limit=1" +# PostgreSQL connection string used for migrations +DIRECT_URL="postgres://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT-REF].supabase.co:6543/postgres" +# PostgreSQL connection string with pgBouncer config — used by Prisma Client +DATABASE_URL="postgres://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT-REF].supabase.co:6543/postgres?connection_limit=1" + SHADOW_DATABASE_URL="postgres://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT-REF].supabase.co:5432/postgres_shadow" ``` -Prisma Migrate uses database transactions to check out the current state of the database and the migrations table. However, the Migration Engine is designed to use a single connection to the database, and does not support connection pooling with PgBouncer. If you attempt to run Prisma Migrate commands in any environment that uses PgBouncer for connection pooling, you might see the following error: +Update your Prisma schema by setting the `directUrl` in the datasource block: -```bash -Error: undefined: Database error -Error querying the database: db error: ERROR: prepared statement “s0” already exists +```go +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + directURL = env("DIRECT_URL") + shadowDatabaseUrl = env("SHADOW_DATABASE_URL") +} ``` -This is a known issue and it is being worked on, you can follow the progress on this [GitHub issue](https://github.com/prisma/prisma/issues/6485). -If you want to learn more about Prisma, check out the [docs](https://www.prisma.io/docs). Also in case you have any questions or run into any issue, feel free to start a discussion in the repo’s [discussions section](https://github.com/prisma/prisma/discussions). +> **Note**: This feature is available from Prisma version [4.10.0](https://github.com/prisma/prisma/releases/tag/4.10.0) and higher. + +If you want to learn more about Prisma, check out the [docs](https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#fields). Also in case you have any questions or run into any issue, feel free to start a discussion in the repo’s [discussions section](https://github.com/prisma/prisma/discussions). ## Troubleshooting -If you run `prisma migrate dev --name init` multiple times, it sometimes asks if you want to recreate the whole schema. If you chose yes, it will delete the public schema and recreate it. The default grants are missing after this. If you run into this problem, add a helper SQL for fixing the grants: +### 1. Missing grants + +If you run `prisma migrate dev --name init` multiple times, it sometimes asks if you want to recreate the whole schema. If you chose yes, it will delete the public schema and recreate it. The default grants are missing after this. + + If you run into this problem, create a draft migration using `prisma migrate dev --create-only`, and add the following helper SQL: ```sql grant usage on schema public to postgres, anon, authenticated, service_role; @@ -175,6 +202,93 @@ alter default privileges in schema public grant all on functions to postgres, an alter default privileges in schema public grant all on sequences to postgres, anon, authenticated, service_role; ``` +Run `prisma migrate dev` to apply the draft migration to the database. + +### 2. Using Prisma with multiple PostgreSQL schemas + +If you're using multiple database schemas, enable the `multiSchema` Preview feature flag in the `generator` block of your Prisma schema: + +```go +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + directURL = env("DIRECT_URL") + shadowDatabaseUrl = env("SHADOW_DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" + previewFeatures = ["multiSchema"] +} +``` + +Next, specify the database schemas you would like to include in your Prisma schema: + +```go +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + directURL = env("DIRECT_URL") + shadowDatabaseUrl = env("SHADOW_DATABASE_URL") + schemas = ["public", "auth"] +} + +generator client { + provider = "prisma-client-js" + previewFeatures = ["multiSchema"] +} +``` + +You can then specify what schema a model or enum belongs to using the `@@schema` attribute: + +```go +model User { + id Int @id + // ... + + @@schema("auth") // or @@schema("public") +} +``` + +To learn more about using Prisma with multiple database schemas, refer to [this page in the Prisma docs](https://www.prisma.io/docs/guides/database/multi-schema#learn-more-about-the-multischema-preview-feature). + +### 3. Enabling PosgreSQL extensions + +If you would like to use a PostgreSQL extension with Prisma, enable the `postgresqlExtensions` Preview feature flag in the `generator` block of your Prisma schema: + +```go +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + directURL = env("DIRECT_URL") + shadowDatabaseUrl = env("SHADOW_DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" + previewFeatures = ["postgresqlExtensions"] +} +``` + +Next, specify the extensions you need in the `datasource` block: + +```go +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + directURL = env("DIRECT_URL") + shadowDatabaseUrl = env("SHADOW_DATABASE_URL") + extensions = [hstore(schema: "myHstoreSchema"), pg_trgm, postgis(version: "2.1")] +} + +generator client { + provider = "prisma-client-js" + previewFeatures = ["postgresqlExtensions"] +} +``` + +To learn more about using Prisma with PostgreSQL extensions, refer to [this page in the Prisma docs](https://www.prisma.io/docs/concepts/components/prisma-schema/postgresql-extensions). + ## Resources - [Prisma](https://prisma.io) official website. diff --git a/apps/docs/pages/guides/platform/database-size.mdx b/apps/docs/pages/guides/platform/database-size.mdx new file mode 100644 index 00000000000..b827b926832 --- /dev/null +++ b/apps/docs/pages/guides/platform/database-size.mdx @@ -0,0 +1,101 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'database-size', + title: 'Database size', + description: 'Understanding how database size applies to your subscription.', +} + +Database size refers to the _monthly average storage usage_, as reported by Postgres. This metric is reported in your project's [billing usage](https://app.supabase.com/project/_/settings/billing/usage) and is updated daily. As you read this document we will refer to "database size" and "disk size": + +- "Database size" is the total size of used storage from your database. +- "Disk size" describes the size of the underlying available storage. + +## Database space management + +### Database size + +This SQL query will show the current size of your Postgres database: + +```sql +select + sum(pg_database_size (pg_database.datname)) / (1024 * 1024) as db_size_mb +from + pg_database; +``` + +This value is reported in the [database settings page](https://app.supabase.com/project/_/settings/database). + +Database Space is consumed primarily by your data, indexes, and materialized views. You can reduce your disk size by removing any of these and running a Vacuum operation. + + + +Depending on your billing tier, your database can go into read-only mode which can prevent you inserting and deleting data. There are instructions for managing read-only mode in the [Disk Management](#disk-management) section. + + + +### Vacuum operations + +Postgres does not immediately reclaim the physical space used by dead tuples (i.e., deleted rows) in the DB. They are marked as "removed" until a [vacuum operation](https://www.postgresql.org/docs/current/routine-vacuuming.html) is executed. As a result, deleting data from your database may not immediately reduce the reported disk usage. + + + +Vacuum operations can temporarily increase resource utilization, which may adversely impact the observed performance of your project until the maintenance is completed. + + + +Supabase projects have automatic vacuuming enabled, which ensures that these operations are performed regularly to keep the database healthy and performant. +It is possible to [fine-tune](https://www.percona.com/blog/2018/08/10/tuning-autovacuum-in-postgresql-and-autovacuum-internals/) +the [autovacuum parameters](https://www.enterprisedb.com/blog/postgresql-vacuum-and-analyze-best-practice-tips), +or [manually initiate](https://www.postgresql.org/docs/current/sql-vacuum.html) vacuum operations. +Running a manual vacuum after deleting large amounts of data from your DB could help reduce the database size reported by Postgres. + +### Preoccupied Space + +New Supabase projects have a database size of ~40-60mb. This space includes pre-installed extensions, schemas, and default Postgres data. Additional database size is used when installing extensions, even if those extensions are inactive. + +## Disk management + +Supabase uses network-attached storage to balance performance with scalability. The behavior of your disk depends on your billing tier. + +### Paid Tier Behavior + +Pro and Enterprise projects have auto-scaling Disk Storage. + +Disk storage expands automatically when the database reaches 90% of the disk size. The disk is expanded to be 50% larger (e.g., 8GB -> 12GB). Auto-scaling can only take place once every 6 hours. If within those 6 hours you reach 95% of the disk space, your project will enter read-only mode. + + + +If you intend to import a lot of data into your database which requires multiple disk expansions then [reach out to our team](https://app.supabase.com/support/new). For example, uploading more than 1.5x the current size of your database storage will put your database into [read-only mode](#read-only-mode). + + + +The maximum Disk Size for Pro Tier is 1024TB. If you need more than this, [contact us](https://app.supabase.com/support/new) to learn more about the Enterprise plan. + +### Free Tier Behavior + +Free Tier projects enter [read-only](#read-only-mode) mode when you exceed the 500mb limit. Once in read-only mode, you have several options: + +- [Upgrade to the Pro or Enterprise tier](https://app.supabase.com/project/_/settings/billing/subscription) to enable auto-scaling and expand beyond the 500mb database size limit. +- [Disable read-only mode](#disabling-read-only-mode) and reduce your database size. + +### Read-only mode + +In some cases Supabase may put your database into read-only mode to prevent your database from exceeding the billing or disk limitations. + +In read-only mode, clients will encounter errors such as `cannot execute INSERT in a read-only transaction`. Regular operation (read-write mode) is automatically re-enabled once usage is below 95% of the disk size, + +### Disabling read-only mode + +You can manually override read-only mode to reduce disk size. To do this, run the following in the [SQL Editor](https://app.supabase.com/project/_/sql): + +```sql +SET + default_transaction_read_only = 'off'; +``` + +This allows you to delete data from within the session. After deleting data, you should run a vacuum to reclaim as much space as possible. + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/platform/database-usage.mdx b/apps/docs/pages/guides/platform/database-usage.mdx deleted file mode 100644 index e99bf995aba..00000000000 --- a/apps/docs/pages/guides/platform/database-usage.mdx +++ /dev/null @@ -1,103 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'database-usage', - title: 'Database usage', - description: 'Understanding how database usage applies to your subscription.', -} - -Database size refers to the _monthly average storage usage_, as reported by Postgres. This metric is reported in your project's [billing usage](https://app.supabase.com/project/_/settings/billing/usage) and is updated daily. -Database size is the total size of used storage from your database, whereas disk size describes the size of the underlying available storage. - -For an instantaneous live view of the DB size, you can execute in Postgres: - -```sql -select - sum(pg_database_size (pg_database.datname)) / (1024 * 1024) as db_size_mb -from - pg_database; -``` - -This value is also reported in the [database settings page](https://app.supabase.com/project/_/settings/database). - -## Database storage management - -Supabase uses network-attached storage to balance performance with scalability. - -- For **Free tier projects**, the database can enter ['read only' mode when you exceed the 500mb limit](#free-tier-project-read-only-mode). -- For **Pro and Enterprise tier projects**, your [disk size expands automatically](#paid-tier-disk-auto-scaling). - -### Free tier project 'read-only' mode - -Free tier projects can enter read-only mode when exceeding the 500mb database size limit. - -In read-only mode, clients will encounter errors such as `cannot execute INSERT in a read-only transaction`. -Regular operation (read-write mode) is automatically re-enabled once database storage is below the 500mb database size limit. - -For more database storage and avoid your database entering read-only mode when exceeding the limit, you can [upgrade to the Pro or Enterprise plan](https://app.supabase.com/project/_/settings/billing/subscription). - -### Paid tier disk auto-scaling - -Pro and Enterprise have disk auto-scaling. Database storage expands automatically when the database reaches 90% of the disk size. The disk is expanded to be 50% larger (e.g., 8GB -> 12GB). -Auto-scaling can only take place once every 6 hours. - - - -If you intend to import a lot of data into your database that would require multiple storage expansions then [reach out to our team](https://app.supabase.com/support/new), otherwise your project might enter a ['read-only' mode](#paid-tier-project-read-only-mode). - - - -You may require multiple storage expansions in a short period of time. For example, you need to upload more than 1.5x the current size of your database storage. This will lead to your database entering a [read-only mode](#paid-tier-project-read-only-mode). You can avoid this by [contacting the support team](https://app.supabase.com/support/new) so that your database can be resized to a sufficient amount before you start uploading to it. - -If you need more than 1024TB of disk size, you can [contact us](https://app.supabase.com/support/new) to learn more about the Enterprise plan. - -### Paid tier project 'read-only' mode - -Database storage expands, at most, once every 6 hours. -If within those 6 hours you reach 95% of the disk space, your project will enter read-only mode. - -In read-only mode, clients will encounter errors such as `cannot execute INSERT in a read-only transaction`. -Regular operation (read-write mode) is automatically re-enabled once usage is below 95% of the disk size, or when the database automatically resizes after the 6 hour period. - -### Increasing available database space - -If your project is on the free tier, you can [Upgrade to the Pro or Enterprise tier](https://app.supabase.com/project/_/settings/billing/subscription) so that you can start to use more than 500mb. - -For Pro and Enterprise tier projects that are [nearing their disk size limit](#paid-tier-project-read-only-mode), you can increase available disk size by deleting data from your project's database to lower its disk usage. - -If your project database is already in read-only mode, run the following command to change the transaction mode to read-write for your session: - -```sql -SET - default_transaction_read_only = 'off'; -``` - -This allows you to delete data from within the session. - -### Preoccupied Space - -When launching a new project, your database size will be roughly ~40-60mb. -The space is used up by preinstalled extensions, schema/data used by our services that are offered with each project and default Postgres data. - -When installing additional extensions, even if you don't actively use them, additional database size is used. - -## Vacuum operations - -Postgres does not immediately reclaim the physical space used by dead tuples (i.e., deleted rows) in the DB. Instead, they are internally marked as removed until a [vacuum operation](https://www.postgresql.org/docs/current/routine-vacuuming.html) is executed. -As a result, deleting data from your database may not immediately reduce the reported disk usage. - - - -Vacuum operations can temporarily increase resource utilization, which may adversely impact the observed performance of your project until the maintenance is completed. - - - -Supabase projects have automatic vacuuming enabled, which ensures that these operations are performed regularly to keep the database healthy and performant. -However, it can be necessary to either [fine-tune](https://www.percona.com/blog/2018/08/10/tuning-autovacuum-in-postgresql-and-autovacuum-internals/) -the [autovacuum parameters](https://www.enterprisedb.com/blog/postgresql-vacuum-and-analyze-best-practice-tips), -or [manually initiate](https://www.postgresql.org/docs/current/sql-vacuum.html) vacuum operations. -For example, running a manual vacuum after deleting large amounts of data from your DB could help reduce the reported disk usage by Postgres. - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/platform/performance.mdx b/apps/docs/pages/guides/platform/performance.mdx index 8d7ae48e422..a00b509dbb3 100644 --- a/apps/docs/pages/guides/platform/performance.mdx +++ b/apps/docs/pages/guides/platform/performance.mdx @@ -8,6 +8,159 @@ export const meta = { The Supabase platform automatically optimizes your Postgres database to take advantage of the compute resources of the tier your project is on. However, these optimizations are based on assumptions about the type of workflow the project is being utilized for, and it is likely that better results can be obtained by tuning the database for your particular workflow. +## Examining Query Performance + +Unoptimized queries are a major cause of poor database performance. The techniques on this page can help you identify and understand queries that take the most time and resources from your database. + +Database performance is a large topic and many factors can contribute. Some of the most common causes of poor performance include: + +* An inefficiently designed schema +* Inefficiently designed queries +* A lack of indexes causing slower than required queries over large tables +* Unused indexes causing slow `INSERT`, `UPDATE` and `DELETE` operations +* Not enough compute resources, such as memory, causing your database to go to disk for results too often +* Lock contention from multiple queries operating on highly utilized tables +* Large amount of bloat on your tables causing poor query planning + +Thankfully there are solutions to all these issues, which we will cover in the following sections. + +### Postgres Cumulative Statistics system + +Postgres collects data about its own operations using the [cumulative statistics system](https://www.postgresql.org/docs/current/monitoring-stats.html). In addition to this, every Supabase project has the [pg_stat_statements extension](/docs/guides/database/extensions/pg_stat_statements) enabled by default. This extension records query execution performance details and is the best way to find inefficient queries. This information can be combined with the Postgres query plan analyzer to develop more efficient queries. + +Here are some example queries to get you started. + +#### Most frequently called queries: + +```sql +select + auth.rolname, + statements.query, + statements.calls, + -- -- Postgres 13, 14, 15 + statements.total_exec_time + statements.total_plan_time as total_time, + statements.min_exec_time + statements.min_plan_time as min_time, + statements.max_exec_time + statements.max_plan_time as max_time, + statements.mean_exec_time + statements.mean_plan_time as mean_time, + -- -- Postgres <= 12 + -- total_time, + -- min_time, + -- max_time, + -- mean_time, + statements.rows / statements.calls as avg_rows + +from pg_stat_statements as statements + inner join pg_authid as auth on statements.userid = auth.oid +order by + statements.calls desc +limit + 100; +``` + +This query shows: + +- query statistics, ordered by the number of times each query has been executed +- the role that ran the query +- the number of times it has been called +- the average number of rows returned +- the cumulative total time the query has spent running +- the min, max and mean query times. + +This provides useful information about the queries you run most frequently. Queries that have high `max_time` or `mean_time` times and are being called often can be good candidates for optimization. + +#### Slowest queries by execution time: + +```sql +select + auth.rolname, + statements.query, + statements.calls, + -- -- Postgres 13, 14, 15 + statements.total_exec_time + statements.total_plan_time as total_time, + statements.min_exec_time + statements.min_plan_time as min_time, + statements.max_exec_time + statements.max_plan_time as max_time, + statements.mean_exec_time + statements.mean_plan_time as mean_time, + -- -- Postgres <= 12 + -- total_time, + -- min_time, + -- max_time, + -- mean_time, + statements.rows / statements.calls as avg_rows +from pg_stat_statements as statements + inner join pg_authid as auth on statements.userid = auth.oid + order by + max_time desc + limit + 100; +``` + +This query will show you statistics about queries ordered by the maximum execution time. It is similar to the query above ordered by calls, but this one highlights outliers that may have high executions times. Queries which have high or mean execution times are good candidates for optimisation. + +#### Most time consuming queries: + +```sql +select + auth.rolname, + statements.query, + statements.calls, + statements.total_exec_time + statements.total_plan_time as total_time, + to_char(((statements.total_exec_time + statements.total_plan_time)/sum(statements.total_exec_time + statements.total_plan_time) over()) * 100, 'FM90D0') || '%' as prop_total_time +from pg_stat_statements as statements + inner join pg_authid as auth on statements.userid = auth.oid +order by + total_time desc +limit + 100; +``` + +This query will show you statistics about queries ordered by the cumulative total execution time. It shows the total time the query has spent running as well as the proportion of total execution time the query has taken up. + +Queries which are the most time consuming are not necessarily bad, you may have a very effiecient and frequently ran queries that end up taking a large total % time, but it can be useful to help spot queries that are taking up more time than they should. + +### Hit rate + +Generally for most applications a small percentage of data is accessed more regularly than the rest. To make sure that your regularly accessed data is available, Postgres tracks your data access patterns and keeps this in its [shared_buffers](https://www.postgresql.org/docs/15/runtime-config-resource.html#RUNTIME-CONFIG-RESOURCE-MEMORY) cache. + +Applications with lower cache hit rates generally perform more poorly since they have to hit the disk to get results rather than serving them from memory. Very poor hit rates can also cause you to burst past your [Disk I/O limits](https://supabase.com/docs/guides/platform/compute-add-ons#disk-io-bandwidth) causing significant performance issues. + +You can view your cache and index hit rate by executing the following query: + +```sql +select + 'index hit rate' as name, + (sum(idx_blks_hit)) / nullif(sum(idx_blks_hit + idx_blks_read),0) * 100 as ratio +from pg_statio_user_indexes +union all +select + 'table hit rate' as name, + sum(heap_blks_hit) / nullif(sum(heap_blks_hit) + sum(heap_blks_read),0) * 100 as ratio +from pg_statio_user_tables; +``` + +This shows the ratio of data blocks fetched from the Postgres [shared_buffers](https://www.postgresql.org/docs/15/runtime-config-resource.html#RUNTIME-CONFIG-RESOURCE-MEMORY) cache against the data blocks that were read from disk/OS cache. + +If either of your index or table hit rate are < 99% then this can indicate your compute plan is too small for your current workload and you would benefit from more memory. [Upgrading your compute](https://supabase.com/docs/guides/platform/compute-add-ons) is easy and can be done from your [project dashboard](https://app.supabase.com/project/_/settings/billing/subscription). + + +### Optimizing poor performing queries + +Postgres has built in tooling to help you optimize poorly performing queries. You can use the [query plan analyzer](https://www.postgresql.org/docs/current/sql-explain.html) on any expensive queries that you have identified: + +```sql +explain analyze ; +``` + +Be careful using `explain analyze` with `insert`/`update`/`delete` queries, because the query will actually run, and could have unintended side-effects. + +Using the query plan analyzer to optimize your queries is a large topic, with a number of online resources available: + +- [Official docs.](https://www.postgresql.org/docs/current/using-explain.html) +- [The Art of PostgreSQL.](https://theartofpostgresql.com/explain-plan-visualizer/) +- [Postgres Wiki.](https://wiki.postgresql.org/wiki/Using_EXPLAIN) +- [Enterprise DB.](https://www.enterprisedb.com/blog/postgresql-query-optimization-performance-tuning-with-explain-analyze) + +You can pair the information available from `pg_stat_statements` with the detailed system metrics available [via your metrics endpoint](../platform/metrics) to better understand the behavior of your DB and the queries you're executing against it. + ## Optimizing the number of connections By default, the number of connections allowed to Postgres and PgBouncer is configured based on the resources available to the database. @@ -64,33 +217,6 @@ alter system reset ; Configuring the number of PgBouncer connections is not supported at this time. -## Examining Query Performance - -Every Supabase project has [the pg_stat_statements extension](https://www.postgresql.org/docs/14/pgstatstatements.html) enabled by default. This extension records query execution performance details and is the best way to find queries that take the most time to execute. This information can be combined with the Postgres query plan analyzer to develop more efficient queries. - -Obtaining information from pg_stat_statements: - -```sql -select mean_exec_time + stddev_exec_time, * from pg_stat_statements order by 1 desc; -``` - -Using the query plan analyzer on your expensive queries: - -```sql -explain analyze ; -``` - -Be careful using `explain analyze` with `insert`/`update`/`delete` queries, because the query will actually run, and could have unintended side-effects. - -Using the query plan analyzer to optimize your queries is a large topic, with a number of online resources available: - -- [Official docs.](https://www.postgresql.org/docs/current/using-explain.html) -- [The Art of PostgreSQL.](https://theartofpostgresql.com/explain-plan-visualizer/) -- [Postgres Wiki.](https://wiki.postgresql.org/wiki/Using_EXPLAIN) -- [Enterprise DB.](https://www.enterprisedb.com/blog/postgresql-query-optimization-performance-tuning-with-explain-analyze) - -You can pair the information available from `pg_stat_statements` with the detailed system metrics available [via your metrics endpoint](../platform/metrics) to better understand the behavior of your DB and the queries you're executing against it. - export const Page = ({ children }) => export default Page diff --git a/apps/docs/public/img/guides/integrations/prisma/w0oowg8vq435ob5c3gf0.png b/apps/docs/public/img/guides/integrations/prisma/w0oowg8vq435ob5c3gf0.png index 9bfb34db474..05db2d109b6 100644 Binary files a/apps/docs/public/img/guides/integrations/prisma/w0oowg8vq435ob5c3gf0.png and b/apps/docs/public/img/guides/integrations/prisma/w0oowg8vq435ob5c3gf0.png differ diff --git a/apps/www/_blog/2023-02-07-chatgpt-supabase-docs.mdx b/apps/www/_blog/2023-02-07-chatgpt-supabase-docs.mdx index 0773f7c9713..85b061947c7 100644 --- a/apps/www/_blog/2023-02-07-chatgpt-supabase-docs.mdx +++ b/apps/www/_blog/2023-02-07-chatgpt-supabase-docs.mdx @@ -11,7 +11,7 @@ date: '2023-02-07' toc_depth: 3 --- -We all know that Microsoft's real agenda for pouring billions into OpenAI to revive their favorite friend Clippy. +We all know that Microsoft's real agenda for pouring billions into OpenAI is to revive their favorite friend Clippy. Today, we're doing our part to support the momentum by releasing “Supabase Clippy” for our docs (and we don't expect this name to last long before the lawyers catch on). ![Clippy](/images/blog/docsgpt/clippy.png) @@ -30,7 +30,7 @@ Our product suite has grown in the past 2 years and our docs have grown as a res ### The “ask” interface -Developers have recently gained an the ability to trust a bot. Where Clippy failed, ChatGPT succeeded. +Developers have recently gained the ability to trust a bot. Where Clippy failed, ChatGPT succeeded. This is convenient timing for us, since our documentation content is more than the average developer wants to consume in one go. Today we're providing a similar interface to ChatGPT which is trained on our own docs. diff --git a/apps/www/lib/redirects.js b/apps/www/lib/redirects.js index e73aa509b57..00b9b15e5c5 100644 --- a/apps/www/lib/redirects.js +++ b/apps/www/lib/redirects.js @@ -1857,4 +1857,9 @@ module.exports = [ source: '/docs/reference/javascript/v0/rpc', destination: '/docs/reference/javascript/rpc', }, + { + permanent: true, + source: '/docs/guides/platform/database-usage', + destination: '/docs/guides/platform/database-size', + }, ] diff --git a/package-lock.json b/package-lock.json index dccce67d772..d2bc8fa6836 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "requires": true, "packages": { "": { + "name": "supabase", "version": "0.0.0", "license": "Apache-2.0", "workspaces": [ @@ -46996,7 +46997,7 @@ "@sinclair/typebox": "^0.25.1", "pg": "^8.7.1", "pg-format": "^1.0.4", - "pgsql-parser": "^13.3.0", + "pgsql-parser": "13.4.0", "postgres-array": "^3.0.1", "prettier": "^2.6.0", "prettier-plugin-sql": "^0.12.1" diff --git a/studio/components/interfaces/SQLEditor/SQLEditor.constants.ts b/studio/components/interfaces/SQLEditor/SQLEditor.constants.ts index 2483efe39da..f1b8e5c4c85 100644 --- a/studio/components/interfaces/SQLEditor/SQLEditor.constants.ts +++ b/studio/components/interfaces/SQLEditor/SQLEditor.constants.ts @@ -969,4 +969,106 @@ GRANT ALL ON TABLE next_auth.verification_tokens TO postgres; GRANT ALL ON TABLE next_auth.verification_tokens TO service_role; `.trim(), }, + { + id: 16, + type: 'template', + title: 'Most frequently invoked', + description: 'Most frequently called queries in your database.', + sql: `-- Most frequently called queries + +-- A limit of 100 has been added below + +select + auth.rolname, + statements.query, + statements.calls, + -- -- Postgres 13, 14, 15 + statements.total_exec_time + statements.total_plan_time as total_time, + statements.min_exec_time + statements.min_plan_time as min_time, + statements.max_exec_time + statements.max_plan_time as max_time, + statements.mean_exec_time + statements.mean_plan_time as mean_time, + -- -- Postgres <= 12 + -- total_time, + -- min_time, + -- max_time, + -- mean_time, + statements.rows / statements.calls as avg_rows + + from pg_stat_statements as statements + inner join pg_authid as auth on statements.userid = auth.oid + order by + statements.calls desc + limit + 100;`, + }, + { + id: 17, + type: 'template', + title: 'Most time consuming', + description: 'Aggregate time spent on a query type.', + sql: `-- Most time consuming queries + +-- A limit of 100 has been added below + +select + auth.rolname, + statements.query, + statements.calls, + statements.total_exec_time + statements.total_plan_time as total_time, + to_char(((statements.total_exec_time + statements.total_plan_time)/sum(statements.total_exec_time + statements.total_plan_time) over()) * 100, 'FM90D0') || '%' as prop_total_time + from pg_stat_statements as statements + inner join pg_authid as auth on statements.userid = auth.oid + order by + total_time desc + limit + 100;`, + }, + { + id: 18, + type: 'template', + title: 'Slowest execution time', + description: 'Slowest queries based on max execution time.', + sql: `-- Slowest queries by max execution time + +-- A limit of 100 has been added below + +select + auth.rolname, + statements.query, + statements.calls, + -- -- Postgres 13, 14, 15 + statements.total_exec_time + statements.total_plan_time as total_time, + statements.min_exec_time + statements.min_plan_time as min_time, + statements.max_exec_time + statements.max_plan_time as max_time, + statements.mean_exec_time + statements.mean_plan_time as mean_time, + -- -- Postgres <= 12 + -- total_time, + -- min_time, + -- max_time, + -- mean_time, + statements.rows / statements.calls as avg_rows + from pg_stat_statements as statements + inner join pg_authid as auth on statements.userid = auth.oid + order by + max_time desc + limit + 100;`, + }, + { + id: 19, + type: 'template', + title: 'Hit rate', + description: 'See your cache and index hit rate.', + sql: `-- Cache and index hit rate + +select + 'index hit rate' as name, + (sum(idx_blks_hit)) / nullif(sum(idx_blks_hit + idx_blks_read),0) as ratio + from pg_statio_user_indexes + union all + select + 'table hit rate' as name, + sum(heap_blks_hit) / nullif(sum(heap_blks_hit) + sum(heap_blks_read),0) as ratio + from pg_statio_user_tables;`, + } ] diff --git a/studio/components/interfaces/Settings/ProjectUsageBars/ProjectUsageBars.tsx b/studio/components/interfaces/Settings/ProjectUsageBars/ProjectUsageBars.tsx index 8172fbafc64..c24fbe5a685 100644 --- a/studio/components/interfaces/Settings/ProjectUsageBars/ProjectUsageBars.tsx +++ b/studio/components/interfaces/Settings/ProjectUsageBars/ProjectUsageBars.tsx @@ -1,5 +1,5 @@ import Link from 'next/link' -import { FC, useEffect, useState } from 'react' +import { FC, useEffect } from 'react' import { useRouter } from 'next/router' import * as Tooltip from '@radix-ui/react-tooltip' import { PermissionAction } from '@supabase/shared-types/out/constants' @@ -22,7 +22,7 @@ import ShimmeringLoader from 'components/ui/ShimmeringLoader' import InformationBox from 'components/ui/InformationBox' import { USAGE_BASED_PRODUCTS } from 'components/interfaces/Billing/Billing.constants' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' -import { executeSql } from 'data/sql/execute-sql-query' +import { useProjectReadOnlyQuery } from 'data/config/project-read-only-query' import { ProjectUsageResponseUsageKeys, useProjectUsageQuery } from 'data/usage/project-usage-query' interface Props { @@ -35,7 +35,11 @@ const ProjectUsage: FC = ({ projectRef }) => { const router = useRouter() const { project } = useProjectContext() - const [isReadOnlyMode, setIsReadOnlyMode] = useState(false) + const { data: isReadOnlyMode } = useProjectReadOnlyQuery({ + projectRef: project?.ref, + connectionString: project?.connectionString, + }) + const canUpdateSubscription = checkPermissions( PermissionAction.BILLING_WRITE, 'stripe.subscriptions' @@ -78,24 +82,10 @@ const ProjectUsage: FC = ({ projectRef }) => { /> ) } - // [Terry] - // temporary solution to check if project is in read only mode - // until we get an api endpoint for this - - async function checkForReadOnlyMode() { - const sql = `show default_transaction_read_only;` - const connectionString = project?.connectionString - const { result: readOnlyStatus } = await executeSql({ projectRef, connectionString, sql }) - if (readOnlyStatus[0]?.default_transaction_read_only === 'on') setIsReadOnlyMode(true) - } - - useEffect(() => { - if (projectRef) checkForReadOnlyMode() - }, [projectRef]) const isPaidTier = subscriptionTier !== PRICING_TIER_PRODUCT_IDS.FREE - const featureFootnotes: Record = { + const featureFootnotes: Record = { db_size: isPaidTier ? (

@@ -109,9 +99,7 @@ const ProjectUsage: FC = ({ projectRef }) => {
- ) : ( - <> - ), + ) : null, } return ( diff --git a/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx b/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx index aeb4a2ffd80..90700cd6c35 100644 --- a/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx +++ b/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx @@ -11,12 +11,10 @@ import HelpPopover from './HelpPopover' import NotificationsPopover from './NotificationsPopover' import { getResourcesExceededLimits } from 'components/ui/OveragesBanner/OveragesBanner.utils' import { useProjectUsageQuery } from 'data/usage/project-usage-query' +import { useProjectReadOnlyQuery } from 'data/config/project-read-only-query' import { Badge } from 'ui' -import { executeSql } from 'data/sql/execute-sql-query' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' -import { useEffect, useState } from 'react' - const LayoutHeader = ({ customHeaderComponents, breadcrumbs = [], headerBorder = true }: any) => { const { ui } = useStore() const { selectedOrganization, selectedProject } = ui @@ -24,22 +22,10 @@ const LayoutHeader = ({ customHeaderComponents, breadcrumbs = [], headerBorder = const { ref: projectRef } = useParams() const { project } = useProjectContext() - const [isReadOnlyMode, setIsReadOnlyMode] = useState(false) - - // [Terry] - // temporary solution to check if project is in read only mode - // until we get an api endpoint for this - async function checkForReadOnlyMode() { - const sql = `show default_transaction_read_only;` - const connectionString = project?.connectionString - const { result: readOnlyStatus } = await executeSql({ projectRef, connectionString, sql }) - if (readOnlyStatus[0]?.default_transaction_read_only === 'on') setIsReadOnlyMode(true) - } - - useEffect(() => { - if (projectRef) checkForReadOnlyMode() - else setIsReadOnlyMode(false) - }, [projectRef]) + const { data: isReadOnlyMode } = useProjectReadOnlyQuery({ + projectRef: project?.ref, + connectionString: project?.connectionString, + }) const { data: usage } = useProjectUsageQuery({ projectRef }) const resourcesExceededLimits = getResourcesExceededLimits(usage) diff --git a/studio/components/ui/ProductMenu/ProductMenuItem.tsx b/studio/components/ui/ProductMenu/ProductMenuItem.tsx index ca2fdcf51a7..42fb817e326 100644 --- a/studio/components/ui/ProductMenu/ProductMenuItem.tsx +++ b/studio/components/ui/ProductMenu/ProductMenuItem.tsx @@ -34,9 +34,9 @@ const ProductMenuItem: FC = ({
- {name}{' '} + {name} {label !== undefined && ( {label} )} diff --git a/studio/data/config/project-read-only-query.ts b/studio/data/config/project-read-only-query.ts new file mode 100644 index 00000000000..23312f2f67e --- /dev/null +++ b/studio/data/config/project-read-only-query.ts @@ -0,0 +1,56 @@ +import { UseQueryOptions } from '@tanstack/react-query' +import { ExecuteSqlData, useExecuteSqlPrefetch, useExecuteSqlQuery } from '../sql/execute-sql-query' + +// TODO: temporary solution to check if project is in read only mode +// until we get an api endpoint for this + +export const getProjectReadOnlySql = () => { + const sql = /* SQL */ ` + show default_transaction_read_only; + ` + + return sql +} + +export type ProjectReadOnlyVariables = { + projectRef?: string + connectionString?: string +} + +export type ProjectReadOnlyData = boolean +export type ProjectReadOnlyError = unknown + +export const useProjectReadOnlyQuery = ( + { projectRef, connectionString }: ProjectReadOnlyVariables, + options: Omit< + UseQueryOptions, + 'select' + > = {} +) => + useExecuteSqlQuery( + { + projectRef, + connectionString, + sql: getProjectReadOnlySql(), + queryKey: ['project-read-only'], + }, + { + select(data) { + return data.result[0]?.default_transaction_read_only === 'on' + }, + enabled: typeof projectRef !== 'undefined' && typeof connectionString !== 'undefined', + ...options, + } + ) + +export const useProjectReadOnlyPrefetch = ({ + projectRef, + connectionString, +}: ProjectReadOnlyVariables) => { + return useExecuteSqlPrefetch({ + projectRef, + connectionString, + sql: getProjectReadOnlySql(), + queryKey: ['project-read-only'], + }) +} diff --git a/studio/hooks/misc/withAuth.tsx b/studio/hooks/misc/withAuth.tsx index b41734cb1c8..d2cdc5d94fd 100644 --- a/studio/hooks/misc/withAuth.tsx +++ b/studio/hooks/misc/withAuth.tsx @@ -50,6 +50,7 @@ export function withAuth( }) usePermissionsQuery({ + enabled: IS_PLATFORM, onSuccess(permissions) { ui.setPermissions(permissions) }, diff --git a/studio/stores/common/PostgresMetaInterface.ts b/studio/stores/common/PostgresMetaInterface.ts index 09f605b5acc..bd51fd319d5 100644 --- a/studio/stores/common/PostgresMetaInterface.ts +++ b/studio/stores/common/PostgresMetaInterface.ts @@ -117,6 +117,7 @@ export default class PostgresMetaInterface implements IPostgresMetaInterface< } } + // [Joshen] Only used for tables and views for now async loadBySchema(schema: string) { let { LOADING, ERROR, LOADED } = this.STATES try { @@ -132,7 +133,14 @@ export default class PostgresMetaInterface implements IPostgresMetaInterface< const data = response as T[] const formattedData = keyBy(data, this.identifier) - this.data = { ...this.data, ...formattedData } + // Purge existing data that belongs to given schema, otherwise + // stale data will persist + const purgedData = Object.keys(this.data) + .map((identifier: any) => this.data[identifier]) + .filter((item: any) => item.schema !== schema) + const formattedPurgedData = keyBy(purgedData, this.identifier) + + this.data = { ...formattedPurgedData, ...formattedData } this.setState(LOADED) return data