## Context
The Integrations -> Cron overview page renders a list of cron jobs by
doing a join on both `cron.jobs` and `cron.job_run_details` in order to
pull the data for "Latest run" and "Status" ("Next run" is just derived
from "Latest Run" and the job's schedule)
In the past, we've had users run into issues where this query would time
out, likely due to the join on `job_run_details` especially if that
table is really big (and the columns on that table aren't indexed), and
our solution was to inform users to clear out that table + schedule a
job to clean the `job_run_details` table on a certain cadence. This CTA
however blocks the UI from rendering the cron jobs overview entirely
<img width="1644" height="590" alt="image"
src="https://github.com/user-attachments/assets/1a0458e9-f686-452b-88bc-44459bc7c615"
/>
## Changes involved
Am opting to use the new preflight check introduced
[here](https://github.com/supabase/supabase/pulls?q=is%3Apr+author%3Ajoshenlim+is%3Aclosed)
- Attempt to fetch the data for the cron jobs overview normally
- If preflight check rejects (due to high cost), then opt to fetch the
data while omitting the join
- Show a banner instead to inform the user accordingly
- So that way users can still view their cron jobs (the latest run
column is a nice to have convenience really)
<img width="1151" height="379" alt="image"
src="https://github.com/user-attachments/assets/72cfaafb-e951-4601-b54e-c36a6f36332d"
/>
- Clicking learn more will then open a dialog to explain what this is
all about
- The steps to clear the `job_run_details` table + schedule a clean up
job still remains
<img width="450" height="484" alt="image"
src="https://github.com/user-attachments/assets/4491e766-64a1-40c9-9703-2e8b0da11c3f"
/>
<img width="450" height="610" alt="Screenshot 2026-02-05 at 12 56 39"
src="https://github.com/user-attachments/assets/644c9a7b-5afb-46bb-8c1e-9ece3986c2fb"
/>
<img width="450" height="507" alt="Screenshot 2026-02-05 at 13 01 37"
src="https://github.com/user-attachments/assets/9f0f40f2-7376-4c28-90f1-0b21a1a4560a"
/>
- Non-UI related changes include
- Automatically refresh cron jobs after scheduling a job, remove manual
CTA to refresh the jobs after scheduling the clean up job
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Previous Runs tab on cron job pages
* Interactive "Learn more" cleanup dialog with guided delete and
schedule steps
* Close-confirmation modal when editing cron jobs
* High-cost banner that enables a lightweight "minimal" list mode
* **Bug Fixes**
* Clearer placeholder (minus icon/message) when a job has no runs
* **Performance**
* Safer, more efficient fetching and pagination for large cron job
lists; reduced retries on high-cost queries
* **Tests**
* Expanded end-to-end tests covering high-cost flows and the cleanup
dialog
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Context
Addresses a UX confusion where users are unsure of which schema to
install database extensions in. Am opting to show a "Default" badge on
the extensions schema just to lower the cognitive load for this step if
the extension doesn't have a specific schema it needs to be installed in
<img width="416" height="322" alt="image"
src="https://github.com/user-attachments/assets/b938afdb-31d7-4e9f-a065-7b3b185ac8e1"
/>
If the extension has a recommended schema, we'll show a badge as well
(Related [PR](https://github.com/supabase/supabase/pull/40968))
<img width="429" height="300" alt="image"
src="https://github.com/user-attachments/assets/cf42bce3-a99c-4877-9d16-9f2013b91e16"
/>
Otherwise, if there's a fixed schema, then no badge
<img width="427" height="283" alt="image"
src="https://github.com/user-attachments/assets/608c0c4f-ea8e-4462-ad66-9e3b3bb43265"
/>
## Other changes
- Refactored `EnableExtensionModal` to use react hook form + Dialog
component
- Refactored checking of default schema to use React Query
## To test
- [ ] Verify the UI changes
- [ ] Verify that you can still enable / disable extensions - and ensure
that they're being installed in the right schemas
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Enhanced extension enable dialog with form-driven schema selection,
read-only default-schema display, and "create custom schema" flow
* "Recommended" and "Default" badges next to schema choices;
platform-specific warnings and callouts
* Fetches and surfaces extension default/recommended schema to guide
selection
* **Refactor**
* Dialog-based UI with integrated form validation and improved enable
workflow
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Alaister Young <alaister@users.noreply.github.com>
* fix(studio): check job_run_details size in cron display
When cron.job_run_details grows too large (200k+ rows), loading the
cron jobs overview can timeout and affect other queries by pulling
excessive data into shared buffers.
This change:
- Estimates table size using pg_stat before fetching cron jobs data
- Shows a cleanup notice when the table exceeds the threshold
- Provides batched deletion using ctid ranges to avoid buffer pollution
- Allows scheduling an automated daily cleanup cron job
- Handles timeout errors gracefully with a "suspected overflow" state
The useCronJobsData hook now returns a discriminated union status that
tracks loading, estimate-error, overflow-confirmed, overflow-suspected,
and ready states, allowing the UI to respond appropriately to each case.
* fix(studio): use index when querying cron.job_run_details
cron.job_run_details is only indexed by runid, not by start_time. Change
the query to use the runid index (which gives the same result, since
runid is auto-incrementing).
The global storage size validation depends on an unpaginated buckets
query to determine whether it is lower than any individual bucket's
cutoff. This causes a problem for users with tens of thousands of
buckets.
There's a bit of a UX/performance problem here, because in order to
determine whether any bucket's `file_size_limit` exceeds the global
setting, we need to get the max `file_size_limit` of `storage.buckets`
-- however, that column is not indexed.
My workaround is:
- Below a certain threshold (10,000) buckets, the query for max
`file_size_limit` is automatically run on form submit.
- Above that threshold, the user must confirm whether they want to run
the query. They're still allowed to change the storage config without
running it -- this does open a loophole where they can have a global
storage setting lower than an individual bucket's file size limit, but
though this is a potentially confusing situation, it's not strictly an
error.
---------
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>