-
Notifications
You must be signed in to change notification settings - Fork 256
Add CRR Cascaded capabilities #6179
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: development/9.4
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 | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,3 +1,4 @@ | ||||||||||||||||||||||||
| const { constants: { HTTP_STATUS_CONFLICT } } = require('http2'); | ||||||||||||||||||||||||
| const url = require('url'); | ||||||||||||||||||||||||
| const async = require('async'); | ||||||||||||||||||||||||
| const httpProxy = require('http-proxy'); | ||||||||||||||||||||||||
|
|
@@ -7,7 +8,13 @@ const joi = require('@hapi/joi'); | |||||||||||||||||||||||
| const backbeatProxy = httpProxy.createProxyServer({ | ||||||||||||||||||||||||
| ignorePath: true, | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| const { auth, errors, errorInstances, s3middleware, s3routes, models, storage } = require('arsenal'); | ||||||||||||||||||||||||
| const { auth, errors, errorInstances, s3middleware, s3routes, models, storage, versioning } = require('arsenal'); | ||||||||||||||||||||||||
| const { decode, encode } = versioning.VersionID; | ||||||||||||||||||||||||
| const { | ||||||||||||||||||||||||
| VersionIdCollisionException, | ||||||||||||||||||||||||
| StaleMicroVersionIdException, | ||||||||||||||||||||||||
| MicroVersionIdAlreadyStoredException, | ||||||||||||||||||||||||
| } = require('@scality/cloudserverclient'); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| const { responseJSONBody } = s3routes.routesUtils; | ||||||||||||||||||||||||
| const { getSubPartIds } = s3middleware.azureHelper.mpuUtils; | ||||||||||||||||||||||||
|
|
@@ -21,6 +28,7 @@ const locationStorageCheck = require('../api/apiUtils/object/locationStorageChec | |||||||||||||||||||||||
| const { dataStore } = require('../api/apiUtils/object/storeObject'); | ||||||||||||||||||||||||
| const prepareRequestContexts = require('../api/apiUtils/authorization/prepareRequestContexts'); | ||||||||||||||||||||||||
| const { decodeVersionId } = require('../api/apiUtils/object/versioning'); | ||||||||||||||||||||||||
| const getReplicationInfo = require('../api/apiUtils/object/getReplicationInfo'); | ||||||||||||||||||||||||
| const locationKeysHaveChanged = require('../api/apiUtils/object/locationKeysHaveChanged'); | ||||||||||||||||||||||||
| const { standardMetadataValidateBucketAndObj, metadataGetObject } = require('../metadata/metadataUtils'); | ||||||||||||||||||||||||
| const { config } = require('../Config'); | ||||||||||||||||||||||||
|
|
@@ -32,6 +40,7 @@ const { | |||||||||||||||||||||||
| } = require('../api/apiUtils/integrity/validateChecksums'); | ||||||||||||||||||||||||
| const { BackendInfo } = models; | ||||||||||||||||||||||||
| const { pushReplicationMetric } = require('./utilities/pushReplicationMetric'); | ||||||||||||||||||||||||
| const writeContinue = require('../utilities/writeContinue'); | ||||||||||||||||||||||||
| const kms = require('../kms/wrapper'); | ||||||||||||||||||||||||
| const { listLifecycleCurrents } = require('../api/backbeat/listLifecycleCurrents'); | ||||||||||||||||||||||||
| const { listLifecycleNonCurrents } = require('../api/backbeat/listLifecycleNonCurrents'); | ||||||||||||||||||||||||
|
|
@@ -93,7 +102,7 @@ function _isObjectRequest(req) { | |||||||||||||||||||||||
| return ['data', 'metadata', 'multiplebackenddata', 'multiplebackendmetadata'].includes(req.resourceType); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| function _respondWithHeaders(response, payload, extraHeaders, log, callback) { | ||||||||||||||||||||||||
| function _respondWithHeaders(response, payload, extraHeaders, log, callback, statusCode = 200) { | ||||||||||||||||||||||||
| let body = ''; | ||||||||||||||||||||||||
| if (typeof payload === 'string') { | ||||||||||||||||||||||||
| body = payload; | ||||||||||||||||||||||||
|
|
@@ -115,10 +124,10 @@ function _respondWithHeaders(response, payload, extraHeaders, log, callback) { | |||||||||||||||||||||||
| // eslint-disable-next-line no-param-reassign | ||||||||||||||||||||||||
| response.serverAccessLog.endTurnAroundTime = process.hrtime.bigint(); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| response.writeHead(200, httpHeaders); | ||||||||||||||||||||||||
| response.writeHead(statusCode, httpHeaders); | ||||||||||||||||||||||||
| response.end(body, 'utf8', () => { | ||||||||||||||||||||||||
| log.end().info('responded with payload', { | ||||||||||||||||||||||||
| httpCode: 200, | ||||||||||||||||||||||||
| httpCode: statusCode, | ||||||||||||||||||||||||
| contentLength: Buffer.byteLength(body), | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| callback(); | ||||||||||||||||||||||||
|
|
@@ -129,6 +138,15 @@ function _respond(response, payload, log, callback) { | |||||||||||||||||||||||
| _respondWithHeaders(response, payload, {}, log, callback); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| function _respondWithHeaderCrrConflict(response, log, callback, code, message, mvId) { | ||||||||||||||||||||||||
| return _respondWithHeaders( | ||||||||||||||||||||||||
| response, | ||||||||||||||||||||||||
| { code, message }, | ||||||||||||||||||||||||
| { 'x-scal-micro-version-id': mvId ? encode(mvId) : '' }, | ||||||||||||||||||||||||
| log, callback, HTTP_STATUS_CONFLICT, | ||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| function _getRequestPayload(req, cb) { | ||||||||||||||||||||||||
| const payload = []; | ||||||||||||||||||||||||
| let payloadLen = 0; | ||||||||||||||||||||||||
|
|
@@ -414,6 +432,38 @@ function putData(request, response, bucketInfo, objMd, log, callback) { | |||||||||||||||||||||||
| log.error(errMessage); | ||||||||||||||||||||||||
| return callback(errorInstances.BadRequest.customizeDescription(errMessage)); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| const incomingVersionIdEncoded = request.headers['x-scal-version-id']; | ||||||||||||||||||||||||
| if (incomingVersionIdEncoded) { | ||||||||||||||||||||||||
| const incomingVersionIdDecoded = decode(incomingVersionIdEncoded); | ||||||||||||||||||||||||
| if (incomingVersionIdDecoded instanceof Error) { | ||||||||||||||||||||||||
| log.error('crr putData: failed to decode x-scal-version-id header', { | ||||||||||||||||||||||||
| method: 'putData', | ||||||||||||||||||||||||
| error: incomingVersionIdDecoded.message, | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| return callback(errorInstances.BadRequest.customizeDescription( | ||||||||||||||||||||||||
| 'bad request: invalid x-scal-version-id header')); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| if (objMd && objMd.versionId === incomingVersionIdDecoded) { | ||||||||||||||||||||||||
| // Data already at destination for this version; return 409 with the existing | ||||||||||||||||||||||||
| // microVersionId so backbeat can decide if putMetadata is still needed. | ||||||||||||||||||||||||
| log.debug('crr putData: version already at destination', { | ||||||||||||||||||||||||
| method: 'putData', | ||||||||||||||||||||||||
| bucketName: request.bucketName, | ||||||||||||||||||||||||
| objectKey: request.objectKey, | ||||||||||||||||||||||||
| hasMicroVersionId: !!objMd.microVersionId, | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| request.resume(); | ||||||||||||||||||||||||
| return _respondWithHeaderCrrConflict( | ||||||||||||||||||||||||
| response, log, callback, | ||||||||||||||||||||||||
| VersionIdCollisionException.name, | ||||||||||||||||||||||||
| 'version id already at destination', | ||||||||||||||||||||||||
| objMd.microVersionId, | ||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| writeContinue(request, response); | ||||||||||||||||||||||||
| const context = { | ||||||||||||||||||||||||
| bucketName: request.bucketName, | ||||||||||||||||||||||||
| owner: canonicalID, | ||||||||||||||||||||||||
|
|
@@ -539,6 +589,65 @@ function getCanonicalIdsByAccountId(accountId, log, cb) { | |||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| function putMetadata(request, response, bucketInfo, objMd, log, callback) { | ||||||||||||||||||||||||
| const { bucketName, objectKey } = request; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| const encodedMicroVersionId = request.headers['x-scal-micro-version-id']; | ||||||||||||||||||||||||
|
francoisferrand marked this conversation as resolved.
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. when the header is set, maybe we should have a sanity check (later) to verify the metadata contains the same |
||||||||||||||||||||||||
| const isCascadeWrite = encodedMicroVersionId !== undefined; | ||||||||||||||||||||||||
| let incomingMicroVersionId = null; | ||||||||||||||||||||||||
| if (isCascadeWrite) { | ||||||||||||||||||||||||
| // '' means source has no microVersionId, treated as older revision | ||||||||||||||||||||||||
| incomingMicroVersionId = encodedMicroVersionId === '' ? null : decode(encodedMicroVersionId); | ||||||||||||||||||||||||
|
Comment on lines
+595
to
+599
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. cloudserver DOES NOT know this is replication: this route is The semantics here is not that of replication, it is a generic conditional-write : "when
Suggested change
|
||||||||||||||||||||||||
| if (incomingMicroVersionId instanceof Error) { | ||||||||||||||||||||||||
| log.error('crr putMetadata: failed to decode x-scal-micro-version-id header', { | ||||||||||||||||||||||||
| method: 'putMetadata', | ||||||||||||||||||||||||
| error: incomingMicroVersionId.message, | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| return callback(errorInstances.BadRequest.customizeDescription( | ||||||||||||||||||||||||
| 'bad request: invalid x-scal-micro-version-id header')); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| // Loop/stale detection only applies when the object already exists at destination | ||||||||||||||||||||||||
|
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. same: this is not loop/stale detection, but really loop/stale is the feature of replication we build on top - in backbeat only, where the replication lives. |
||||||||||||||||||||||||
| if (objMd) { | ||||||||||||||||||||||||
|
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. do we need to validate microVersionId if We could keep things simpler with |
||||||||||||||||||||||||
| // null = oldest revision (original putObject, before any metadata update). | ||||||||||||||||||||||||
| // VersionID strings are in reverse chronological order: a lexicographically | ||||||||||||||||||||||||
| // larger string is an older revision. | ||||||||||||||||||||||||
| const isSourceOlderThanDestination = (sourceMicroVersionId, destinationMicroVersionId) => | ||||||||||||||||||||||||
| destinationMicroVersionId !== null && | ||||||||||||||||||||||||
| (sourceMicroVersionId === null || sourceMicroVersionId > destinationMicroVersionId); | ||||||||||||||||||||||||
|
Comment on lines
+613
to
+615
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. is this not handled already in arsenal's |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // Treat '' and undefined as null (no microVersionId set). | ||||||||||||||||||||||||
| const sourceMicroVersionId = incomingMicroVersionId || null; | ||||||||||||||||||||||||
|
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.
|
||||||||||||||||||||||||
| const destinationMicroVersionId = objMd.microVersionId || null; | ||||||||||||||||||||||||
|
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. source and destination are a bit replication-specific : just |
||||||||||||||||||||||||
| if (sourceMicroVersionId === destinationMicroVersionId) { | ||||||||||||||||||||||||
| log.debug('crr cascade putMetadata: loop detected, skipping write', { | ||||||||||||||||||||||||
| method: 'putMetadata', | ||||||||||||||||||||||||
| bucketName, | ||||||||||||||||||||||||
| objectKey, | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| request.resume(); | ||||||||||||||||||||||||
| return _respondWithHeaderCrrConflict( | ||||||||||||||||||||||||
| response, log, callback, | ||||||||||||||||||||||||
| MicroVersionIdAlreadyStoredException.name, | ||||||||||||||||||||||||
| 'incoming microVersionId already at destination', | ||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| if (isSourceOlderThanDestination(sourceMicroVersionId, destinationMicroVersionId)) { | ||||||||||||||||||||||||
|
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 am afraid this may not be enough : we are basically
This is not atomic, so 2 such calls could race each-other, both pass the check 2 and write the metadata... To make it safe, we need to make a conditional write in mongodb somehow: i.e. let mongo do the |
||||||||||||||||||||||||
| log.debug('crr cascade putMetadata: stale event, rejecting', { | ||||||||||||||||||||||||
| method: 'putMetadata', | ||||||||||||||||||||||||
| bucketName, | ||||||||||||||||||||||||
| objectKey, | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| request.resume(); | ||||||||||||||||||||||||
| return _respondWithHeaderCrrConflict( | ||||||||||||||||||||||||
| response, log, callback, | ||||||||||||||||||||||||
| StaleMicroVersionIdException.name, | ||||||||||||||||||||||||
| 'incoming revision is older than destination', | ||||||||||||||||||||||||
| objMd?.microVersionId, | ||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
SylvainSenechal marked this conversation as resolved.
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
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.
Suggested change
|
||||||||||||||||||||||||
| return _getRequestPayload(request, (err, payload) => { | ||||||||||||||||||||||||
| if (err) { | ||||||||||||||||||||||||
| return callback(err); | ||||||||||||||||||||||||
|
|
@@ -552,15 +661,15 @@ function putMetadata(request, response, bucketInfo, objMd, log, callback) { | |||||||||||||||||||||||
| return callback(errors.MalformedPOSTRequest); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| const { headers, bucketName, objectKey } = request; | ||||||||||||||||||||||||
| const { headers } = request; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // Destination-side delete-marker replication. | ||||||||||||||||||||||||
| // We need the REPLICA status to distinguish from | ||||||||||||||||||||||||
| // source-side replication status updates that also carry isDeleteMarker=true. | ||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||
| omVal.isDeleteMarker && | ||||||||||||||||||||||||
| omVal.replicationInfo && | ||||||||||||||||||||||||
| omVal.replicationInfo.status === 'REPLICA' && | ||||||||||||||||||||||||
| (omVal.replicationInfo.isReplica === true || omVal.replicationInfo.status === 'REPLICA') && | ||||||||||||||||||||||||
| request.serverAccessLog | ||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||
| // eslint-disable-next-line no-param-reassign | ||||||||||||||||||||||||
|
|
@@ -576,7 +685,7 @@ function putMetadata(request, response, bucketInfo, objMd, log, callback) { | |||||||||||||||||||||||
| // The REPLICA status excludes source-side replication-status updates. | ||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||
| omVal.replicationInfo && | ||||||||||||||||||||||||
| omVal.replicationInfo.status === 'REPLICA' && | ||||||||||||||||||||||||
| (omVal.replicationInfo.isReplica === true || omVal.replicationInfo.status === 'REPLICA') && | ||||||||||||||||||||||||
| (omVal.originOp === 's3:ObjectTagging:Put' || omVal.originOp === 's3:ObjectTagging:Delete') && | ||||||||||||||||||||||||
| request.serverAccessLog | ||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||
|
|
@@ -593,7 +702,7 @@ function putMetadata(request, response, bucketInfo, objMd, log, callback) { | |||||||||||||||||||||||
| // The REPLICA status excludes source-side replication-status updates. | ||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||
| omVal.replicationInfo && | ||||||||||||||||||||||||
| omVal.replicationInfo.status === 'REPLICA' && | ||||||||||||||||||||||||
| (omVal.replicationInfo.isReplica === true || omVal.replicationInfo.status === 'REPLICA') && | ||||||||||||||||||||||||
| omVal.originOp === 's3:ObjectAcl:Put' && | ||||||||||||||||||||||||
| request.serverAccessLog | ||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||
|
|
@@ -607,17 +716,23 @@ function putMetadata(request, response, bucketInfo, objMd, log, callback) { | |||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if (headers['x-scal-replication-content'] === 'METADATA') { | ||||||||||||||||||||||||
| if (!objMd) { | ||||||||||||||||||||||||
| return callback(errors.ObjNotFound); | ||||||||||||||||||||||||
| // For metadata-only writes, the destination's data location must be | ||||||||||||||||||||||||
| // preserved from the existing object. If there is no existing object | ||||||||||||||||||||||||
| // and the source has data, there is nothing to copy the location from. | ||||||||||||||||||||||||
| // Zero-byte objects have no data location, so this is safe to skip. | ||||||||||||||||||||||||
|
Comment on lines
+719
to
+722
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. it is safe to skip, but also safe to keep as well : and the previous code was much simpler. If there is no need change it, best not to change it. Especially semantically, → keep the previous chunk, this should not be changed |
||||||||||||||||||||||||
| if (omVal['content-length'] > 0) { | ||||||||||||||||||||||||
| return callback(errors.ObjNotFound); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||
| [ | ||||||||||||||||||||||||
| 'location', | ||||||||||||||||||||||||
| 'x-amz-server-side-encryption', | ||||||||||||||||||||||||
| 'x-amz-server-side-encryption-aws-kms-key-id', | ||||||||||||||||||||||||
| 'x-amz-server-side-encryption-customer-algorithm', | ||||||||||||||||||||||||
| ].forEach(headerName => { | ||||||||||||||||||||||||
| omVal[headerName] = objMd[headerName]; | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| [ | ||||||||||||||||||||||||
| 'location', | ||||||||||||||||||||||||
| 'x-amz-server-side-encryption', | ||||||||||||||||||||||||
| 'x-amz-server-side-encryption-aws-kms-key-id', | ||||||||||||||||||||||||
| 'x-amz-server-side-encryption-customer-algorithm', | ||||||||||||||||||||||||
| ].forEach(headerName => { | ||||||||||||||||||||||||
| omVal[headerName] = objMd[headerName]; | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| let versionId = decodeVersionId(request.query); | ||||||||||||||||||||||||
|
|
@@ -672,7 +787,8 @@ function putMetadata(request, response, bucketInfo, objMd, log, callback) { | |||||||||||||||||||||||
| // then we want to create a version for the replica object even though | ||||||||||||||||||||||||
| // none was provided in the object metadata value. | ||||||||||||||||||||||||
| if (omVal.replicationInfo.isNFS) { | ||||||||||||||||||||||||
| const { isReplica } = omVal.replicationInfo; | ||||||||||||||||||||||||
| const isReplica = omVal.replicationInfo.isReplica === true | ||||||||||||||||||||||||
| || omVal.replicationInfo.status === 'REPLICA'; | ||||||||||||||||||||||||
| versioning = isReplica; | ||||||||||||||||||||||||
| omVal.replicationInfo.isNFS = !isReplica; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
@@ -724,6 +840,48 @@ function putMetadata(request, response, bucketInfo, objMd, log, callback) { | |||||||||||||||||||||||
| options.isNull = isNull; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // Cascade triggering | ||||||||||||||||||||||||
| // If the bucket receiving this replica has its own CRR rules, set | ||||||||||||||||||||||||
| // status to PENDING so the queue populator here picks it up for the | ||||||||||||||||||||||||
| // next hop. If not, clear the source-side replicationInfo fields | ||||||||||||||||||||||||
| // Always mark isReplica=true. | ||||||||||||||||||||||||
| if (isCascadeWrite) { | ||||||||||||||||||||||||
|
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.
so this is not the right condition: microVersionId is about making the call conditioned, while this block is really about "begin called as part of replication"... → For the first point, checking
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. if
This could be caused by 2 things:
Suggested change
→ in any case |
||||||||||||||||||||||||
| const isMDOnly = headers['x-scal-replication-content'] === 'METADATA'; | ||||||||||||||||||||||||
| const objSize = omVal['content-length'] || 0; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // These S3-compatible Scality locations are excluded | ||||||||||||||||||||||||
| // as cascade targets because they use the MultiBackend S3 path which | ||||||||||||||||||||||||
| // bypasses the putData/putMetadata routes, so loop detection cannot fire | ||||||||||||||||||||||||
| // on those destinations. | ||||||||||||||||||||||||
|
francoisferrand marked this conversation as resolved.
|
||||||||||||||||||||||||
| const BLOCKED_LOCATION_TYPES = ['location-scality-ring-s3-v1', 'location-scality-artesca-s3-v1']; | ||||||||||||||||||||||||
|
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. Could it be tested on a real lab ?
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. should be honestly, i think i need to double check this together with the design and the code
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. can we know the "source" (sender) of the request here, and map it to a location? → please create followup, could be a nice optimization? (kind of flimsy, but we could extract the "replication group" from versionId ; not sure how to map to locations though. Or checking if source IP is in the location's bootstrap list. Other maybe other ways...) |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| const nextReplInfo = getReplicationInfo(config, objectKey, bucketInfo, isMDOnly, objSize, null, null, null); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if (nextReplInfo) { | ||||||||||||||||||||||||
| nextReplInfo.backends = nextReplInfo.backends.filter(b => { | ||||||||||||||||||||||||
| const loc = config.locationConstraints[b.site]; | ||||||||||||||||||||||||
| return !loc || !BLOCKED_LOCATION_TYPES.includes(loc.type); | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
|
Comment on lines
+861
to
+864
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 logic should be in
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. Are you sure you wanna do this ? It means I have to add some kind of flag to getReplicationInfo to do the current logic in getReplicationInfo 🤔
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. Ok I see, maybe it can be done cleanly if, in getReplicationInfo.js I add a helper function, leave the existing function almost as it is and just call that extra helper at the end of getReplicationInfo if the flag is on ?
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. up, just wanna double check whats the potential issue here
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 think it would be better to move it out of here indeed : easier to test (isolated), easier to find (logic in a single place), and makes the current function smaller... Not sure if we could do with the existing params, extend them, or if we need a new one ; but (esp if we add one) we should also take the chance to refactor the API of (may be in followup PR though, but should be done either way I think) |
||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if (nextReplInfo && nextReplInfo.backends.length > 0) { | ||||||||||||||||||||||||
| omVal.replicationInfo = nextReplInfo; | ||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||
| omVal.replicationInfo = { | ||||||||||||||||||||||||
|
SylvainSenechal marked this conversation as resolved.
|
||||||||||||||||||||||||
| status: '', | ||||||||||||||||||||||||
| backends: [], | ||||||||||||||||||||||||
| content: [], | ||||||||||||||||||||||||
| destination: '', | ||||||||||||||||||||||||
| storageClass: '', | ||||||||||||||||||||||||
| role: '', | ||||||||||||||||||||||||
| storageType: '', | ||||||||||||||||||||||||
| dataStoreVersionId: '', | ||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| omVal.replicationInfo.isReplica = true; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| return async.series( | ||||||||||||||||||||||||
| [ | ||||||||||||||||||||||||
| // Zenko's CRR delegates replacing the account | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
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.
header may be present but empty, for
null versions