SQL-Migration extension - fix refresh reentrancy hang (#16482)

* Fix application hang on refresh

* fix re-entrancy issue with refresh

* adding additional refresh checking
This commit is contained in:
brian-harris
2021-07-29 16:44:56 -07:00
committed by GitHub
parent 251d250523
commit e3c7e06983
4 changed files with 49 additions and 22 deletions

View File

@@ -49,6 +49,8 @@ export class DashboardWidget {
private _autoRefreshHandle!: NodeJS.Timeout;
private _disposables: vscode.Disposable[] = [];
private isRefreshing: boolean = false;
constructor() {
}
@@ -242,14 +244,19 @@ export class DashboardWidget {
}
private setAutoRefresh(interval: SupportedAutoRefreshIntervals): void {
let classVariable = this;
const classVariable = this;
clearInterval(this._autoRefreshHandle);
if (interval !== -1) {
this._autoRefreshHandle = setInterval(function () { classVariable.refreshMigrations(); }, interval);
this._autoRefreshHandle = setInterval(async function () { await classVariable.refreshMigrations(); }, interval);
}
}
private async refreshMigrations(): Promise<void> {
if (this.isRefreshing) {
return;
}
this.isRefreshing = true;
this._viewAllMigrationsButton.enabled = false;
this._migrationStatusCardLoadingContainer.loading = true;
try {
@@ -304,6 +311,7 @@ export class DashboardWidget {
} catch (error) {
console.log(error);
} finally {
this.isRefreshing = false;
this._migrationStatusCardLoadingContainer.loading = false;
this._viewAllMigrationsButton.enabled = true;
}

View File

@@ -44,6 +44,8 @@ export class MigrationCutoverDialog {
private _autoRefreshHandle!: any;
private _disposables: vscode.Disposable[] = [];
private isRefreshing = false;
readonly _infoFieldWidth: string = '250px';
constructor(migration: MigrationContext) {
@@ -279,8 +281,8 @@ export class MigrationCutoverDialog {
d => { try { d.dispose(); } catch { } });
}));
return view.initializeModel(form).then((value) => {
this.refreshStatus();
return view.initializeModel(form).then(async (value) => {
await this.refreshStatus();
});
} catch (e) {
console.log(e);
@@ -493,13 +495,18 @@ export class MigrationCutoverDialog {
const classVariable = this;
clearInterval(this._autoRefreshHandle);
if (interval !== -1) {
this._autoRefreshHandle = setInterval(function () { classVariable.refreshStatus(); }, interval);
this._autoRefreshHandle = setInterval(async function () { await classVariable.refreshStatus(); }, interval);
}
}
private async refreshStatus(): Promise<void> {
if (this.isRefreshing) {
return;
}
try {
this.isRefreshing = true;
this._refreshLoader.loading = true;
this._cutoverButton.enabled = false;
this._cancelButton.enabled = false;
@@ -615,8 +622,10 @@ export class MigrationCutoverDialog {
}
} catch (e) {
console.log(e);
} finally {
this.isRefreshing = false;
this._refreshLoader.loading = false;
}
this._refreshLoader.loading = false;
}
private createInfoField(label: string, value: string): {

View File

@@ -33,6 +33,8 @@ export class MigrationStatusDialog {
private _autoRefreshHandle!: NodeJS.Timeout;
private _disposables: vscode.Disposable[] = [];
private isRefreshing = false;
constructor(migrations: MigrationContext[], private _filter: AdsMigrationStatus) {
this._model = new MigrationStatusDialogModel(migrations);
this._dialogObject = azdata.window.createModelViewDialog(loc.MIGRATION_STATUS, 'MigrationControllerDialog', 'wide');
@@ -109,19 +111,16 @@ export class MigrationStatusDialog {
}));
this._refresh = this._view.modelBuilder.button().withProps({
iconPath: {
light: IconPathHelper.refresh.light,
dark: IconPathHelper.refresh.dark
},
iconPath: IconPathHelper.refresh,
iconHeight: '16px',
iconWidth: '20px',
height: '30px',
label: loc.REFRESH_BUTTON_LABEL,
}).component();
this._disposables.push(this._refresh.onDidClick((e) => {
this.refreshTable();
}));
this._disposables.push(
this._refresh.onDidClick(
async (e) => { await this.refreshTable(); }));
const flexContainer = this._view.modelBuilder.flexContainer().withProps({
width: 900,
@@ -169,7 +168,7 @@ export class MigrationStatusDialog {
const classVariable = this;
clearInterval(this._autoRefreshHandle);
if (interval !== -1) {
this._autoRefreshHandle = setInterval(function () { classVariable.refreshTable(); }, interval);
this._autoRefreshHandle = setInterval(async function () { await classVariable.refreshTable(); }, interval);
}
}
@@ -447,11 +446,22 @@ export class MigrationStatusDialog {
}
private async refreshTable(): Promise<void> {
this._refreshLoader.loading = true;
const currentConnection = await azdata.connection.getCurrentConnection();
this._model._migrations = await MigrationLocalStorage.getMigrationsBySourceConnections(currentConnection, true);
await this.populateMigrationTable();
this._refreshLoader.loading = false;
if (this.isRefreshing) {
return;
}
this.isRefreshing = true;
try {
this._refreshLoader.loading = true;
const currentConnection = await azdata.connection.getCurrentConnection();
this._model._migrations = await MigrationLocalStorage.getMigrationsBySourceConnections(currentConnection, true);
await this.populateMigrationTable();
} catch (e) {
console.log(e);
} finally {
this.isRefreshing = false;
this._refreshLoader.loading = false;
}
}
private createStatusTable(): azdata.DeclarativeTableComponent {

View File

@@ -16,7 +16,7 @@ export class MigrationLocalStorage {
}
public static async getMigrationsBySourceConnections(connectionProfile: azdata.connection.ConnectionProfile, refreshStatus?: boolean): Promise<MigrationContext[]> {
const undefinedSessionId = '{undefined}';
const result: MigrationContext[] = [];
const validMigrations: MigrationContext[] = [];
@@ -32,7 +32,7 @@ export class MigrationLocalStorage {
migration.azureAccount,
migration.subscription,
migration.migrationContext,
migration.sessionId!
migration.sessionId ?? undefinedSessionId
);
migration.migrationContext.properties.sourceDatabaseName = sourceDatabase;
migration.migrationContext.properties.backupConfiguration = backupConfiguration;
@@ -41,7 +41,7 @@ export class MigrationLocalStorage {
migration.azureAccount,
migration.subscription,
migration.asyncUrl,
migration.sessionId!
migration.sessionId ?? undefinedSessionId
);
}
}