-
Notifications
You must be signed in to change notification settings - Fork 41
[catalog] Sketch out initial RFC scope #115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,163 @@ | ||
| # RFC 0003: Catalog | ||
|
|
||
| **Status**: Draft | ||
|
|
||
| **Authors**: | ||
| - [Jason Gustafson](https://github.com/hachikuji) | ||
|
|
||
| ## Summary | ||
|
|
||
| This RFC proposes a catalog system for OpenData that serves as a central management plane for OpenData storage systems. The catalog provides a single point to manage metadata about the systems ("slates") a user has installed, including their names, types, and object storage configuration. The catalog itself is implemented as a slate backed by SlateDB, following the same patterns as other OpenData subsystems. | ||
|
|
||
| ## Motivation | ||
|
|
||
| OpenData comprises multiple storage subsystems (log, timeseries, vector, etc.) that share a common foundation on SlateDB. As users deploy multiple instances of these systems, several operational concerns emerge that are not well-served by existing orchestration systems: | ||
|
|
||
| 1. **Discovery** — There is no unified way to enumerate which slates exist within an environment. Operators must track this information externally or inspect object storage directly. | ||
|
|
||
| 2. **Usage tracking** — Orchestration systems like Kubernetes know what pods are running, but not which applications are actively reading or writing to a given slate. This information lives at the storage layer. | ||
|
|
||
| 3. **Safe deprovisioning** — Before removing a slate, operators need to know whether it has active consumers. Without usage tracking, this requires manual coordination or risks data loss. | ||
|
|
||
| 4. **Operational tooling** — Cross-cutting tools (backup, monitoring, migration) need a way to discover and enumerate slates without subsystem-specific knowledge. | ||
|
|
||
| A catalog addresses these concerns by providing: | ||
|
|
||
| - A registry of running slates via self-registration | ||
| - Tracking of active readers and writers for each slate | ||
| - A foundation for safety checks and operational tooling | ||
|
|
||
| The catalog is itself a slate backed by SlateDB, ensuring it benefits from the same durability and operational characteristics as the systems it manages. | ||
|
|
||
| ### Hypothetical Workflow | ||
|
|
||
| Slates and their consumers self-register when they start up. The CLI is primarily for discovery and usage tracking: | ||
|
|
||
| ``` | ||
| $ opendata slate list | ||
| NAME TYPE BUCKET | ||
| events log s3://acme-data/events | ||
| metrics timeseries s3://acme-data/metrics | ||
|
|
||
| # A new slate is provisioned and starts up, self-registering with the catalog... | ||
|
|
||
| $ opendata slate list | ||
| NAME TYPE BUCKET | ||
| events log s3://acme-data/events | ||
| metrics timeseries s3://acme-data/metrics | ||
| orders log s3://acme-data/orders | ||
|
|
||
| $ opendata slate describe orders | ||
| Name: orders | ||
| Type: log | ||
| Bucket: s3://acme-data/orders | ||
| Writers: order-service | ||
| Readers: analytics, billing | ||
|
|
||
| # Before deprovisioning, check if it's safe to remove | ||
| $ opendata slate describe metrics | ||
| Name: metrics | ||
| Type: timeseries | ||
| Bucket: s3://acme-data/metrics | ||
| Writers: telemetry-collector | ||
| Readers: dashboard, alerting | ||
| ``` | ||
|
|
||
| ## Goals | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is great! I wonder, however, if we should add a concept of the 'owner' of a slate. for example, if you have a prometheus server that's backed by a slate TSDB. Simply deleting the s3 buckets will orphan the server. We need to be able to know that there is a service that depends on the slate, where it lives, etc. so that the operations maintain the integrity of the system.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense. This gets into liveness I guess. I was hoping we could go with a model where the catalog only communicates with object storage. Perhaps we could add some kind of explicit fencing marker to each opendata system, which must be added by the writer. The purpose is to fence writers/readers and signal the catalog that it is safe to delete.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To make sure I understand, the marker would be added by the writer to indicate that the slate is no longer being written to? And that makes it safe to delete? If so, I'd presume the readers would also have to write a marker. This is more like a lease system than fencing markers if that's how you are thinking it would work. From a user perspective, if you have a service, you want to manage the lifecycle of the service as a whole. For example, I think a useful admin
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The marker could be like a poison pill inserted into the slatedb manifest. It would kill readers and writers. I think the main point is trying to define the communication model. How does the catalog interact with provisioning systems? How does the catalog interact with system readers/writers? The ideal from my perspective is that all communication with the catalog is done through object storage. It is a return of our "storage as protocol" idea at its heart. For example, the catalog could write provisioning requests as files in object storage. Some kind of k8s service could watch that file and do the actual provisioning work. Deletion workflows could follow a similar pattern.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Downstream systems could just be catalog readers I guess. They might follow changes to the catalog as any SlateDb reader does and act when necessary.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like this line of thinking. So then the catalog needs to hold enough metadata to make those flows possible. I think this is captured under 'define process for registering/deleting slates' in your goals. So I can imagine this type of flow in the longer term.
Does that match what you had in mind?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I think that's right. I guess the main point is that downstream systems are just Readers in our Slatedb-backed framing. So provisioning systems would follow the catalog as readers. Perhaps they could even modify the catalog themselves by temporarily assuming the Writer role. Perhaps we do not need long-lived writers at all. If we could get a model like this to work, it would remove a huge amount of complexity. You don't need to have catalog as a persistent service.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added some text to the RFC about the communication model. I like it. It leans into the advantages of slatedb/object storage.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice. I like the direction. I think SlateDB transactions will be crucial to making this work. |
||
|
|
||
| - Define "slate" as the fundamental unit of an OpenData system | ||
| - Specify the metadata required to describe a slate (name, type, object storage location) | ||
| - Establish the catalog as a SlateDB-backed registry of slate metadata | ||
| - Define the self-registration process by which slates register themselves on startup | ||
| - Define the deregistration process for when slates are shut down | ||
| - Track active readers and writers for each slate | ||
| - Ensure the catalog can manage slates of all types (log, timeseries, vector) | ||
|
|
||
| ## Non-Goals | ||
|
|
||
| - **Provisioning** — The catalog does not manage target state or drive provisioning. It reflects current state only. Provisioning is the responsibility of external systems (Kubernetes, Terraform, etc.). | ||
|
|
||
| ## Design | ||
|
|
||
| ### Communication Model | ||
|
|
||
| The catalog does not operate as a persistent service with a communication endpoint. Instead, all interaction with the catalog occurs through its SlateDB-backed storage. This follows directly from SlateDB's Reader/Writer model, where components temporarily assume one of two roles: | ||
|
|
||
| - **Writer** — A component that opens the catalog with write access to mutate state (self-register, update metadata, deregister). | ||
| - **Reader** — A component that opens a read-only view of the catalog to observe state changes. | ||
|
|
||
| This model enables coordination between loosely-coupled components without requiring a running service or explicit RPC. Components communicate implicitly by writing state that other components read. | ||
|
|
||
| #### Example: Self-Registration on Startup | ||
|
|
||
| The catalog sits downstream of provisioning. Each slate can be configured with an optional reference to a catalog. When a slate starts up, it registers itself, ensuring the catalog reflects the current state of running systems. | ||
|
|
||
| Consider a flow where a Kubernetes operator provisions a new slate: | ||
|
|
||
| 1. A user creates a Kubernetes Custom Resource specifying a new slate. | ||
| 2. The K8s operator provisions the slate with a catalog reference in its configuration. | ||
| 3. When the slate starts, it assumes the **Writer** role to register itself in the catalog. | ||
| 4. CLI tooling or other components can observe the catalog as **Readers** to discover running slates. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like the new version a lot. I agree this needs time to bake. And I think the crux of what needs to bake is this 4th item. What do the readers actually do with the data in the catalog. I think this, more than anything, is what will inform if and how the catalog co-exists with orchestration systems like k8s. |
||
|
|
||
| ``` | ||
| ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ | ||
| │ Provisioner │ │ Slate │ │ Catalog │ | ||
| │ (K8s, etc.) │ │ (orders) │ │ (SlateDB) │ | ||
| └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ | ||
| │ │ │ | ||
| create slate with │ │ | ||
| catalog reference │ │ | ||
| │──────────────────────────►│ │ | ||
| │ │ │ | ||
| │ │ [Writer] self-register │ | ||
| │ │ on startup │ | ||
| │ │──────────────────────────►│ | ||
| │ │ │ | ||
| │ │ │ | ||
| ┌────────┴────────┐ │ │ | ||
| │ CLI │ │ │ | ||
| └────────┬────────┘ │ │ | ||
| │ │ │ | ||
| │ [Reader] discover running slates │ | ||
| │──────────────────────────────────────────────────────►│ | ||
| │ │ │ | ||
| ``` | ||
|
|
||
| This approach keeps provisioning out of the catalog's scope. The catalog represents *current state*—what systems are actually running—rather than *target state*. Provisioning remains the responsibility of existing orchestration systems (Kubernetes, Terraform, manual deployment, etc.). | ||
|
|
||
| #### Implications | ||
|
|
||
| - **No always-on service** — The catalog does not require a continuously running process. Components open Reader or Writer handles as needed. | ||
| - **Distributed coordination via storage** — Object storage (S3, GCS, etc.) serves as the durable communication medium. | ||
| - **Consistency from SlateDB** — SlateDB's single-writer guarantee ensures catalog mutations are serialized. Readers see a consistent snapshot. | ||
| - **Polling for changes** — Readers must poll to observe updates. Future work may explore change notification mechanisms built on SlateDB. | ||
|
|
||
| _Additional design sections to be completed in a future revision._ | ||
|
|
||
| ## Alternatives | ||
|
|
||
| ### Catalog-Driven Provisioning | ||
|
|
||
| An alternative approach positions the catalog as the source of truth for *target state*, with provisioning systems watching the catalog and responding to changes. A user would register a new slate in the catalog, a provisioning system (e.g., a Kubernetes operator) would observe the registration and create the necessary infrastructure, then update the catalog with provisioning status. | ||
|
|
||
| This approach was rejected because it duplicates functionality that provisioning systems already provide. Systems like Kubernetes have their own declarative model for specifying target state (e.g., Custom Resource Definitions). Adding a catalog-driven provisioning layer would require: | ||
|
|
||
| - The catalog to maintain target vs. current state, adding complexity. | ||
| - Provisioning systems to sync state back to the catalog, creating potential for drift. | ||
| - Users to learn a new provisioning model rather than using native tooling they already know. | ||
|
|
||
| Instead, we explicitly take provisioning out of the catalog's scope. The catalog represents *current state*—what systems actually exist—rather than *target state*. Provisioning remains the responsibility of existing orchestration systems. | ||
|
|
||
| ## Open Questions | ||
|
|
||
| 1. **Catalog bootstrap** — How is the catalog itself discovered? If the catalog is a slate, where is its object storage configuration stored? | ||
|
|
||
| 2. **Bucket metadata** — What specific fields are required for object storage configuration? (bucket name, region, path prefix, credentials reference?) | ||
|
|
||
| 3. **Catalog location** — Should there be one catalog per bucket, per region, or per "environment"? What is the deployment topology? | ||
|
|
||
| ## Updates | ||
|
|
||
| | Date | Description | | ||
| |------------|-------------| | ||
| | 2026-01-21 | Initial draft | | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a review of this lined up but now I'm wondering whether this RFC is over-prescribed to a dogfood philosophy. What if instead we tried to design this as k8s-native?
I posed this question to Claude and here's an alternative we came up with:
The current RFC essentially builds a bespoke control plane on top of SlateDB, but if your primary deployment target is Kubernetes, you'd be reinventing machinery that K8s already provides (watches, reconciliation loops, status subresources, RBAC, etc.).
Instead of the catalog being a SlateDB-backed store that components poll, the Kubernetes API server becomes the catalog. Each slate is represented as a Custom Resource, and an operator reconciles desired state to actual state.
Then using the CRDs we could use:
Tradeoffs
The big benefit, then, of using opendata is that you install just one operator and one common language for CRDs instead of learning a new operator and new CRD for each of the data systems your deploy in your k8s stack.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also think philosophically it's OK to lean into kubernetes + object storage as the two primitives we rely on. The 'pitch' in my mind is that those two solve the hardest distributed systems problems: the former solves elastic compute and the latter solves elastic storage/consistency. Without both opendata's vision can't come to fruition.
Another big win with using
kubectlas the primary control plane CLI is that the AI agents are really good with it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should definitely design to be k8s native, but I don't think a catalog as being discussed here is occupying the same place as k8s. Any system will need some storage to figure out what's deployed, where it's deployed etc. The deployments very often span regions and k8s clusters. The question is: where is that information going to live. We need a catalog for that, which drives the k8s actions in a particular region.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I conflated the two. I believe we should start by figuring out the deployment models in a single k8s cluster and work up from there. The CLI as proposed here has a lot of overlap with the type of things that k8s should handle for me if its all within a single k8s cluster.
I'm not convinced that multi-region/multi-k8s is something we should figure out until we have a solid understanding of the single-k8s, multi-AZ design. A single k8s cluster can span multiple AZs, which is likely where 99% of data systems stop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with the concern. I don't think we want the catalog directly involved in provisioning. At the same time, I'm not too comfortable being super dogmatic about k8s and sticking it at the heart of the system. I took a shot at reframing the catalog in the latest patch. Rather than tracking a target state, the catalog might simply track the current state. It might be aware of active readers/writers in the system. Kubernetes could consult the catalog prior to deprovisioning a resource rather than having the catalog drive deprovisioning itself for example. Not sure if this is enough value to justify the catalog's existence just yet. I suspect we need to let this stew for a while.