mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-17 01:25:36 -05:00
[Feature] SKU recommendations in SQL migration extension (#18252)
* Initial check in for SQL migration SKU recommendation feature (#18116) Co-authored-by: Raymond Truong <ratruong@microsoft.com> * add TargetSelectionPage, remove AccountSelectionPage, fix saveAndClose bugs (#18092) * update sku interfaces (#18150) * create the skuRecommendationResultsDialog (#18151) * add TargetSelectionPage, remove AccountSelectionPage, fix saveAndClose bugs * create skuRecommendationResultsDialog * Replace placeholder SKU recommendation results with actual results (#18153) * Replace placeholder SKU recommendation results with actual backend call results * Remove skuRecommendationExample * Replace number fields in interfaces with correct enums, and update UI text * add getAzureRecommendationDialog for performance collection (#18159) * add getAzureRecommendationDialog when there are no recommendations available * update 'get azure rec' / 'view details' link values * add condition to check if recommendations are available * Implement start/stop perf data collection + import perf data into new UI (#18149) * Implement start/stop perf data collection * add getAzureRecommendationDialog when there are no recommendations available * update 'get azure rec' / 'view details' link values * add condition to check if recommendations are available * Implement import existing data + start/stop perf collection with new UI Co-authored-by: Rachel Kim <rackim@microsoft.com> * Expose SqlInstanceRequirements in SKU recommendation results (#18207) * Expose SqlInstanceRequirements * Move string literals to constants file * Fix formatting in mssql.d.ts * create storage properties table (#18215) * Edit sku recommendation parameters (#18244) * Edit sku recommendation parameters * make _targetPercentileDropdown not editable; styling updates * Azure recommendation section exposes data collection status and stop option (#18246) * Edit sku recommendation parameters * create azure recommendation details section on sku page * Improve error handling + add auto refresh + other small changes (#18228) * Update source properties table * WIP - refresh perf data collection * Add auto refresh logic * Address comments Co-authored-by: Rachel Kim <rackim@microsoft.com> * Show/hide azure recommendation components based on data collection source and status (#18254) * add refresh recommendation button; show/hide content based on perf collection status * show/hide azure rec content based on perf data source scenarios * add popups for start/stop; allow user to restart data collection; add perf collection to save close; add info tooltips (#18278) * Update SKU recommendation timer logic (#18281) * Update timer logic * Fix misc UI bugs * update sql migration extension readme (#18295) * Remove empty constant, as this may have broken the build * Fix 'save and close' behavior for SKU recommendation (#18301) * Update timer logic * Fix misc UI bugs * 'WIP' * Add logic to restore correct SKU recommendation state when reloading * SKU UX enhancements - status info, button validations, savedInfo logic (#18320) * add stop/inprogress status icons to perf collection status text, update restart icon * refactor savedInfo as an interface, edit parameter recommednations are saved, add open folder inputbox validation, handle no recommendations available scenario * fix getazureredialog bug, cleanup cold * nit card styling * Update recommendations whenever user changes list of databases to assess + misc clean up (#18323) * Consolidate constants, clean up redundant functions, misc clean up * Remove old SKU recommendation interfaces * Update some more strings * Telemetry events for SKU Recommendation (#18282) * Telemetry events for SKU Recommendation * Addressing comments - 1) fixed camel casing 2) removed extra logging to console 3) added telemetry for subid, rg, tenantid on targetselectionpage * Resolving conflicts * Addressing comments - 1) removing filename 2) moving all numbers to measurements. * Resolving comment - Fixing telemetry value for tenant id. * removing warning 'logError' is declared but its value is never read (#18333) * Stop existing data collection when leaving and starting a new migration + update timers (#18339) * Refresh recommendations when pressing stop data collection button * Fix orphaned data collection when save and closing and starting a new migration * Revert "Refresh recommendations when pressing stop data collection button" This reverts commit e6fb2ade8f8a41952adb81cb0ee852414dfa4ef2. * Update timers to use production values * Remove unused import * address bug bash issues: add learn more link, add last refreshed time, fix vm card view detail open issue, remember last selected folder, remove strings, refactor refresh logic on sku page (#18340) * Address comments * Update to sqltoolsservice 3.0.0-release.204 Co-authored-by: Rachel Kim <rackim@microsoft.com> Co-authored-by: Neetu Singh <23.neetu@gmail.com>
This commit is contained in:
@@ -9,13 +9,13 @@ import * as azurecore from 'azurecore';
|
||||
import * as vscode from 'vscode';
|
||||
import * as mssql from '../../../mssql';
|
||||
import { getAvailableManagedInstanceProducts, getAvailableStorageAccounts, getBlobContainers, getFileShares, getSqlMigrationServices, getSubscriptions, SqlMigrationService, SqlManagedInstance, startDatabaseMigration, StartDatabaseMigrationRequest, StorageAccount, getAvailableSqlVMs, SqlVMServer, getLocations, getResourceGroups, getLocationDisplayName, getSqlManagedInstanceDatabases, getBlobs } from '../api/azure';
|
||||
import { SKURecommendations } from './externalContract';
|
||||
import * as constants from '../constants/strings';
|
||||
import { MigrationLocalStorage } from './migrationLocalStorage';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { sendSqlMigrationActionEvent, TelemetryAction, TelemetryViews } from '../telemtery';
|
||||
import { hashString, deepClone } from '../api/utils';
|
||||
import { SKURecommendationPage } from '../wizard/skuRecommendationPage';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export enum State {
|
||||
@@ -59,9 +59,9 @@ export enum NetworkContainerType {
|
||||
}
|
||||
|
||||
export enum Page {
|
||||
AzureAccount,
|
||||
DatabaseSelector,
|
||||
SKURecommendation,
|
||||
TargetSelection,
|
||||
MigrationMode,
|
||||
DatabaseBackup,
|
||||
IntegrationRuntime,
|
||||
@@ -74,6 +74,10 @@ export enum WizardEntryPoint {
|
||||
RetryMigration = 'RetryMigration',
|
||||
}
|
||||
|
||||
export enum PerformanceDataSourceOptions {
|
||||
CollectData = 'CollectData',
|
||||
OpenExisting = 'OpenExisting',
|
||||
}
|
||||
export interface DatabaseBackupModel {
|
||||
migrationMode: MigrationMode;
|
||||
networkContainerType: NetworkContainerType;
|
||||
@@ -103,7 +107,6 @@ export interface Model {
|
||||
readonly sourceConnectionId: string;
|
||||
readonly currentState: State;
|
||||
gatheringInformationError: string | undefined;
|
||||
skuRecommendations: SKURecommendations | undefined;
|
||||
_azureAccount: azdata.Account | undefined;
|
||||
_databaseBackup: DatabaseBackupModel | undefined;
|
||||
}
|
||||
@@ -134,8 +137,18 @@ export interface SavedInfo {
|
||||
blobs: Blob[];
|
||||
targetDatabaseNames: string[];
|
||||
migrationServiceId: string | null;
|
||||
skuRecommendation: SkuRecommendationSavedInfo | null;
|
||||
}
|
||||
|
||||
export interface SkuRecommendationSavedInfo {
|
||||
skuRecommendationPerformanceDataSource: PerformanceDataSourceOptions;
|
||||
skuRecommendationPerformanceLocation: string;
|
||||
perfDataCollectionStartDate?: Date;
|
||||
perfDataCollectionStopDate?: Date;
|
||||
skuTargetPercentile: number;
|
||||
skuScalingFactor: number;
|
||||
skuEnablePreview: boolean;
|
||||
}
|
||||
|
||||
export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
public _azureAccounts!: azdata.Account[];
|
||||
@@ -176,12 +189,42 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
private _currentState: State;
|
||||
private _gatheringInformationError: string | undefined;
|
||||
|
||||
private _skuRecommendations: SKURecommendations | undefined;
|
||||
public _assessmentResults!: ServerAssessment;
|
||||
public _runAssessments: boolean = true;
|
||||
private _assessmentApiResponse!: mssql.AssessmentResult;
|
||||
public mementoString: string;
|
||||
|
||||
public _skuRecommendationResults!: SkuRecommendation;
|
||||
public _skuRecommendationPerformanceDataSource!: PerformanceDataSourceOptions;
|
||||
private _skuRecommendationApiResponse!: mssql.SkuRecommendationResult;
|
||||
public _skuRecommendationPerformanceLocation!: string;
|
||||
private _skuRecommendationRecommendedDatabaseList!: string[];
|
||||
private _startPerfDataCollectionApiResponse!: mssql.StartPerfDataCollectionResult;
|
||||
private _stopPerfDataCollectionApiResponse!: mssql.StopPerfDataCollectionResult;
|
||||
private _refreshPerfDataCollectionApiResponse!: mssql.RefreshPerfDataCollectionResult;
|
||||
public _perfDataCollectionStartDate!: Date | undefined;
|
||||
public _perfDataCollectionStopDate!: Date | undefined;
|
||||
public _perfDataCollectionLastRefreshedDate!: Date;
|
||||
public _perfDataCollectionMessages!: string[];
|
||||
public _perfDataCollectionErrors!: string[];
|
||||
public _perfDataCollectionIsCollecting!: boolean;
|
||||
|
||||
public readonly _performanceDataQueryIntervalInSeconds = 30;
|
||||
public readonly _staticDataQueryIntervalInSeconds = 60;
|
||||
public readonly _numberOfPerformanceDataQueryIterations = 19;
|
||||
public readonly _defaultDataPointStartTime = '1900-01-01 00:00:00';
|
||||
public readonly _defaultDataPointEndTime = '2200-01-01 00:00:00';
|
||||
public readonly _recommendationTargetPlatforms = [MigrationTargetType.SQLDB, MigrationTargetType.SQLMI, MigrationTargetType.SQLVM];
|
||||
|
||||
public refreshPerfDataCollectionFrequency = this._performanceDataQueryIntervalInSeconds * 1000;
|
||||
private _autoRefreshPerfDataCollectionHandle!: NodeJS.Timeout;
|
||||
public refreshGetSkuRecommendationFrequency = 600000; // 10 minutes
|
||||
private _autoRefreshGetSkuRecommendationHandle!: NodeJS.Timeout;
|
||||
|
||||
public _skuScalingFactor!: number;
|
||||
public _skuTargetPercentile!: number;
|
||||
public _skuEnablePreview!: boolean;
|
||||
|
||||
public _vmDbs: string[] = [];
|
||||
public _miDbs: string[] = [];
|
||||
public _targetType!: MigrationTargetType;
|
||||
@@ -213,6 +256,10 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
this._databaseBackup.networkShares = [];
|
||||
this._databaseBackup.blobs = [];
|
||||
this.mementoString = 'sqlMigration.assessmentResults';
|
||||
|
||||
this._skuScalingFactor = 100;
|
||||
this._skuTargetPercentile = 95;
|
||||
this._skuEnablePreview = true;
|
||||
}
|
||||
|
||||
public get sourceConnectionId(): string {
|
||||
@@ -233,6 +280,17 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
let finalResult = temp.filter((name) => !this.excludeDbs.includes(name));
|
||||
return finalResult;
|
||||
}
|
||||
public hasRecommendedDatabaseListChanged(): boolean {
|
||||
const oldDbList = this._skuRecommendationRecommendedDatabaseList;
|
||||
const newDbList = this._databaseAssessment;
|
||||
|
||||
if (!oldDbList || !newDbList) {
|
||||
return false;
|
||||
}
|
||||
return !((oldDbList.length === newDbList.length) && oldDbList.every(function (element, index) {
|
||||
return element === newDbList[index];
|
||||
}));
|
||||
}
|
||||
|
||||
public async getDatabaseAssessments(targetType: MigrationTargetType): Promise<ServerAssessment> {
|
||||
const ownerUri = await azdata.connection.getUriForConnection(this.sourceConnectionId);
|
||||
@@ -291,6 +349,314 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
return this._assessmentResults;
|
||||
}
|
||||
|
||||
public async getSkuRecommendations(): Promise<SkuRecommendation> {
|
||||
try {
|
||||
const serverInfo = await azdata.connection.getServerInfo(this.sourceConnectionId);
|
||||
const machineName = (<any>serverInfo)['machineName']; // get actual machine name instead of whatever the user entered as the server name (e.g. DESKTOP-xxx instead of localhost)
|
||||
|
||||
const response = (await this.migrationService.getSkuRecommendations(
|
||||
this._skuRecommendationPerformanceLocation,
|
||||
this._performanceDataQueryIntervalInSeconds,
|
||||
this._recommendationTargetPlatforms.map(p => p.toString()),
|
||||
machineName,
|
||||
this._skuTargetPercentile,
|
||||
this._skuScalingFactor,
|
||||
this._defaultDataPointStartTime,
|
||||
this._defaultDataPointEndTime,
|
||||
this._skuEnablePreview,
|
||||
this._databaseAssessment))!;
|
||||
this._skuRecommendationApiResponse = response;
|
||||
|
||||
// clone list of databases currently being assessed and store them, so that if the user ever changes the list we can refresh new recommendations
|
||||
this._skuRecommendationRecommendedDatabaseList = this._databaseAssessment.slice();
|
||||
|
||||
console.log('sqlinstancerequirements: ');
|
||||
console.log(this._skuRecommendationApiResponse.instanceRequirements);
|
||||
|
||||
if (response?.sqlDbRecommendationResults || response?.sqlMiRecommendationResults || response?.sqlVmRecommendationResults) {
|
||||
this._skuRecommendationResults = {
|
||||
recommendations: {
|
||||
sqlDbRecommendationResults: response?.sqlDbRecommendationResults ?? [],
|
||||
sqlMiRecommendationResults: response?.sqlMiRecommendationResults ?? [],
|
||||
sqlVmRecommendationResults: response?.sqlVmRecommendationResults ?? [],
|
||||
instanceRequirements: response?.instanceRequirements
|
||||
},
|
||||
};
|
||||
} else {
|
||||
this._skuRecommendationResults = {
|
||||
recommendations: {
|
||||
sqlDbRecommendationResults: [],
|
||||
sqlMiRecommendationResults: [],
|
||||
sqlVmRecommendationResults: [],
|
||||
instanceRequirements: response?.instanceRequirements
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
this._skuRecommendationResults = {
|
||||
recommendations: {
|
||||
sqlDbRecommendationResults: this._skuRecommendationApiResponse?.sqlDbRecommendationResults ?? [],
|
||||
sqlMiRecommendationResults: this._skuRecommendationApiResponse?.sqlMiRecommendationResults ?? [],
|
||||
sqlVmRecommendationResults: this._skuRecommendationApiResponse?.sqlVmRecommendationResults ?? [],
|
||||
instanceRequirements: this._skuRecommendationApiResponse?.instanceRequirements
|
||||
},
|
||||
recommendationError: error
|
||||
};
|
||||
} // Generating all the telemetry asynchronously as we don't need to block the user for it.
|
||||
this.generateSkuRecommendationTelemetry().catch(e => console.error(e));
|
||||
|
||||
return this._skuRecommendationResults;
|
||||
}
|
||||
|
||||
private async generateSkuRecommendationTelemetry(): Promise<void> {
|
||||
try {
|
||||
|
||||
this._skuRecommendationResults?.recommendations?.sqlMiRecommendationResults?.forEach(resultItem => {
|
||||
// Send telemetry for recommended MI SKU
|
||||
sendSqlMigrationActionEvent(
|
||||
TelemetryViews.SkuRecommendationWizard,
|
||||
TelemetryAction.GetMISkuRecommendation,
|
||||
{
|
||||
'sessionId': this._sessionId,
|
||||
'recommendedSku': JSON.stringify(resultItem?.targetSku)
|
||||
},
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
this._skuRecommendationResults?.recommendations?.sqlVmRecommendationResults?.forEach(resultItem => {
|
||||
// Send telemetry for recommended VM SKU
|
||||
sendSqlMigrationActionEvent(
|
||||
TelemetryViews.SkuRecommendationWizard,
|
||||
TelemetryAction.GetVMSkuRecommendation,
|
||||
{
|
||||
'sessionId': this._sessionId,
|
||||
'recommendedSku': JSON.stringify(resultItem?.targetSku)
|
||||
},
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
// Send Instance requirements used for calculating recommendations
|
||||
sendSqlMigrationActionEvent(
|
||||
TelemetryViews.SkuRecommendationWizard,
|
||||
TelemetryAction.GetInstanceRequirements,
|
||||
{
|
||||
'sessionId': this._sessionId,
|
||||
'performanceDataSource': this._skuRecommendationPerformanceDataSource,
|
||||
'databaseLevelRequirements': JSON.stringify(this._skuRecommendationResults?.recommendations?.instanceRequirements?.databaseLevelRequirements?.map(i => {
|
||||
return {
|
||||
cpuRequirementInCores: i.cpuRequirementInCores,
|
||||
dataIOPSRequirement: i.dataIOPSRequirement,
|
||||
logIOPSRequirement: i.logIOPSRequirement,
|
||||
ioLatencyRequirementInMs: i.ioLatencyRequirementInMs,
|
||||
ioThroughputRequirementInMBps: i.ioThroughputRequirementInMBps,
|
||||
dataStorageRequirementInMB: i.dataStorageRequirementInMB,
|
||||
logStorageRequirementInMB: i.logStorageRequirementInMB,
|
||||
databaseName: hashString(i.databaseName),
|
||||
memoryRequirementInMB: i.memoryRequirementInMB,
|
||||
cpuRequirementInPercentageOfTotalInstance: i.cpuRequirementInPercentageOfTotalInstance,
|
||||
numberOfDataPointsAnalyzed: i.numberOfDataPointsAnalyzed,
|
||||
fileLevelRequirements: i.fileLevelRequirements?.map(file => {
|
||||
return {
|
||||
fileType: file.fileType,
|
||||
sizeInMB: file.sizeInMB,
|
||||
readLatencyInMs: file.readLatencyInMs,
|
||||
writeLatencyInMs: file.writeLatencyInMs,
|
||||
iopsRequirement: file.iopsRequirement,
|
||||
ioThroughputRequirementInMBps: file.ioThroughputRequirementInMBps,
|
||||
numberOfDataPointsAnalyzed: file.numberOfDataPointsAnalyzed
|
||||
};
|
||||
})
|
||||
};
|
||||
}))
|
||||
},
|
||||
{
|
||||
'cpuRequirementInCores': this._skuRecommendationResults?.recommendations?.instanceRequirements?.cpuRequirementInCores,
|
||||
'dataStorageRequirementInMB': this._skuRecommendationResults?.recommendations?.instanceRequirements?.dataStorageRequirementInMB,
|
||||
'logStorageRequirementInMB': this._skuRecommendationResults?.recommendations?.instanceRequirements?.logStorageRequirementInMB,
|
||||
'memoryRequirementInMB': this._skuRecommendationResults?.recommendations?.instanceRequirements?.memoryRequirementInMB,
|
||||
'dataIOPSRequirement': this._skuRecommendationResults?.recommendations?.instanceRequirements?.dataIOPSRequirement,
|
||||
'logIOPSRequirement': this._skuRecommendationResults?.recommendations?.instanceRequirements?.logIOPSRequirement,
|
||||
'ioLatencyRequirementInMs': this._skuRecommendationResults?.recommendations?.instanceRequirements?.ioLatencyRequirementInMs,
|
||||
'ioThroughputRequirementInMBps': this._skuRecommendationResults?.recommendations?.instanceRequirements?.ioThroughputRequirementInMBps,
|
||||
'tempDBSizeInMB': this._skuRecommendationResults?.recommendations?.instanceRequirements?.tempDBSizeInMB,
|
||||
'aggregationTargetPercentile': this._skuRecommendationResults?.recommendations?.instanceRequirements?.aggregationTargetPercentile,
|
||||
'numberOfDataPointsAnalyzed': this._skuRecommendationResults?.recommendations?.instanceRequirements?.numberOfDataPointsAnalyzed,
|
||||
}
|
||||
);
|
||||
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
public async startPerfDataCollection(
|
||||
dataFolder: string,
|
||||
perfQueryIntervalInSec: number,
|
||||
staticQueryIntervalInSec: number,
|
||||
numberOfIterations: number,
|
||||
page: SKURecommendationPage): Promise<boolean> {
|
||||
try {
|
||||
if (!this.performanceCollectionInProgress()) {
|
||||
const ownerUri = await azdata.connection.getUriForConnection(this.sourceConnectionId);
|
||||
const response = await this.migrationService.startPerfDataCollection(ownerUri, dataFolder, perfQueryIntervalInSec, staticQueryIntervalInSec, numberOfIterations);
|
||||
|
||||
this._startPerfDataCollectionApiResponse = response!;
|
||||
this._perfDataCollectionStartDate = this._startPerfDataCollectionApiResponse.dateTimeStarted;
|
||||
this._perfDataCollectionStopDate = undefined;
|
||||
|
||||
void vscode.window.showInformationMessage(constants.AZURE_RECOMMENDATION_START_POPUP);
|
||||
|
||||
await this.startSkuTimers(page, this.refreshPerfDataCollectionFrequency);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
// Generate telemetry for start data collection request
|
||||
this.generateStartDataCollectionTelemetry().catch(e => console.error(e));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async generateStartDataCollectionTelemetry(): Promise<void> {
|
||||
try {
|
||||
sendSqlMigrationActionEvent(
|
||||
TelemetryViews.DataCollectionWizard,
|
||||
TelemetryAction.StartDataCollection,
|
||||
{
|
||||
'sessionId': this._sessionId,
|
||||
'timeDataCollectionStarted': this._perfDataCollectionStartDate?.toString() || ''
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
public async startSkuTimers(page: SKURecommendationPage, refreshIntervalInMs: number): Promise<void> {
|
||||
const classVariable = this;
|
||||
|
||||
if (!this._autoRefreshPerfDataCollectionHandle) {
|
||||
clearInterval(this._autoRefreshPerfDataCollectionHandle);
|
||||
if (this.refreshPerfDataCollectionFrequency !== -1) {
|
||||
this._autoRefreshPerfDataCollectionHandle = setInterval(async function () {
|
||||
await classVariable.refreshPerfDataCollection();
|
||||
|
||||
if (await classVariable.isWaitingForFirstTimeRefresh()) {
|
||||
await page.refreshSkuRecommendationComponents(); // update timer
|
||||
}
|
||||
}, refreshIntervalInMs);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this._autoRefreshGetSkuRecommendationHandle) {
|
||||
// start one-time timer to get SKU recommendation
|
||||
clearTimeout(this._autoRefreshGetSkuRecommendationHandle);
|
||||
if (this.refreshGetSkuRecommendationFrequency !== -1) {
|
||||
this._autoRefreshGetSkuRecommendationHandle = setTimeout(async function () {
|
||||
await page.refreshAzureRecommendation();
|
||||
}, this.refreshGetSkuRecommendationFrequency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async stopPerfDataCollection(): Promise<boolean> {
|
||||
try {
|
||||
const response = await this.migrationService.stopPerfDataCollection();
|
||||
void vscode.window.showInformationMessage(constants.AZURE_RECOMMENDATION_STOP_POPUP);
|
||||
|
||||
this._stopPerfDataCollectionApiResponse = response!;
|
||||
this._perfDataCollectionStopDate = this._stopPerfDataCollectionApiResponse.dateTimeStopped;
|
||||
|
||||
// stop auto refresh
|
||||
clearInterval(this._autoRefreshPerfDataCollectionHandle);
|
||||
clearInterval(this._autoRefreshGetSkuRecommendationHandle);
|
||||
}
|
||||
catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
// Generate telemetry for stop data collection request
|
||||
this.generateStopDataCollectionTelemetry().catch(e => console.error(e));
|
||||
return true;
|
||||
}
|
||||
|
||||
private async generateStopDataCollectionTelemetry(): Promise<void> {
|
||||
try {
|
||||
sendSqlMigrationActionEvent(
|
||||
TelemetryViews.DataCollectionWizard,
|
||||
TelemetryAction.StopDataCollection,
|
||||
{
|
||||
'sessionId': this._sessionId,
|
||||
'timeDataCollectionStopped': this._perfDataCollectionStopDate?.toString() || ''
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
public async refreshPerfDataCollection(): Promise<boolean> {
|
||||
try {
|
||||
const response = await this.migrationService.refreshPerfDataCollection(this._perfDataCollectionLastRefreshedDate ?? new Date());
|
||||
this._refreshPerfDataCollectionApiResponse = response!;
|
||||
this._perfDataCollectionLastRefreshedDate = this._refreshPerfDataCollectionApiResponse.refreshTime;
|
||||
this._perfDataCollectionMessages = this._refreshPerfDataCollectionApiResponse.messages;
|
||||
this._perfDataCollectionErrors = this._refreshPerfDataCollectionApiResponse.errors;
|
||||
this._perfDataCollectionIsCollecting = this._refreshPerfDataCollectionApiResponse.isCollecting;
|
||||
|
||||
if (this._perfDataCollectionErrors?.length > 0) {
|
||||
void vscode.window.showInformationMessage(constants.PERF_DATA_COLLECTION_ERROR(this._assessmentApiResponse?.assessmentResult?.name, this._perfDataCollectionErrors));
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async isWaitingForFirstTimeRefresh(): Promise<boolean> {
|
||||
const elapsedTimeInMins = Math.abs(new Date().getTime() - new Date(this._perfDataCollectionStartDate!).getTime()) / 60000;
|
||||
const skuRecAutoRefreshTimeInMins = this.refreshGetSkuRecommendationFrequency / 60000;
|
||||
|
||||
return elapsedTimeInMins < skuRecAutoRefreshTimeInMins;
|
||||
}
|
||||
|
||||
public performanceCollectionNotStarted(): boolean {
|
||||
if (!this._perfDataCollectionStartDate
|
||||
&& !this._perfDataCollectionStopDate) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public performanceCollectionInProgress(): boolean {
|
||||
if (this._perfDataCollectionStartDate
|
||||
&& !this._perfDataCollectionStopDate) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public performanceCollectionStopped(): boolean {
|
||||
if (this._perfDataCollectionStartDate
|
||||
&& this._perfDataCollectionStopDate) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private async generateAssessmentTelemetry(): Promise<void> {
|
||||
try {
|
||||
|
||||
@@ -324,9 +690,6 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
TelemetryAction.ServerAssessment,
|
||||
{
|
||||
'sessionId': this._sessionId,
|
||||
'tenantId': this._azureAccount.properties.tenants[0].id,
|
||||
'subscriptionId': this._targetSubscription?.id,
|
||||
'resourceGroup': this._resourceGroup?.name,
|
||||
'hashedServerName': hashString(this._assessmentApiResponse?.assessmentResult?.name),
|
||||
'startTime': startTime.toString(),
|
||||
'endTime': endTime.toString(),
|
||||
@@ -360,8 +723,6 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
TelemetryAction.DatabaseAssessment,
|
||||
{
|
||||
'sessionId': this._sessionId,
|
||||
'subscriptionId': this._targetSubscription?.id,
|
||||
'resourceGroup': this._resourceGroup?.name,
|
||||
'hashedDatabaseName': hashString(d.name),
|
||||
'compatibilityLevel': d.compatibilityLevel
|
||||
},
|
||||
@@ -398,8 +759,6 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
TelemetryAction.DatabaseAssessmentWarning,
|
||||
{
|
||||
'sessionId': this._sessionId,
|
||||
'subscriptionId': this._targetSubscription?.id,
|
||||
'resourceGroup': this._resourceGroup?.name,
|
||||
'warnings': JSON.stringify(databaseWarnings)
|
||||
},
|
||||
{}
|
||||
@@ -418,14 +777,13 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
TelemetryAction.DatabaseAssessmentError,
|
||||
{
|
||||
'sessionId': this._sessionId,
|
||||
'subscriptionId': this._targetSubscription?.id,
|
||||
'resourceGroup': this._resourceGroup?.name,
|
||||
'errors': JSON.stringify(databaseErrors)
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
} catch (e) {
|
||||
console.log('error during assessment telemetry:');
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
@@ -438,14 +796,6 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
this._gatheringInformationError = error;
|
||||
}
|
||||
|
||||
public get skuRecommendations(): SKURecommendations | undefined {
|
||||
return this._skuRecommendations;
|
||||
}
|
||||
|
||||
public set skuRecommendations(recommendations: SKURecommendations | undefined) {
|
||||
this._skuRecommendations = recommendations;
|
||||
}
|
||||
|
||||
public get stateChangeEvent(): vscode.Event<StateChangeEvent> {
|
||||
return this._stateChangeEventEmitter.event;
|
||||
}
|
||||
@@ -1068,6 +1418,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
blobs: [],
|
||||
targetDatabaseNames: [],
|
||||
migrationServiceId: null,
|
||||
skuRecommendation: null,
|
||||
};
|
||||
switch (currentPage) {
|
||||
case Page.Summary:
|
||||
@@ -1081,23 +1432,40 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
saveInfo.targetSubscription = this._databaseBackup.subscription;
|
||||
saveInfo.blobs = this._databaseBackup.blobs;
|
||||
saveInfo.targetDatabaseNames = this._targetDatabaseNames;
|
||||
|
||||
case Page.MigrationMode:
|
||||
saveInfo.migrationMode = this._databaseBackup.migrationMode;
|
||||
|
||||
case Page.TargetSelection:
|
||||
saveInfo.azureAccount = deepClone(this._azureAccount);
|
||||
saveInfo.azureTenant = deepClone(this._azureTenant);
|
||||
saveInfo.subscription = this._targetSubscription;
|
||||
saveInfo.location = this._location;
|
||||
saveInfo.resourceGroup = this._resourceGroup;
|
||||
saveInfo.targetServerInstance = this._targetServerInstance;
|
||||
|
||||
case Page.SKURecommendation:
|
||||
saveInfo.migrationTargetType = this._targetType;
|
||||
saveInfo.databaseAssessment = this._databaseAssessment;
|
||||
saveInfo.serverAssessment = this._assessmentResults;
|
||||
saveInfo.migrationDatabases = this._databaseSelection;
|
||||
saveInfo.databaseList = this._migrationDbs;
|
||||
saveInfo.subscription = this._targetSubscription;
|
||||
saveInfo.location = this._location;
|
||||
saveInfo.resourceGroup = this._resourceGroup;
|
||||
saveInfo.targetServerInstance = this._targetServerInstance;
|
||||
|
||||
if (this._skuRecommendationPerformanceDataSource) {
|
||||
let skuRecommendation: SkuRecommendationSavedInfo = {
|
||||
skuRecommendationPerformanceDataSource: this._skuRecommendationPerformanceDataSource,
|
||||
skuRecommendationPerformanceLocation: this._skuRecommendationPerformanceLocation,
|
||||
perfDataCollectionStartDate: this._perfDataCollectionStartDate,
|
||||
perfDataCollectionStopDate: this._perfDataCollectionStopDate,
|
||||
skuTargetPercentile: this._skuTargetPercentile,
|
||||
skuScalingFactor: this._skuScalingFactor,
|
||||
skuEnablePreview: this._skuEnablePreview,
|
||||
};
|
||||
saveInfo.skuRecommendation = skuRecommendation;
|
||||
}
|
||||
|
||||
case Page.DatabaseSelector:
|
||||
saveInfo.selectedDatabases = this.databaseSelectorTableValues;
|
||||
case Page.AzureAccount:
|
||||
saveInfo.azureAccount = deepClone(this._azureAccount);
|
||||
saveInfo.azureTenant = deepClone(this._azureTenant);
|
||||
await this.extensionContext.globalState.update(`${this.mementoString}.${serverName}`, saveInfo);
|
||||
}
|
||||
}
|
||||
@@ -1113,3 +1481,8 @@ export interface ServerAssessment {
|
||||
errors?: mssql.ErrorModel[];
|
||||
assessmentError?: Error;
|
||||
}
|
||||
|
||||
export interface SkuRecommendation {
|
||||
recommendations: mssql.SkuRecommendationResult;
|
||||
recommendationError?: Error;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user