Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
sandy081 authored Jan 29, 2025
1 parent 9b9093e commit 10e7115
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 53 deletions.
78 changes: 41 additions & 37 deletions src/vs/platform/userDataSync/common/userDataAutoSyncService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { localize } from '../../../nls.js';
import { IProductService } from '../../product/common/productService.js';
import { IStorageService, StorageScope, StorageTarget } from '../../storage/common/storage.js';
import { ITelemetryService } from '../../telemetry/common/telemetry.js';
import { IUserDataSyncTask, IUserDataAutoSyncService, IUserDataManifest, IUserDataSyncLogService, IUserDataSyncEnablementService, IUserDataSyncService, IUserDataSyncStoreManagementService, IUserDataSyncStoreService, UserDataAutoSyncError, UserDataSyncError, UserDataSyncErrorCode } from './userDataSync.js';
import { IUserDataSyncTask, IUserDataAutoSyncService, IUserDataManifest, IUserDataSyncLogService, IUserDataSyncEnablementService, IUserDataSyncService, IUserDataSyncStoreManagementService, IUserDataSyncStoreService, UserDataAutoSyncError, UserDataSyncError, UserDataSyncErrorCode, SyncOptions } from './userDataSync.js';
import { IUserDataSyncAccountService } from './userDataSyncAccount.js';
import { IUserDataSyncMachinesService } from './userDataSyncMachines.js';

Expand Down Expand Up @@ -87,21 +87,21 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto

if (this.syncUrl) {

this.logService.info('Using settings sync service', this.syncUrl.toString());
this.logService.info('[AutoSync] Using settings sync service', this.syncUrl.toString());
this._register(userDataSyncStoreManagementService.onDidChangeUserDataSyncStore(() => {
if (!isEqual(this.syncUrl, userDataSyncStoreManagementService.userDataSyncStore?.url)) {
this.lastSyncUrl = this.syncUrl;
this.syncUrl = userDataSyncStoreManagementService.userDataSyncStore?.url;
if (this.syncUrl) {
this.logService.info('Using settings sync service', this.syncUrl.toString());
this.logService.info('[AutoSync] Using settings sync service', this.syncUrl.toString());
}
}
}));

if (this.userDataSyncEnablementService.isEnabled()) {
this.logService.info('Auto Sync is enabled.');
this.logService.info('[AutoSync] Enabled.');
} else {
this.logService.info('Auto Sync is disabled.');
this.logService.info('[AutoSync] Disabled.');
}
this.updateAutoSync();

Expand All @@ -111,9 +111,9 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto

this._register(userDataSyncAccountService.onDidChangeAccount(() => this.updateAutoSync()));
this._register(userDataSyncStoreService.onDidChangeDonotMakeRequestsUntil(() => this.updateAutoSync()));
this._register(userDataSyncService.onDidChangeLocal(source => this.triggerSync([source], false, false)));
this._register(Event.filter(this.userDataSyncEnablementService.onDidChangeResourceEnablement, ([, enabled]) => enabled)(() => this.triggerSync(['resourceEnablement'], false, false)));
this._register(this.userDataSyncStoreManagementService.onDidChangeUserDataSyncStore(() => this.triggerSync(['userDataSyncStoreChanged'], false, false)));
this._register(userDataSyncService.onDidChangeLocal(source => this.triggerSync([source])));
this._register(Event.filter(this.userDataSyncEnablementService.onDidChangeResourceEnablement, ([, enabled]) => enabled)(() => this.triggerSync(['resourceEnablement'])));
this._register(this.userDataSyncStoreManagementService.onDidChangeUserDataSyncStore(() => this.triggerSync(['userDataSyncStoreChanged'])));
}
}

Expand Down Expand Up @@ -149,16 +149,16 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto

