-
Notifications
You must be signed in to change notification settings - Fork 250
CLDSRV-632 ✨ put metadata edge cases #5913
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.1
Are you sure you want to change the base?
Changes from all commits
54a30d8
e9e610f
453108e
e07b97a
7a40202
33ee865
c79a743
1e937b3
2935282
c7d617d
ded1719
e653fa3
1ec8497
b8b584c
6f3057a
eb229d7
f65e80e
0fe8b78
06dba2b
4357e13
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 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -40,6 +40,13 @@ const { listLifecycleOrphanDeleteMarkers } = require('../api/backbeat/listLifecy | |||||||||||||
const { objectDeleteInternal } = require('../api/objectDelete'); | ||||||||||||||
const quotaUtils = require('../api/apiUtils/quotas/quotaUtils'); | ||||||||||||||
const { handleAuthorizationResults } = require('../api/api'); | ||||||||||||||
const { versioningPreprocessing } | ||||||||||||||
= require('../api/apiUtils/object/versioning'); | ||||||||||||||
const {promisify} = require('util'); | ||||||||||||||
|
||||||||||||||
const versioningPreprocessingPromised = promisify(versioningPreprocessing); | ||||||||||||||
metadata.getObjectMDPromised = promisify(metadata.getObjectMD); | ||||||||||||||
metadata.getBucketAndObjectMDPromised = promisify(metadata.getBucketAndObjectMD); | ||||||||||||||
|
||||||||||||||
const { CURRENT_TYPE, NON_CURRENT_TYPE, ORPHAN_DM_TYPE } = constants.lifecycleListing; | ||||||||||||||
|
||||||||||||||
|
@@ -508,23 +515,22 @@ function putMetadata(request, response, bucketInfo, objMd, log, callback) { | |||||||||||||
if (err) { | ||||||||||||||
return callback(err); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
let omVal; | ||||||||||||||
|
||||||||||||||
try { | ||||||||||||||
omVal = JSON.parse(payload); | ||||||||||||||
} catch { | ||||||||||||||
// FIXME: add error type MalformedJSON | ||||||||||||||
return callback(errors.MalformedPOSTRequest); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
const { headers, bucketName, objectKey } = request; | ||||||||||||||
// check if it's metadata only operation | ||||||||||||||
|
||||||||||||||
if (headers['x-scal-replication-content'] === 'METADATA') { | ||||||||||||||
if (!objMd) { | ||||||||||||||
// if the target does not exist, return an error to | ||||||||||||||
// backbeat, who will have to retry the operation as a | ||||||||||||||
// complete replication | ||||||||||||||
return callback(errors.ObjNotFound); | ||||||||||||||
} | ||||||||||||||
// use original data locations and encryption info | ||||||||||||||
Comment on lines
-522
to
-527
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. these comments do not seem useless at all, they should be kept |
||||||||||||||
|
||||||||||||||
[ | ||||||||||||||
'location', | ||||||||||||||
'x-amz-server-side-encryption', | ||||||||||||||
|
@@ -611,6 +617,16 @@ function putMetadata(request, response, bucketInfo, objMd, log, callback) { | |||||||||||||
// To prevent this, the versionId field is only included in options when it is defined. | ||||||||||||||
if (versionId !== undefined) { | ||||||||||||||
options.versionId = versionId; | ||||||||||||||
omVal.versionId = versionId; | ||||||||||||||
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. the semantics of putObject in metadata layer (either backends, i.e. metadata or mongodbClientInterface) are not clear or precisely defined : does setting this not interract (in some weird or unacceptable way?) the behavior of the 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. As far as I checked, this seems not be the case. But maybe I'm missing something here 😬. Maybe the second review will have more insight 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. did you perform such second review? (you can resolve this comment once you confirmed) |
||||||||||||||
|
||||||||||||||
if (isNull) { | ||||||||||||||
if (!nullVersionCompatMode) { | ||||||||||||||
omVal.isNull2 = true; | ||||||||||||||
} | ||||||||||||||
Comment on lines
+623
to
+625
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
|
||||||||||||||
|
||||||||||||||
omVal.isNull = isNull; | ||||||||||||||
DarkIsDude marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
} | ||||||||||||||
|
||||||||||||||
// In the MongoDB metadata backend, setting the versionId option leads to the creation | ||||||||||||||
// or update of the version object, the master object is only updated if its versionId | ||||||||||||||
// is the same as the version. This can lead to inconsistencies when replicating objects | ||||||||||||||
|
@@ -641,6 +657,7 @@ function putMetadata(request, response, bucketInfo, objMd, log, callback) { | |||||||||||||
if (!request.query?.accountId) { | ||||||||||||||
return next(); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
return getCanonicalIdsByAccountId(request.query.accountId, log, (err, res) => { | ||||||||||||||
if (err) { | ||||||||||||||
return next(err); | ||||||||||||||
|
@@ -650,6 +667,45 @@ function putMetadata(request, response, bucketInfo, objMd, log, callback) { | |||||||||||||
return next(); | ||||||||||||||
}); | ||||||||||||||
}, | ||||||||||||||
async () => { | ||||||||||||||
// If we create a new version of an object (so objMd is null), | ||||||||||||||
// we should make sure that the masterVersion is versionned. | ||||||||||||||
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
|
||||||||||||||
// If an object already exists, we just want to update the metadata | ||||||||||||||
// of the existing object and not create a new one | ||||||||||||||
if (versioning && !objMd) { | ||||||||||||||
let masterMD; | ||||||||||||||
|
||||||||||||||
try { | ||||||||||||||
masterMD = await metadata.getObjectMDPromised(bucketName, objectKey, {}, log); | ||||||||||||||
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 introduces an extra metadata IO in case of insert (first one was in ➡ maybe acceptable to keep the extra I/O for now, but we should create a ticket for tracking this, or make a note in https://scality.atlassian.net/browse/ARTESCA-8449 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. Side note, if we want to get both the master and the version, then this is just moving the problem, as we will still need two queries. Today we try to get the master version only for objects like delete markers, but if we provide a versionID and it's not there, then we just return The best would be to know in advance |
||||||||||||||
} catch (err) { | ||||||||||||||
if (err.is?.NoSuchKey) { | ||||||||||||||
log.debug('no master found for versioned object', { | ||||||||||||||
method: 'putMetadata', | ||||||||||||||
bucketName, | ||||||||||||||
objectKey, | ||||||||||||||
}); | ||||||||||||||
} else { | ||||||||||||||
throw err; | ||||||||||||||
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'm not sure we want to throw, the current stack is not using promises yet, so we tend to use more errors and calling the callbacks with it. I fear that a throw here will make the process crash, as we do not try/catch our code?
Suggested change
|
||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
if (!masterMD) { | ||||||||||||||
return; | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
const versioningPreprocessingResult = | ||||||||||||||
await versioningPreprocessingPromised(bucketName, bucketInfo, objectKey, masterMD, log); | ||||||||||||||
|
||||||||||||||
if (versioningPreprocessingResult?.nullVersionId) { | ||||||||||||||
omVal.nullVersionId = versioningPreprocessingResult.nullVersionId; | ||||||||||||||
options.deleteNullKey = versioningPreprocessingResult.deleteNullKey; | ||||||||||||||
|
||||||||||||||
if (versioningPreprocessingResult.extraMD) { | ||||||||||||||
Object.assign(omVal, options.extraMD); | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
}, | ||||||||||||||
next => { | ||||||||||||||
log.trace('putting object version', { | ||||||||||||||
objectKey: request.objectKey, omVal, options }); | ||||||||||||||
|
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.
This will probably be automated soon with some prettier solutions
I started this but it was on hold as we wanted to finish the unification first #5877