Skip to content

feat(cogs): ChangeStream trait, preparation for backend instrumentation - #590

Open
matt-codecov wants to merge 2 commits into
matth/storage-inventory-trackerfrom
matth/storage-inventory-tracker-2
Open

feat(cogs): ChangeStream trait, preparation for backend instrumentation#590
matt-codecov wants to merge 2 commits into
matth/storage-inventory-trackerfrom
matth/storage-inventory-tracker-2

Conversation

@matt-codecov

@matt-codecov matt-codecov commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Depends on #588

ChangeStream trait wraps InventoryTracker to log/count/swallow errors and allow a NoopStream to be plugged in for tests to avoid pulling in Kafka stuff.

@matt-codecov
matt-codecov requested a review from a team as a code owner August 6, 2026 01:55
@codecov

codecov Bot commented Aug 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 73.04965% with 76 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.97%. Comparing base (1cec3c4) to head (a2576e8).

Files with missing lines Patch % Lines
objectstore-service/src/change_stream/factory.rs 12.50% 28 Missing ⚠️
...ectstore-service/src/change_stream/cost_tracker.rs 91.08% 14 Missing ⚠️
bigtable-bench/src/main.rs 0.00% 11 Missing ⚠️
objectstore-service/src/change_stream/mod.rs 23.07% 10 Missing ⚠️
objectstore-service/src/backend/mod.rs 50.00% 9 Missing ⚠️
objectstore-service/src/backend/bigtable.rs 87.50% 2 Missing ⚠️
objectstore-service/src/backend/gcs.rs 87.50% 2 Missing ⚠️
Additional details and impacted files
@@                         Coverage Diff                         @@
##           matth/storage-inventory-tracker     #590      +/-   ##
===================================================================
- Coverage                            88.15%   87.97%   -0.18%     
===================================================================
  Files                                  101      104       +3     
  Lines                                16526    16776     +250     
===================================================================
+ Hits                                 14568    14759     +191     
- Misses                                1958     2017      +59     
Components Coverage Δ
Rust Backend 92.05% <76.01%> (-0.30%) ⬇️
Rust Client 81.97% <ø> (ø)
Python Client 93.31% <ø> (ø)

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread objectstore-inventory-tracker/src/kafka.rs Outdated
Comment thread objectstore-service/src/backend/common.rs Outdated
/// a change stream for our storage backends.
///
/// See [module docs](self).
pub trait InventorySink: fmt::Debug + Send + Sync + 'static {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling out that this interface is close to what is being created in #582. I don't have a concrete design in mind yet, but it seems the "inventory" concept would be capable of solving both problems:

  • Storage cost tracking, which requires to know which files are stored until when
  • GC / eviction, which requires to know which files are stored until when

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes. Agreed with this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apart from that we don't want to couple the two in-flight PRs too much, so this is probably more relevant for the other PR:

The biggest difference is how they are needed: Cost tracking requires the inventory independent of the backend, and will export it via kafka to a remote system. Conversely, the S3 and FS backends need the inventory as part of their inner workings. Per design, GC is part of the backend's responsibility.

We might be able to separate this cleanly if we:

  • Hook the inventory up in the service like the PR here does (sort of like an event bus)
  • Allow multiple inventory exporters
  • One is the kafka sink used for cost tracking
  • Another one is a database sink (like sqlite, PG) for GC, configured/hooked when S3/FS are used
    • GC is still an external command then, that queries said database

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my direction here is very much "fail open" and errors are logged and swallowed. if inventory tracking is load-bearing for GC in some backends, that'll need to change

correct me if i'm wrong: for self-hosted, in order to support the automatic TTI/TTL GC that we advertise, we have to implement it ourselves atop filesystem and S3 as we anticipate those will be popular in self-hosted deployments?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's right, and that's what @aldy505's PR linked in my initial comment does. The trait is called Keeper there and it's intended to be integrated directly into the backends. Conceptually, they hold similar responsibilities.

While for cogs accounting errors are less critical, we should still find an implementation that's correct by design or has well-understood failure modes. Failing loudly/openly does work, but we need to look closely at what happens during concurrent requests.

@matt-codecov
matt-codecov force-pushed the matth/storage-inventory-tracker-2 branch from 94e1e3a to f819003 Compare August 7, 2026 01:45
@matt-codecov matt-codecov changed the title feat(cogs): InventorySink trait, preparation for backend instrumentation feat(cogs): ChangeStream trait, preparation for backend instrumentation Aug 7, 2026
Comment thread objectstore-service/src/backend/change_stream.rs Outdated
@matt-codecov
matt-codecov force-pushed the matth/storage-inventory-tracker-2 branch 2 times, most recently from 4fd0bdc to 560244f Compare August 11, 2026 21:33
Comment thread Dockerfile.cross
Comment thread objectstore-server/src/state.rs

