Skip to content

Commit 0a186de

Browse files
authored
Fix issue on SQL: Delete a container with blob, then create container/blob with same name, and delete container will fail. (#2572)
* Fix 2563: on SQL, create container/blob, delete container, then create container again, then delete will fail. * fix issue 2563: delete a contain with blob, then create container/blob and delete container fail on sql
1 parent 7a62399 commit 0a186de

File tree

3 files changed

+133
-89
lines changed

3 files changed

+133
-89
lines changed

ChangeLog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Blob:
1111
- Fixed issue of filtering blobs with correct multiple conditions on single tag (range queries). (issue #2514)
1212
- Added support for sealing append blobs. (issue #810)
1313
- Added support for delegation sas with version of 2015-07-05.
14+
- Fix issue on SQL: Delete a container with blob, then create container/blob with same name, and delete container will fail. (issue #2563)
1415

1516
Table:
1617

src/blob/persistence/SqlBlobMetadataStore.ts

Lines changed: 95 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import {
88
Options as SequelizeOptions,
99
Sequelize,
1010
TEXT,
11-
Transaction
11+
Transaction,
12+
WhereOptions
1213
} from "sequelize";
1314

1415
import uuid from "uuid/v4";
@@ -646,34 +647,19 @@ export default class SqlBlobMetadataStore implements IBlobMetadataStore {
646647
transaction: t
647648
});
648649

649-
// TODO: GC blobs under deleting status
650-
await BlobsModel.update(
651-
{
652-
deleting: literal("deleting + 1")
650+
await this.deleteBlobFromSQL({
651+
accountName: account,
652+
containerName: container
653653
},
654-
{
655-
where: {
656-
accountName: account,
657-
containerName: container
658-
},
659-
transaction: t
660-
}
654+
t
661655
);
662656

663-
// TODO: GC blocks under deleting status
664-
await BlocksModel.update(
665-
{
666-
deleting: literal("deleting + 1")
657+
await this.deleteBlockFromSQL({
658+
accountName: account,
659+
containerName: container
667660
},
668-
{
669-
where: {
670-
accountName: account,
671-
containerName: container
672-
},
673-
transaction: t
674-
}
661+
t
675662
);
676-
/* Transaction ends */
677663
});
678664
}
679665

@@ -1509,6 +1495,7 @@ export default class SqlBlobMetadataStore implements IBlobMetadataStore {
15091495
blobName: block.blobName,
15101496
blockName: block.name,
15111497
size: block.size,
1498+
deleting: 0,
15121499
persistency: this.serializeModelValue(block.persistency)
15131500
},
15141501
{ transaction: t }
@@ -1977,51 +1964,33 @@ export default class SqlBlobMetadataStore implements IBlobMetadataStore {
19771964
if (count > 1) {
19781965
throw StorageErrorFactory.getSnapshotsPresent(context.contextId!);
19791966
} else {
1980-
await BlobsModel.update(
1981-
{
1982-
deleting: literal("deleting + 1")
1967+
await this.deleteBlobFromSQL({
1968+
accountName: account,
1969+
containerName: container,
1970+
blobName: blob
19831971
},
1984-
{
1985-
where: {
1986-
accountName: account,
1987-
containerName: container,
1988-
blobName: blob
1989-
},
1990-
transaction: t
1991-
}
1972+
t
19921973
);
19931974

1994-
await BlocksModel.update(
1995-
{
1996-
deleting: literal("deleting + 1")
1975+
await this.deleteBlockFromSQL({
1976+
accountName: account,
1977+
containerName: container,
1978+
blobName: blob
19971979
},
1998-
{
1999-
where: {
2000-
accountName: account,
2001-
containerName: container,
2002-
blobName: blob
2003-
},
2004-
transaction: t
2005-
}
1980+
t
20061981
);
20071982
}
20081983
}
20091984

20101985
// Scenario: Delete one snapshot only
20111986
if (!againstBaseBlob) {
2012-
await BlobsModel.update(
2013-
{
2014-
deleting: literal("deleting + 1")
2015-
},
2016-
{
2017-
where: {
1987+
await this.deleteBlobFromSQL({
20181988
accountName: account,
20191989
containerName: container,
20201990
blobName: blob,
20211991
snapshot: blobModel.snapshot
20221992
},
2023-
transaction: t
2024-
}
1993+
t
20251994
);
20261995
}
20271996

@@ -2030,32 +1999,19 @@ export default class SqlBlobMetadataStore implements IBlobMetadataStore {
20301999
againstBaseBlob &&
20312000
options.deleteSnapshots === Models.DeleteSnapshotsOptionType.Include
20322001
) {
2033-
await BlobsModel.update(
2034-
{
2035-
deleting: literal("deleting + 1")
2002+
await this.deleteBlobFromSQL({
2003+
accountName: account,
2004+
containerName: container,
2005+
blobName: blob
20362006
},
2037-
{
2038-
where: {
2039-
accountName: account,
2040-
containerName: container,
2041-
blobName: blob
2042-
},
2043-
transaction: t
2044-
}
2007+
t
20452008
);
20462009

2047-
await BlocksModel.update(
2048-
{
2049-
deleting: literal("deleting + 1")
2050-
},
2051-
{
2052-
where: {
2053-
accountName: account,
2054-
containerName: container,
2055-
blobName: blob
2056-
},
2057-
transaction: t
2058-
}
2010+
await this.deleteBlockFromSQL({
2011+
accountName: account,
2012+
containerName: container,
2013+
blobName: blob
2014+
},t
20592015
);
20602016
}
20612017

@@ -2064,19 +2020,13 @@ export default class SqlBlobMetadataStore implements IBlobMetadataStore {
20642020
againstBaseBlob &&
20652021
options.deleteSnapshots === Models.DeleteSnapshotsOptionType.Only
20662022
) {
2067-
await BlobsModel.update(
2068-
{
2069-
deleting: literal("deleting + 1")
2023+
await this.deleteBlobFromSQL({
2024+
accountName: account,
2025+
containerName: container,
2026+
blobName: blob,
2027+
snapshot: { [Op.gt]: "" }
20702028
},
2071-
{
2072-
where: {
2073-
accountName: account,
2074-
containerName: container,
2075-
blobName: blob,
2076-
snapshot: { [Op.gt]: "" }
2077-
},
2078-
transaction: t
2079-
}
2029+
t
20802030
);
20812031
}
20822032
});
@@ -3545,6 +3495,62 @@ export default class SqlBlobMetadataStore implements IBlobMetadataStore {
35453495
return Models.AccessTier.Cold;
35463496
}
35473497
return undefined;
3498+
}
3499+
3500+
/**
3501+
* Delete blob from SQL database.
3502+
* For performance, we used to mark deleting+1, instead of really delete. But this take issue like #2563. So change to real delete.
3503+
*
3504+
* @private
3505+
* @param {WhereOptions<any>} where
3506+
* @param {Transaction} [t]
3507+
* @returns {Promise<void>}
3508+
* @memberof SqlBlobMetadataStore
3509+
*/
3510+
private async deleteBlobFromSQL(where: WhereOptions<any>, t?: Transaction): Promise<void> {
3511+
await BlobsModel.destroy({
3512+
where,
3513+
transaction: t
3514+
});
3515+
3516+
// // TODO: GC blobs under deleting status
3517+
// await BlobsModel.update(
3518+
// {
3519+
// deleting: literal("deleting + 1")
3520+
// },
3521+
// {
3522+
// where,
3523+
// transaction: t
3524+
// }
3525+
// );
3526+
}
3527+
3528+
/**
3529+
* Delete block from SQL database.
3530+
* For performance, we used to mark deleting+1, instead of really delete. But this take issue like #2563. So change to real delete.
3531+
*
3532+
* @private
3533+
* @param {WhereOptions<any>} where
3534+
* @param {Transaction} [t]
3535+
* @returns {Promise<void>}
3536+
* @memberof SqlBlobMetadataStore
3537+
*/
3538+
private async deleteBlockFromSQL(where: WhereOptions<any>, t?: Transaction): Promise<void> {
3539+
await BlocksModel.destroy({
3540+
where,
3541+
transaction: t
3542+
});
3543+
3544+
// TODO: GC blocks under deleting status
3545+
// await BlocksModel.update(
3546+
// {
3547+
// deleting: literal("deleting + 1")
3548+
// },
3549+
// {
3550+
// where,
3551+
// transaction: t
3552+
// }
3553+
// );
35483554
}
35493555

35503556
/**

tests/blob/apis/container.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1675,4 +1675,41 @@ describe("ContainerAPIs", () => {
16751675
await blob.delete();
16761676
}
16771677
});
1678+
1679+
it("Delete a container with block blob, then create container/blob with same name, and delete container should success. @loki @sql", async function () {
1680+
//create container and block blob
1681+
const containerName = getUniqueName("container1");
1682+
const containerClient = serviceClient.getContainerClient(containerName);
1683+
await containerClient.create();
1684+
1685+
const blobName1 = getUniqueName("blobname1");
1686+
const blockBlobClient = containerClient.getBlockBlobClient(blobName1);
1687+
const body = "HelloWorld";
1688+
await blockBlobClient.stageBlock(base64encode("1"), body, body.length);
1689+
await blockBlobClient.stageBlock(base64encode("2"), body, body.length);
1690+
await blockBlobClient.commitBlockList(
1691+
[base64encode("1"), base64encode("2")]
1692+
);
1693+
1694+
// delete container
1695+
await containerClient.delete();
1696+
1697+
assert.strictEqual(false, await containerClient.exists());
1698+
assert.strictEqual(false, await blockBlobClient.exists());
1699+
1700+
//recreate
1701+
await containerClient.create();
1702+
1703+
await blockBlobClient.stageBlock(base64encode("1"), body, body.length);
1704+
await blockBlobClient.stageBlock(base64encode("2"), body, body.length);
1705+
await blockBlobClient.commitBlockList(
1706+
[base64encode("1"), base64encode("2")]
1707+
);
1708+
1709+
// delete container
1710+
await containerClient.delete();
1711+
1712+
assert.strictEqual(false, await containerClient.exists());
1713+
assert.strictEqual(false, await blockBlobClient.exists());
1714+
});
16781715
});

0 commit comments

Comments
 (0)