Skip to content

Commit 08e7b1b

Browse files
CLDSRV-835: Improve bucket utility empty function
No need to separate trailing slash keys. Use multi object delete instead of parallel single delete List all pagination
1 parent 12097a6 commit 08e7b1b

File tree

1 file changed

+44
-43
lines changed

1 file changed

+44
-43
lines changed

tests/functional/aws-node-sdk/lib/utility/bucket-util.js

Lines changed: 44 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const {
44
CreateBucketCommand,
55
DeleteBucketCommand,
66
ListObjectVersionsCommand,
7-
DeleteObjectCommand,
7+
DeleteObjectsCommand,
88
ListBucketsCommand,
99
} = require('@aws-sdk/client-s3');
1010
const projectFixture = require('../fixtures/project');
@@ -90,48 +90,49 @@ class BucketUtility {
9090
* @param bucketName
9191
* @returns {Promise.<T>}
9292
*/
93-
empty(bucketName, BypassGovernanceRetention = false) {
94-
const param = {
95-
Bucket: bucketName,
96-
};
97-
98-
return this.s3.send(new ListObjectVersionsCommand(param))
99-
.then(data => Promise.all(
100-
(data.Versions || [])
101-
.filter(object => !object.Key.endsWith('/'))
102-
.map(object =>
103-
this.s3.send(new DeleteObjectCommand({
104-
Bucket: bucketName,
105-
Key: object.Key,
106-
VersionId: object.VersionId,
107-
...(BypassGovernanceRetention && { BypassGovernanceRetention }),
108-
})).then(() => object)
109-
)
110-
.concat((data.Versions || [])
111-
.filter(object => object.Key.endsWith('/'))
112-
.map(object =>
113-
this.s3.send(new DeleteObjectCommand({
114-
Bucket: bucketName,
115-
Key: object.Key,
116-
VersionId: object.VersionId,
117-
...(BypassGovernanceRetention && { BypassGovernanceRetention }),
118-
}))
119-
.then(() => object)
120-
)
121-
)
122-
.concat((data.DeleteMarkers || [])
123-
.map(object =>
124-
this.s3.send(new DeleteObjectCommand({
125-
Bucket: bucketName,
126-
Key: object.Key,
127-
VersionId: object.VersionId,
128-
...(BypassGovernanceRetention && { BypassGovernanceRetention }),
129-
}))
130-
.then(() => object)
131-
)
132-
)
133-
)
134-
);
93+
async empty(bucketName, BypassGovernanceRetention = false) {
94+
let keyMarker = undefined;
95+
let versionIdMarker = undefined;
96+
let isTruncated = true;
97+
98+
while (isTruncated) {
99+
const response = await this.s3.send(new ListObjectVersionsCommand({
100+
Bucket: bucketName,
101+
KeyMarker: keyMarker,
102+
VersionIdMarker: versionIdMarker,
103+
}));
104+
105+
const objects = [
106+
...(response.Versions || []),
107+
...(response.DeleteMarkers || []),
108+
].map(({ Key, VersionId }) => ({ Key, VersionId }));
109+
110+
if (objects.length > 0) {
111+
const result = await this.s3.send(new DeleteObjectsCommand({
112+
Bucket: bucketName,
113+
Delete: {
114+
Objects: objects,
115+
Quiet: true
116+
},
117+
...(BypassGovernanceRetention && { BypassGovernanceRetention }),
118+
}));
119+
if (result.Errors && result.Errors.length > 0) {
120+
for (const e of result.Errors) {
121+
// eslint-disable-next-line no-console
122+
console.warn(
123+
`Warning BucketUtility.empty(): failed to delete s3://${bucketName}/${e.Key}` +
124+
` (versionId=${e.VersionId}): ${e.Code} - ${e.Message}`
125+
);
126+
}
127+
}
128+
}
129+
130+
isTruncated = response.IsTruncated;
131+
if (isTruncated) {
132+
keyMarker = response.NextKeyMarker;
133+
versionIdMarker = response.NextVersionIdMarker;
134+
}
135+
}
135136
}
136137

137138
emptyMany(bucketNames) {

0 commit comments

Comments
 (0)