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
15 changes: 14 additions & 1 deletion ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@

## Upcoming Release

General:

- Applied npm audit fix to updates across multiple dependencies to address security vulnerabilities and maintenance updates.
- Bumped the default Blob, Queue, and Table service API version to `2026-06-06`.
- Added support for service API versions `2026-04-06` and `2026-02-06` for Blob, Queue, and Table endpoints.

Blob:

- Fixed issue #2672 startup failures with legacy persisted data by adding backward-compatible restore for persisted `contentMD5` formats.

Queue:

- Fixed issue #2672 startup race condition by avoiding GC-triggered `close()` calls while server status is `Starting`.

## 2026.06 Version 3.36.0

General:
Expand All @@ -22,7 +36,6 @@ General:
- Fix Windows SEA executable build producing a corrupted/bad `.exe` by stripping the Authenticode signature from the copied Node.js binary before injecting the SEA blob via `postject`.
- Replace the hardcoded user delegation signing key literal with a deterministic internal signing seed to avoid VS Code extension publish secret-scan blocks while preserving stable user delegation SAS behavior.


Blob:

- Remove the default value for the `BlobSequenceNumber` parameter in the Swagger definition.
Expand Down
484 changes: 257 additions & 227 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@
"@azure/ms-rest-js": "^2.7.0",
"applicationinsights": "^2.9.6",
"args": "^5.0.1",
"axios": "^1.17.0",
"axios": "^1.18.1",
"etag": "^1.8.1",
"express": "^4.16.4",
"fs-extra": "^11.1.1",
"glob-to-regexp": "^0.4.1",
"jsonwebtoken": "^9.0.0",
"lokijs": "^1.5.6",
"morgan": "^1.9.1",
"morgan": "^1.11.0",
"multistream": "^2.1.1",
"mysql2": "^3.10.1",
"rimraf": "^3.0.2",
Expand Down Expand Up @@ -74,7 +74,7 @@
"autorest": "^3.6.0",
"cross-env": "^7.0.3",
"cross-var": "^1.1.0",
"esbuild": "^0.25.5",
"esbuild": "^0.28.1",
"eslint": "^8.35.0",
"find-process": "^1.4.4",
"husky": "^8.0.1",
Expand Down Expand Up @@ -354,4 +354,4 @@
"url": "https://github.com/azure/azurite/issues"
},
"homepage": "https://github.com/azure/azurite#readme"
}
}
Empty file modified scripts/azurite-cli.sh
100644 → 100755
Empty file.
59 changes: 40 additions & 19 deletions src/azurite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import TableServer from "./table/TableServer";
import { DEFAULT_TABLE_LOKI_DB_PATH } from "./table/utils/constants";
import { setExtentMemoryLimit } from "./common/ConfigurationBase";
import { AzuriteTelemetryClient } from "./common/Telemetry";
import { ServerStatus } from "./common/ServerBase";

// tslint:disable:no-console

Expand All @@ -42,30 +43,50 @@ function shutdown(

AzuriteTelemetryClient.TraceStopEvent();

console.log(blobBeforeCloseMessage);
blobServer.close().then(() => {
console.log(blobAfterCloseMessage);
});

console.log(queueBeforeCloseMessage);
queueServer.close().then(() => {
console.log(queueAfterCloseMessage);
});

console.log(tableBeforeCloseMessage);
tableServer.close().then(() => {
console.log(tableAfterCloseMessage);
});
const closeWithDiagnostics = (
beforeMessage: string,
afterMessage: string,
server: { getStatus(): ServerStatus; close(): Promise<void> }
) => {
const statusBeforeClose = server.getStatus();
console.log(beforeMessage);
server
.close()
.then(() => {
console.log(afterMessage);
})
.catch((err) => {
const errorMsg = err instanceof Error ? err.message : String(err);
console.error(
`Shutdown close failed in status ${statusBeforeClose}: ${errorMsg}`
);
Comment thread
jainakanksha-msft marked this conversation as resolved.
Comment thread
jainakanksha-msft marked this conversation as resolved.
});
};

closeWithDiagnostics(
blobBeforeCloseMessage,
blobAfterCloseMessage,
blobServer
);
closeWithDiagnostics(
queueBeforeCloseMessage,
queueAfterCloseMessage,
queueServer
);
closeWithDiagnostics(
tableBeforeCloseMessage,
tableAfterCloseMessage,
tableServer
);
}

