diff --git a/lib/routes/routeBackbeat.js b/lib/routes/routeBackbeat.js index 1b6d86d490..a4a10d3188 100644 --- a/lib/routes/routeBackbeat.js +++ b/lib/routes/routeBackbeat.js @@ -1007,7 +1007,9 @@ function putMetadata(request, response, bucketInfo, objMd, log, callback) { // transition processor. Check the content-length as a null location can // also be from an empty object. (omVal['content-length'] === 0 || (omVal.location && Array.isArray(omVal.location))) && - locationKeysHaveChanged(objMd.location, omVal.location) + locationKeysHaveChanged(objMd.location, omVal.location) && + // Do not delete data on remote location (example : pull replication) + !locationConstraints[objMd.dataStoreName]?.isCRR ) { log.info('removing old data locations', { method: 'putMetadata', diff --git a/tests/unit/routes/routeBackbeat.js b/tests/unit/routes/routeBackbeat.js index 891a62274d..bde478087f 100644 --- a/tests/unit/routes/routeBackbeat.js +++ b/tests/unit/routes/routeBackbeat.js @@ -686,6 +686,47 @@ describe('routeBackbeat', () => { sinon.assert.calledWith(dataDeleteSpy, existingMd.location[0]); }); + it('should not delete data on a CRR location when locations change', async () => { + const existingMd = { + 'dataStoreName': 'location-crr-v1', + 'location': [ + { + key: 'key0', + dataStoreName: 'location-crr-v1', + size: 100, + }, + ], + 'content-length': 100, + }; + metadataUtils.standardMetadataValidateBucketAndObj.callsFake((params, denies, log, callback) => { + callback(null, bucketInfo, existingMd); + }); + + // the data was pulled from the CRR location to a local one + const reqBody = { + 'location': [ + { + key: 'key1', + dataStoreName: 'us-east-1', + size: 100, + }, + ], + 'content-length': 100, + }; + mockRequest = preparePutMetadataRequest(reqBody); + mockRequest.url += '?versionId=aIXVkw5Tw2Pd00000000001I4j3QKsvf'; + + sandbox + .stub(metadata, 'putObjectMD') + .callsFake((_bucketName, _objectKey, _omVal, _options, _logParam, cb) => cb(null, {})); + + routeBackbeat('127.0.0.1', mockRequest, mockResponse, log); + void (await endPromise); + + assert.strictEqual(mockResponse.statusCode, 200); + assert.strictEqual(dataDeleteSpy.called, false); + }); + it('should not delete data when some keys are still used', async () => { const existingMd = { 'location': [