private isAutoSyncEnabled(): { enabled: boolean; message?: string } {
if (!this.userDataSyncEnablementService.isEnabled()) {
return { enabled: false, message: 'Auto Sync: Disabled.' };
return { enabled: false, message: '[AutoSync] Disabled.' };
}
if (!this.userDataSyncAccountService.account) {
return { enabled: false, message: 'Auto Sync: Suspended until auth token is available.' };
return { enabled: false, message: '[AutoSync] Suspended until auth token is available.' };
}
if (this.userDataSyncStoreService.donotMakeRequestsUntil) {
return { enabled: false, message: `Auto Sync: Suspended until ${toLocalISOString(this.userDataSyncStoreService.donotMakeRequestsUntil)} because server is not accepting requests until then.` };
return { enabled: false, message: `[AutoSync] Suspended until ${toLocalISOString(this.userDataSyncStoreService.donotMakeRequestsUntil)} because server is not accepting requests until then.` };
}
if (this.suspendUntilRestart) {
return { enabled: false, message: 'Auto Sync: Suspended until restart.' };
return { enabled: false, message: '[AutoSync] Suspended until restart.' };
}
return { enabled: true };
}
Expand Down Expand Up @@ -211,6 +211,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
}

private async onDidFinishSync(error: Error | undefined): Promise<void> {
this.logService.debug('[AutoSync] Sync Finished');
if (!error) {
// Sync finished without errors
this.successiveFailures = 0;
Expand All @@ -223,19 +224,19 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
// Session got expired
if (userDataSyncError.code === UserDataSyncErrorCode.SessionExpired) {
await this.turnOff(false, true /* force soft turnoff on error */);
this.logService.info('Auto Sync: Turned off sync because current session is expired');
this.logService.info('[AutoSync] Turned off sync because current session is expired');
}

// Turned off from another device
else if (userDataSyncError.code === UserDataSyncErrorCode.TurnedOff) {
await this.turnOff(false, true /* force soft turnoff on error */);
this.logService.info('Auto Sync: Turned off sync because sync is turned off in the cloud');
this.logService.info('[AutoSync] Turned off sync because sync is turned off in the cloud');
}

// Exceeded Rate Limit on Client
else if (userDataSyncError.code === UserDataSyncErrorCode.LocalTooManyRequests) {
this.suspendUntilRestart = true;
this.logService.info('Auto Sync: Suspended sync because of making too many requests to server');
this.logService.info('[AutoSync] Suspended sync because of making too many requests to server');
this.updateAutoSync();
}

Expand All @@ -244,33 +245,33 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
await this.turnOff(false, true /* force soft turnoff on error */,
true /* do not disable machine because disabling a machine makes request to server and can fail with TooManyRequests */);
this.disableMachineEventually();
this.logService.info('Auto Sync: Turned off sync because of making too many requests to server');
this.logService.info('[AutoSync] Turned off sync because of making too many requests to server');
}

// Method Not Found
else if (userDataSyncError.code === UserDataSyncErrorCode.MethodNotFound) {
await this.turnOff(false, true /* force soft turnoff on error */);
this.logService.info('Auto Sync: Turned off sync because current client is making requests to server that are not supported');
this.logService.info('[AutoSync] Turned off sync because current client is making requests to server that are not supported');
}

// Upgrade Required or Gone
else if (userDataSyncError.code === UserDataSyncErrorCode.UpgradeRequired || userDataSyncError.code === UserDataSyncErrorCode.Gone) {
await this.turnOff(false, true /* force soft turnoff on error */,
true /* do not disable machine because disabling a machine makes request to server and can fail with upgrade required or gone */);
this.disableMachineEventually();
this.logService.info('Auto Sync: Turned off sync because current client is not compatible with server. Requires client upgrade.');
this.logService.info('[AutoSync] Turned off sync because current client is not compatible with server. Requires client upgrade.');
}

// Incompatible Local Content
else if (userDataSyncError.code === UserDataSyncErrorCode.IncompatibleLocalContent) {
await this.turnOff(false, true /* force soft turnoff on error */);
this.logService.info(`Auto Sync: Turned off sync because server has ${userDataSyncError.resource} content with newer version than of client. Requires client upgrade.`);
this.logService.info(`[AutoSync] Turned off sync because server has ${userDataSyncError.resource} content with newer version than of client. Requires client upgrade.`);
}

// Incompatible Remote Content
else if (userDataSyncError.code === UserDataSyncErrorCode.IncompatibleRemoteContent) {
await this.turnOff(false, true /* force soft turnoff on error */);
this.logService.info(`Auto Sync: Turned off sync because server has ${userDataSyncError.resource} content with older version than of client. Requires server reset.`);
this.logService.info(`[AutoSync] Turned off sync because server has ${userDataSyncError.resource} content with older version than of client. Requires server reset.`);
}

// Service changed
Expand All @@ -280,15 +281,15 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
// Then turn off settings sync and ask user to turn on again
if (isWeb && userDataSyncError.code === UserDataSyncErrorCode.DefaultServiceChanged && !this.hasProductQualityChanged()) {
await this.turnOff(false, true /* force soft turnoff on error */);
this.logService.info('Auto Sync: Turned off sync because default sync service is changed.');
this.logService.info('[AutoSync] Turned off sync because default sync service is changed.');
}

// Service has changed by the user. So turn off and turn on sync.
// Show a prompt to the user about service change.
else {
await this.turnOff(false, true /* force soft turnoff on error */, true /* do not disable machine */);
await this.turnOn();
this.logService.info('Auto Sync: Sync Service changed. Turned off auto sync, reset local state and turned on auto sync.');
this.logService.info('[AutoSync] Sync Service changed. Turned off auto sync, reset local state and turned on auto sync.');
}

}
Expand Down Expand Up @@ -327,32 +328,35 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
}

