-
-
Notifications
You must be signed in to change notification settings - Fork 5
feat(service): Instrument the remaining backends with the change stream #607
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
Open
matt-codecov
wants to merge
2
commits into
main
Choose a base branch
from
matth/change-stream-backends
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -146,16 +146,11 @@ rate-limiting failures at a higher layer) are not counted. | |
|
|
||
| This is gated behind the `storage_cogs` Cargo feature. | ||
|
|
||
| Each backend reports every write/overwrite, TTI bump, and delete it performs on | ||
| stored objects to a [`ChangeStream`](change_stream::ChangeStream). To | ||
| turn this change stream into COGS data, a stream consumer has to merge each | ||
| change event into an external table to update an inventory of objects. The | ||
| inventory table can be queried to break down each backend's storage utilization | ||
| by `app_feature`. Note that [`NoopStream`](change_stream::NoopStream) is used | ||
| unless the backend's config includes a | ||
| [`CostTrackerStreamConfig`](change_stream::CostTrackerStreamConfig) and the | ||
| service has a usable transport for it, and unless the `storage-cogs` feature is | ||
| compiled in at all. | ||
| Storage attribution is derived from the [change stream](#change-streams) each | ||
| backend publishes. To turn a change stream into COGS data, a stream consumer has | ||
| to merge each change event into an external table to update an inventory of | ||
| objects. The inventory table can be queried to break down each backend's storage | ||
| utilization by `app_feature`. | ||
|
|
||
| Each row in the inventory table has an anonymized hash of an `ObjectId` as well | ||
| as the row's size, expiry, Sentry org/project, `app_feature`, and relevant | ||
|
|
@@ -164,11 +159,10 @@ long-term backend the inventory table will contain _two rows_ for an object: a | |
| row for the actual object and its size in long-term backend, and a separate row | ||
| for the tombstone and the tombstone's size in the high-volume backend. | ||
|
|
||
| `ChangeStream` is not aware of any automatic garbage collection that backends | ||
| may perform. Expired objects must be filtered out when querying the inventory | ||
| table. | ||
| Because the change stream does not observe automatic garbage collection, expired | ||
| objects must be filtered out when querying the inventory table. | ||
|
|
||
| Under the hood, `CostTrackerStream` uses | ||
| Under the hood, [`CostTrackerStream`](change_stream::CostTrackerStream) uses | ||
| [`InventoryTracker`](objectstore_inventory_tracker::InventoryTracker) to publish | ||
| change events; it is generic over the transport rather than tied to Kafka. Each | ||
| backend has its own sampling rate to lessen the load put on the stream | ||
|
|
@@ -179,6 +173,48 @@ sampling rate. When aggregating, divide each row's value by its `sample_rate`. | |
|
|
||
| See also: [`objectstore_inventory_tracker`] documentation. | ||
|
|
||
| # Change Streams | ||
|
|
||
| Every backend publishes the changes it makes to the objects it stores as a | ||
| [`ChangeStream`](change_stream::ChangeStream). It is a fire-and-forget, | ||
| per-backend feed of three operations: | ||
|
|
||
| - `write(id, size, expires_at)`: `id` now occupies `size` bytes. Used for both | ||
| new objects and overwrites. | ||
| - `update(id, expires_at)`: `id`'s expiration moved while its stored size is | ||
| unchanged. In practice this is a TTI bump. | ||
| - `delete(id)`: `id` was deleted explicitly. | ||
|
|
||
| The stream describes physical storage per backend. When using | ||
| [`TieredStorage`](backend::tiered::TieredStorage), objects that are stored in | ||
| long-term storage will emit a change record for the actual object in long-term | ||
| storage as well as for the tombstone record in high-volume storage. | ||
|
|
||
| `size` is a count of bytes that the backend actually stores for an object. This | ||
| includes object payloads, metadata, and sometimes backend-specific overhead. | ||
|
|
||
| Decorators such as [`CountingBackend`](backend::counting::CountingBackend) and | ||
| [`TieredStorage`](backend::tiered::TieredStorage) don't publish change streams | ||
| of their own; only leaf backends that actually own bytes do. | ||
|
|
||
| Automatic garbage collection is invisible to the change stream. Downstream | ||
| consumers of the stream need to consider the `expires_at` field on messages. | ||
|
|
||
| ## `ChangeStream` implementation guidance | ||
|
|
||
| While the [`ChangeStream`](change_stream::ChangeStream) trait is abstract, that | ||
| abstraction is not surfaced in service configuration. For instance, the | ||
| [storage COGS change stream](#storage-cogs) is configured with a service-wide | ||
| [`CostTrackerConfig`](change_stream::CostTrackerConfig) and per-backend | ||
| [`CostTrackerStreamConfig`](change_stream::CostTrackerStreamConfig)s. These | ||
| configurations are connected in [`ChangeStreamFactory`](change_stream::ChangeStreamFactory) | ||
| to build a [`CostTrackerStream`](change_stream::CostTrackerStream). | ||
|
|
||
| New `ChangeStream` implementations may follow the same pattern: | ||
| - per-backend configuration for per-backend IDs or configuration | ||
| - service-wide configuration for a stream sink | ||
| - glue code in and around `ChangeStreamFactory` | ||
|
Collaborator
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. Not sure what you meant by this
Member
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. If you want to add a new
|
||
|
|
||
| # Metadata and Payload | ||
|
|
||
| Every object consists of structured **metadata** and a binary **payload**. | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Nit: should we say that
sizeshould be an estimate or proportional to the bytes that you're storing/billed for in/by the backend, rather than literally the size?Why: looking at S3 docs it seems that the storage bytes you're charged for has some nuances that depend on things such as the storage tier: https://aws.amazon.com/s3/pricing/#:~:text=*%20S3%20Intelligent%2DTiering,pricing%20page.