Skip to content

Add Blob Versioning Support to Azurite - #2735

Open
Rodolfo Orozco Vasquez (rorozcov) wants to merge 100 commits into
Azure:mainfrom
rorozcov:users/rorozcov/blobversioning
Open

Rodolfo Orozco Vasquez (rorozcov) wants to merge 100 commits into
Azure:mainfrom
rorozcov:users/rorozcov/blobversioning

Conversation

@rorozcov

@rorozcov Rodolfo Orozco Vasquez (rorozcov) commented Aug 13, 2026 •

Copy link
Copy Markdown

Title: Add Blob Versioning Support to Azurite

Summary

Adds Azure Blob Storage versioning support to Azurite, enabling applications to create, list, retrieve, and delete previous blob versions during local development.

Closes #665.

Account model

This work introduces the account model abstraction designed in collaboration with the Azurite team. The account model provides a central place for account-scoped feature configuration so additional Azure Storage capabilities can be added without coupling them directly to server startup or individual blob handlers.

Blob versioning is currently the first and only feature backed by this abstraction. The design supports Azurite's multi-account mode, allowing each configured account to enable or disable versioning independently. Accounts without an explicit versioning setting retain the existing non-versioned behavior for backward compatibility.

Motivation

Blob versioning automatically preserves previous blob states after modification or deletion. Without emulator support, applications that depend on version-aware workflows cannot be tested locally before deployment to Azure.

Implementation

  • Adds an account-level AccountModel setting for enabling blob versioning.
  • Supports file-based and inline JSON account configuration, including multiple accounts.
  • Persists account configuration in Loki metadata storage.
  • Creates version IDs as ISO 8601 timestamps when version-producing blob operations occur.
  • Supports retrieving and deleting a specific version through versionId.
  • Supports listing versions through includeVersions with version-aware continuation markers.
  • Preserves previous versions as immutable records while tracking the current version.
  • Adds versioning behavior across block, append, and page blob operations.
  • Adds VS Code extension settings for account configuration.
  • Preserves backward compatibility by defaulting accounts without versioning configuration to versioning disabled.

Configuration

Blob versioning can be configured using:

  • --accountConfigFilePath for JSON configuration files.
  • --accountConfigAsJson for inline JSON configuration.

Configured accounts must also be present in AZURITE_ACCOUNTS for authentication.

Behavior

  • Block blob writes create versions except for Put Block.
  • Page and append blobs create versions for Put Blob, Put Block List, Set Blob Metadata, and Copy Blob.
  • Put Page and Append Block do not create versions.
  • Previous versions can be read or deleted using their version ID.
  • Version IDs use ISO 8601 timestamps with seven fractional-second digits. JavaScript supplies the millisecond timestamp, and the final four digits distinguish versions created within the same millisecond.

Limitations

This change does not currently support:

  • Soft-delete integration
  • Blob expiration with versioning
  • SAS URIs targeting specific versions
  • Version-level immutability policies

Validation

  • TypeScript build and lint pass.
  • Blob version pagination tests pass.
  • Startup and persisted-data upgrade regression tests pass with versioning disabled.

