Compare commits
1 Commits
developmen
...
improvemen
Author | SHA1 | Date |
---|---|---|
Francois Ferrand | 5846266dd6 |
|
@ -1024,6 +1024,13 @@ export const TooManyRequests: ErrorFormat = {
|
||||||
code: 429,
|
code: 429,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const UpdateConflict: ErrorFormat = {
|
||||||
|
code: 500,
|
||||||
|
|
||||||
|
description:
|
||||||
|
'The request was rejected because there was a conflict attempting to update a resource.'
|
||||||
|
};
|
||||||
|
|
||||||
// --------------------- cdmiclient ---------------------
|
// --------------------- cdmiclient ---------------------
|
||||||
|
|
||||||
export const ReadOnly: ErrorFormat = {
|
export const ReadOnly: ErrorFormat = {
|
||||||
|
|
|
@ -89,6 +89,10 @@ export type ObjectMDData = {
|
||||||
// the master is set as a placeholder and gets updated with the new latest
|
// the master is set as a placeholder and gets updated with the new latest
|
||||||
// version data after a certain amount of time.
|
// version data after a certain amount of time.
|
||||||
isPHD: boolean;
|
isPHD: boolean;
|
||||||
|
// Count the number of 'updates' to the object, incremented every time the
|
||||||
|
// metadata "document" is updated. This is used by backend to detect
|
||||||
|
// conflicting updates to the same object.
|
||||||
|
revisionCount?: number
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -51,6 +51,7 @@ const SOCKET_TIMEOUT_MS = 360000;
|
||||||
const CONCURRENT_CURSORS = 10;
|
const CONCURRENT_CURSORS = 10;
|
||||||
|
|
||||||
const initialInstanceID = process.env.INITIAL_INSTANCE_ID;
|
const initialInstanceID = process.env.INITIAL_INSTANCE_ID;
|
||||||
|
const METADATA_CONFLICT_DETECTION = process.env.METADATA_CONFLICT_DETECTION;
|
||||||
|
|
||||||
let uidCounter = 0;
|
let uidCounter = 0;
|
||||||
|
|
||||||
|
@ -561,6 +562,30 @@ class MongoClientInterface {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_updateCondition(key, isVersion, objVal) {
|
||||||
|
const cond = { _id: key };
|
||||||
|
if (!METADATA_CONFLICT_DETECTION) {
|
||||||
|
return cond;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (objVal.revisionCount) {
|
||||||
|
cond['value.revisionCount'] = objVal.revisionCount;
|
||||||
|
} else if (isVersion) {
|
||||||
|
// This is the case where the document was created with an older version of Arsenal,
|
||||||
|
// and which did not have the revisionCount protection
|
||||||
|
|
||||||
|
// We don't want to do this for master object, which get overwritten in non-versioned
|
||||||
|
// buckets without following the read object MD / update / write object MD pattern
|
||||||
|
cond['value.revisionCount'] = { $exists: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
/* eslint-disable no-param-reassign */
|
||||||
|
objVal.revisionCount = (objVal.revisionCount || 0) + 1;
|
||||||
|
/* eslint-enable no-param-reassign */
|
||||||
|
|
||||||
|
return cond;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In this case we generate a versionId and
|
* In this case we generate a versionId and
|
||||||
* sequentially create the object THEN update the master.
|
* sequentially create the object THEN update the master.
|
||||||
|
@ -592,10 +617,8 @@ class MongoClientInterface {
|
||||||
const masterKey = formatMasterKey(objName, params.vFormat);
|
const masterKey = formatMasterKey(objName, params.vFormat);
|
||||||
// initiating array of operations with version creation
|
// initiating array of operations with version creation
|
||||||
const ops = [{
|
const ops = [{
|
||||||
updateOne: {
|
updateOne: { // TODO: this is supposed to be an insertion, should not try to update....
|
||||||
filter: {
|
filter: { _id: versionKey },
|
||||||
_id: versionKey,
|
|
||||||
},
|
|
||||||
update: {
|
update: {
|
||||||
$set: { _id: versionKey, value: objVal },
|
$set: { _id: versionKey, value: objVal },
|
||||||
},
|
},
|
||||||
|
@ -694,7 +717,8 @@ class MongoClientInterface {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
objVal.versionId = versionId;
|
objVal.versionId = versionId;
|
||||||
const masterKey = formatMasterKey(objName, params.vFormat);
|
const masterKey = formatMasterKey(objName, params.vFormat);
|
||||||
c.updateOne({ _id: masterKey },
|
c.updateOne( // TODO: this is supposed to be an insertion, should not try to update....
|
||||||
|
{ _id: masterKey },
|
||||||
{ $set: { value: objVal }, $setOnInsert: { _id: masterKey } },
|
{ $set: { value: objVal }, $setOnInsert: { _id: masterKey } },
|
||||||
{ upsert: true },
|
{ upsert: true },
|
||||||
)
|
)
|
||||||
|
@ -751,12 +775,11 @@ class MongoClientInterface {
|
||||||
|
|
||||||
c.findOne({ _id: masterKey }).then(checkObj => {
|
c.findOne({ _id: masterKey }).then(checkObj => {
|
||||||
const objUpsert = !checkObj;
|
const objUpsert = !checkObj;
|
||||||
|
const cond = this._updateCondition(versionKey, true, objVal);
|
||||||
// initiating array of operations with version creation/update
|
// initiating array of operations with version creation/update
|
||||||
const ops = [{
|
const ops = [{
|
||||||
updateOne: {
|
updateOne: {
|
||||||
filter: {
|
filter: cond,
|
||||||
_id: versionKey,
|
|
||||||
},
|
|
||||||
update: {
|
update: {
|
||||||
$set: {
|
$set: {
|
||||||
_id: versionKey,
|
_id: versionKey,
|
||||||
|
@ -776,10 +799,10 @@ class MongoClientInterface {
|
||||||
$set: { _id: masterKey, value: objVal },
|
$set: { _id: masterKey, value: objVal },
|
||||||
};
|
};
|
||||||
|
|
||||||
c.findOne({ _id: versionKey }).then(verObj => {
|
c.findOne(cond).then(verObj => {
|
||||||
// existing versioned entry update.
|
// existing versioned entry update.
|
||||||
// if master entry doesn't exist, skip upsert of master
|
// if master entry doesn't exist, skip upsert of master
|
||||||
if (verObj && !checkObj) {
|
if (verObj && objUpsert) {
|
||||||
putObjectEntry(ops, cb);
|
putObjectEntry(ops, cb);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -797,7 +820,13 @@ class MongoClientInterface {
|
||||||
ops.push(masterOp);
|
ops.push(masterOp);
|
||||||
putObjectEntry(ops, cb);
|
putObjectEntry(ops, cb);
|
||||||
return null;
|
return null;
|
||||||
}).catch(() => {
|
}).catch(err => {
|
||||||
|
if (err.code === 11000) { // E11000 duplicate key error
|
||||||
|
log.error('putObjectVerCase4: conflict upserting object version',
|
||||||
|
{ error: err.message });
|
||||||
|
return cb(errors.UpdateConflict);
|
||||||
|
}
|
||||||
|
|
||||||
log.error('putObjectVerCase3: mongoDB error finding object');
|
log.error('putObjectVerCase3: mongoDB error finding object');
|
||||||
return cb(errors.InternalError);
|
return cb(errors.InternalError);
|
||||||
});
|
});
|
||||||
|
@ -831,9 +860,7 @@ class MongoClientInterface {
|
||||||
putObjectVerCase4(c, bucketName, objName, objVal, params, log, cb) {
|
putObjectVerCase4(c, bucketName, objName, objVal, params, log, cb) {
|
||||||
const versionKey = formatVersionKey(objName, params.versionId, params.vFormat);
|
const versionKey = formatVersionKey(objName, params.versionId, params.vFormat);
|
||||||
const masterKey = formatMasterKey(objName, params.vFormat);
|
const masterKey = formatMasterKey(objName, params.vFormat);
|
||||||
c.updateOne({
|
c.updateOne(this._updateCondition(versionKey, true, objVal), {
|
||||||
_id: versionKey,
|
|
||||||
}, {
|
|
||||||
$set: {
|
$set: {
|
||||||
_id: versionKey,
|
_id: versionKey,
|
||||||
value: objVal,
|
value: objVal,
|
||||||
|
@ -893,6 +920,12 @@ class MongoClientInterface {
|
||||||
return cb(errors.InternalError);
|
return cb(errors.InternalError);
|
||||||
});
|
});
|
||||||
})).catch(err => {
|
})).catch(err => {
|
||||||
|
if (err.code === 11000) { // E11000 duplicate key error
|
||||||
|
log.error('putObjectVerCase4: conflict upserting object version',
|
||||||
|
{ error: err.message });
|
||||||
|
return cb(errors.UpdateConflict);
|
||||||
|
}
|
||||||
|
|
||||||
log.error(
|
log.error(
|
||||||
'putObjectVerCase4: error upserting object version',
|
'putObjectVerCase4: error upserting object version',
|
||||||
{ error: err.message });
|
{ error: err.message });
|
||||||
|
@ -913,16 +946,20 @@ class MongoClientInterface {
|
||||||
*/
|
*/
|
||||||
putObjectNoVer(c, bucketName, objName, objVal, params, log, cb) {
|
putObjectNoVer(c, bucketName, objName, objVal, params, log, cb) {
|
||||||
const masterKey = formatMasterKey(objName, params.vFormat);
|
const masterKey = formatMasterKey(objName, params.vFormat);
|
||||||
c.updateOne({
|
c.updateOne(this._updateCondition(masterKey, false, objVal), {
|
||||||
_id: masterKey,
|
|
||||||
}, {
|
|
||||||
$set: {
|
$set: {
|
||||||
_id: masterKey,
|
_id: masterKey,
|
||||||
value: objVal,
|
value: objVal,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
upsert: true,
|
upsert: true,
|
||||||
}).then(() => cb()).catch((err) => {
|
}).then(cb).catch((err) => {
|
||||||
|
if (err.code === 11000) { // E11000 duplicate key error
|
||||||
|
log.error('putObjectVerCase4: conflict upserting object version',
|
||||||
|
{ error: err.message });
|
||||||
|
return cb(errors.UpdateConflict);
|
||||||
|
}
|
||||||
|
|
||||||
log.error('putObjectNoVer: error putting obect with no versioning', { error: err.message });
|
log.error('putObjectNoVer: error putting obect with no versioning', { error: err.message });
|
||||||
return cb(errors.InternalError);
|
return cb(errors.InternalError);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue