Merge from vscode 5b9869eb02fa4c96205a74d05cad9164dfd06d60 (#5607)

This commit is contained in:
Anthony Dresser
2019-05-24 12:20:30 -07:00
committed by GitHub
parent 361ada4963
commit bcc449b524
126 changed files with 3096 additions and 2255 deletions

View File

@@ -45,20 +45,21 @@ export class GlobalStorageDatabaseChannel extends Disposable implements IServerC
this.whenReady = this.init();
}
private init(): Promise<void> {
return this.storageMainService.initialize().then(undefined, error => {
private async init(): Promise<void> {
try {
await this.storageMainService.initialize();
} catch (error) {
onUnexpectedError(error);
this.logService.error(error);
}).then(() => {
}
// Apply global telemetry values as part of the initialization
// These are global across all windows and thereby should be
// written from the main process once.
this.initTelemetry();
// Apply global telemetry values as part of the initialization
// These are global across all windows and thereby should be
// written from the main process once.
this.initTelemetry();
// Setup storage change listeners
this.registerListeners();
});
// Setup storage change listeners
this.registerListeners();
}
private initTelemetry(): void {
@@ -112,33 +113,39 @@ export class GlobalStorageDatabaseChannel extends Disposable implements IServerC
throw new Error(`Event not found: ${event}`);
}
call(_: unknown, command: string, arg?: any): Promise<any> {
async call(_: unknown, command: string, arg?: any): Promise<any> {
// ensure to always wait for ready
await this.whenReady;
// handle call
switch (command) {
case 'getItems': {
return this.whenReady.then(() => mapToSerializable(this.storageMainService.items));
return mapToSerializable(this.storageMainService.items);
}
case 'updateItems': {
return this.whenReady.then(() => {
const items: ISerializableUpdateRequest = arg;
if (items.insert) {
for (const [key, value] of items.insert) {
this.storageMainService.store(key, value);
}
const items: ISerializableUpdateRequest = arg;
if (items.insert) {
for (const [key, value] of items.insert) {
this.storageMainService.store(key, value);
}
}
if (items.delete) {
items.delete.forEach(key => this.storageMainService.remove(key));
}
});
if (items.delete) {
items.delete.forEach(key => this.storageMainService.remove(key));
}
break;
}
case 'checkIntegrity': {
return this.whenReady.then(() => this.storageMainService.checkIntegrity(arg));
return this.storageMainService.checkIntegrity(arg);
}
}
throw new Error(`Call not found: ${command}`);
default:
throw new Error(`Call not found: ${command}`);
}
}
}
@@ -167,8 +174,10 @@ export class GlobalStorageDatabaseChannelClient extends Disposable implements IS
}
}
getItems(): Promise<Map<string, string>> {
return this.channel.call('getItems').then((data: Item[]) => serializableToMap(data));
async getItems(): Promise<Map<string, string>> {
const items: Item[] = await this.channel.call('getItems');
return serializableToMap(items);
}
updateItems(request: IUpdateRequest): Promise<void> {

View File

@@ -10,7 +10,6 @@ import { ILogService, LogLevel } from 'vs/platform/log/common/log';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IStorage, Storage, SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions, InMemoryStorageDatabase } from 'vs/base/node/storage';
import { join } from 'vs/base/common/path';
import { exists } from 'vs/base/node/pfs';
export const IStorageMainService = createDecorator<IStorageMainService>('storageMainService');
@@ -121,25 +120,14 @@ export class StorageMainService extends Disposable implements IStorageMainServic
}
private doInitialize(): Promise<void> {
const useInMemoryStorage = this.storagePath === SQLiteStorageDatabase.IN_MEMORY_PATH;
this.storage.dispose();
this.storage = new Storage(new SQLiteStorageDatabase(this.storagePath, {
logging: this.createLogginOptions()
}));
let globalStorageExists: Promise<boolean>;
if (useInMemoryStorage) {
globalStorageExists = Promise.resolve(true);
} else {
globalStorageExists = exists(this.storagePath);
}
this._register(this.storage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key })));
return globalStorageExists.then(exists => {
this.storage.dispose();
this.storage = new Storage(new SQLiteStorageDatabase(this.storagePath, {
logging: this.createLogginOptions()
}));
this._register(this.storage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key })));
return this.storage.init();
});
return this.storage.init();
}
get(key: string, fallbackValue: string): string;

View File