private sources: string[] = [];
async triggerSync(sources: string[], skipIfSyncedRecently: boolean, disableCache: boolean): Promise<void> {
async triggerSync(sources: string[], options?: SyncOptions): Promise<void> {
if (this.autoSync.value === undefined) {
return this.syncTriggerDelayer.cancel();
}

if (skipIfSyncedRecently && this.lastSyncTriggerTime
&& Math.round((new Date().getTime() - this.lastSyncTriggerTime) / 1000) < 10) {
this.logService.debug('Auto Sync: Skipped. Limited to once per 10 seconds.');
if (options?.skipIfSyncedRecently && this.lastSyncTriggerTime && new Date().getTime() - this.lastSyncTriggerTime < 15_000) {
this.logService.debug('[AutoSync] Skipping because sync was triggered recently.', sources);
return;
}

this.sources.push(...sources);
return this.syncTriggerDelayer.trigger(async () => {
this.logService.trace('activity sources', ...this.sources);
this.logService.trace('[AutoSync] Activity sources', ...this.sources);
this.sources = [];
if (this.autoSync.value) {
await this.autoSync.value.sync('Activity', disableCache);
await this.autoSync.value.sync('Activity', !!options?.disableCache);
}
}, this.successiveFailures
? this.getSyncTriggerDelayTime() * 1 * Math.min(Math.pow(2, this.successiveFailures), 60) /* Delay exponentially until max 1 minute */
: this.getSyncTriggerDelayTime());
? Math.min(this.getSyncTriggerDelayTime() * this.successiveFailures, 60_000) /* Delay linearly until max 1 minute */
: options?.immediately ? 0 : this.getSyncTriggerDelayTime());

}

protected getSyncTriggerDelayTime(): number {
return 2000; /* Debounce for 2 seconds if there are no failures */
if (this.lastSyncTriggerTime && new Date().getTime() - this.lastSyncTriggerTime > 15_000) {
this.logService.debug('[AutoSync] Sync immediately because last sync was triggered more than 15 seconds ago.');
return 0;
}
return 10_000; /* Debounce for 10 seconds if there are no failures */
}

}
Expand Down Expand Up @@ -392,11 +396,11 @@ class AutoSync extends Disposable {
this._register(toDisposable(() => {
if (this.syncPromise) {
this.syncPromise.cancel();
this.logService.info('Auto sync: Cancelled sync that is in progress');
this.logService.info('[AutoSync] Cancelled sync that is in progress');
this.syncPromise = undefined;
}
this.syncTask?.stop();
this.logService.info('Auto Sync: Stopped');
this.logService.info('[AutoSync] Stopped');
}));
this.sync(AutoSync.INTERVAL_SYNCING, false);
}
Expand All @@ -413,7 +417,7 @@ class AutoSync extends Disposable {
if (this.syncPromise) {
try {
// Wait until existing sync is finished
this.logService.debug('Auto Sync: Waiting until sync is finished.');
this.logService.debug('[AutoSync] Waiting until sync is finished.');
await this.syncPromise;
} catch (error) {
if (isCancellationError(error)) {
Expand Down Expand Up @@ -444,7 +448,7 @@ class AutoSync extends Disposable {
}

private async doSync(reason: string, disableCache: boolean, token: CancellationToken): Promise<void> {
this.logService.info(`Auto Sync: Triggered by ${reason}`);
this.logService.info(`[AutoSync] Triggered by ${reason}`);
this._onDidStartSync.fire();

let error: Error | undefined;
Expand All @@ -455,9 +459,9 @@ class AutoSync extends Disposable {
error = e;
if (UserDataSyncError.toUserDataSyncError(e).code === UserDataSyncErrorCode.MethodNotFound) {
try {
this.logService.info('Auto Sync: Client is making invalid requests. Cleaning up data...');
this.logService.info('[AutoSync] Client is making invalid requests. Cleaning up data...');
await this.userDataSyncService.cleanUpRemoteData();
this.logService.info('Auto Sync: Retrying sync...');
this.logService.info('[AutoSync] Retrying sync...');
await this.createAndRunSyncTask(disableCache, token);
error = undefined;
} catch (e1) {
Expand Down
4 changes: 3 additions & 1 deletion src/vs/platform/userDataSync/common/userDataSync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -601,13 +601,15 @@ export interface IUserDataSyncResourceProviderService {
resolveUserDataSyncResource(syncResourceHandle: ISyncResourceHandle): IUserDataSyncResource | undefined;
}

export type SyncOptions = { immediately?: boolean; skipIfSyncedRecently?: boolean; disableCache?: boolean };

export const IUserDataAutoSyncService = createDecorator<IUserDataAutoSyncService>('IUserDataAutoSyncService');
export interface IUserDataAutoSyncService {
_serviceBrand: any;
readonly onError: Event<UserDataSyncError>;
turnOn(): Promise<void>;
turnOff(everywhere: boolean): Promise<void>;
triggerSync(sources: string[], hasToLimitSync: boolean, disableCache: boolean): Promise<void>;
triggerSync(sources: string[], options?: SyncOptions): Promise<void>;
}

export const IUserDataSyncUtilService = createDecorator<IUserDataSyncUtilService>('IUserDataSyncUtilService');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class UserDataAutoSyncService extends BaseUserDataAutoSyncService {
this._register(Event.debounce<string, string[]>(Event.any<string>(
Event.map(nativeHostService.onDidFocusMainWindow, () => 'windowFocus'),
Event.map(nativeHostService.onDidOpenMainWindow, () => 'windowOpen'),
), (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerSync(sources, true, false)));
), (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerSync(sources, { skipIfSyncedRecently: true })));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class TestUserDataAutoSyncService extends UserDataAutoSyncService {
protected override getSyncTriggerDelayTime(): number { return 50; }

sync(): Promise<void> {
return this.triggerSync(['sync'], false, false);
return this.triggerSync(['sync']);
}
}

Expand All @@ -44,7 +44,7 @@ suite('UserDataAutoSyncService', () => {
const testObject: UserDataAutoSyncService = disposableStore.add(client.instantiationService.createInstance(TestUserDataAutoSyncService));

// Trigger auto sync with settings change
await testObject.triggerSync([SyncResource.Settings], false, false);
await testObject.triggerSync([SyncResource.Settings]);

// Filter out machine requests
const actual = target.requests.filter(request => !request.url.startsWith(`${target.url}/v1/resource/machines`));
Expand All @@ -69,7 +69,7 @@ suite('UserDataAutoSyncService', () => {

// Trigger auto sync with settings change multiple times
for (let counter = 0; counter < 2; counter++) {
await testObject.triggerSync([SyncResource.Settings], false, false);
await testObject.triggerSync([SyncResource.Settings]);
}

// Filter out machine requests
Expand All @@ -96,7 +96,7 @@ suite('UserDataAutoSyncService', () => {
const testObject: UserDataAutoSyncService = disposableStore.add(client.instantiationService.createInstance(TestUserDataAutoSyncService));

// Trigger auto sync with window focus once
await testObject.triggerSync(['windowFocus'], true, false);
await testObject.triggerSync(['windowFocus']);

// Filter out machine requests
const actual = target.requests.filter(request => !request.url.startsWith(`${target.url}/v1/resource/machines`));
Expand All @@ -121,7 +121,7 @@ suite('UserDataAutoSyncService', () => {

// Trigger auto sync with window focus multiple times
for (let counter = 0; counter < 2; counter++) {
await testObject.triggerSync(['windowFocus'], true, false);
await testObject.triggerSync(['windowFocus'], { skipIfSyncedRecently: true });
}

// Filter out machine requests
Expand Down Expand Up @@ -440,7 +440,7 @@ suite('UserDataAutoSyncService', () => {
await testClient.setUp();
const testObject: TestUserDataAutoSyncService = disposableStore.add(testClient.instantiationService.createInstance(TestUserDataAutoSyncService));

await testObject.triggerSync(['some reason'], true, true);
await testObject.triggerSync(['some reason'], { disableCache: true });
assert.strictEqual(target.requestsWithAllHeaders[0].headers!['Cache-Control'], 'no-cache');
});
});
Expand All @@ -454,7 +454,7 @@ suite('UserDataAutoSyncService', () => {
await testClient.setUp();
const testObject: TestUserDataAutoSyncService = disposableStore.add(testClient.instantiationService.createInstance(TestUserDataAutoSyncService));

await testObject.triggerSync(['some reason'], true, false);
await testObject.triggerSync(['some reason']);
assert.strictEqual(target.requestsWithAllHeaders[0].headers!['Cache-Control'], undefined);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2674,7 +2674,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
} else {
this.extensionsSyncManagementService.updateIgnoredExtensions(extension.identifier.id, !isIgnored);
}
await this.userDataAutoSyncService.triggerSync(['IgnoredExtensionsUpdated'], false, false);
await this.userDataAutoSyncService.triggerSync(['IgnoredExtensionsUpdated']);
}

async toggleApplyExtensionToAllProfiles(extension: IExtension): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ export class UserDataSyncTrigger extends Disposable implements IWorkbenchContrib
Event.map(hostService.onDidChangeFocus, () => 'windowFocus'),
Event.map(event, source => source!),
), (last, source) => last ? [...last, source] : [source], 1000)
(sources => userDataAutoSyncService.triggerSync(sources, true, false)));
(sources => userDataAutoSyncService.triggerSync(sources, { skipIfSyncedRecently: true })));
} else {
this._register(event(source => userDataAutoSyncService.triggerSync([source!], true, false)));
this._register(event(source => userDataAutoSyncService.triggerSync([source!], { skipIfSyncedRecently: true })));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat
}

syncNow(): Promise<void> {
return this.userDataAutoSyncService.triggerSync(['Sync Now'], false, true);
return this.userDataAutoSyncService.triggerSync(['Sync Now'], { immediately: true, disableCache: true });
}

private async doTurnOnSync(token: CancellationToken): Promise<void> {
Expand Down
Loading

0 comments on commit 10e7115

Please sign in to comment.