Skip to content

Commit 41b7d90

Browse files
committed
Merge branch 'improvement/BB-592' into q/8.6
2 parents 31bf72f + 18d223c commit 41b7d90

File tree

3 files changed

+113
-3
lines changed

3 files changed

+113
-3
lines changed

extensions/replication/tasks/CopyLocationTask.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -797,8 +797,8 @@ class CopyLocationTask extends BackbeatTask {
797797
}
798798

799799
/**
800-
* Get the source object metadata and ensure the latest object MD5 hash is
801-
* the same as in the action entry.
800+
* Ensure the latest object MD5 hash is the same as in the action entry,
801+
* and that the object has not already been transitioned to the destination
802802
* @param {ActionQueueEntry} actionEntry - the action entry
803803
* @param {ObjectMD} objMD - metadata object
804804
@@ -816,6 +816,11 @@ class CopyLocationTask extends BackbeatTask {
816816
'object contents have changed');
817817
}
818818
}
819+
if (objMD.getDataStoreName() === actionEntry.getAttribute('toLocation')) {
820+
// The object was already transitioned to the destination location
821+
return errors.InvalidObjectState.customizeDescription(
822+
'object already transitioned');
823+
}
819824
return null;
820825
}
821826

@@ -824,6 +829,11 @@ class CopyLocationTask extends BackbeatTask {
824829
actionEntry.setError(err);
825830
}
826831
log.info('action execution ended', actionEntry.getLogInfo());
832+
// skip object if it was already transitioned
833+
if (err && (err.InvalidObjectState || err.code === 'InvalidObjectState')) {
834+
log.info('object skipped: invalid object state', actionEntry.getLogInfo());
835+
return { committable: true };
836+
}
827837
if (!actionEntry.getResultsTopic()) {
828838
// no result requested, we may commit immediately
829839
return { committable: true };

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "backbeat",
3-
"version": "8.6.43",
3+
"version": "8.6.44",
44
"description": "Asynchronous queue and job manager",
55
"main": "index.js",
66
"scripts": {
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
const assert = require('assert');
2+
const sinon = require('sinon');
3+
4+
const CopyLocationTask = require('../../../extensions/replication/tasks/CopyLocationTask');
5+
const ActionQueueEntry = require('../../../lib/models/ActionQueueEntry');
6+
const { errors } = require('arsenal');
7+
const { ObjectMD } = require('arsenal').models;
8+
9+
const fakeLogger = require('../../utils/fakeLogger');
10+
11+
describe('CopyLocationTask', () => {
12+
describe('_checkObjectState', () => {
13+
let task;
14+
15+
beforeEach(() => {
16+
task = new CopyLocationTask({
17+
getStateVars: () => ({
18+
site: 'test-site',
19+
mProducer: {
20+
getProducer: () => {},
21+
}
22+
}),
23+
});
24+
});
25+
26+
it('should return invalidState error object has been changed', () => {
27+
const objMd = new ObjectMD();
28+
objMd.setContentMd5('1234-9');
29+
30+
const entry = new ActionQueueEntry({
31+
target: {
32+
eTag: '"156781-9"',
33+
},
34+
});
35+
36+
const res = task._checkObjectState(entry, objMd);
37+
assert(res.InvalidObjectState);
38+
});
39+
40+
it('should return invalidState error when object already transitioned', () => {
41+
const objMd = new ObjectMD();
42+
objMd.setDataStoreName('test-site');
43+
44+
const entry = new ActionQueueEntry({
45+
target: {},
46+
toLocation: 'test-site',
47+
});
48+
49+
const res = task._checkObjectState(entry, objMd);
50+
assert(res.InvalidObjectState);
51+
});
52+
53+
it('should not return error if object is valid', () => {
54+
const objMd = new ObjectMD();
55+
objMd.setDataStoreName('STANDARD');
56+
objMd.setContentMd5('1234-9');
57+
58+
const entry = new ActionQueueEntry({
59+
target: {
60+
eTag: '"1234-9"',
61+
},
62+
toLocation: 'test-site',
63+
});
64+
65+
const res = task._checkObjectState(entry, objMd);
66+
assert.equal(res, null);
67+
});
68+
});
69+
70+
describe('_publishCopyLocationStatus', () => {
71+
let task;
72+
73+
beforeEach(() => {
74+
task = new CopyLocationTask({
75+
getStateVars: () => ({
76+
site: 'test-site',
77+
mProducer: {
78+
getProducer: () => {},
79+
}
80+
}),
81+
});
82+
});
83+
84+
it('should skip object if object state is invalid', () => {
85+
const entry = new ActionQueueEntry({
86+
target: {
87+
key: 'key',
88+
eTag: '"1234-9"',
89+
},
90+
toLocation: 'test-site',
91+
});
92+
93+
task.replicationStatusProducer = sinon.stub().yields();
94+
95+
const res = task._publishCopyLocationStatus(errors.InvalidObjectState, entry, null, fakeLogger);
96+
assert.strictEqual(res.committable, true);
97+
assert(task.replicationStatusProducer.notCalled);
98+
});
99+
});
100+
});

0 commit comments

Comments
 (0)