userDataAutoSyncService.ts ×26

Frontier kind: Code frontier

unlabeled · c_af1602d33328

21 tests · 22223 LOC · 134 files · introduces 0 tests · 120 LOC · 1 file

Introduces — evidence that enters the hierarchy at this concept

Code
26 ranges120 lines · 1 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
3083 ranges22223 lines · 134 files · Browse complete extent
All tests (intent)
21 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

1 file ranked by introduced lines: 120 introduced LOC across 26 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/platform/userDataSync/common/userDataAutoSyncService.ts 120 introduced LOC · 26 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- userDataAutoSyncService.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { CancelablePromise, createCancelablePromise, disposableTimeout, ThrottledDelayer, timeout } from '../../../base/common/async.js';
7 > import { CancellationToken } from '../../../base/common/cancellation.js';
8 > import { toLocalISOString } from '../../../base/common/date.js';
9 > import { toErrorMessage } from '../../../base/common/errorMessage.js';
10 > import { isCancellationError } from '../../../base/common/errors.js';
11 > import { Emitter, Event } from '../../../base/common/event.js';
12 > import { Disposable, IDisposable, MutableDisposable, toDisposable } from '../../../base/common/lifecycle.js';
13 > import { isWeb } from '../../../base/common/platform.js';
14 > import { isEqual } from '../../../base/common/resources.js';
15 > import { URI } from '../../../base/common/uri.js';
16 > import { localize } from '../../../nls.js';
17 > import { IMeteredConnectionService } from '../../meteredConnection/common/meteredConnection.js';
18 > import { IProductService } from '../../product/common/productService.js';
19 > import { IStorageService, StorageScope, StorageTarget } from '../../storage/common/storage.js';
20 > import { ITelemetryService } from '../../telemetry/common/telemetry.js';
21 > import { IUserDataSyncTask, IUserDataAutoSyncService, IUserDataManifest, IUserDataSyncLogService, IUserDataSyncEnablementService, IUserDataSyncService, IUserDataSyncStoreManagementService, IUserDataSyncStoreService, UserDataAutoSyncError, UserDataSyncError, UserDataSyncErrorCode, SyncOptions } from './userDataSync.js';
22 > import { IUserDataSyncAccountService } from './userDataSyncAccount.js';
23 > import { IUserDataSyncMachinesService } from './userDataSyncMachines.js';
24 >
25 > const disableMachineEventuallyKey = 'sync.disableMachineEventually';
26 > const sessionIdKey = 'sync.sessionId';
27 > const storeUrlKey = 'sync.storeUrl';
28 > const productQualityKey = 'sync.productQuality';
29 >
30 > export class UserDataAutoSyncService extends Disposable implements IUserDataAutoSyncService {
31 >
32 > _serviceBrand: undefined;
33 >
34 > private readonly autoSync = this._register(new MutableDisposable<AutoSync>());
35 > private successiveFailures: number = 0;
36 > private lastSyncTriggerTime: number | undefined = undefined;
37 > private readonly syncTriggerDelayer: ThrottledDelayer<void>;
38 > private suspendUntilRestart: boolean = false;
39 >
40 > private readonly _onError: Emitter<UserDataSyncError> = this._register(new Emitter<UserDataSyncError>());
41 > readonly onError: Event<UserDataSyncError> = this._onError.event;
42 >
43 > private lastSyncUrl: URI | undefined;
44 > private get syncUrl(): URI | undefined {
45 > const value = this.storageService.get(storeUrlKey, StorageScope.APPLICATION);
46 > return value ? URI.parse(value) : undefined;
47 > }
48 > private set syncUrl(syncUrl: URI | undefined) {
49 if (syncUrl) {
50 this.storageService.store(storeUrlKey, syncUrl.toString(), StorageScope.APPLICATION, StorageTarget.MACHINE);
53 }
54 }
56 > private previousProductQuality: string | undefined;
57 > private get productQuality(): string | undefined {
58 return this.storageService.get(productQualityKey, StorageScope.APPLICATION);
59 }
60 > private set productQuality(productQuality: string | undefined) { userDataAutoSyncService.ts
61 if (productQuality) {
62 this.storageService.store(productQualityKey, productQuality, StorageScope.APPLICATION, StorageTarget.MACHINE);
65 }
66 }
68 > constructor(
69 @IProductService productService: IProductService,
70 @IUserDataSyncStoreManagementService private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService,
120 }
121 }
123 > private updateAutoSync(): void {
124 const { enabled, message } = this.isAutoSyncEnabled();
125 if (enabled) {
147 }
148 }
150 > // For tests purpose only
151 > protected startAutoSync(): boolean { return true; }
152 >
153 > private isAutoSyncEnabled(): { enabled: boolean; message?: string } {
154 if (!this.userDataSyncEnablementService.isEnabled()) {
155 return { enabled: false, message: '[AutoSync] Disabled.' };
169 return { enabled: true };
170 }
172 > async turnOn(): Promise<void> {
173 this.stopDisableMachineEventually();
174 this.lastSyncUrl = this.syncUrl;
175 this.updateEnablement(true);
176 }
178 > async turnOff(everywhere: boolean, softTurnOffOnError?: boolean, donotRemoveMachine?: boolean): Promise<void> {
179 try {
180
205 }
206 }
208 > private updateEnablement(enabled: boolean): void {
209 if (this.userDataSyncEnablementService.isEnabled() !== enabled) {
210 this.userDataSyncEnablementService.setEnablement(enabled);
212 }
213 }
215 > private hasProductQualityChanged(): boolean {
216 return !!this.previousProductQuality && !!this.productQuality && this.previousProductQuality !== this.productQuality;
217 }
219 > private async onDidFinishSync(error: Error | undefined): Promise<void> {
220 this.logService.debug('[AutoSync] Sync Finished');
221 if (!error) {
308 this._onError.fire(userDataSyncError);
309 }
311 > private async disableMachineEventually(): Promise<void> {
312 this.storageService.store(disableMachineEventuallyKey, true, StorageScope.APPLICATION, StorageTarget.MACHINE);
313 await timeout(1000 * 60 * 10);
325 }
326 }
328 > private hasToDisableMachineEventually(): boolean {
329 return this.storageService.getBoolean(disableMachineEventuallyKey, StorageScope.APPLICATION, false);
330 }
332 > private stopDisableMachineEventually(): void {
333 this.storageService.remove(disableMachineEventuallyKey, StorageScope.APPLICATION);
334 }
336 > private sources: string[] = [];
337 > async triggerSync(sources: string[], options?: SyncOptions): Promise<void> {
338 if (this.autoSync.value === undefined) {
339 return this.syncTriggerDelayer.cancel();
357
358 }
360 > protected getSyncTriggerDelayTime(): number {
361 if (this.lastSyncTriggerTime && new Date().getTime() - this.lastSyncTriggerTime > 10_000) {
362 this.logService.debug('[AutoSync] Sync immediately because last sync was triggered more than 10 seconds ago.');
365 return 3_000; /* Debounce for 3 seconds if there are no failures */
366 }
368 > }
369 >
370 > class AutoSync extends Disposable {
371 >
372 > private static readonly INTERVAL_SYNCING = 'Interval';
373 >
374 > private readonly intervalHandler = this._register(new MutableDisposable<IDisposable>());
375 >
376 > private readonly _onDidStartSync = this._register(new Emitter<void>());
377 > readonly onDidStartSync = this._onDidStartSync.event;
378 >
379 > private readonly _onDidFinishSync = this._register(new Emitter<Error | undefined>());
380 > readonly onDidFinishSync = this._onDidFinishSync.event;
381 >
382 > private manifest: IUserDataManifest | null = null;
383 > private syncTask: IUserDataSyncTask | undefined;
384 > private syncPromise: CancelablePromise<void> | undefined;
385 >
386 > constructor(
387 private readonly lastSyncUrl: URI | undefined,
388 private readonly interval: number /* in milliseconds */,
397 super();
398 }
400 > start(): void {
401 this._register(this.onDidFinishSync(() => this.waitUntilNextIntervalAndSync()));
402 this._register(toDisposable(() => {
411 this.sync(AutoSync.INTERVAL_SYNCING, false);
412 }
414 > private waitUntilNextIntervalAndSync(): void {
415 this.intervalHandler.value = disposableTimeout(() => {
416 this.sync(AutoSync.INTERVAL_SYNCING, false);
418 }, this.interval);
419 }
421 > sync(reason: string, disableCache: boolean): Promise<void> {
422 const syncPromise = createCancelablePromise(async token => {
423 if (this.syncPromise) {
439 return this.syncPromise;
440 }
442 > private hasSyncServiceChanged(): boolean {
443 return this.lastSyncUrl !== undefined && !isEqual(this.lastSyncUrl, this.userDataSyncStoreManagementService.userDataSyncStore?.url);
444 }
446 > private async hasDefaultServiceChanged(): Promise<boolean> {
447 const previous = await this.userDataSyncStoreManagementService.getPreviousUserDataSyncStore();
448 const current = this.userDataSyncStoreManagementService.userDataSyncStore;
453 !isEqual(current.stableUrl, previous.stableUrl));
454 }
456 > private async doSync(reason: string, disableCache: boolean, token: CancellationToken): Promise<void> {
457 this.logService.info(`[AutoSync] Triggered by ${reason}`);
458
481 this._onDidFinishSync.fire(error);
482 }
484 > private async createAndRunSyncTask(disableCache: boolean, token: CancellationToken): Promise<void> {
485 this.syncTask = await this.userDataSyncService.createSyncTask(this.manifest, disableCache);
486 if (token.isCancellationRequested) {