…tore. Now local testing needed. Saving progress
…tests with versioning enabled. Must add versioning related checks

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +3158 to +3164
} else {
// Commit block list
doc.properties.blobType = blob.properties.blobType;
doc.properties.lastModified = blob.properties.lastModified;
doc.committedBlocksInOrder = selectedBlockList;
doc.isCommitted = true;
doc.metadata = blob.metadata;
Copilot AI lite review requested due to automatic review settings October 13, 2026 10:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Unresolved correctness and compatibility issues remain in server construction, SQL persistence, version handling, pagination, configuration, and VS Code logging.

8 open findings
Previously missed (1)

In code that hasn't changed since last review

Medium severity SQL listing mishandles legacy continuation markers

src/​blob/​persistence/​SqlBlobMetadataStore.ts:1471

For a legacy continuation token, decodeBlobListMarker returns recordId = -1, so this SQL predicate admits every row with the same blob name (blobId > -1) instead of preserving the old name-exclusive marker semantics. A resumed internal listing can reprocess same-name records; handle legacy markers with only blobName > markerName before constructing the tuple predicate.

🧠 Review effort: Lite

Copilot AI lite review requested due to automatic review settings October 14, 2026 11:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Unresolved moderate issues remain in server compatibility, SQL copy/version handling, block-list retrieval, listing parity, marker parsing, validation, and VS Code integration.

7 open findings
1 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Medium severity Reject array account configurations

src/​common/​EnvironmentFunctions.ts:182

The parser claims each account configuration must be a JSON object, but arrays pass this check because typeof [] === "object"; an input such as account1:[{"isBlobVersioningEnabled":true}] is silently accepted and defaults versioning to false. Reject arrays explicitly so malformed configuration cannot disable the requested feature without an error.

🧠 Review effort: Lite

Copilot AI lite review requested due to automatic review settings October 14, 2026 11:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +1457 to +1471
const { name: markerName, recordId: markerRecordId } =
decodeBlobListMarker(marker);
whereQuery[Op.or] = [
{
blobName: {
[Op.gt]: markerName
}
},
{
blobName: markerName,
blobId: {
[Op.gt]: markerRecordId
}
}
];
Copilot AI lite review requested due to automatic review settings October 14, 2026 12:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved moderate and critical findings affect startup compatibility, VS Code operation, persistence isolation, validation, and pagination.

9 open findings
Previously missed (1)

In code that hasn't changed since last review

Medium severity In-memory mode incorrectly loads persistent account settings

src/​common/​account/​LokiAccountModelStore.ts:115

The inMemory mode is not honored while opening the account store: an existing database file is still loaded unconditionally. If Azurite is restarted with --inMemoryPersistence after a persistent run, old account settings (including enabling versioning) are resurrected instead of starting empty. Skip the stat/load path when this.inMemory is true.

🧠 Review effort: Lite

Comment thread src/common/VSCServerManagerBlob.ts Outdated
Copilot AI lite review requested due to automatic review settings October 15, 2026 07:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot AI lite review requested due to automatic review settings October 15, 2026 07:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved compatibility, listing, continuation-token, version-response, blob-type, and SQL behavior issues remain.

8 open findings
Previously missed (1)

In code that hasn't changed since last review

Medium severity Legacy blob-list tokens re-emit same-name records

src/​blob/​persistence/​LokiBlobMetadataStore.ts:1231

Legacy continuation tokens from the previous listAllBlobs implementation were plain blob names and meant that every record with that name had already been returned. Decoding such a token gives recordId = -1, so this predicate now re-emits every same-name record ($loki > -1) instead of skipping that name; this can make the GC iterator process old records again after an upgrade. Branch on isLegacyBlobListMarker(decodedMarker) and use only obj.name > markerName for legacy tokens.

🧠 Review effort: Lite

Comment on lines +4193 to +4195
if (!this.isBlobVersioningEnabled(account)) {
return;
}
Copilot AI lite review requested due to automatic review settings October 15, 2026 10:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Unresolved issues remain in nullable VS Code schemas, SQL version-copy handling, version responses, uncommitted-record retention, and legacy pagination markers.

7 open findings
1 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Medium severity Do not preserve uncommitted blobs as versions

src/​blob/​persistence/​LokiBlobMetadataStore.ts:1326

When a versioned account overwrites an uncommitted blob created by Put Block, this branch promotes that uncommitted record into a previous version and leaves it in the blob collection. Uncommitted records are not valid blob versions and can retain stale metadata/blocks for GC and later listings. Only preserve the existing record when isCommitted !== false; otherwise remove it as the pre-existing non-versioned behavior does.

🧠 Review effort: Lite

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enable Blob versioning

5 participants