Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/storage/version/put.js
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,9 @@ export async function putObjectWithVersion(
if (versionResp.status !== 200 && versionResp.status !== 412) {
return { status: versionResp.status, metadata: { id: ID } };
}
versionCreated = versionResp.status === 200;
// 412 means the version key already exists — a concurrent request created it first.
// The version IS persisted in R2, so treat it as created.
versionCreated = versionResp.status === 200 || versionResp.status === 412;
}

// Audit: one entry per versionable PUT; versionLabel + versionId when labelled version created.
Expand Down
17 changes: 8 additions & 9 deletions test/storage/version/put.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2228,7 +2228,7 @@ describe('Version Put', () => {
assert.strictEqual(true, resp.versionCreated);
});

it('putObjectWithVersion sets versionCreated false when version already existed (putVersion 412)', async () => {
it('putObjectWithVersion sets versionCreated true when version already existed (putVersion 412 = concurrent race)', async () => {
const mockGetObject = async () => ({
body: 'content',
contentType: 'text/html',
Expand Down Expand Up @@ -2261,10 +2261,10 @@ describe('Version Put', () => {
org: 'o', key: 'a.html', body: 'new', label: 'My version',
}, true);
assert.equal(200, resp.status);
assert.strictEqual(false, resp.versionCreated);
assert.strictEqual(true, resp.versionCreated);
});

it('postObjectVersion returns 500 when version was not created (putVersion 412)', async () => {
it('postObjectVersion returns 201 when version already exists in R2 (putVersion 412 concurrent race)', async () => {
const req = { json: async () => ({ label: 'my-label' }) };
const env = {};
const ctx = {
Expand Down Expand Up @@ -2299,8 +2299,7 @@ describe('Version Put', () => {
});

const resp = await postObjectVersion(req, env, ctx);
assert.equal(500, resp.status);
assert.strictEqual(resp.error, 'Version was not created');
assert.equal(201, resp.status);
});

it('postObjectVersion returns 201 when version is successfully created', async () => {
Expand Down Expand Up @@ -3029,7 +3028,7 @@ describe('Version Put', () => {
assert.equal(201, resp.status);
});

it('returns 500 when versionCreated is false (version already exists / 412)', async () => {
it('returns 201 when version PUT gets 412 (concurrent race — version already exists in R2)', async () => {
const mockGetObject = async () => ({
body: 'doc content',
contentType: 'text/html',
Expand All @@ -3043,7 +3042,8 @@ describe('Version Put', () => {
return { $metadata: { httpStatusCode: 200 } };
},
};
// version put throws 412 → putVersion returns { status: 412 } → versionCreated stays false
// Concurrent request already created the version object; R2 returns 412 on our PUT.
// The version IS persisted — this request should still return 201, not 500.
const versionClient = {
async send() {
const err = new Error('precondition failed');
Expand All @@ -3069,8 +3069,7 @@ describe('Version Put', () => {
};
const resp = await postObjectVersionWithLabel('My Label', {}, daCtx);

assert.strictEqual(resp.status, 500, 'must return 500 when version was not created');
assert.strictEqual(resp.error, 'Version was not created');
assert.strictEqual(resp.status, 201, 'must return 201 when version already exists (concurrent 412)');
});

it('returns 201 when version client body is a ReadableStream that would be disturbed by SDK retry', async () => {
Expand Down
Loading