@@ -62,38 +62,38 @@ export class StorageService extends Disposable implements IStorageService {
return this.initializePromise;
}
private doInitialize(payload: IWorkspaceInitializationPayload): Promise<void> {
return Promise.all([
private async doInitialize(payload: IWorkspaceInitializationPayload): Promise<void> {
await Promise.all([
this.initializeGlobalStorage(),
this.initializeWorkspaceStorage(payload)
]).then(() => undefined);
]);
}
private initializeGlobalStorage(): Promise<void> {
return this.globalStorage.init();
}
private initializeWorkspaceStorage(payload: IWorkspaceInitializationPayload): Promise<void> {
private async initializeWorkspaceStorage(payload: IWorkspaceInitializationPayload): Promise<void> {
// Prepare workspace storage folder for DB
return this.prepareWorkspaceStorageFolder(payload).then(result => {
try {
const result = await this.prepareWorkspaceStorageFolder(payload);
const useInMemoryStorage = !!this.environmentService.extensionTestsLocationURI; // no storage during extension tests!
// Create workspace storage and initalize
mark('willInitWorkspaceStorage');
return this.createWorkspaceStorage(useInMemoryStorage ? SQLiteStorageDatabase.IN_MEMORY_PATH : join(result.path, StorageService.WORKSPACE_STORAGE_NAME), result.wasCreated ? StorageHint.STORAGE_DOES_NOT_EXIST : undefined).init().then(() => {
try {
await this.createWorkspaceStorage(useInMemoryStorage ? SQLiteStorageDatabase.IN_MEMORY_PATH : join(result.path, StorageService.WORKSPACE_STORAGE_NAME), result.wasCreated ? StorageHint.STORAGE_DOES_NOT_EXIST : undefined).init();
} finally {
mark('didInitWorkspaceStorage');
}, error => {
mark('didInitWorkspaceStorage');
return Promise.reject(error);
});
}).then(undefined, error => {
}
} catch (error) {
onUnexpectedError(error);
// Upon error, fallback to in-memory storage
return this.createWorkspaceStorage(SQLiteStorageDatabase.IN_MEMORY_PATH).init();
});
}
}
private createWorkspaceStorage(workspaceStoragePath: string, hint?: StorageHint): IStorage {
@@ -120,22 +120,20 @@ export class StorageService extends Disposable implements IStorageService {
return join(this.environmentService.workspaceStorageHome, payload.id); // workspace home + workspace id;
}
private prepareWorkspaceStorageFolder(payload: IWorkspaceInitializationPayload): Promise<{ path: string, wasCreated: boolean }> {
private async prepareWorkspaceStorageFolder(payload: IWorkspaceInitializationPayload): Promise<{ path: string, wasCreated: boolean }> {
const workspaceStorageFolderPath = this.getWorkspaceStorageFolderPath(payload);
return exists(workspaceStorageFolderPath).then<{ path: string, wasCreated: boolean }>(exists => {
if (exists) {
return { path: workspaceStorageFolderPath, wasCreated: false };
}
const storageExists = await exists(workspaceStorageFolderPath);
if (storageExists) {
return { path: workspaceStorageFolderPath, wasCreated: false };
}
return mkdirp(workspaceStorageFolderPath).then(() => {
await mkdirp(workspaceStorageFolderPath);
// Write metadata into folder
this.ensureWorkspaceStorageFolderMeta(payload);
// Write metadata into folder
this.ensureWorkspaceStorageFolderMeta(payload);
return { path: workspaceStorageFolderPath, wasCreated: true };
});
});
return { path: workspaceStorageFolderPath, wasCreated: true };
}
private ensureWorkspaceStorageFolderMeta(payload: IWorkspaceInitializationPayload): void {
@@ -148,13 +146,16 @@ export class StorageService extends Disposable implements IStorageService {
if (meta) {
const workspaceStorageMetaPath = join(this.getWorkspaceStorageFolderPath(payload), StorageService.WORKSPACE_META_NAME);
exists(workspaceStorageMetaPath).then(exists => {
if (exists) {
return undefined; // already existing
(async function () {
try {
const storageExists = await exists(workspaceStorageMetaPath);
if (!storageExists) {
await writeFile(workspaceStorageMetaPath, JSON.stringify(meta, undefined, 2));
}
} catch (error) {
onUnexpectedError(error);
}
return writeFile(workspaceStorageMetaPath, JSON.stringify(meta, undefined, 2));
}).then(undefined, error => onUnexpectedError(error));
})();
}
}
@@ -184,16 +185,16 @@ export class StorageService extends Disposable implements IStorageService {
this.getStorage(scope).delete(key);
}
close(): Promise<void> {
async close(): Promise<void> {
// Signal as event so that clients can still store data
this._onWillSaveState.fire({ reason: WillSaveStateReason.SHUTDOWN });
// Do it
return Promise.all([
await Promise.all([
this.globalStorage.close(),
this.workspaceStorage.close()
]).then(() => undefined);
]);
}
private getStorage(scope: StorageScope): IStorage {
@@ -208,77 +209,75 @@ export class StorageService extends Disposable implements IStorageService {
return scope === StorageScope.GLOBAL ? this.globalStorage.checkIntegrity(full) : this.workspaceStorage.checkIntegrity(full);
}
logStorage(): Promise<void> {
return Promise.all([
async logStorage(): Promise<void> {
const result = await Promise.all([
this.globalStorage.items,
this.workspaceStorage.items,
this.globalStorage.checkIntegrity(true /* full */),
this.workspaceStorage.checkIntegrity(true /* full */)
]).then(result => {
const safeParse = (value: string) => {
try {
return JSON.parse(value);
} catch (error) {
return value;
}
};
]);
const globalItems = new Map<string, string>();
const globalItemsParsed = new Map<string, string>();
result[0].forEach((value, key) => {
globalItems.set(key, value);
globalItemsParsed.set(key, safeParse(value));
});
const safeParse = (value: string) => {
try {
return JSON.parse(value);
} catch (error) {
return value;
}
};
const workspaceItems = new Map<string, string>();
const workspaceItemsParsed = new Map<string, string>();
result[1].forEach((value, key) => {
workspaceItems.set(key, value);
workspaceItemsParsed.set(key, safeParse(value));
});
console.group(`Storage: Global (integrity: ${result[2]}, path: ${this.environmentService.globalStorageHome})`);
let globalValues: { key: string, value: string }[] = [];
globalItems.forEach((value, key) => {
globalValues.push({ key, value });
});
console.table(globalValues);
console.groupEnd();
console.log(globalItemsParsed);
console.group(`Storage: Workspace (integrity: ${result[3]}, load: ${getDuration('willInitWorkspaceStorage', 'didInitWorkspaceStorage')}, path: ${this.workspaceStoragePath})`);
let workspaceValues: { key: string, value: string }[] = [];
workspaceItems.forEach((value, key) => {
workspaceValues.push({ key, value });
});
console.table(workspaceValues);
console.groupEnd();
console.log(workspaceItemsParsed);
const globalItems = new Map<string, string>();
const globalItemsParsed = new Map<string, string>();
result[0].forEach((value, key) => {
globalItems.set(key, value);
globalItemsParsed.set(key, safeParse(value));
});
const workspaceItems = new Map<string, string>();
const workspaceItemsParsed = new Map<string, string>();
result[1].forEach((value, key) => {
workspaceItems.set(key, value);
workspaceItemsParsed.set(key, safeParse(value));
});
console.group(`Storage: Global (integrity: ${result[2]}, path: ${this.environmentService.globalStorageHome})`);
let globalValues: { key: string, value: string }[] = [];
globalItems.forEach((value, key) => {
globalValues.push({ key, value });
});
console.table(globalValues);
console.groupEnd();
console.log(globalItemsParsed);
console.group(`Storage: Workspace (integrity: ${result[3]}, load: ${getDuration('willInitWorkspaceStorage', 'didInitWorkspaceStorage')}, path: ${this.workspaceStoragePath})`);
let workspaceValues: { key: string, value: string }[] = [];
workspaceItems.forEach((value, key) => {
workspaceValues.push({ key, value });
});
console.table(workspaceValues);
console.groupEnd();
console.log(workspaceItemsParsed);
}
migrate(toWorkspace: IWorkspaceInitializationPayload): Promise<void> {
async migrate(toWorkspace: IWorkspaceInitializationPayload): Promise<void> {
if (this.workspaceStoragePath === SQLiteStorageDatabase.IN_MEMORY_PATH) {
return Promise.resolve(); // no migration needed if running in memory
}
// Close workspace DB to be able to copy
return this.workspaceStorage.close().then(() => {
await this.workspaceStorage.close();
// Prepare new workspace storage folder
return this.prepareWorkspaceStorageFolder(toWorkspace).then(result => {
const newWorkspaceStoragePath = join(result.path, StorageService.WORKSPACE_STORAGE_NAME);
// Prepare new workspace storage folder
const result = await this.prepareWorkspaceStorageFolder(toWorkspace);
// Copy current storage over to new workspace storage
return copy(this.workspaceStoragePath, newWorkspaceStoragePath).then(() => {
const newWorkspaceStoragePath = join(result.path, StorageService.WORKSPACE_STORAGE_NAME);
// Recreate and init workspace storage
return this.createWorkspaceStorage(newWorkspaceStoragePath).init();
});
});
});
// Copy current storage over to new workspace storage
await copy(this.workspaceStoragePath, newWorkspaceStoragePath);
// Recreate and init workspace storage
return this.createWorkspaceStorage(newWorkspaceStoragePath).init();
}
}