src/vs/platform/userDataSync/common/userDataAutoSyncService.ts

571 LOC · 397 covered · 174 uncovered · 102 ranges · 39 concepts · 18 introducers · 21 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

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 related-file, concept, and source links on this page.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the filesrc/vs/base/common/async.ts · 2749 LOCcommon/async.tssrc/vs/platform/userDataSync/common/abstractSynchronizer.ts · 958 LOCcommon/abstractSynchroni…src/vs/platform/userDataSync/common/userDataSync.ts · 666 LOCcommon/userDataSync.tssrc/vs/platform/userDataSync/common/userDataSyncEnablementService.ts · 94 LOCcommon/userDataSyncEnabl…src/vs/platform/userDataSync/common/userDataSyncMachines.ts · 219 LOCcommon/userDataSyncMachi…src/vs/platform/userDataSync/common/userDataSyncService.ts · 948 LOCcommon/userDataSyncServi…src/vs/platform/userDataSync/common/userDataSyncStoreService.ts · 761 LOCcommon/userDataSyncStore…userDataAutoSyncService.test|title=UserDataAutoSyncService test further auto sync requests with changes|occurrence=1 · 0 introduced LOCuserDataAutoSyncService.…userDataSyncService.ts ×3 · 13 introduced LOCuserDataSyncService.ts ×…userDataAutoSyncService.ts ×3 · 8 introduced LOCuserDataAutoSyncService.…userDataAutoSyncService.ts ×1 · 2 introduced LOCuserDataAutoSyncService.…userDataAutoSyncService.test|title=UserDataAutoSyncService test auto sync with sync resource change triggers sync for every change|occurrence=1 · 0 introduced LOCuserDataAutoSyncService.…userDataAutoSyncService.ts ×1 · 3 introduced LOCuserDataAutoSyncService.…abstractJsonSynchronizer.ts ×2 · 8 introduced LOCabstractJsonSynchronizer…userDataAutoSyncService.test|title=UserDataAutoSyncService test removing the machine adds machine back|occurrence=1 · 0 introduced LOCuserDataAutoSyncService.…userDataAutoSyncService.test|title=UserDataAutoSyncService test auto sync send execution id header|occurrence=1, userDataAutoSyncService.test|title=UserDataAutoSyncService test further auto sync requests without changes|occurrence=1 · 0 introduced LOCuserDataAutoSyncService.…globalStateSync.ts ×2 · 30 introduced LOCglobalStateSync.ts ×2userDataAutoSyncService.ts ×1 · 12 introduced LOCuserDataAutoSyncService.…userDataAutoSyncService.ts ×1 · 3 introduced LOCuserDataAutoSyncService.…userDataAutoSyncService.ts ×1 · 4 introduced LOCuserDataAutoSyncService.…userDataSyncMachines.ts ×1 · 3 introduced LOCuserDataSyncMachines.ts …userDataAutoSyncService.test|title=UserDataAutoSyncService test first auto sync requests|occurrence=1 · 0 introduced LOCuserDataAutoSyncService.…userDataAutoSyncService.ts ×2 · 4 introduced LOCuserDataAutoSyncService.…userDataSyncMachines.ts ×2 · 5 introduced LOCuserDataSyncMachines.ts …userDataSyncMachines.ts ×3 · 6 introduced LOCuserDataSyncMachines.ts …userDataSyncStoreService.ts ×1 · 2 introduced LOCuserDataSyncStoreService…userDataSyncMachines.ts ×8 · 66 introduced LOCuserDataSyncMachines.ts …userDataAutoSyncService.ts ×1 · 2 introduced LOCuserDataAutoSyncService.…userDataSyncService.ts ×1 · 2 introduced LOCuserDataSyncService.ts ×…userDataAutoSyncService.test|title=UserDataAutoSyncService test cache control header is not sent when triggered without disable cache option|occurrence=1 · 0 introduced LOCuserDataAutoSyncService.…userDataAutoSyncService.ts ×6 · 28 introduced LOCuserDataAutoSyncService.…userDataAutoSyncService.ts ×3 · 12 introduced LOCuserDataAutoSyncService.…userDataAutoSyncService.ts ×1 · 3 introduced LOCuserDataAutoSyncService.…userDataSyncService.ts ×3 · 32 introduced LOCuserDataSyncService.ts ×…userDataAutoSyncService.ts ×7 · 37 introduced LOCuserDataAutoSyncService.…userDataSyncService.ts ×3 · 8 introduced LOCuserDataSyncService.ts ×…userDataSyncService.ts ×9 · 26 introduced LOCuserDataSyncService.ts ×…userDataSync.ts ×3 · 9 introduced LOCuserDataSync.ts ×3userDataAutoSyncService.ts ×2 · 3 introduced LOCuserDataAutoSyncService.…userDataAutoSyncService.ts ×37 · 168 introduced LOCuserDataAutoSyncService.…userDataAutoSyncService.test|title=ProfileStorageService write in storage with data (insert, update, remove)|occurrence=1 · 0 introduced LOCuserDataAutoSyncService.…userDataAutoSyncService.test|title=ProfileStorageService write in storage with data|occurrence=1 · 0 introduced LOCuserDataAutoSyncService.…userDataAutoSyncService.test|title=ProfileStorageService write in empty storage|occurrence=1 · 0 introduced LOCuserDataAutoSyncService.…userDataAutoSyncService.test|title=ProfileStorageService read storage with data|occurrence=1 · 0 introduced LOCuserDataAutoSyncService.…userDataAutoSyncService.test|title=ProfileStorageService read empty storage|occurrence=1 · 0 introduced LOCuserDataAutoSyncService.…userDataAutoSyncService.ts ×26 · 120 introduced LOCuserDataAutoSyncService.…userDataAutoSyncService.test|title=ProfileStorageService read empty storage|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/userDataSync/test/common/userDataAutoSyncService.test|title=ProfileStorageService read empty storage|occurrence=1userDataAutoSyncService.…userDataAutoSyncService.test|title=ProfileStorageService read storage with data|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/userDataSync/test/common/userDataAutoSyncService.test|title=ProfileStorageService read storage with data|occurrence=1userDataAutoSyncService.…userDataAutoSyncService.test|title=ProfileStorageService write in empty storage|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/userDataSync/test/common/userDataAutoSyncService.test|title=ProfileStorageService write in empty storage|occurrence=1userDataAutoSyncService.…userDataAutoSyncService.test|title=ProfileStorageService write in storage with data (insert, update, remove)|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/userDataSync/test/common/userDataAutoSyncService.test|title=ProfileStorageService write in storage with data (insert, update, remove)|occurrence=1userDataAutoSyncService.…userDataAutoSyncService.test|title=ProfileStorageService write in storage with data|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/userDataSync/test/common/userDataAutoSyncService.test|title=ProfileStorageService write in storage with data|occurrence=1userDataAutoSyncService.…userDataAutoSyncService.test|title=UserDataAutoSyncService test auto sync is suspended when server donot accepts requests|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/userDataSync/test/common/userDataAutoSyncService.test|title=UserDataAutoSyncService test auto sync is suspended when server donot accepts requests|occurrence=1userDataAutoSyncService.…userDataAutoSyncService.test|title=UserDataAutoSyncService test auto sync send execution id header|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/userDataSync/test/common/userDataAutoSyncService.test|title=UserDataAutoSyncService test auto sync send execution id header|occurrence=1userDataAutoSyncService.…userDataAutoSyncService.test|title=UserDataAutoSyncService test auto sync with non sync resource change does not trigger continuous syncs|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/userDataSync/test/common/userDataAutoSyncService.test|title=UserDataAutoSyncService test auto sync with non sync resource change does not trigger continuous syncs|occurrence=1userDataAutoSyncService.…userDataAutoSyncService.test|title=UserDataAutoSyncService test auto sync with non sync resource change triggers sync|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/userDataSync/test/common/userDataAutoSyncService.test|title=UserDataAutoSyncService test auto sync with non sync resource change triggers sync|occurrence=1userDataAutoSyncService.…userDataAutoSyncService.test|title=UserDataAutoSyncService test auto sync with sync resource change triggers sync for every change|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/userDataSync/test/common/userDataAutoSyncService.test|title=UserDataAutoSyncService test auto sync with sync resource change triggers sync for every change|occurrence=1userDataAutoSyncService.…userDataAutoSyncService.test|title=UserDataAutoSyncService test auto sync with sync resource change triggers sync|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/userDataSync/test/common/userDataAutoSyncService.test|title=UserDataAutoSyncService test auto sync with sync resource change triggers sync|occurrence=1userDataAutoSyncService.…userDataAutoSyncService.test|title=UserDataAutoSyncService test cache control header is not sent when triggered without disable cache option|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/userDataSync/test/common/userDataAutoSyncService.test|title=UserDataAutoSyncService test cache control header is not sent when triggered without disable cache option|occurrence=1userDataAutoSyncService.…userDataAutoSyncService.test|title=UserDataAutoSyncService test cache control header with no cache is sent when triggered with disable cache option|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/userDataSync/test/common/userDataAutoSyncService.test|title=UserDataAutoSyncService test cache control header with no cache is sent when triggered with disable cache option|occurrence=1userDataAutoSyncService.…userDataAutoSyncService.test|title=UserDataAutoSyncService test creating new session from one client throws session expired error on another client while syncing|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/userDataSync/test/common/userDataAutoSyncService.test|title=UserDataAutoSyncService test creating new session from one client throws session expired error on another client while syncing|occurrence=1userDataAutoSyncService.…userDataAutoSyncService.test|title=UserDataAutoSyncService test delete on one client throws turned off error on other client while syncing|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/userDataSync/test/common/userDataAutoSyncService.test|title=UserDataAutoSyncService test delete on one client throws turned off error on other client while syncing|occurrence=1userDataAutoSyncService.…userDataAutoSyncService.test|title=UserDataAutoSyncService test disabling the machine turns off sync|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/userDataSync/test/common/userDataAutoSyncService.test|title=UserDataAutoSyncService test disabling the machine turns off sync|occurrence=1userDataAutoSyncService.…userDataAutoSyncService.test|title=UserDataAutoSyncService test first auto sync requests|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/userDataSync/test/common/userDataAutoSyncService.test|title=UserDataAutoSyncService test first auto sync requests|occurrence=1userDataAutoSyncService.…userDataAutoSyncService.test|title=UserDataAutoSyncService test further auto sync requests with changes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/userDataSync/test/common/userDataAutoSyncService.test|title=UserDataAutoSyncService test further auto sync requests with changes|occurrence=1userDataAutoSyncService.…userDataAutoSyncService.test|title=UserDataAutoSyncService test further auto sync requests without changes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/userDataSync/test/common/userDataAutoSyncService.test|title=UserDataAutoSyncService test further auto sync requests without changes|occurrence=1userDataAutoSyncService.…userDataAutoSyncService.test|title=UserDataAutoSyncService test rate limit on server|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/userDataSync/test/common/userDataAutoSyncService.test|title=UserDataAutoSyncService test rate limit on server|occurrence=1userDataAutoSyncService.…userDataAutoSyncService.test|title=UserDataAutoSyncService test removing the machine adds machine back|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/userDataSync/test/common/userDataAutoSyncService.test|title=UserDataAutoSyncService test removing the machine adds machine back|occurrence=1userDataAutoSyncService.…Focused file · src/vs/platform/userDataSync/common/userDataAutoSyncService.ts · 571 LOCcommon/userDataAutoSyncS…

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- userDataAutoSyncService.ts ×26
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) { userDataAutoSyncService.ts ×37
50 > this.storageService.store(storeUrlKey, syncUrl.toString(), StorageScope.APPLICATION, StorageTarget.MACHINE);
51 > } else {
52 this.storageService.remove(storeUrlKey, StorageScope.APPLICATION);
53 }
56 > private previousProductQuality: string | undefined;
57 > private get productQuality(): string | undefined {
58 > return this.storageService.get(productQualityKey, StorageScope.APPLICATION); userDataAutoSyncService.ts ×37
59 > }
60 > private set productQuality(productQuality: string | undefined) { userDataAutoSyncService.ts ×26
61 > if (productQuality) { userDataAutoSyncService.ts ×37
62 this.storageService.store(productQualityKey, productQuality, StorageScope.APPLICATION, StorageTarget.MACHINE);
64 > this.storageService.remove(productQualityKey, StorageScope.APPLICATION);
65 > }
66 > }
68 > constructor(
69 > @IProductService productService: IProductService, userDataAutoSyncService.ts ×37
70 > @IUserDataSyncStoreManagementService private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService,
71 > @IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService,
72 > @IUserDataSyncEnablementService private readonly userDataSyncEnablementService: IUserDataSyncEnablementService,
73 > @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService,
74 > @IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
75 > @IUserDataSyncAccountService private readonly userDataSyncAccountService: IUserDataSyncAccountService,
76 > @ITelemetryService private readonly telemetryService: ITelemetryService,
77 > @IUserDataSyncMachinesService private readonly userDataSyncMachinesService: IUserDataSyncMachinesService,
78 > @IStorageService private readonly storageService: IStorageService,
79 > @IMeteredConnectionService private readonly meteredConnectionService: IMeteredConnectionService,
80 > ) {
81 > super();
82 > this.syncTriggerDelayer = this._register(new ThrottledDelayer<void>(this.getSyncTriggerDelayTime()));
83 >
84 > this.lastSyncUrl = this.syncUrl;
85 > this.syncUrl = userDataSyncStoreManagementService.userDataSyncStore?.url;
86 >
87 > this.previousProductQuality = this.productQuality;
88 > this.productQuality = productService.quality;
89 >
90 > if (this.syncUrl) {
91 >
92 > this.logService.info('[AutoSync] Using settings sync service', this.syncUrl.toString());
93 > this._register(userDataSyncStoreManagementService.onDidChangeUserDataSyncStore(() => {
94 if (!isEqual(this.syncUrl, userDataSyncStoreManagementService.userDataSyncStore?.url)) {
95 this.lastSyncUrl = this.syncUrl;
96 this.syncUrl = userDataSyncStoreManagementService.userDataSyncStore?.url;
97 if (this.syncUrl) {
98 this.logService.info('[AutoSync] Using settings sync service', this.syncUrl.toString());
99 }
100 }
102 >
103 > if (this.userDataSyncEnablementService.isEnabled()) {
104 > this.logService.info('[AutoSync] Enabled.');
105 > } else {
106 this.logService.info('[AutoSync] Disabled.');
107 }
108 > this.updateAutoSync(); userDataAutoSyncService.ts ×37
109 >
110 > if (this.hasToDisableMachineEventually()) {
111 this.disableMachineEventually();
112 }
114 > this._register(userDataSyncAccountService.onDidChangeAccount(() => this.updateAutoSync()));
115 > this._register(userDataSyncStoreService.onDidChangeDonotMakeRequestsUntil(() => this.updateAutoSync()));
116 > this._register(userDataSyncService.onDidChangeLocal(source => this.triggerSync([source])));
117 > this._register(Event.filter(this.userDataSyncEnablementService.onDidChangeResourceEnablement, ([, enabled]) => enabled)(() => this.triggerSync(['resourceEnablement'])));
118 > this._register(this.userDataSyncStoreManagementService.onDidChangeUserDataSyncStore(() => this.triggerSync(['userDataSyncStoreChanged'])));
119 > this._register(meteredConnectionService.onDidChangeIsConnectionMetered(() => this.updateAutoSync()));
120 > }
121 > }
123 > private updateAutoSync(): void {
124 > const { enabled, message } = this.isAutoSyncEnabled(); userDataAutoSyncService.ts ×37
125 > if (enabled) {
126 > if (this.autoSync.value === undefined) {
127 > this.autoSync.value = new AutoSync(this.lastSyncUrl, 1000 * 60 * 5 /* 5 miutes */, this.userDataSyncStoreManagementService, this.userDataSyncStoreService, this.userDataSyncService, this.userDataSyncMachinesService, this.logService, this.telemetryService, this.storageService);
128 > this.autoSync.value.register(this.autoSync.value.onDidStartSync(() => this.lastSyncTriggerTime = new Date().getTime()));
129 > this.autoSync.value.register(this.autoSync.value.onDidFinishSync(e => this.onDidFinishSync(e)));
130 > if (this.startAutoSync()) {
131 this.autoSync.value.start();
132 }
134 > } else {
135 > this.syncTriggerDelayer.cancel(); userDataAutoSyncService.ts ×3
136 > if (this.autoSync.value !== undefined) {
137 > if (message) {
138 > this.logService.info(message);
139 > }
140 > this.autoSync.clear();
141 > }
142
143 /* log message when auto sync is not disabled by user */
144 else if (message && this.userDataSyncEnablementService.isEnabled()) {
145 this.logService.info(message);
146 }
150 > // For tests purpose only
151 > protected startAutoSync(): boolean { return true; }
152 >
153 > private isAutoSyncEnabled(): { enabled: boolean; message?: string } {
154 > if (!this.userDataSyncEnablementService.isEnabled()) { userDataAutoSyncService.ts ×37
155 return { enabled: false, message: '[AutoSync] Disabled.' };
156 }
157 > if (!this.userDataSyncAccountService.account) { userDataAutoSyncService.ts ×37
158 return { enabled: false, message: '[AutoSync] Suspended until auth token is available.' };
159 }
160 > if (this.userDataSyncStoreService.donotMakeRequestsUntil) { userDataAutoSyncService.ts ×37
161 > return { enabled: false, message: `[AutoSync] Suspended until ${toLocalISOString(this.userDataSyncStoreService.donotMakeRequestsUntil)} because server is not accepting requests until then.` }; userDataAutoSyncService.ts ×3
162 > }
163 > if (this.suspendUntilRestart) { userDataAutoSyncService.ts ×37
164 return { enabled: false, message: '[AutoSync] Suspended until restart.' };
165 }
166 > if (this.meteredConnectionService.isConnectionMetered) { userDataAutoSyncService.ts ×37
167 return { enabled: false, message: '[AutoSync] Suspended because connection is metered.' };
168 }
169 > return { enabled: true }; userDataAutoSyncService.ts ×37
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> {
180 >
181 > // Remove machine
182 > if (this.userDataSyncAccountService.account && !donotRemoveMachine) {
183 > await this.userDataSyncMachinesService.removeCurrentMachine(); userDataAutoSyncService.ts ×1
184 > }
186 > // Disable Auto Sync
187 > this.updateEnablement(false);
188 >
189 > // Reset Session
190 > this.storageService.remove(sessionIdKey, StorageScope.APPLICATION);
191 >
192 > // Reset
193 > if (everywhere) {
194 await this.userDataSyncService.reset();
196 > await this.userDataSyncService.resetLocal();
197 > }
198 > } catch (error) {
199 this.logService.error(error);
200 if (softTurnOffOnError) {
201 this.updateEnablement(false);
202 } else {
203 throw error;
204 }
205 }
208 > private updateEnablement(enabled: boolean): void {
209 > if (this.userDataSyncEnablementService.isEnabled() !== enabled) { userDataAutoSyncService.ts ×7
210 > this.userDataSyncEnablementService.setEnablement(enabled);
211 > this.updateAutoSync();
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'); userDataAutoSyncService.ts ×2
221 > if (!error) {
222 > // Sync finished without errors userDataSyncMachines.ts ×8
223 > this.successiveFailures = 0;
224 > return;
225 > }
227 > // Error while syncing
228 > const userDataSyncError = UserDataSyncError.toUserDataSyncError(error);
229 >
230 > // Session got expired
231 > if (userDataSyncError.code === UserDataSyncErrorCode.SessionExpired) {
232 > await this.turnOff(false, true /* force soft turnoff on error */); userDataAutoSyncService.ts ×3
233 > this.logService.info('[AutoSync] Turned off sync because current session is expired');
234 > }
236 > // Turned off from another device
237 > else if (userDataSyncError.code === UserDataSyncErrorCode.TurnedOff) {
238 > await this.turnOff(false, true /* force soft turnoff on error */); userDataAutoSyncService.ts ×1
239 > this.logService.info('[AutoSync] Turned off sync because sync is turned off in the cloud');
240 > }
242 > // Exceeded Rate Limit on Client
243 > else if (userDataSyncError.code === UserDataSyncErrorCode.LocalTooManyRequests) {
244 this.suspendUntilRestart = true;
245 this.logService.info('[AutoSync] Suspended sync because of making too many requests to server');
246 this.updateAutoSync();
247 }
249 > // Exceeded Rate Limit on Server
250 > else if (userDataSyncError.code === UserDataSyncErrorCode.TooManyRequests) {
251 > await this.turnOff(false, true /* force soft turnoff on error */,
252 > true /* do not disable machine because disabling a machine makes request to server and can fail with TooManyRequests */);
253 > this.disableMachineEventually();
254 > this.logService.info('[AutoSync] Turned off sync because of making too many requests to server');
255 > }
256
257 // Method Not Found
258 else if (userDataSyncError.code === UserDataSyncErrorCode.MethodNotFound) {
259 await this.turnOff(false, true /* force soft turnoff on error */);
260 this.logService.info('[AutoSync] Turned off sync because current client is making requests to server that are not supported');
261 }
262
263 // Upgrade Required or Gone
264 else if (userDataSyncError.code === UserDataSyncErrorCode.UpgradeRequired || userDataSyncError.code === UserDataSyncErrorCode.Gone) {
265 await this.turnOff(false, true /* force soft turnoff on error */,
266 true /* do not disable machine because disabling a machine makes request to server and can fail with upgrade required or gone */);
267 this.disableMachineEventually();
268 this.logService.info('[AutoSync] Turned off sync because current client is not compatible with server. Requires client upgrade.');
269 }
270
271 // Incompatible Local Content
272 else if (userDataSyncError.code === UserDataSyncErrorCode.IncompatibleLocalContent) {
273 await this.turnOff(false, true /* force soft turnoff on error */);
274 this.logService.info(`[AutoSync] Turned off sync because server has ${userDataSyncError.resource} content with newer version than of client. Requires client upgrade.`);
275 }
276
277 // Incompatible Remote Content
278 else if (userDataSyncError.code === UserDataSyncErrorCode.IncompatibleRemoteContent) {
279 await this.turnOff(false, true /* force soft turnoff on error */);
280 this.logService.info(`[AutoSync] Turned off sync because server has ${userDataSyncError.resource} content with older version than of client. Requires server reset.`);
281 }
282
283 // Service changed
284 else if (userDataSyncError.code === UserDataSyncErrorCode.ServiceChanged || userDataSyncError.code === UserDataSyncErrorCode.DefaultServiceChanged) {
285
286 // Check if default settings sync service has changed in web without changing the product quality
287 // Then turn off settings sync and ask user to turn on again
288 if (isWeb && userDataSyncError.code === UserDataSyncErrorCode.DefaultServiceChanged && !this.hasProductQualityChanged()) {
289 await this.turnOff(false, true /* force soft turnoff on error */);
290 this.logService.info('[AutoSync] Turned off sync because default sync service is changed.');
291 }
292
293 // Service has changed by the user. So turn off and turn on sync.
294 // Show a prompt to the user about service change.
295 else {
296 await this.turnOff(false, true /* force soft turnoff on error */, true /* do not disable machine */);
297 await this.turnOn();
298 this.logService.info('[AutoSync] Sync Service changed. Turned off auto sync, reset local state and turned on auto sync.');
299 }
300
301 }
302
303 else {
304 this.logService.error(userDataSyncError);
305 this.successiveFailures++;
306 }
308 > this._onError.fire(userDataSyncError);
311 > private async disableMachineEventually(): Promise<void> {
312 > this.storageService.store(disableMachineEventuallyKey, true, StorageScope.APPLICATION, StorageTarget.MACHINE); userDataAutoSyncService.ts ×6
313 > await timeout(1000 * 60 * 10);
314 >
315 > // Return if got stopped meanwhile.
316 > if (!this.hasToDisableMachineEventually()) {
317 return;
318 }
320 > this.stopDisableMachineEventually();
321 >
322 > // disable only if sync is disabled
323 > if (!this.userDataSyncEnablementService.isEnabled() && this.userDataSyncAccountService.account) {
324 await this.userDataSyncMachinesService.removeCurrentMachine();
325 }
328 > private hasToDisableMachineEventually(): boolean {
329 > return this.storageService.getBoolean(disableMachineEventuallyKey, StorageScope.APPLICATION, false); userDataAutoSyncService.ts ×37
330 > }
332 > private stopDisableMachineEventually(): void {
333 > this.storageService.remove(disableMachineEventuallyKey, StorageScope.APPLICATION); userDataAutoSyncService.ts ×6
334 > }
336 > private sources: string[] = [];
337 > async triggerSync(sources: string[], options?: SyncOptions): Promise<void> {
338 > if (this.autoSync.value === undefined) { userDataAutoSyncService.ts ×37
339 > return this.syncTriggerDelayer.cancel(); userDataAutoSyncService.ts ×1
340 > }
342 > if (options?.skipIfSyncedRecently && this.lastSyncTriggerTime && new Date().getTime() - this.lastSyncTriggerTime < 10_000) {
343 > this.logService.debug('[AutoSync] Skipping because sync was triggered recently.', sources); userDataAutoSyncService.ts ×1
344 > return;
345 > }
347 > this.sources.push(...sources);
348 > return this.syncTriggerDelayer.trigger(async () => {
349 > this.logService.trace('[AutoSync] Activity sources', ...this.sources);
350 > this.sources = [];
351 > if (this.autoSync.value) {
352 > await this.autoSync.value.sync('Activity', !!options?.disableCache);
353 > }
354 > }, this.successiveFailures
355 ? Math.min(this.getSyncTriggerDelayTime() * this.successiveFailures, 60_000) /* Delay linearly until max 1 minute */
356 > : options?.immediately ? 0 : this.getSyncTriggerDelayTime()); userDataAutoSyncService.ts ×37
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.');
363 return 0;
364 }
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, userDataAutoSyncService.ts ×37
388 > private readonly interval: number /* in milliseconds */,
389 > private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService,
390 > private readonly userDataSyncStoreService: IUserDataSyncStoreService,
391 > private readonly userDataSyncService: IUserDataSyncService,
392 > private readonly userDataSyncMachinesService: IUserDataSyncMachinesService,
393 > private readonly logService: IUserDataSyncLogService,
394 > private readonly telemetryService: ITelemetryService,
395 > private readonly storageService: IStorageService,
396 > ) {
397 > super();
398 > }
400 > start(): void {
401 this._register(this.onDidFinishSync(() => this.waitUntilNextIntervalAndSync()));
402 this._register(toDisposable(() => {
403 if (this.syncPromise) {
404 this.syncPromise.cancel();
405 this.logService.info('[AutoSync] Cancelled sync that is in progress');
406 this.syncPromise = undefined;
407 }
408 this.syncTask?.stop();
409 this.logService.info('[AutoSync] Stopped');
410 }));
411 this.sync(AutoSync.INTERVAL_SYNCING, false);
412 }
414 > private waitUntilNextIntervalAndSync(): void {
415 this.intervalHandler.value = disposableTimeout(() => {
416 this.sync(AutoSync.INTERVAL_SYNCING, false);
417 this.intervalHandler.value = undefined;
418 }, this.interval);
419 }
421 > sync(reason: string, disableCache: boolean): Promise<void> {
422 > const syncPromise = createCancelablePromise(async token => { userDataAutoSyncService.ts ×37
423 > if (this.syncPromise) {
424 try {
425 // Wait until existing sync is finished
426 this.logService.debug('[AutoSync] Waiting until sync is finished.');
427 await this.syncPromise;
428 } catch (error) {
429 if (isCancellationError(error)) {
430 // Cancelled => Disposed. Donot continue sync.
431 return;
432 }
433 }
434 }
435 > return this.doSync(reason, disableCache, token); userDataAutoSyncService.ts ×37
436 > });
437 > this.syncPromise = syncPromise;
438 > this.syncPromise.finally(() => this.syncPromise = undefined);
439 > return this.syncPromise;
440 > }
442 > private hasSyncServiceChanged(): boolean {
443 > return this.lastSyncUrl !== undefined && !isEqual(this.lastSyncUrl, this.userDataSyncStoreManagementService.userDataSyncStore?.url); userDataAutoSyncService.ts ×1
444 > }
446 > private async hasDefaultServiceChanged(): Promise<boolean> {
447 const previous = await this.userDataSyncStoreManagementService.getPreviousUserDataSyncStore();
448 const current = this.userDataSyncStoreManagementService.userDataSyncStore;
449 // check if defaults changed
450 return !!current && !!previous &&
451 (!isEqual(current.defaultUrl, previous.defaultUrl) ||
452 !isEqual(current.insidersUrl, previous.insidersUrl) ||
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}`); userDataAutoSyncService.ts ×37
458 >
459 > this._onDidStartSync.fire();
460 >
461 > let error: Error | undefined;
462 > try {
463 > await this.createAndRunSyncTask(disableCache, token);
464 > } catch (e) {
465 > this.logService.error(e); userDataSync.ts ×3
466 > error = e;
467 > if (UserDataSyncError.toUserDataSyncError(e).code === UserDataSyncErrorCode.MethodNotFound) {
468 try {
469 this.logService.info('[AutoSync] Client is making invalid requests. Cleaning up data...');
470 await this.userDataSyncService.cleanUpRemoteData();
471 this.logService.info('[AutoSync] Retrying sync...');
472 await this.createAndRunSyncTask(disableCache, token);
473 error = undefined;
474 } catch (e1) {
475 this.logService.error(e1);
476 error = e1;
477 }
478 }
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); userDataAutoSyncService.ts ×37
486 > if (token.isCancellationRequested) {
487 return;
488 }
489 > this.manifest = this.syncTask.manifest; userDataAutoSyncService.ts ×37
490 >
491 > // Server has no data but this machine was synced before
492 > if (this.manifest === null && await this.userDataSyncService.hasPreviouslySynced()) {
493 > if (this.hasSyncServiceChanged()) { userDataSyncService.ts ×3
494 if (await this.hasDefaultServiceChanged()) {
495 throw new UserDataAutoSyncError(localize('default service changed', "Cannot sync because default service has changed"), UserDataSyncErrorCode.DefaultServiceChanged);
496 } else {
497 throw new UserDataAutoSyncError(localize('service changed', "Cannot sync because sync service has changed"), UserDataSyncErrorCode.ServiceChanged);
498 }
500 > // Sync was turned off in the cloud
501 > throw new UserDataAutoSyncError(localize('turned off', "Cannot sync because syncing is turned off in the cloud"), UserDataSyncErrorCode.TurnedOff);
502 > }
503 > }
505 > const sessionId = this.storageService.get(sessionIdKey, StorageScope.APPLICATION);
506 > // Server session is different from client session
507 > if (sessionId && this.manifest && sessionId !== this.manifest.session) {
508 > if (this.hasSyncServiceChanged()) { userDataAutoSyncService.ts ×3
509 if (await this.hasDefaultServiceChanged()) {
510 throw new UserDataAutoSyncError(localize('default service changed', "Cannot sync because default service has changed"), UserDataSyncErrorCode.DefaultServiceChanged);
511 } else {
512 throw new UserDataAutoSyncError(localize('service changed', "Cannot sync because sync service has changed"), UserDataSyncErrorCode.ServiceChanged);
513 }
515 > throw new UserDataAutoSyncError(localize('session expired', "Cannot sync because current session is expired"), UserDataSyncErrorCode.SessionExpired);
516 > }
517 > }
519 > const machines = await this.userDataSyncMachinesService.getMachines(this.manifest || undefined);
520 > // Return if cancellation is requested
521 > if (token.isCancellationRequested) {
522 return;
523 }
525 > const currentMachine = machines.find(machine => machine.isCurrent);
526 > // Check if sync was turned off from other machine
527 > if (currentMachine?.disabled) {
528 > // Throw TurnedOff error userDataAutoSyncService.ts ×1
529 > throw new UserDataAutoSyncError(localize('turned off machine', "Cannot sync because syncing is turned off on this machine from another machine."), UserDataSyncErrorCode.TurnedOff);
530 > }
532 > const startTime = new Date().getTime();
533 > await this.syncTask.run();
534 > this.telemetryService.publicLog2<{ userDataSyncMachines.ts ×8
535 > duration: number;
536 > }, {
537 > owner: 'sandy081';
538 > comment: 'Report when running a sync operation';
539 > duration: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Time taken to run sync operation' };
540 > }>('settingsSync:sync', { duration: new Date().getTime() - startTime });
541 >
542 > // After syncing, get the manifest if it was not available before
543 > if (this.manifest === null) {
545 > this.manifest = await this.userDataSyncStoreService.manifest(null);
546 > } catch (error) {
547 throw new UserDataAutoSyncError(toErrorMessage(error), error instanceof UserDataSyncError ? error.code : UserDataSyncErrorCode.Unknown);
548 }
551 > // Update local session id
552 > if (this.manifest && this.manifest.session !== sessionId) { userDataAutoSyncService.ts ×37
553 > this.storageService.store(sessionIdKey, this.manifest.session, StorageScope.APPLICATION, StorageTarget.MACHINE); userDataSyncMachines.ts ×8
554 > }
555 >
556 > // Return if cancellation is requested
557 > if (token.isCancellationRequested) {
558 return;
559 }
561 > // Add current machine
562 > if (!currentMachine) {
563 > await this.userDataSyncMachinesService.addCurrentMachine(this.manifest || undefined);
564 > }
567 > register<T extends IDisposable>(t: T): T {
568 > return super._register(t); userDataAutoSyncService.ts ×37
569 > }
571 > }