Compare commits
4 Commits
developmen
...
bugfix/rep
Author | SHA1 | Date |
---|---|---|
Nicolas Humbert | 890c0815e3 | |
Nicolas Humbert | 6f2c5362bf | |
Nicolas Humbert | 9af111f1b8 | |
Nicolas Humbert | 448b2fee06 |
|
@ -1,4 +1,5 @@
|
|||
export { default as ARN } from './ARN';
|
||||
export { default as BackendInfo } from './BackendInfo';
|
||||
export { default as BucketInfo } from './BucketInfo';
|
||||
export { default as ObjectMD } from './ObjectMD';
|
||||
export { default as ObjectMDLocation } from './ObjectMDLocation';
|
||||
|
|
|
@ -103,6 +103,27 @@ export class Version {
|
|||
);
|
||||
}
|
||||
|
||||
static updateOrAppendNullVersionId(value: string, nullVersionId: string): string {
|
||||
// Check if "nullVersionId" already exists in the string
|
||||
const nullVersionIdPattern = /"nullVersionId":"[^"]*"/;
|
||||
const nullVersionIdExists = nullVersionIdPattern.test(value);
|
||||
|
||||
if (nullVersionIdExists) {
|
||||
// Replace the existing nullVersionId with the new one
|
||||
return value.replace(nullVersionIdPattern, `"nullVersionId":"${nullVersionId}"`);
|
||||
} else {
|
||||
// Append nullVersionId in the cheap way as before
|
||||
let index = value.length - 2;
|
||||
while (value.charAt(index--) === ' ');
|
||||
const comma = value.charAt(index + 1) !== '{';
|
||||
return (
|
||||
`${value.slice(0, value.length - 1)}` + // eslint-disable-line
|
||||
(comma ? ',' : '') +
|
||||
`"nullVersionId":"${nullVersionId}"}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [MetaData Internal] Check if a version is a place holder for deletion.
|
||||
*
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import errors, { ArsenalError } from '../errors';
|
||||
import { Version } from './Version';
|
||||
import { generateVersionId as genVID } from './VersionID';
|
||||
import { generateVersionId as genVID, getInfVid } from './VersionID';
|
||||
import WriteCache from './WriteCache';
|
||||
import WriteGatheringManager from './WriteGatheringManager';
|
||||
|
||||
|
@ -291,6 +291,7 @@ export default class VersioningRequestProcessor {
|
|||
// also update master version in case the put
|
||||
// version is newer or same version than master.
|
||||
// if versionId === '' update master version
|
||||
console.log('options!!!', options);
|
||||
const versioning = options &&
|
||||
(options.versioning || options.versioning === '');
|
||||
const versionId = options &&
|
||||
|
@ -380,12 +381,47 @@ export default class VersioningRequestProcessor {
|
|||
const versionId = request.options.versionId;
|
||||
const versionKey = formatVersionKey(request.key, versionId);
|
||||
const ops = [{ key: versionKey, value: request.value }];
|
||||
if (data === undefined ||
|
||||
(Version.from(data).getVersionId() ?? '') >= versionId) {
|
||||
// master does not exist or is not newer than put
|
||||
// version and needs to be updated as well.
|
||||
// Note that older versions have a greater version ID.
|
||||
ops.push({ key: request.key, value: request.value });
|
||||
const masterVersion = data !== undefined &&
|
||||
Version.from(data);
|
||||
if (masterVersion) {
|
||||
const versionIdFromMaster = masterVersion.getVersionId();
|
||||
// master key exists
|
||||
console.log('MASTER EXIST!!!');
|
||||
console.log('versionIdFromMaster!!!', versionIdFromMaster);
|
||||
console.log('versionId!!!', versionId);
|
||||
if (versionIdFromMaster === undefined ||
|
||||
versionIdFromMaster >= versionId) {
|
||||
console.log('MASTER CREATED!!!!');
|
||||
// master key is not newer than the put version
|
||||
let masterVersionId;
|
||||
let value = request.value;
|
||||
console.log('masterVersion!!!', masterVersion);
|
||||
if (masterVersion.isNullVersion() && versionIdFromMaster) {
|
||||
// master key is a null version
|
||||
masterVersionId = versionIdFromMaster;
|
||||
} else if (versionIdFromMaster === undefined) {
|
||||
// master key does not have a versionID
|
||||
// => create one with the "infinite" version ID
|
||||
masterVersionId = getInfVid(this.replicationGroupId);
|
||||
masterVersion.setVersionId(masterVersionId);
|
||||
}
|
||||
if (masterVersionId) {
|
||||
// => create a new version key from the master version
|
||||
const masterVersionKey = formatVersionKey(key, masterVersionId);
|
||||
value = Version.updateOrAppendNullVersionId(request.value, masterVersionId);
|
||||
masterVersion.setNullVersion();
|
||||
console.log('masterVersionId!!!', masterVersionId);
|
||||
ops.push({ key: masterVersionKey,
|
||||
value: masterVersion.toString() });
|
||||
}
|
||||
// => update the master key, note that older
|
||||
// versions have a greater version ID
|
||||
ops.push({ key, value });
|
||||
}
|
||||
// otherwise, master key is newer so do not update it
|
||||
} else {
|
||||
// master key does not exist: create it
|
||||
ops.push({ key, value: request.value });
|
||||
}
|
||||
return callback(null, ops, versionId);
|
||||
});
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
"lint": "eslint $(git ls-files '*.js')",
|
||||
"lint_md": "mdlint $(git ls-files '*.md')",
|
||||
"lint_yml": "yamllint $(git ls-files '*.yml')",
|
||||
"test": "jest tests/unit",
|
||||
"test": "jest tests/unit/versioning/VersioningRequestProcessor.spec.js",
|
||||
"build": "tsc",
|
||||
"prepare": "yarn build",
|
||||
"ft_test": "jest tests/functional --testTimeout=120000 --forceExit",
|
||||
|
|
|
@ -219,4 +219,140 @@ describe('test VSP', () => {
|
|||
}],
|
||||
done);
|
||||
});
|
||||
|
||||
it('should be able to put Metadata on top of a null version', done => {
|
||||
const versionId = '00000000000000999999PARIS ';
|
||||
|
||||
async.waterfall([next => {
|
||||
const request = {
|
||||
db: 'foo',
|
||||
key: 'bar',
|
||||
value: '{"qux":"quz"}',
|
||||
options: {},
|
||||
};
|
||||
vsp.put(request, logger, next);
|
||||
},
|
||||
(res, next) => {
|
||||
const request = {
|
||||
db: 'foo',
|
||||
key: 'bar',
|
||||
value: `{"qux":"quz2","versionId":"${versionId}"}`,
|
||||
options: {
|
||||
isNull: false,
|
||||
versioning: true,
|
||||
versionId,
|
||||
},
|
||||
};
|
||||
vsp.put(request, logger, next);
|
||||
},
|
||||
(res, next) => {
|
||||
wgm.list({}, logger, next);
|
||||
},
|
||||
(res, next) => {
|
||||
const expectedListing = [
|
||||
// master version should have the provided version id and a reference of the null version id.
|
||||
{
|
||||
key: 'bar',
|
||||
value: `{"qux":"quz2","versionId":"${versionId}","nullVersionId":"99999999999999999999PARIS "}`
|
||||
},
|
||||
// the "internal" master version should have the provided version id.
|
||||
{
|
||||
key: `bar\x00${versionId}`,
|
||||
value: `{"qux":"quz2","versionId":"${versionId}"}`,
|
||||
},
|
||||
// should create a version that represents the old null master with the infinite version id and
|
||||
// the isNull property set to true.
|
||||
{
|
||||
key: 'bar\x0099999999999999999999PARIS ',
|
||||
value: '{"qux":"quz","versionId":"99999999999999999999PARIS ","isNull":true}'
|
||||
},
|
||||
];
|
||||
assert.deepStrictEqual(res, expectedListing);
|
||||
const request = {
|
||||
db: 'foo',
|
||||
key: 'bar',
|
||||
};
|
||||
vsp.get(request, logger, next);
|
||||
},
|
||||
(res, next) => {
|
||||
const expectedGet = {
|
||||
qux: 'quz2',
|
||||
versionId,
|
||||
nullVersionId: '99999999999999999999PARIS ',
|
||||
};
|
||||
assert.deepStrictEqual(JSON.parse(res), expectedGet);
|
||||
next();
|
||||
}],
|
||||
done);
|
||||
});
|
||||
|
||||
it('should be able to put Metadata on top of a null suspended version', done => {
|
||||
const versionId = '00000000000000999999PARIS ';
|
||||
let nullVersionId;
|
||||
|
||||
async.waterfall([next => {
|
||||
const request = {
|
||||
db: 'foo',
|
||||
key: 'bar',
|
||||
value: '{"qux":"quz","isNull":true}',
|
||||
options: {
|
||||
versionId: '',
|
||||
},
|
||||
};
|
||||
vsp.put(request, logger, next);
|
||||
},
|
||||
(res, next) => {
|
||||
nullVersionId = JSON.parse(res).versionId;
|
||||
const request = {
|
||||
db: 'foo',
|
||||
key: 'bar',
|
||||
value: `{"qux":"quz2","versionId":"${versionId}"}`,
|
||||
options: {
|
||||
isNull: false,
|
||||
versioning: true,
|
||||
versionId,
|
||||
},
|
||||
};
|
||||
vsp.put(request, logger, next);
|
||||
},
|
||||
(res, next) => {
|
||||
wgm.list({}, logger, next);
|
||||
},
|
||||
(res, next) => {
|
||||
const expectedListing = [
|
||||
// master version should have the provided version id and a reference of the null version id.
|
||||
{
|
||||
key: 'bar',
|
||||
value: `{"qux":"quz2","versionId":"${versionId}","nullVersionId":"${nullVersionId}"}`
|
||||
},
|
||||
// the "internal" master version should have the provided version id.
|
||||
{
|
||||
key: `bar\x00${versionId}`,
|
||||
value: `{"qux":"quz2","versionId":"${versionId}"}`,
|
||||
},
|
||||
// should create a version that represents the old null master with the infinite version id and
|
||||
// the isNull property set to true.
|
||||
{
|
||||
key: `bar\x00${nullVersionId}`,
|
||||
value: `{"qux":"quz","isNull":true,"versionId":"${nullVersionId}"}`,
|
||||
},
|
||||
];
|
||||
assert.deepStrictEqual(res, expectedListing);
|
||||
const request = {
|
||||
db: 'foo',
|
||||
key: 'bar',
|
||||
};
|
||||
vsp.get(request, logger, next);
|
||||
},
|
||||
(res, next) => {
|
||||
const expectedGet = {
|
||||
qux: 'quz2',
|
||||
versionId,
|
||||
nullVersionId,
|
||||
};
|
||||
assert.deepStrictEqual(JSON.parse(res), expectedGet);
|
||||
next();
|
||||
}],
|
||||
done);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue