Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow disk-based persistence to be completely disabled #2228

Merged
merged 39 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
a187cf6
Allow in-memory Loki and extent store
joelverhagen Oct 15, 2023
af85ace
Update config
joelverhagen Oct 15, 2023
b3846f8
Plumb config
joelverhagen Oct 15, 2023
6532e5a
Switch to newer buffer stream library
joelverhagen Oct 15, 2023
d00464e
Add the config in more places and update markdowns
joelverhagen Oct 15, 2023
58aa6f0
Use built-in stream
joelverhagen Oct 16, 2023
a255c7f
Refactor table creation to a single factory
joelverhagen Oct 17, 2023
657879f
Move in-memory persistence to an env var
joelverhagen Oct 17, 2023
fca212e
Add QueueTestServerFactory to centralize test server creation
joelverhagen Oct 17, 2023
c3aee91
Properly handle offset and count in extents
joelverhagen Oct 17, 2023
939291a
Add in-memory tests to all variants
joelverhagen Oct 17, 2023
2ae4746
Fix two flaky tests
joelverhagen Oct 17, 2023
b006592
Make random local files have some newline characters so diff tools ar…
joelverhagen Oct 17, 2023
ba1410b
Use a larger buffer for the download, the test is very slow otherwise
joelverhagen Oct 17, 2023
7d2bf7f
Add design spec
joelverhagen Oct 17, 2023
b0f382f
Add logging to MemoryExtentStore
joelverhagen Oct 17, 2023
481c5d1
Make queue GC logging consistent with blob GC logging
joelverhagen Oct 17, 2023
d7c707a
Add VS Code option
joelverhagen Oct 17, 2023
7379048
Improve setting description
joelverhagen Oct 17, 2023
cda16fb
Don't include docs in the VS Code extension
joelverhagen Oct 18, 2023
cb0a6bb
Merge remote-tracking branch 'azure/main' into in-memory
joelverhagen Oct 20, 2023
a85519d
Fix merge
joelverhagen Oct 20, 2023
a05dfd8
Prevent SQL and in-memory persistence at the same time
joelverhagen Oct 20, 2023
c371f77
Add MemoryExtentChunkStore for shared in-memory size tracking
joelverhagen Oct 20, 2023
350dee7
Use a shared MemoryExtentChunkStore by default
joelverhagen Oct 20, 2023
057df96
Align Table and Queue cleaning approach with Blob
joelverhagen Oct 25, 2023
a028724
Add extentMemoryLimit option per review feedback
joelverhagen Oct 25, 2023
724b733
Merge remote-tracking branch 'azure/main' into in-memory
joelverhagen Oct 25, 2023
afb98fd
Update spec with comments and add note about slower memory release
joelverhagen Oct 25, 2023
0daf51c
Reject --location along with --inMemoryPersistence
joelverhagen Oct 26, 2023
97acd4e
Fail fast on the binary tests if the binaries don't exist
joelverhagen Oct 26, 2023
68938c8
Properly tag tests
joelverhagen Oct 26, 2023
6928602
Polish READMEs
joelverhagen Oct 26, 2023
8d43f65
Address code review comments
joelverhagen Nov 4, 2023
12dee72
Eliminate unnecessary diff noise
joelverhagen Nov 4, 2023
17906ab
Change extentMemoryLimit to use megabytes as the unit
joelverhagen Nov 5, 2023
d9de373
Address comments
joelverhagen Nov 8, 2023
803e27c
Update README
joelverhagen Nov 14, 2023
9b91305
Fix typo
joelverhagen Nov 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 13 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@
"skipFiles": ["node_modules/*/**", "<node_internals>/*/**"],
"outputCapture": "std"
},
{
"type": "node",
"request": "launch",
"name": "Azurite Service - Loki, in-memory",
"cwd": "${workspaceFolder}",
"runtimeArgs": ["-r", "ts-node/register"],
"args": ["${workspaceFolder}/src/azurite.ts", "-d", "debug.log", "--inMemoryPersistence"],
"env": {
"AZURITE_ACCOUNTS": ""
},
"skipFiles": ["node_modules/*/**", "<node_internals>/*/**"],
"outputCapture": "std"
},
{
"type": "node",
"request": "launch",
Expand Down
4 changes: 4 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

## Upcoming Release

General:

- Add `--inMemoryStorage` option and related configs to persist all data in-memory without disk persistence. (issue #2227)

Blob:

- Fix validation of Blob SAS token when using the second key for an account in `AZURITE_ACCOUNTS`
Expand Down
2 changes: 2 additions & 0 deletions README.mcr.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ Above command will try to start Azurite image with configurations:

`--disableProductStyleUrl` force parsing storage account name from request Uri path, instead of from request Uri host.

`--inMemoryPersistence` disable persisting any data to disk. If the Azurite process is terminated, all data is lost.

> If you use customized azurite paramters for docker image, `--blobHost 0.0.0.0`, `--queueHost 0.0.0.0` are required parameters.

> In above sample, you need to use **double first forward slash** for location and debug path parameters to avoid a [known issue](https://stackoverflow.com/questions/48427366/docker-build-command-add-c-program-files-git-to-the-path-passed-as-build-argu) for Git on Windows.
Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ Following extension configurations are supported:
- `azurite.oauth` OAuth oauthentication level. Candidate level values: `basic`.
- `azurite.skipApiVersionCheck` Skip the request API version check, by default false.
- `azurite.disableProductStyleUrl` Force parsing storage account name from request Uri path, instead of from request Uri host.
- `azurite.inMemoryPersistence` Disable persisting any data to disk. If the Azurite process is terminated, all data is lost.

### [DockerHub](https://hub.docker.com/_/microsoft-azure-storage-azurite)

Expand Down Expand Up @@ -257,6 +258,8 @@ Above command will try to start Azurite image with configurations:

`--disableProductStyleUrl` force parsing storage account name from request Uri path, instead of from request Uri host.

`--inMemoryPersistence` disable persisting any data to disk. If the Azurite process is terminated, all data is lost.

> If you use customized azurite paramters for docker image, `--blobHost 0.0.0.0`, `--queueHost 0.0.0.0` are required parameters.

> In above sample, you need to use **double first forward slash** for location and debug path parameters to avoid a [known issue](https://stackoverflow.com/questions/48427366/docker-build-command-add-c-program-files-git-to-the-path-passed-as-build-argu) for Git on Windows.
Expand Down Expand Up @@ -430,6 +433,16 @@ Optional. When using FQDN instead of IP in request Uri host, by default Azurite
--disableProductStyleUrl
```

### Use in-memory storage

Optional. Disable persisting any data to disk. If the Azurite process is terminated, all data is lost.
By default, LokiJS persists blob and queue metadata to disk and content to extent files. Table storage
persists all data to disk. This behavior can be disabled using this option.

```cmd
--inMemoryStorage
```

### Command Line Options Differences between Azurite V2

Azurite V3 supports SharedKey, Account Shared Access Signature (SAS), Service SAS, OAuth, and Public Container Access authentications, you can use any Azure Storage SDKs or tools like Storage Explorer to connect Azurite V3 with any authentication strategy.
Expand Down
6 changes: 4 additions & 2 deletions src/azurite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ async function main() {
env.key(),
env.pwd(),
env.oauth(),
env.disableProductStyleUrl()
env.disableProductStyleUrl(),
env.inMemoryPersistence(),
);

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

// We use logger singleton as global debugger logger to track detailed outputs cross layers
Expand Down
6 changes: 4 additions & 2 deletions src/blob/BlobConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ export default class BlobConfiguration extends ConfigurationBase {
key: string = "",
pwd: string = "",
oauth?: string,
disableProductStyleUrl: boolean = false
disableProductStyleUrl: boolean = false,
isMemoryPersistence: boolean = false,
) {
super(
host,
Expand All @@ -54,7 +55,8 @@ export default class BlobConfiguration extends ConfigurationBase {
key,
pwd,
oauth,
disableProductStyleUrl
disableProductStyleUrl,
isMemoryPersistence,
);
}
}
11 changes: 11 additions & 0 deletions src/blob/BlobEnvironment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ if (!(args as any).config.name) {
.option(["", "oauth"], 'Optional. OAuth level. Candidate values: "basic"')
.option(["", "cert"], "Optional. Path to certificate file")
.option(["", "key"], "Optional. Path to certificate key .pem file")
.option(
["", "inMemoryPersistence"],
"Optional. Disable persisting any data to disk. If the Azurite process is terminated, all data is lost."
)
.option(
["d", "debug"],
"Optional. Enable debug log by providing a valid local file path as log destination"
Expand Down Expand Up @@ -118,6 +122,13 @@ export default class BlobEnvironment implements IBlobEnvironment {
return false;
}

public inMemoryPersistence(): boolean {
if (this.flags.inMemoryPersistence !== undefined) {
return true;
}
return false;
}

public async debug(): Promise<string | undefined> {
if (typeof this.flags.debug === "string") {
// Enable debug log to file
Expand Down
13 changes: 9 additions & 4 deletions src/blob/BlobServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import IGCManager from "../common/IGCManager";
import IRequestListenerFactory from "../common/IRequestListenerFactory";
import logger from "../common/Logger";
import FSExtentStore from "../common/persistence/FSExtentStore";
import MemoryExtentStore from "../common/persistence/MemoryExtentStore";
import IExtentMetadataStore from "../common/persistence/IExtentMetadataStore";
import IExtentStore from "../common/persistence/IExtentStore";
import LokiExtentMetadataStore from "../common/persistence/LokiExtentMetadataStore";
Expand Down Expand Up @@ -73,15 +74,19 @@ export default class BlobServer extends ServerBase implements ICleaner {
// creating a new XXXDataStore class implementing IBlobMetadataStore interface
// and replace the default LokiBlobMetadataStore
const metadataStore: IBlobMetadataStore = new LokiBlobMetadataStore(
configuration.metadataDBPath
// logger
configuration.metadataDBPath,
configuration.isMemoryPersistence
);

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

const extentStore: IExtentStore = new FSExtentStore(
const extentStore: IExtentStore = configuration.isMemoryPersistence ? new MemoryExtentStore(
extentMetadataStore,
logger
) : new FSExtentStore(
extentMetadataStore,
configuration.persistencePathArray,
logger
Expand Down
6 changes: 4 additions & 2 deletions src/blob/BlobServerFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ export class BlobServerFactory {
env.key(),
env.pwd(),
env.oauth(),
env.disableProductStyleUrl()
env.disableProductStyleUrl(),
env.inMemoryPersistence(),
);

return new SqlBlobServer(config);
Expand All @@ -79,7 +80,8 @@ export class BlobServerFactory {
env.key(),
env.pwd(),
env.oauth(),
env.disableProductStyleUrl()
env.disableProductStyleUrl(),
env.inMemoryPersistence(),
);
return new BlobServer(config);
}
Expand Down
1 change: 1 addition & 0 deletions src/blob/IBlobEnvironment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export default interface IBlobEnvironment {
debug(): Promise<string | boolean | undefined>;
oauth(): string | undefined;
disableProductStyleUrl(): boolean;
inMemoryPersistence(): boolean;
}
6 changes: 4 additions & 2 deletions src/blob/SqlBlobConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ export default class SqlBlobConfiguration extends ConfigurationBase {
key: string = "",
pwd: string = "",
oauth?: string,
disableProductStyleUrl: boolean = false
disableProductStyleUrl: boolean = false,
isMemoryPersistence: boolean = false,
) {
super(
host,
Expand All @@ -50,7 +51,8 @@ export default class SqlBlobConfiguration extends ConfigurationBase {
key,
pwd,
oauth,
disableProductStyleUrl
disableProductStyleUrl,
isMemoryPersistence,
);
}
}
6 changes: 5 additions & 1 deletion src/blob/SqlBlobServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import IGCManager from "../common/IGCManager";
import IRequestListenerFactory from "../common/IRequestListenerFactory";
import logger from "../common/Logger";
import FSExtentStore from "../common/persistence/FSExtentStore";
import MemoryExtentStore from "../common/persistence/MemoryExtentStore";
import IExtentMetadataStore from "../common/persistence/IExtentMetadataStore";
import IExtentStore from "../common/persistence/IExtentStore";
import SqlExtentMetadataStore from "../common/persistence/SqlExtentMetadataStore";
Expand Down Expand Up @@ -76,7 +77,10 @@ export default class SqlBlobServer extends ServerBase {
configuration.sequelizeOptions
);

const extentStore: IExtentStore = new FSExtentStore(
const extentStore: IExtentStore = configuration.isMemoryPersistence ? new MemoryExtentStore(
extentMetadataStore,
logger
) : new FSExtentStore(
extentMetadataStore,
configuration.persistenceArray,
logger
Expand Down
7 changes: 5 additions & 2 deletions src/blob/persistence/LokiBlobMetadataStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,11 @@ export default class LokiBlobMetadataStore

private readonly pageBlobRangesManager = new PageBlobRangesManager();

public constructor(public readonly lokiDBPath: string) {
this.db = new Loki(lokiDBPath, {
public constructor(public readonly lokiDBPath: string, inMemory: boolean) {
this.db = new Loki(lokiDBPath, inMemory ? {
persistenceMethod: "memory"
} : {
persistenceMethod: "fs",
autosave: true,
autosaveInterval: 5000
});
Expand Down
1 change: 1 addition & 0 deletions src/common/ConfigurationBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export default abstract class ConfigurationBase {
public readonly pwd: string = "",
public readonly oauth?: string,
public readonly disableProductStyleUrl: boolean = false,
public readonly isMemoryPersistence: boolean = false,
) {}

public hasCert() {
Expand Down
11 changes: 11 additions & 0 deletions src/common/Environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ args
.option(["", "cert"], "Optional. Path to certificate file")
.option(["", "key"], "Optional. Path to certificate key .pem file")
.option(["", "pwd"], "Optional. Password for .pfx file")
.option(
["", "inMemoryPersistence"],
"Optional. Disable persisting any data to disk. If the Azurite process is terminated, all data is lost."
)
.option(
["d", "debug"],
"Optional. Enable debug log by providing a valid local file path as log destination"
Expand Down Expand Up @@ -155,6 +159,13 @@ export default class Environment implements IEnvironment {
return this.flags.oauth;
}

public inMemoryPersistence(): boolean {
if (this.flags.inMemoryPersistence !== undefined) {
return true;
}
return false;
}

public async debug(): Promise<string | undefined> {
if (typeof this.flags.debug === "string") {
// Enable debug log to file
Expand Down
4 changes: 3 additions & 1 deletion src/common/IEnvironment.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import IBlobEnvironment from "../blob/IBlobEnvironment";
import IQueueEnvironment from "../queue/IQueueEnvironment";
import ITableEnvironment from "../table/ITableEnvironment";

export default interface IEnvironment
extends IBlobEnvironment,
IQueueEnvironment {}
IQueueEnvironment,
ITableEnvironment { }
4 changes: 4 additions & 0 deletions src/common/VSCEnvironment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,8 @@ export default class VSCEnvironment implements IEnvironment {
this.workspaceConfiguration.get<boolean>("disableProductStyleUrl") || false
);
}

public inMemoryPersistence(): boolean {
return this.workspaceConfiguration.get<boolean>("inMemoryPersistence") || false;
}
}
3 changes: 2 additions & 1 deletion src/common/VSCServerManagerBlob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ export default class VSCServerManagerBlob extends VSCServerManagerBase {
env.key(),
env.pwd(),
env.oauth(),
env.disableProductStyleUrl()
env.disableProductStyleUrl(),
env.inMemoryPersistence(),
);
return config;
}
Expand Down
7 changes: 5 additions & 2 deletions src/common/persistence/LokiExtentMetadataStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@ export default class LokiExtentMetadata implements IExtentMetadataStore {

private readonly EXTENTS_COLLECTION = "$EXTENTS_COLLECTION$";

public constructor(public readonly lokiDBPath: string) {
this.db = new Loki(lokiDBPath, {
public constructor(public readonly lokiDBPath: string, inMemory: boolean) {
this.db = new Loki(lokiDBPath, inMemory ? {
persistenceMethod: "memory"
} : {
persistenceMethod: "fs",
autosave: true,
autosaveInterval: 5000
});
Expand Down