Compare commits

...

4 Commits

Author SHA1 Message Date
Nicolas Humbert 890c0815e3 -- 2024-02-07 15:45:12 +01:00
Nicolas Humbert 6f2c5362bf fix 2024-02-02 11:06:24 +01:00
Nicolas Humbert 9af111f1b8 ++ 2024-02-01 16:45:51 +01:00
Nicolas Humbert 448b2fee06 fix replication 2024-02-01 14:48:49 +01:00
5 changed files with 202 additions and 8 deletions

View File

@ -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';

View File

@ -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.
*

View File

@ -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);
});

View File

@ -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",

View File

@ -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);
});
});