Support per-backend role and destination in replication engine#2743
Support per-backend role and destination in replication engine#2743maeldonn wants to merge 3 commits into
Conversation
Hello maeldonn,My role is to assist you with the merge of this Available options
Available commands
Status report is not available. |
Codecov Report❌ Patch coverage is
Additional details and impacted files
... and 6 files with indirect coverage changes
@@ Coverage Diff @@
## development/9.4 #2743 +/- ##
===================================================
+ Coverage 74.84% 74.95% +0.10%
===================================================
Files 199 199
Lines 13657 13667 +10
===================================================
+ Hits 10222 10244 +22
+ Misses 3425 3413 -12
Partials 10 10
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
|
36b459a to
98c878f
Compare
|
|
d5a834d to
83c75ef
Compare
|
|
3426739 to
4791b8b
Compare
|
4791b8b to
14ef8f1
Compare
|
PR Review Summary
Review by Claude Code |
14ef8f1 to
cfdd8fb
Compare
The rest of the PR looks solid. The per-backend key abstraction (site + destination + role) is well-designed, backward compatibility is properly handled via top-level fallback in the site-aware getters, and the async/await migration in QueueProcessor.processReplicationEntry is clean. Test coverage is thorough, including same-site multi-destination disambiguation, retry round-tripping, and mixed CRR + cloud backend scenarios. Review by Claude Code |
cfdd8fb to
34fba43
Compare
| "@smithy/node-http-handler": "^3.3.3", | ||
| "JSONStream": "^1.3.5", | ||
| "arsenal": "git+https://github.com/scality/arsenal#8.3.9", | ||
| "arsenal": "git+https://github.com/scality/arsenal#e4860ebc364db6c3cd5b3f3210339d0fa512ed24", |
There was a problem hiding this comment.
Arsenal is pinned to a commit hash instead of a tag. The yarn.lock shows this resolves to version 8.4.3 — pin to the tag instead for consistency with the project's dependency pinning convention.
— Claude Code
|
Read destination bucket and role from each backends[] entry instead of the shared top-level fields, so a single source object can be replicated to multiple CRR destinations with their own role. Legacy entries without per-backend fields keep working via top-level fallback in ObjectQueueEntry's site-aware getters; MongoQueueProcessor's oplog path now matches every applicable rule and dedups backends per the design's (site, destination, role) rule. Issue: BB-762
34fba43 to
7a21bbc
Compare
7a21bbc to
91a58ad
Compare
| "@smithy/node-http-handler": "^3.3.3", | ||
| "JSONStream": "^1.3.5", | ||
| "arsenal": "git+https://github.com/scality/arsenal#8.3.9", | ||
| "arsenal": "git+https://github.com/scality/arsenal#e4860ebc364db6c3cd5b3f3210339d0fa512ed24", |
There was a problem hiding this comment.
arsenal is pinned to a commit hash instead of a tag. The resolved version is 8.4.3 — if that tag exists, pin to it for consistency with the other git-based deps (breakbeat, httpagent, etc.).
— Claude Code
The code changes themselves are solid. The per-backend routing in QueueProcessor, the backendKey-based disambiguation in ObjectQueueEntry, the role-validation expansion in ReplicateObject._setupRolesOnce, and the callbackify bridge for the async migration are all well-structured. Legacy backward compatibility (top-level fallback when backends lack destination/role) is covered by tests. The Kafka message format extension (adding destination/role fields) is additive and safe for rolling upgrades. Review by Claude Code |
| const replicationEnabled = data.ReplicationConfiguration.Rules | ||
| .some(rule => rule.Status === 'Enabled' && | ||
| entry.getObjectKey().startsWith(rule.Prefix)); | ||
| entry.getObjectKey().startsWith( |
There was a problem hiding this comment.
MultipleBackendTask._setupRolesOnce still calls entry.getReplicationRoles() without passing the backend key, while the parent ReplicateObject._setupRolesOnce was updated to entry.getReplicationRoles(entry.getBackendKey()). If arsenal's getReplicationRoles(key) resolves a per-backend role when a key is provided, this cloud-backend path will silently use the top-level (legacy) role string instead of the per-backend one, defeating the purpose of this PR for cloud destinations.
— Claude Code
| Bucket: sourceEntry.getBucket(), | ||
| Key: sourceEntry.getObjectKey(), | ||
| StorageType: sourceEntry.getReplicationStorageType(), | ||
| StorageType: this._getReplicationEndpointType(), |
There was a problem hiding this comment.
Several calls in MultipleBackendTask.processQueueEntry still use this.site (a plain string) for per-backend lookups instead of the key object:\n- Line 1220: refreshedEntry.getReplicationSiteStatus(this.site) should be refreshedEntry.getReplicationSiteStatus(sourceEntry.getBackendKey())\n- Line 1240: sourceEntry.getReplicationSiteDataStoreVersionId(this.site) should also use the backend key\n\nThese were not updated to match the pattern applied in ReplicateObject and UpdateReplicationStatus. If arsenal's methods now support the key object, this will miss per-backend state.
— Claude Code
| status: 'PENDING', | ||
| backends, | ||
| content, | ||
| role: ReplicationConfiguration.resolveSourceRole(bucketRepInfo.role), |
There was a problem hiding this comment.
The _updateReplicationInfo call on line 349 sets role: ReplicationConfiguration.resolveSourceRole(bucketRepInfo.role) but doesn't set destination or storageClass in the replication info object. The old code set both destination and storageClass. If these fields are now handled inside ReplicationConfiguration.resolveBackends at the backend level, this is fine — but please confirm the downstream consumers (queue populator, status processor) don't rely on the top-level destination / storageClass fields being present.
— Claude Code
| err => err ? reject(err) : resolve(), | ||
| ); | ||
| }); | ||
| } |
There was a problem hiding this comment.
The sequential for...of loop processes backends one at a time. If one backend's taskScheduler.push rejects, the await will throw and subsequent backends will be skipped entirely (the error propagates to callbackify which calls done(err)). This means a transient failure replicating to destination A will prevent replication to destinations B and C for this Kafka entry. Consider whether each backend should be attempted independently (catching errors per-backend) so one failure doesn't block the others.
— Claude Code
| toRetryEntry(key) { | ||
| const backend = this._findBackend(key); | ||
| return this.clone() | ||
| .setAccountId(this.getAccountId()) |
There was a problem hiding this comment.
The toRetryEntry method now uses this._findBackend(key) but the method _findBackend is not defined on ObjectQueueEntry — it must be on the parent ObjectMD in arsenal. Please confirm this method exists in the arsenal commit being pinned (c3f3bff), since it's not possible to verify from this repo alone.
— Claude Code
|
Read destination bucket and role from each backends[] entry instead of the shared top-level fields, so a single source object can be replicated to multiple CRR destinations with their own role. Legacy entries without per-backend fields keep working via top-level fallback in ObjectQueueEntry's site-aware getters; MongoQueueProcessor's oplog path now matches every applicable rule and dedups backends per the design's (site, destination, role) rule.
Issue: BB-762