Unified DB-backed cloud catalog with background provider refresh #10414
zdeneklapes
started this conversation in
Ideas
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Proposal: Unified DB-backed cloud catalog with background provider refresh
Summary
I would like to propose consolidating SkyPilot's cloud catalog handling around a single DB-backed catalog maintained by a dedicated background refresher service.
Today, SkyPilot already has API-backed catalog/data fetchers for several providers, but refresh, caching, persistence, and consumption are handled differently across providers.
The proposed architecture is:
A separate
catalog-refresherservice would periodically query configured providers and update instance types, regions/zones, hardware information, prices, spot prices, and other provider-exposed catalog data.The API server and optimizer would then always consume the latest validated catalog from the database.
The main goal is to consolidate the lifecycle:
while keeping provider-specific API logic inside small adapters.
Motivation
Catalog freshness
The current catalog flow works well for relatively stable cloud data, but it is less suitable for dynamic providers.
RunPod is a good example. Its offerings and pricing can change much faster than SkyPilot's periodically refreshed catalog.
This was already discussed during the RunPod catalog work:
#6824
With the current hosted catalog approach, freshness depends on multiple refresh cycles:
For dynamic providers, it would be better for the SkyPilot server itself to maintain a much fresher catalog.
Provider-specific catalog implementations
SkyPilot already contains provider fetchers for several clouds:
The provider API calls will always be provider-specific.
However, the following parts do not need to be:
I think these should become common SkyPilot infrastructure.
Goals
Non-goals
This proposal does not make catalog data an authoritative guarantee of capacity.
Provisioning-time capacity errors, retries, and failover remain necessary.
Providers also do not have to expose identical information. Fields that are unavailable should remain
NULL/UNKNOWN.Architecture
1. Dedicated catalog refresher service
Introduce a separate background process, for example:
For a remote SkyPilot deployment:
The refresher would have no user-facing API.
Its responsibility is only:
The API server only reads catalog state.
2. Common provider fetcher interface
Provider implementations could expose a common interface such as:
Example:
The provider adapters remain responsible for translating their API into SkyPilot's normalized catalog format.
The important consolidation is everything after the adapter.
3. Reuse the current SkyPilot catalog schema
The existing catalog already represents fields such as:
The DB model should represent the same logical information.
This allows existing catalog helper functions and optimizer logic to remain mostly unchanged.
The main difference becomes:
Database model
4. Dedicated catalog table
A possible schema:
provider_metadatacan store provider-specific information that does not fit the common schema, for example:Unsupported fields remain
NULL/UNKNOWN.5. Catalog revisions
Every successful provider refresh should publish one coherent revision.
Every row from one refresh uses the same revision.
The API server can cache the current DataFrame in memory and only reload it when the DB revision changes.
This avoids rebuilding the DataFrame on every request.
Refresh behavior
6. Atomic provider replacement
A refresh should update a whole provider catalog atomically:
Readers must never observe a partially updated catalog.
The implementation could either replace all rows in one transaction or insert immutable revisions and atomically switch the active revision.
7. Keep the last valid revision on failure
If fetching, validation, or DB publishing fails, the previous revision stays active.
Only a complete, validated catalog is published.
Refresh scheduling
8. One scheduler, provider-specific intervals
The refresh architecture should be common, while frequency can remain provider-specific.
For example:
The exact intervals are not important to this proposal.
The important property is:
Dynamic providers can refresh frequently, while slower or more expensive provider queries can run less often.
9. Refresh only configured providers
The service should only refresh providers enabled/configured on the SkyPilot server.
This avoids unnecessary credentials, API traffic, DB writes, and provider rate-limit usage.
10. Jitter and retries
Refresh schedules should include jitter so many SkyPilot installations do not query provider APIs simultaneously.
Failed calls should use exponential backoff while the current DB revision remains active.
Catalog consumption
11. DB-backed DataFrame cache
The optimizer does not need to query individual catalog rows directly.
The catalog layer can continue exposing a DataFrame:
The API-server path remains:
No full provider catalog API query occurs during a launch.
Account/workspace scope
12. Support scoped catalogs
Some catalog information may depend on:
For that reason, the catalog should include
scope_key.A global provider catalog can use:
while account-specific catalogs can use a stable account/project/tenant identity.
The exact scope identity can be discussed separately.
Why a separate service?
I prefer a separate refresher service rather than running full provider fetchers inside API request workers.
Isolation
Slow or failing provider APIs do not block SkyPilot API requests.
Independent lifecycle
The refresher can be restarted or upgraded independently.
Clear ownership
Better observability
Catalog refresh behavior can have dedicated metrics and logs.
A Kubernetes deployment could use a single replica. A CronJob would also work, but a long-running service makes per-provider intervals, retries, jitter, and metrics easier to manage.
Why not query providers during each launch?
Querying provider catalogs synchronously during optimization would add:
Instead:
All jobs reuse the same recently refreshed data.
Why use the DB as the source of truth?
For a long-running SkyPilot API server, the DB provides:
The server architecture becomes:
Relation to existing SkyPilot work
I think this proposal mostly consolidates patterns that already exist.
Existing provider fetchers
SkyPilot already has API-backed fetchers for several providers:
The proposal changes mainly:
RunPod freshness discussion
The RunPod work already identified the mismatch between fast-changing provider state and slower static catalog refresh:
#6824
Realtime Kubernetes discovery
SkyPilot also has realtime resource discovery for Kubernetes, where repeated provider queries and TTL caching were discussed:
#3499
The DB-backed background refresher addresses the same general tradeoff for cloud catalogs:
Minimal implementation
I would keep the first implementation intentionally small.
1. Add
cloud_catalog_entriesAdd one dedicated DB table containing the current catalog entries and revision metadata.
2. Add
ProviderCatalogFetcherWrap existing provider fetchers behind one common interface.
3. Add
catalog-refresherOne background service handles:
4. Add DB-backed catalog loading
The API server checks provider revision and reloads its DataFrame only when that revision changes.
5. Start with two providers
I suggest:
6. Keep optimizer/provisioner unchanged
The initial proposal should not require changes to resource selection semantics or provisioning logic.
The main change is the catalog source and lifecycle.
Failure model
NULL/UNKNOWNThe DB catalog therefore acts as the last-known-good catalog state.
Observability
Useful metrics could include:
Diagnostics could expose:
Tradeoffs
Provider API amplification
With server-side refresh, each SkyPilot deployment queries its configured providers independently.
Mitigations:
Credentials
The refresher needs provider credentials for APIs that require authentication.
For account-specific catalogs, credentials need to map to a stable
scope_key.Additional DB data
Catalog rows move into the SkyPilot DB. This adds some storage and DB traffic, but refreshes are relatively infrequent and the catalog is read mostly through an in-memory DataFrame cache.
Open questions
I would like feedback on:
catalog-refresherservice or an API-server internal daemon?Proposed MVP
The intended architecture is:
I think this would make catalog handling considerably more consistent and give long-running SkyPilot API servers much fresher provider data without putting provider catalog queries into the launch path.
Would this direction make sense for SkyPilot?
All reactions