Skip to content

Trigger localization of clean room objects - #2826

Closed
francoisferrand wants to merge 2 commits into
development/9.6from
francoisferrand-improvement/BB-814
Closed

Trigger localization of clean room objects#2826
francoisferrand wants to merge 2 commits into
development/9.6from
francoisferrand-improvement/BB-814

Conversation

@francoisferrand

Copy link
Copy Markdown
Contributor

In a clean room, objects are created locally but their metadata still points at the source cluster's location: the data itself has not been copied over yet. Something has to notice those objects and ask for the data to be pulled in.

The queue populator is the natural place for that, since bootstrap, re-bootstrap and streamed updates all go through the same oplog, so a single code path covers all three. When an object lands on a location flagged isCRR, we publish a copyLocation action on the data mover topic and let the existing data mover + transition merge pipeline do the actual copy.

The check sits before any replication condition, on purpose: replicationInfo describes replication of a local object to remote sites, and the clean room copy resets it since the copy has not been replicated anywhere. A clean room site may also legitimately have its own forward replication rules. Localization is not tracked there either, updating location/dataStoreName is enough.

Activation is simply the presence of extensions.replication.localization.toLocation in the config (EXTENSIONS_REPLICATION_LOCALIZATION_TO_LOCATION). Without it the branch is inert and nothing changes, which is why it is deliberately absent from conf/config.json.

Account id resolution

The populator only knows the object owner's canonical id, and a Vault lookup per entry would throttle the whole populator. So the action carries target.owner only, and the lookup is deferred to the transition processor, which resolves it once up front and stamps it back onto the action, the same way the garbage collector already does. Lifecycle-originated actions always carry an account id, so the lookup never runs for them, and the GC entry emitted after the merge gets the resolved id rather than resolving it again.

Metrics

Two new counters, s3_backbeat_populator_localization_objects_total and ..._bytes_total, kept separate from the CRR site metrics so localization does not get conflated with forward replication.

Skipped entries

Master keys (clean room buckets are versioned, the master is repaired by the metadata layer), delete markers, entries without a dataStoreName (partial oplog projections), and objects with no location, since a 0-byte object has nothing to localize. isCRR with a non-zero content length and no location is inconsistent metadata and gets logged as an error.

Duplicates are expected and harmless: the topic is keyed per object, the data mover dedupes in memory, CopyLocationTask skips already-transitioned versions, and the merge task skips duplicate location updates.

Not in this PR

  • BB-816: forward replication should skip non-localized objects
  • BB-817: change stream projection / bucket selection
  • BB-819: suppressing duplicates via transitionInProgress
  • BB-813 must land before this is enabled in a real clean room: after the merge, _garbageCollectLocation publishes a deleteData for the old location, which against an isCRR source would delete source data.

Note that the full unit suite could not be run locally (needs Kafka/Redis); lint and the replication, gc, queuePopulator, lib and lifecycle task specs are green.

Issue: BB-814

In a clean room, objects are created locally but their metadata still
points at the source cluster's location: the data itself has not been
copied over yet. Something has to notice those objects and ask for the
data to be pulled in.

The queue populator is the natural place for it, since bootstrap,
re-bootstrap and streamed updates all go through the same oplog. When an
object lands on a location flagged isCRR, publish a copyLocation action
on the data mover topic and let the existing data mover + transition
merge pipeline do the actual copy.

This is unrelated to replicationInfo, which describes replication of a
*local* object to remote sites, so the check sits before any replication
condition. Activation is simply the presence of
extensions.replication.localization.toLocation in the config; without it
nothing changes.

The populator only knows the object owner's canonical id, and resolving
an account id per entry would throttle the whole populator, so the
lookup is deferred to the transition processor, which now resolves it
once up front and stamps it back on the action - the same way the
garbage collector already does.

Issue: BB-814
@codecov

codecov Bot commented Aug 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 13.09524% with 73 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.37%. Comparing base (929145d) to head (7139bf2).

Files with missing lines Patch % Lines
...xtensions/replication/ReplicationQueuePopulator.js 21.62% 29 Missing ⚠️
...ecycle/objectProcessor/LifecycleObjectProcessor.js 0.00% 27 Missing ⚠️
...s/lifecycle/tasks/LifecycleUpdateTransitionTask.js 0.00% 16 Missing ⚠️
lib/Config.js 50.00% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

Files with missing lines Coverage Δ
extensions/lifecycle/LifecycleConfigValidator.js 100.00% <ø> (ø)
...tensions/replication/ReplicationConfigValidator.js 100.00% <ø> (ø)
lib/queuePopulator/QueuePopulator.js 82.89% <100.00%> (+0.15%) ⬆️
lib/Config.js 75.73% <50.00%> (-0.39%) ⬇️
...s/lifecycle/tasks/LifecycleUpdateTransitionTask.js 80.53% <0.00%> (-13.47%) ⬇️
...ecycle/objectProcessor/LifecycleObjectProcessor.js 55.00% <0.00%> (-26.49%) ⬇️
...xtensions/replication/ReplicationQueuePopulator.js 65.62% <21.62%> (-26.32%) ⬇️

... and 2 files with indirect coverage changes

Components Coverage Δ
Bucket Notification 80.27% <ø> (ø)
Core Library 81.90% <75.00%> (-0.06%) ⬇️
Ingestion 70.09% <ø> (ø)
Lifecycle 79.36% <0.00%> (-1.10%) ⬇️
Oplog Populator 85.83% <ø> (ø)
Replication 61.46% <21.62%> (-0.55%) ⬇️
Bucket Scanner 85.76% <ø> (ø)
@@                 Coverage Diff                 @@
##           development/9.6    #2826      +/-   ##
===================================================
- Coverage            75.77%   75.37%   -0.40%     
===================================================
  Files                  200      200              
  Lines                13922    13999      +77     
===================================================
+ Hits                 10549    10552       +3     
- Misses                3363     3437      +74     
  Partials                10       10              
Flag Coverage Δ
api:retry 9.06% <1.19%> (-0.05%) ⬇️
api:routes 8.83% <1.19%> (-0.05%) ⬇️
bucket-scanner 85.76% <ø> (ø)
ft_test:queuepopulator 11.15% <13.09%> (+0.12%) ⬆️
ingestion 12.65% <7.14%> (+0.39%) ⬆️
notification 1.01% <0.00%> (-0.01%) ⬇️
oplogPopulator 0.13% <0.00%> (-0.01%) ⬇️
replication 18.75% <1.19%> (-0.11%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread extensions/lifecycle/objectProcessor/LifecycleObjectProcessor.js Outdated
AccountIdCache.isKnown() is also true for cached misses, where get()
returns undefined. The localization flow would then carry on with
target.accountId unset and surface a confusing "failed to get backbeat
client" InternalError instead of the NoSuchEntity that actually caused
it, making a bad canonical id hard to diagnose in production.

Treat a cached miss as the error it is, and only stamp the resolved
account id onto the entry when there is one.

Issue: BB-814
Comment thread extensions/lifecycle/objectProcessor/LifecycleObjectProcessor.js
@francoisferrand
francoisferrand deleted the francoisferrand-improvement/BB-814 branch August 24, 2026 08:14
@francoisferrand

Copy link
Copy Markdown
Contributor Author

Superseded by #2829 (branch renamed to improvement/BB-814).

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.

1 participant