@jan-auer jan-auer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, the documentation and tests made this easy to review.

There is a good foundation for separation of concerns, but the current config mixes them again:

  • ChangeStream tracks logical changes to objects agnostic of how this information is interpreted. This is what backends are instrumented with, and it can be hooked in various ways.
  • InventoryTracker is the component that maps this to cost-tracking semantics.

There are several downsides: ChangeStream's config now needs to know about the possible implementations of inventory tracker, comes with an optional kafka config, there could now be different change streams per backend, it needs to know about shared resource IDs, and about which scopes are important for cost tracking. This makes it harder to use the change stream for GC.

This can be solved:

  • Keep ChangeStream trait tied to objectstore-service concepts like it is now.
  • Instantiate ChangeStream globally as backends are being created, and pass it generically into the backends. This removes the need for an internal factory.
  • Have every backend annotate its stream items with an identifier. Initially that can be hard coded, but that's the one thing we can make overridable in backend config (i.e. gcs1 vs gcs2).
  • The config for which ChangeStream to instantiate should live adjacent to the backends. There's only a single one available now, the inventory tracker, and then we directly surface the inventory tracker's possible implementations there.
  • Inventory tracker and its config is the level at which we
    • map the built-in backend IDs to shared_resource_id (cost concern)
    • extract org ID and project ID from scopes (if available)
    • optionally map use case to app features (currently part of the usecase config)

Comment thread objectstore-service/Cargo.toml Outdated
gcp_auth = { workspace = true }
humantime = { workspace = true }
humantime-serde = { workspace = true }
objectstore-inventory-tracker = { workspace = true, features = ["kafka"] }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make this optional = true and enable it with a dedicated feature flag. That way, we can control for individual builds, whether we have kafka-based inventory tracking included or not. For sandbox cross builds and for self-hosted releases in the future, we'll not include this.

This also means that we do not have to add cmake to the cross-build Dockerfile anymore.

Comment on lines +125 to +126
fn scope_id(id: &ObjectId, scope: &str) -> Option<u64> {
id.scopes().get_value(scope)?.parse().ok()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does usage-accountant require these to be numeric? Especially at this level we should not make assumptions about the type and expose org and project as strings, and let the consumer handle this.

If this is a requirement, let's do the conversion at the innermost level possible (in the kafka implementation, likely).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

usage-accountant doesn't have proj/org IDs

the new kafka topic (which i just merged and released, oops) has them as integers. not too late to change, would just need a new PR and release

/// for the record format.
///
/// Logs, counts, and swallows errors returned by the [`InventoryTracker`].
pub struct KafkaStream {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's rename this type. Technically, this can be any kind of InventoryTracker, it purely depends on which producer is used to instantiate it.

It just so happens that build_producer and thus the change stream factory currently only have a kafka field; see my overall review comment for this.

/// [`ListenerConfig`], so connections and the send queue are not duplicated per backend.
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(default)]
pub struct SinkConfig {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a complete clone of KafkaConfig. We should probably make this an enum that allows for different kinds of InventoryTracker producers, one of which is the kafka variant. That way, the interface/config becomes extensible.

Even better: Don't assume any of that and just pass a complete InventoryTracker into the constructor of this type. That way, the concerns are split entirely.

@matt-codecov
matt-codecov force-pushed the matth/storage-inventory-tracker-2 branch from 560244f to a2576e8 Compare August 15, 2026 03:32

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a2576e8. Configure here.

Comment thread Dockerfile.cross
&& apt-get update -qq \
&& apt-get upgrade -y \
&& apt-get install -y --no-install-recommends make protobuf-compiler libprotobuf-dev pkg-config git libssl-dev:amd64 gcc-x86-64-linux-gnu g++-x86-64-linux-gnu \
&& apt-get install -y --no-install-recommends make protobuf-compiler libprotobuf-dev pkg-config git libssl-dev:amd64 libcurl4-openssl-dev:amd64 gcc-x86-64-linux-gnu g++-x86-64-linux-gnu \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary Kafka build dependencies

Low Severity

libcurl4-openssl-dev was added for librdkafka, but storage-cogs stays off in these builds: the cross image builds objectstore without that feature, release builds only pass profiling, and test-python runs plain cargo build. objectstore-server also does not forward storage-cogs. The optional feature exists specifically so sandbox/cross and default builds avoid Kafka native deps, so these installs do not match that design.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a2576e8. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants