diff --git a/src/vs/platform/assignment/common/assignmentService.ts b/src/vs/platform/assignment/common/assignmentService.ts index 51e1d1d95de..ad2997b1fec 100644 --- a/src/vs/platform/assignment/common/assignmentService.ts +++ b/src/vs/platform/assignment/common/assignmentService.ts @@ -3,16 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import type { IExperimentationTelemetry, ExperimentationService as TASClient } from 'tas-client-umd'; +import type { IExperimentationTelemetry, ExperimentationService as TASClient, IKeyValueStorage } from 'tas-client-umd'; import { TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IProductService } from 'vs/platform/product/common/productService'; import { getTelemetryLevel } from 'vs/platform/telemetry/common/telemetryUtils'; import { AssignmentFilterProvider, ASSIGNMENT_REFETCH_INTERVAL, ASSIGNMENT_STORAGE_KEY, IAssignmentService, TargetPopulation } from 'vs/platform/assignment/common/assignment'; -class AssignmentServiceTelemetry implements IExperimentationTelemetry { - constructor( - ) { } +class NullAssignmentServiceTelemetry implements IExperimentationTelemetry { + constructor() { } setSharedProperty(name: string, value: string): void { // noop due to lack of telemetry service @@ -23,22 +22,22 @@ class AssignmentServiceTelemetry implements IExperimentationTelemetry { } } -export class AssignmentService implements IAssignmentService { +export abstract class BaseAssignmentService implements IAssignmentService { _serviceBrand: undefined; - private tasClient: Promise | undefined; - private telemetry: AssignmentServiceTelemetry | undefined; + protected tasClient: Promise | undefined; private networkInitialized = false; - private overrideInitDelay: Promise; - private get experimentsEnabled(): boolean { + protected get experimentsEnabled(): boolean { return this.configurationService.getValue('workbench.enableExperiments') === true; } constructor( - private readonly machineId: string, - @IConfigurationService private configurationService: IConfigurationService, - @IProductService private productService: IProductService + private readonly getMachineId: () => Promise, + private readonly configurationService: IConfigurationService, + protected readonly productService: IProductService, + protected telemetry: IExperimentationTelemetry, + private keyValueStorage?: IKeyValueStorage ) { if (productService.tasConfig && this.experimentsEnabled && getTelemetryLevel(this.configurationService) === TelemetryLevel.USAGE) { @@ -69,17 +68,24 @@ export class AssignmentService implements IAssignmentService { let result: T | undefined; const client = await this.tasClient; + + // The TAS client is initialized but we need to check if the initial fetch has completed yet + // If it is complete, return a cached value for the treatment + // If not, use the async call with `checkCache: true`. This will allow the module to return a cached value if it is present. + // Otherwise it will await the initial fetch to return the most up to date value. if (this.networkInitialized) { result = client.getTreatmentVariable('vscode', name); } else { result = await client.getTreatmentVariableAsync('vscode', name, true); } + + result = client.getTreatmentVariable('vscode', name); return result; } private async setupTASClient(): Promise { const targetPopulation = this.productService.quality === 'stable' ? TargetPopulation.Public : TargetPopulation.Insiders; - const machineId = this.machineId; + const machineId = await this.getMachineId(); const filterProvider = new AssignmentFilterProvider( this.productService.version, this.productService.nameLong, @@ -87,14 +93,12 @@ export class AssignmentService implements IAssignmentService { targetPopulation ); - this.telemetry = new AssignmentServiceTelemetry(); - const tasConfig = this.productService.tasConfig!; const tasClient = new (await import('tas-client-umd')).ExperimentationService({ filterProviders: [filterProvider], telemetry: this.telemetry, storageKey: ASSIGNMENT_STORAGE_KEY, - keyValueStorage: undefined, + keyValueStorage: this.keyValueStorage, featuresTelemetryPropertyName: tasConfig.featuresTelemetryPropertyName, assignmentContextTelemetryPropertyName: tasConfig.assignmentContextTelemetryPropertyName, telemetryEventName: tasConfig.telemetryEventName, @@ -103,8 +107,17 @@ export class AssignmentService implements IAssignmentService { }); await tasClient.initializePromise; - tasClient.initialFetch.then(() => this.networkInitialized = true); + return tasClient; } } + +export class AssignmentService extends BaseAssignmentService { + constructor( + machineId: string, + configurationService: IConfigurationService, + productService: IProductService) { + super(() => Promise.resolve(machineId), configurationService, productService, new NullAssignmentServiceTelemetry()); + } +} diff --git a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts index 5d2fa898074..8fd50b5d573 100644 --- a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts @@ -25,7 +25,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IExtensionRecommendationNotificationService, RecommendationsNotificationResult, RecommendationSource } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; -import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService'; +import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService'; import { distinct } from 'vs/base/common/arrays'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -102,7 +102,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { @IStorageService private readonly storageService: IStorageService, @IExtensionRecommendationNotificationService private readonly extensionRecommendationNotificationService: IExtensionRecommendationNotificationService, @IExtensionIgnoredRecommendationsService private readonly extensionIgnoredRecommendationsService: IExtensionIgnoredRecommendationsService, - @ITASExperimentService private tasExperimentService: ITASExperimentService, + @IWorkbenchAssignmentService private tasExperimentService: IWorkbenchAssignmentService, @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, ) { super(); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/profile/notebookProfile.ts b/src/vs/workbench/contrib/notebook/browser/contrib/profile/notebookProfile.ts index 811b6796a30..090e1383270 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/profile/notebookProfile.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/profile/notebookProfile.ts @@ -10,7 +10,7 @@ import { localize } from 'vs/nls'; import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { CellToolbarLocation, CompactView, ConsolidatedRunButton, FocusIndicator, GlobalToolbar, InsertToolbarLocation, ShowCellStatusBar, UndoRedoPerCell } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService'; +import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; @@ -92,7 +92,7 @@ function isSetProfileArgs(args: unknown): args is ISetProfileArgs { } export class NotebookProfileContribution extends Disposable { - constructor(@IConfigurationService configService: IConfigurationService, @ITASExperimentService private readonly experimentService: ITASExperimentService) { + constructor(@IConfigurationService configService: IConfigurationService, @IWorkbenchAssignmentService private readonly experimentService: IWorkbenchAssignmentService) { super(); if (this.experimentService) { diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts index 5f148fa4b91..8e1f4b086e7 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts @@ -25,7 +25,7 @@ import { NotebooKernelActionViewItem } from 'vs/workbench/contrib/notebook/brows import { ActionViewWithLabel } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellActionView'; import { GlobalToolbarShowLabel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService'; +import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService'; import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions'; interface IActionModel { @@ -69,7 +69,7 @@ export class NotebookEditorToolbar extends Disposable { @IMenuService readonly menuService: IMenuService, @IEditorService private readonly editorService: IEditorService, @IKeybindingService private readonly keybindingService: IKeybindingService, - @ITASExperimentService private readonly experimentService: ITASExperimentService + @IWorkbenchAssignmentService private readonly experimentService: IWorkbenchAssignmentService ) { super(); diff --git a/src/vs/workbench/contrib/surveys/browser/ces.contribution.ts b/src/vs/workbench/contrib/surveys/browser/ces.contribution.ts index 38a37e23c1d..125da89cf20 100644 --- a/src/vs/workbench/contrib/surveys/browser/ces.contribution.ts +++ b/src/vs/workbench/contrib/surveys/browser/ces.contribution.ts @@ -13,7 +13,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Severity, INotificationService } from 'vs/platform/notification/common/notification'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService'; +import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService'; import { URI } from 'vs/base/common/uri'; import { platform } from 'vs/base/common/process'; import { ThrottledDelayer } from 'vs/base/common/async'; @@ -30,7 +30,7 @@ const REMIND_LATER_DATE_KEY = 'ces/remindLaterDate'; class CESContribution extends Disposable implements IWorkbenchContribution { private promptDelayer = this._register(new ThrottledDelayer(0)); - private readonly tasExperimentService: ITASExperimentService | undefined; + private readonly tasExperimentService: IWorkbenchAssignmentService | undefined; constructor( @IStorageService private readonly storageService: IStorageService, @@ -38,7 +38,7 @@ class CESContribution extends Disposable implements IWorkbenchContribution { @ITelemetryService private readonly telemetryService: ITelemetryService, @IOpenerService private readonly openerService: IOpenerService, @IProductService private readonly productService: IProductService, - @ITASExperimentService tasExperimentService: ITASExperimentService, + @IWorkbenchAssignmentService tasExperimentService: IWorkbenchAssignmentService, ) { super(); diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.contribution.ts b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.contribution.ts index b79f4f85cb5..848cd9cd251 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.contribution.ts +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.contribution.ts @@ -25,7 +25,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor import { EditorResolution } from 'vs/platform/editor/common/editor'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService'; +import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { isLinux, isMacintosh, isWindows, OperatingSystem as OS } from 'vs/base/common/platform'; @@ -230,12 +230,12 @@ class WorkbenchConfigurationContribution { constructor( @IInstantiationService _instantiationService: IInstantiationService, @IConfigurationService _configurationService: IConfigurationService, - @ITASExperimentService _experimentSevice: ITASExperimentService, + @IWorkbenchAssignmentService _experimentSevice: IWorkbenchAssignmentService, ) { this.registerConfigs(_experimentSevice); } - private async registerConfigs(_experimentSevice: ITASExperimentService) { + private async registerConfigs(_experimentSevice: IWorkbenchAssignmentService) { const preferReduced = await _experimentSevice.getTreatment('welcomePage.preferReducedMotion').catch(e => false); if (preferReduced) { configurationRegistry.updateConfigurations({ add: [prefersReducedMotionConfig], remove: [prefersStandardMotionConfig] }); diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStartedService.ts b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStartedService.ts index 57ce1f03111..d74192720f0 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStartedService.ts +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStartedService.ts @@ -19,7 +19,7 @@ import { FileAccess } from 'vs/base/common/network'; import { DefaultIconPath, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { walkthroughs } from 'vs/workbench/contrib/welcome/gettingStarted/common/gettingStartedContent'; -import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService'; +import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILink, LinkedText, parseLinkedText } from 'vs/base/common/linkedText'; @@ -135,7 +135,7 @@ export class WalkthroughsService extends Disposable implements IWalkthroughsServ private gettingStartedContributions = new Map(); private steps = new Map(); - private tasExperimentService?: ITASExperimentService; + private tasExperimentService?: IWorkbenchAssignmentService; private sessionInstalledExtensions = new Set(); private categoryVisibilityContextKeys = new Set(); @@ -159,7 +159,7 @@ export class WalkthroughsService extends Disposable implements IWalkthroughsServ @IHostService private readonly hostService: IHostService, @IViewsService private readonly viewsService: IViewsService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @ITASExperimentService tasExperimentService: ITASExperimentService, + @IWorkbenchAssignmentService tasExperimentService: IWorkbenchAssignmentService, ) { super(); diff --git a/src/vs/workbench/services/assignment/common/assignmentService.ts b/src/vs/workbench/services/assignment/common/assignmentService.ts new file mode 100644 index 00000000000..8c290b81d8d --- /dev/null +++ b/src/vs/workbench/services/assignment/common/assignmentService.ts @@ -0,0 +1,127 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import type { IKeyValueStorage, IExperimentationTelemetry } from 'tas-client-umd'; +import { MementoObject, Memento } from 'vs/workbench/common/memento'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { ITelemetryData } from 'vs/base/common/actions'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { IAssignmentService } from 'vs/platform/assignment/common/assignment'; +import { BaseAssignmentService } from 'vs/platform/assignment/common/assignmentService'; + +export const IWorkbenchAssignmentService = createDecorator('WorkbenchAssignmentService'); + +export interface IWorkbenchAssignmentService extends IAssignmentService { + getCurrentExperiments(): Promise; +} + +class MementoKeyValueStorage implements IKeyValueStorage { + private mementoObj: MementoObject; + constructor(private memento: Memento) { + this.mementoObj = memento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); + } + + async getValue(key: string, defaultValue?: T | undefined): Promise { + const value = await this.mementoObj[key]; + return value || defaultValue; + } + + setValue(key: string, value: T): void { + this.mementoObj[key] = value; + this.memento.saveMemento(); + } +} + +class WorkbenchAssignmentServiceTelemetry implements IExperimentationTelemetry { + private _lastAssignmentContext: string | undefined; + constructor( + private telemetryService: ITelemetryService, + private productService: IProductService + ) { } + + get assignmentContext(): string[] | undefined { + return this._lastAssignmentContext?.split(';'); + } + + // __GDPR__COMMON__ "VSCode.ABExp.Features" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + // __GDPR__COMMON__ "abexp.assignmentcontext" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + setSharedProperty(name: string, value: string): void { + if (name === this.productService.tasConfig?.assignmentContextTelemetryPropertyName) { + this._lastAssignmentContext = value; + } + + this.telemetryService.setExperimentProperty(name, value); + } + + postEvent(eventName: string, props: Map): void { + const data: ITelemetryData = {}; + for (const [key, value] of props.entries()) { + data[key] = value; + } + + /* __GDPR__ + "query-expfeature" : { + "ABExp.queriedFeature": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + this.telemetryService.publicLog(eventName, data); + } +} + +export class WorkbenchAssignmentService extends BaseAssignmentService { + constructor( + @ITelemetryService private telemetryService: ITelemetryService, + @IStorageService storageService: IStorageService, + @IConfigurationService configurationService: IConfigurationService, + @IProductService productService: IProductService + ) { + + super(() => { + return telemetryService.getTelemetryInfo().then(telemetryInfo => { + return telemetryInfo.machineId; + }); + }, configurationService, productService, + new WorkbenchAssignmentServiceTelemetry(telemetryService, productService), + new MementoKeyValueStorage(new Memento('experiment.service.memento', storageService))); + } + + override async getTreatment(name: string): Promise { + const result = await super.getTreatment(name); + type TASClientReadTreatmentData = { + treatmentName: string; + treatmentValue: string; + }; + + type TASClientReadTreatmentClassification = { + treatmentValue: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', }; + treatmentName: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', }; + }; + + this.telemetryService.publicLog2('tasClientReadTreatmentComplete', + { treatmentName: name, treatmentValue: JSON.stringify(result) }); + + return result; + } + + async getCurrentExperiments(): Promise { + if (!this.tasClient) { + return undefined; + } + + if (!this.experimentsEnabled) { + return undefined; + } + + await this.tasClient; + + return (this.telemetry as WorkbenchAssignmentServiceTelemetry)?.assignmentContext; + } +} + +registerSingleton(IWorkbenchAssignmentService, WorkbenchAssignmentService, false); diff --git a/src/vs/workbench/services/experiment/common/experimentService.ts b/src/vs/workbench/services/experiment/common/experimentService.ts deleted file mode 100644 index 6605f3545c1..00000000000 --- a/src/vs/workbench/services/experiment/common/experimentService.ts +++ /dev/null @@ -1,207 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import type { IKeyValueStorage, IExperimentationTelemetry, ExperimentationService as TASClient } from 'tas-client-umd'; -import { MementoObject, Memento } from 'vs/workbench/common/memento'; -import { ITelemetryService, TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; -import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { ITelemetryData } from 'vs/base/common/actions'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IProductService } from 'vs/platform/product/common/productService'; -import { AssignmentFilterProvider, ASSIGNMENT_REFETCH_INTERVAL, ASSIGNMENT_STORAGE_KEY, IAssignmentService, TargetPopulation } from 'vs/platform/assignment/common/assignment'; - -export const ITASExperimentService = createDecorator('TASExperimentService'); - -export interface ITASExperimentService extends IAssignmentService { - getCurrentExperiments(): Promise; -} - -class MementoKeyValueStorage implements IKeyValueStorage { - private mementoObj: MementoObject; - constructor(private memento: Memento) { - this.mementoObj = memento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); - } - - async getValue(key: string, defaultValue?: T | undefined): Promise { - const value = await this.mementoObj[key]; - return value || defaultValue; - } - - setValue(key: string, value: T): void { - this.mementoObj[key] = value; - this.memento.saveMemento(); - } -} - -class ExperimentServiceTelemetry implements IExperimentationTelemetry { - private _lastAssignmentContext: string | undefined; - constructor( - private telemetryService: ITelemetryService, - private productService: IProductService - ) { } - - get assignmentContext(): string[] | undefined { - return this._lastAssignmentContext?.split(';'); - } - - // __GDPR__COMMON__ "VSCode.ABExp.Features" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - // __GDPR__COMMON__ "abexp.assignmentcontext" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - setSharedProperty(name: string, value: string): void { - if (name === this.productService.tasConfig?.assignmentContextTelemetryPropertyName) { - this._lastAssignmentContext = value; - } - - this.telemetryService.setExperimentProperty(name, value); - } - - postEvent(eventName: string, props: Map): void { - const data: ITelemetryData = {}; - for (const [key, value] of props.entries()) { - data[key] = value; - } - - /* __GDPR__ - "query-expfeature" : { - "ABExp.queriedFeature": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog(eventName, data); - } -} - -export class ExperimentService implements ITASExperimentService { - _serviceBrand: undefined; - private tasClient: Promise | undefined; - private telemetry: ExperimentServiceTelemetry | undefined; - private static MEMENTO_ID = 'experiment.service.memento'; - private networkInitialized = false; - - private overrideInitDelay: Promise; - - private get experimentsEnabled(): boolean { - return this.configurationService.getValue('workbench.enableExperiments') === true; - } - - constructor( - @ITelemetryService private telemetryService: ITelemetryService, - @IStorageService private storageService: IStorageService, - @IConfigurationService private configurationService: IConfigurationService, - @IProductService private productService: IProductService - ) { - - if (productService.tasConfig && this.experimentsEnabled && this.telemetryService.telemetryLevel === TelemetryLevel.USAGE) { - this.tasClient = this.setupTASClient(); - } - - // For development purposes, configure the delay until tas local tas treatment ovverrides are available - const overrideDelaySetting = this.configurationService.getValue('experiments.overrideDelay'); - const overrideDelay = typeof overrideDelaySetting === 'number' ? overrideDelaySetting : 0; - this.overrideInitDelay = new Promise(resolve => setTimeout(resolve, overrideDelay)); - } - - async getTreatment(name: string): Promise { - // For development purposes, allow overriding tas assignments to test variants locally. - await this.overrideInitDelay; - const override = this.configurationService.getValue('experiments.override.' + name); - if (override !== undefined) { - type TAASClientOverrideTreatmentData = { treatmentName: string; }; - type TAASClientOverrideTreatmentClassification = { treatmentName: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', }; }; - this.telemetryService.publicLog2('tasClientOverrideTreatment', { treatmentName: name, }); - return override; - } - - const startSetup = Date.now(); - - if (!this.tasClient) { - return undefined; - } - - if (!this.experimentsEnabled) { - return undefined; - } - - let result: T | undefined; - const client = await this.tasClient; - if (this.networkInitialized) { - result = client.getTreatmentVariable('vscode', name); - } else { - result = await client.getTreatmentVariableAsync('vscode', name, true); - } - - type TAASClientReadTreatmentData = { - treatmentName: string; - treatmentValue: string; - readTime: number; - }; - - type TAASClientReadTreatmentCalssification = { - treatmentValue: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', }; - treatmentName: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', }; - readTime: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; - }; - this.telemetryService.publicLog2('tasClientReadTreatmentComplete', - { readTime: Date.now() - startSetup, treatmentName: name, treatmentValue: JSON.stringify(result) }); - - return result; - } - - async getCurrentExperiments(): Promise { - if (!this.tasClient) { - return undefined; - } - - if (!this.experimentsEnabled) { - return undefined; - } - - await this.tasClient; - - return this.telemetry?.assignmentContext; - } - - private async setupTASClient(): Promise { - const startSetup = Date.now(); - const telemetryInfo = await this.telemetryService.getTelemetryInfo(); - const targetPopulation = telemetryInfo.msftInternal ? TargetPopulation.Internal : (this.productService.quality === 'stable' ? TargetPopulation.Public : TargetPopulation.Insiders); - const machineId = telemetryInfo.machineId; - const filterProvider = new AssignmentFilterProvider( - this.productService.version, - this.productService.nameLong, - machineId, - targetPopulation - ); - - const keyValueStorage = new MementoKeyValueStorage(new Memento(ExperimentService.MEMENTO_ID, this.storageService)); - - this.telemetry = new ExperimentServiceTelemetry(this.telemetryService, this.productService); - - const tasConfig = this.productService.tasConfig!; - const tasClient = new (await import('tas-client-umd')).ExperimentationService({ - filterProviders: [filterProvider], - telemetry: this.telemetry, - storageKey: ASSIGNMENT_STORAGE_KEY, - keyValueStorage: keyValueStorage, - featuresTelemetryPropertyName: tasConfig.featuresTelemetryPropertyName, - assignmentContextTelemetryPropertyName: tasConfig.assignmentContextTelemetryPropertyName, - telemetryEventName: tasConfig.telemetryEventName, - endpoint: tasConfig.endpoint, - refetchInterval: ASSIGNMENT_REFETCH_INTERVAL, - }); - - await tasClient.initializePromise; - - tasClient.initialFetch.then(() => this.networkInitialized = true); - - type TAASClientSetupData = { setupTime: number; }; - type TAASClientSetupCalssification = { setupTime: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; }; - this.telemetryService.publicLog2('tasClientSetupComplete', { setupTime: Date.now() - startSetup }); - - return tasClient; - } -} - -registerSingleton(ITASExperimentService, ExperimentService, false); diff --git a/src/vs/workbench/services/issue/electron-sandbox/issueService.ts b/src/vs/workbench/services/issue/electron-sandbox/issueService.ts index 8e29675c52c..cf004784fe9 100644 --- a/src/vs/workbench/services/issue/electron-sandbox/issueService.ts +++ b/src/vs/workbench/services/issue/electron-sandbox/issueService.ts @@ -16,7 +16,7 @@ import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/enviro import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { platform } from 'vs/base/common/process'; import { IProductService } from 'vs/platform/product/common/productService'; -import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService'; +import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService'; import { IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; import { registerMainProcessRemoteService } from 'vs/platform/ipc/electron-sandbox/services'; import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; @@ -32,7 +32,7 @@ export class WorkbenchIssueService implements IWorkbenchIssueService { @INativeWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService, @IWorkspaceTrustManagementService private readonly workspaceTrustManagementService: IWorkspaceTrustManagementService, @IProductService private readonly productService: IProductService, - @ITASExperimentService private readonly experimentService: ITASExperimentService, + @IWorkbenchAssignmentService private readonly experimentService: IWorkbenchAssignmentService, @IAuthenticationService private readonly authenticationService: IAuthenticationService ) { } diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 62459265ea6..44c58d72571 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -93,7 +93,7 @@ import 'vs/workbench/services/quickinput/browser/quickInputService'; import 'vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService'; import 'vs/workbench/services/authentication/browser/authenticationService'; import 'vs/workbench/services/hover/browser/hoverService'; -import 'vs/workbench/services/experiment/common/experimentService'; +import 'vs/workbench/services/assignment/common/assignmentService'; import 'vs/workbench/services/outline/browser/outlineService'; import 'vs/workbench/services/languageDetection/browser/languageDetectionWorkerServiceImpl';