Skip to content

Commit b68458f

Browse files
authored
@tus/s3-store: fix min part size condition & offset race condition (#475)
1 parent 872b021 commit b68458f

File tree

2 files changed

+35
-2
lines changed

2 files changed

+35
-2
lines changed

packages/s3-store/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,9 +256,11 @@ export class S3Store extends DataStore {
256256

257257
const readable = fs.createReadStream(path)
258258
readable.on('error', reject)
259-
if (partSize > this.minPartSize || isFinalChunk) {
260-
await this.uploadPart(metadata, readable, partNumber)
259+
if (partSize >= this.minPartSize || isFinalChunk) {
260+
// The stream splitter may be faster than we are able to upload to S3
261+
// so we increment the offset before uploading the part.
261262
offset += partSize
263+
await this.uploadPart(metadata, readable, partNumber)
262264
} else {
263265
await this.uploadIncompletePart(incompletePartId, readable)
264266
}

packages/s3-store/test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,37 @@ describe('S3DataStore', function () {
7272
assert.equal(offset, size + incompleteSize)
7373
})
7474

75+
it('should process chunk size of exactly the min size', async function () {
76+
this.datastore.minPartSize = 1024 * 1024 * 5
77+
const store = this.datastore
78+
const size = 1024 * 1024 * 5
79+
const getIncompletePart = sinon.spy(store, 'getIncompletePart')
80+
const deleteIncompletePart = sinon.spy(store, 'deleteIncompletePart')
81+
const uploadIncompletePart = sinon.spy(store, 'uploadIncompletePart')
82+
const uploadPart = sinon.spy(store, 'uploadPart')
83+
const upload = new Upload({
84+
id: 'min-part-size-test',
85+
size: size + size,
86+
offset: 0,
87+
})
88+
89+
await store.create(upload)
90+
const n1 = await store.write(
91+
Readable.from(Buffer.alloc(size)),
92+
upload.id,
93+
upload.offset
94+
)
95+
assert.equal(n1, size)
96+
const n2 = await store.write(Readable.from(Buffer.alloc(size)), upload.id, n1)
97+
assert.equal(n2, n1 + size)
98+
const {offset} = await store.getUpload(upload.id)
99+
assert.equal(getIncompletePart.called, true)
100+
assert.equal(deleteIncompletePart.called, false)
101+
assert.equal(uploadIncompletePart.called, false)
102+
assert.equal(uploadPart.calledTwice, true)
103+
assert.equal(offset, size + size)
104+
})
105+
75106
shared.shouldHaveStoreMethods()
76107
shared.shouldCreateUploads()
77108
shared.shouldRemoveUploads() // Termination extension

0 commit comments

Comments
 (0)