/**
* Entry for Azurite services.
*/
async function main() {

// Initialize and validate environment values from command line parameters
const env = new Environment();

const location = await env.location();
await ensureDir(location);
await access(location);
Expand Down Expand Up @@ -105,7 +126,7 @@ async function main() {
env.pwd(),
env.oauth(),
env.disableProductStyleUrl(),
env.inMemoryPersistence(),
env.inMemoryPersistence()
);

const tableConfig = new TableConfiguration(
Expand All @@ -124,7 +145,7 @@ async function main() {
env.pwd(),
env.oauth(),
env.disableProductStyleUrl(),
env.inMemoryPersistence(),
env.inMemoryPersistence()
);

// We use logger singleton as global debugger logger to track detailed outputs cross layers
Expand Down Expand Up @@ -167,7 +188,7 @@ async function main() {
console.log(
`Azurite Table service is successfully listening at ${tableServer.getHttpServerAddress()}`
);

AzuriteTelemetryClient.init(location, !env.disableTelemetry(), env);
await AzuriteTelemetryClient.TraceStartEvent();

Expand Down
85 changes: 52 additions & 33 deletions src/blob/BlobServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ import IGCManager from "../common/IGCManager";
import IRequestListenerFactory from "../common/IRequestListenerFactory";
import logger from "../common/Logger";
import FSExtentStore from "../common/persistence/FSExtentStore";
import MemoryExtentStore, { SharedChunkStore } from "../common/persistence/MemoryExtentStore";
import MemoryExtentStore, {
SharedChunkStore
} from "../common/persistence/MemoryExtentStore";
import IExtentMetadataStore from "../common/persistence/IExtentMetadataStore";
import IExtentStore from "../common/persistence/IExtentStore";
import LokiExtentMetadataStore from "../common/persistence/LokiExtentMetadataStore";
import { handleGCCriticalErrorClose } from "../common/GCCriticalErrorCloseHelper";
import ServerBase, { ServerStatus } from "../common/ServerBase";
import BlobConfiguration from "./BlobConfiguration";
import BlobRequestListenerFactory from "./BlobRequestListenerFactory";
Expand Down Expand Up @@ -79,39 +82,43 @@ export default class BlobServer extends ServerBase implements ICleaner {
configuration.isMemoryPersistence
);

const extentMetadataStore: IExtentMetadataStore = new LokiExtentMetadataStore(
configuration.extentDBPath,
configuration.isMemoryPersistence
);

const extentStore: IExtentStore = configuration.isMemoryPersistence ? new MemoryExtentStore(
"blob",
configuration.memoryStore ?? SharedChunkStore,
extentMetadataStore,
logger,
(sc, er, em, ri) => new StorageError(sc, er, em, ri)
) : new FSExtentStore(
extentMetadataStore,
configuration.persistencePathArray,
logger
);
const extentMetadataStore: IExtentMetadataStore =
new LokiExtentMetadataStore(
configuration.extentDBPath,
configuration.isMemoryPersistence
);

const extentStore: IExtentStore = configuration.isMemoryPersistence
? new MemoryExtentStore(
"blob",
configuration.memoryStore ?? SharedChunkStore,
extentMetadataStore,
logger,
(sc, er, em, ri) => new StorageError(sc, er, em, ri)
)
: new FSExtentStore(
extentMetadataStore,
configuration.persistencePathArray,
logger
);

const accountDataStore: IAccountDataStore = new AccountDataStore(logger);

// We can also change the HTTP framework here by
// creating a new XXXListenerFactory implementing IRequestListenerFactory interface
// and replace the default Express based request listener
const requestListenerFactory: IRequestListenerFactory = new BlobRequestListenerFactory(
metadataStore,
extentStore,
accountDataStore,
configuration.enableAccessLog, // Access log includes every handled HTTP request
configuration.accessLogWriteStream,
configuration.loose,
configuration.skipApiVersionCheck,
configuration.getOAuthLevel(),
configuration.disableProductStyleUrl
);
const requestListenerFactory: IRequestListenerFactory =
new BlobRequestListenerFactory(
metadataStore,
extentStore,
accountDataStore,
configuration.enableAccessLog, // Access log includes every handled HTTP request
configuration.accessLogWriteStream,
configuration.loose,
configuration.skipApiVersionCheck,
configuration.getOAuthLevel(),
configuration.disableProductStyleUrl
);

super(host, port, httpServer, requestListenerFactory, configuration);

Expand All @@ -121,14 +128,26 @@ export default class BlobServer extends ServerBase implements ICleaner {
metadataStore,
extentMetadataStore,
extentStore,
() => {
(err: Error) => {
// tslint:disable-next-line:no-console
console.log(BEFORE_CLOSE_MESSAGE_GC_ERROR);
logger.info(BEFORE_CLOSE_MESSAGE_GC_ERROR);
this.close().then(() => {
// tslint:disable-next-line:no-console
console.log(AFTER_CLOSE_MESSAGE);
logger.info(AFTER_CLOSE_MESSAGE);
logger.error(
`Blob GC Manager critical error: ${err.message}\nStack: ${err.stack}`
);
logger.warn(
"This may be due to: " +
"1) Persisted database format changes, " +
"2) File system permission issues, " +
"3) Corrupted metadata files. " +
"See error details above."
);

handleGCCriticalErrorClose({
serviceName: "Blob",
getStatus: () => this.status,
close: () => this.close(),
logger
Comment thread
jainakanksha-msft marked this conversation as resolved.
});
Comment thread
jainakanksha-msft marked this conversation as resolved.
},
logger
Expand Down
49 changes: 26 additions & 23 deletions src/blob/SqlBlobServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { CertOptions } from "../common/ConfigurationBase";
import IAccountDataStore from "../common/IAccountDataStore";
import IGCManager from "../common/IGCManager";
import IRequestListenerFactory from "../common/IRequestListenerFactory";
import { handleGCCriticalErrorClose } from "../common/GCCriticalErrorCloseHelper";
import logger from "../common/Logger";
import FSExtentStore from "../common/persistence/FSExtentStore";
import IExtentMetadataStore from "../common/persistence/IExtentMetadataStore";
Expand Down Expand Up @@ -69,12 +70,13 @@ export default class SqlBlobServer extends ServerBase {
configuration.sequelizeOptions
);

const extentMetadataStore: IExtentMetadataStore = new SqlExtentMetadataStore(
// Currently, extent metadata and blob metadata share same database
// But they can use separate databases per future requirements
configuration.sqlURL,
configuration.sequelizeOptions
);
const extentMetadataStore: IExtentMetadataStore =
new SqlExtentMetadataStore(
// Currently, extent metadata and blob metadata share same database
// But they can use separate databases per future requirements
configuration.sqlURL,
configuration.sequelizeOptions
);

const extentStore: IExtentStore = new FSExtentStore(
extentMetadataStore,
Expand All @@ -87,17 +89,18 @@ export default class SqlBlobServer extends ServerBase {
// We can also change the HTTP framework here by
// creating a new XXXListenerFactory implementing IRequestListenerFactory interface
// and replace the default Express based request listener
const requestListenerFactory: IRequestListenerFactory = new BlobRequestListenerFactory(
metadataStore,
extentStore,
accountDataStore,
configuration.enableAccessLog, // Access log includes every handled HTTP request
configuration.accessLogWriteStream,
configuration.loose,
configuration.skipApiVersionCheck,
configuration.getOAuthLevel(),
configuration.disableProductStyleUrl
);
const requestListenerFactory: IRequestListenerFactory =
new BlobRequestListenerFactory(
metadataStore,
extentStore,
accountDataStore,
configuration.enableAccessLog, // Access log includes every handled HTTP request
configuration.accessLogWriteStream,
configuration.loose,
configuration.skipApiVersionCheck,
configuration.getOAuthLevel(),
configuration.disableProductStyleUrl
);

super(host, port, httpServer, requestListenerFactory, configuration);

Expand All @@ -107,16 +110,16 @@ export default class SqlBlobServer extends ServerBase {
metadataStore,
extentMetadataStore,
extentStore,
error => {
(error) => {
// tslint:disable-next-line:no-console
console.log(BEFORE_CLOSE_MESSAGE_GC_ERROR, error);
logger.info(BEFORE_CLOSE_MESSAGE_GC_ERROR + JSON.stringify(error));

// TODO: Bring this back when GC based on SQL implemented
this.close().then(() => {
// tslint:disable-next-line:no-console
console.log(AFTER_CLOSE_MESSAGE);
logger.info(AFTER_CLOSE_MESSAGE);
handleGCCriticalErrorClose({
serviceName: "Blob",
getStatus: () => this.status,
close: () => this.close(),
logger
});
},
logger
Expand Down
Loading
Loading