33 * Licensed under the MIT License. See License.txt in the project root for license information.
44 *--------------------------------------------------------------------------------------------*/
55
6- import { timeout , Delayer } from 'vs/base/common/async' ;
6+ import { Delayer , disposableTimeout } from 'vs/base/common/async' ;
77import { Event , Emitter } from 'vs/base/common/event' ;
8- import { Disposable } from 'vs/base/common/lifecycle' ;
8+ import { Disposable , toDisposable , MutableDisposable , IDisposable } from 'vs/base/common/lifecycle' ;
99import { IUserDataSyncLogService , IUserDataSyncService , SyncStatus , IUserDataAutoSyncService , UserDataSyncError , UserDataSyncErrorCode , IUserDataSyncEnablementService , ALL_SYNC_RESOURCES } from 'vs/platform/userDataSync/common/userDataSync' ;
1010import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication' ;
1111import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry' ;
@@ -20,10 +20,10 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
2020
2121 _serviceBrand : any ;
2222
23- private enabled : boolean = this . getDefaultEnablementValue ( ) ;
23+ private readonly autoSync = this . _register ( new MutableDisposable < AutoSync > ( ) ) ;
2424 private successiveFailures : number = 0 ;
2525 private lastSyncTriggerTime : number | undefined = undefined ;
26- private readonly syncDelayer : Delayer < void > ;
26+ private readonly syncTriggerDelayer : Delayer < void > ;
2727
2828 private readonly _onError : Emitter < UserDataSyncError > = this . _register ( new Emitter < UserDataSyncError > ( ) ) ;
2929 readonly onError : Event < UserDataSyncError > = this . _onError . event ;
@@ -36,77 +36,31 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
3636 @ITelemetryService private readonly telemetryService : ITelemetryService ,
3737 ) {
3838 super ( ) ;
39- this . updateEnablement ( false , true ) ;
40- this . syncDelayer = this . _register ( new Delayer < void > ( 0 ) ) ;
41- this . _register ( Event . any < any > ( authTokenService . onDidChangeToken ) ( ( ) => this . updateEnablement ( true , true ) ) ) ;
42- this . _register ( Event . any < any > ( userDataSyncService . onDidChangeStatus ) ( ( ) => this . updateEnablement ( true , true ) ) ) ;
43- this . _register ( this . userDataSyncEnablementService . onDidChangeEnablement ( ( ) => this . updateEnablement ( true , false ) ) ) ;
39+ this . updateEnablement ( ) ;
40+ this . syncTriggerDelayer = this . _register ( new Delayer < void > ( 0 ) ) ;
41+ this . _register ( Event . any ( authTokenService . onDidChangeToken , userDataSyncService . onDidChangeStatus , this . userDataSyncEnablementService . onDidChangeEnablement ) ( ( ) => this . updateEnablement ( ) ) ) ;
4442 this . _register ( Event . filter ( this . userDataSyncEnablementService . onDidChangeResourceEnablement , ( [ , enabled ] ) => enabled ) ( ( ) => this . triggerAutoSync ( [ RESOURCE_ENABLEMENT_SOURCE ] ) ) ) ;
4543 }
4644
47- // For tests purpose only
48- protected getDefaultEnablementValue ( ) : boolean { return false ; }
49-
50- private updateEnablement ( stopIfDisabled : boolean , auto : boolean ) : void {
45+ private updateEnablement ( ) : void {
5146 const { enabled, reason } = this . isAutoSyncEnabled ( ) ;
52- if ( this . enabled === enabled ) {
53- return ;
54- }
55-
56- this . enabled = enabled ;
57- if ( this . enabled ) {
58- this . logService . info ( 'Auto Sync: Started' ) ;
59- this . sync ( true , auto ) ;
60- return ;
47+ if ( enabled ) {
48+ if ( this . autoSync . value === undefined ) {
49+ const autoSync = new AutoSync ( this . startAutoSync ( ) , 1000 * 60 * 5 /* 5 miutes */ , this . userDataSyncService , this . logService ) ;
50+ autoSync . register ( autoSync . onDidStartSync ( ( ) => this . lastSyncTriggerTime = new Date ( ) . getTime ( ) ) ) ;
51+ autoSync . register ( autoSync . onDidFinishSync ( e => this . onDidFinishSync ( e ) ) ) ;
52+ this . autoSync . value = autoSync ;
53+ }
6154 } else {
62- this . resetFailures ( ) ;
63- if ( stopIfDisabled ) {
64- this . userDataSyncService . stop ( ) ;
65- this . logService . info ( 'Auto Sync: stopped because' , reason ) ;
55+ if ( this . autoSync . value !== undefined ) {
56+ this . logService . trace ( 'Auto Sync: Disabled because' , reason ) ;
57+ this . autoSync . clear ( ) ;
6658 }
6759 }
68-
6960 }
7061
71- private async sync ( loop : boolean , auto : boolean ) : Promise < void > {
72- if ( this . enabled ) {
73- try {
74- this . lastSyncTriggerTime = new Date ( ) . getTime ( ) ;
75- await this . userDataSyncService . sync ( ) ;
76- this . resetFailures ( ) ;
77- } catch ( e ) {
78- const error = UserDataSyncError . toUserDataSyncError ( e ) ;
79- if ( error . code === UserDataSyncErrorCode . TurnedOff || error . code === UserDataSyncErrorCode . SessionExpired ) {
80- this . logService . info ( 'Auto Sync: Sync is turned off in the cloud.' ) ;
81- this . logService . info ( 'Auto Sync: Resetting the local sync state.' ) ;
82- await this . userDataSyncService . resetLocal ( ) ;
83- this . logService . info ( 'Auto Sync: Completed resetting the local sync state.' ) ;
84- if ( auto ) {
85- this . userDataSyncEnablementService . setEnablement ( false ) ;
86- this . _onError . fire ( error ) ;
87- return ;
88- } else {
89- return this . sync ( loop , auto ) ;
90- }
91- }
92- if ( error . code === UserDataSyncErrorCode . TooManyRequests ) {
93- this . logService . info ( 'Auto Sync: Turned off sync because of making too many requests to server' ) ;
94- this . userDataSyncEnablementService . setEnablement ( false ) ;
95- this . _onError . fire ( error ) ;
96- return ;
97- }
98- this . logService . error ( error ) ;
99- this . successiveFailures ++ ;
100- this . _onError . fire ( error ) ;
101- }
102- if ( loop ) {
103- await timeout ( 1000 * 60 * 5 ) ;
104- this . sync ( loop , true ) ;
105- }
106- } else {
107- this . logService . trace ( 'Auto Sync: Not syncing as it is disabled.' ) ;
108- }
109- }
62+ // For tests purpose only
63+ protected startAutoSync ( ) : boolean { return true ; }
11064
11165 private isAutoSyncEnabled ( ) : { enabled : boolean , reason ?: string } {
11266 if ( ! this . userDataSyncEnablementService . isEnabled ( ) ) {
@@ -121,14 +75,35 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
12175 return { enabled : true } ;
12276 }
12377
124- private resetFailures ( ) : void {
125- this . successiveFailures = 0 ;
78+ private async onDidFinishSync ( error : Error | undefined ) : Promise < void > {
79+ if ( ! error ) {
80+ // Sync finished without errors
81+ this . successiveFailures = 0 ;
82+ return ;
83+ }
84+
85+ // Error while syncing
86+ const userDataSyncError = UserDataSyncError . toUserDataSyncError ( error ) ;
87+ if ( userDataSyncError . code === UserDataSyncErrorCode . TurnedOff || userDataSyncError . code === UserDataSyncErrorCode . SessionExpired ) {
88+ this . logService . info ( 'Auto Sync: Sync is turned off in the cloud.' ) ;
89+ await this . userDataSyncService . resetLocal ( ) ;
90+ this . logService . info ( 'Auto Sync: Did reset the local sync state.' ) ;
91+ this . userDataSyncEnablementService . setEnablement ( false ) ;
92+ this . logService . info ( 'Auto Sync: Turned off sync because sync is turned off in the cloud' ) ;
93+ } else if ( userDataSyncError . code === UserDataSyncErrorCode . TooManyRequests ) {
94+ this . userDataSyncEnablementService . setEnablement ( false ) ;
95+ this . logService . info ( 'Auto Sync: Turned off sync because of making too many requests to server' ) ;
96+ } else {
97+ this . logService . error ( userDataSyncError ) ;
98+ this . successiveFailures ++ ;
99+ }
100+ this . _onError . fire ( userDataSyncError ) ;
126101 }
127102
128103 private sources : string [ ] = [ ] ;
129104 async triggerAutoSync ( sources : string [ ] ) : Promise < void > {
130- if ( ! this . enabled ) {
131- return this . syncDelayer . cancel ( ) ;
105+ if ( this . autoSync . value === undefined ) {
106+ return this . syncTriggerDelayer . cancel ( ) ;
132107 }
133108
134109 /*
@@ -143,16 +118,68 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
143118 }
144119
145120 this . sources . push ( ...sources ) ;
146- return this . syncDelayer . trigger ( ( ) => {
121+ return this . syncTriggerDelayer . trigger ( async ( ) => {
147122 this . telemetryService . publicLog2 < { sources : string [ ] } , AutoSyncClassification > ( 'sync/triggered' , { sources : this . sources } ) ;
148123 this . sources = [ ] ;
149-
150- this . logService . info ( 'Auto Sync: Triggered. ') ;
151- return this . sync ( false , true ) ;
124+ if ( this . autoSync . value ) {
125+ await this . autoSync . value . sync ( 'Activity ') ;
126+ }
152127 } , this . successiveFailures
153128 ? 1000 * 1 * Math . min ( Math . pow ( 2 , this . successiveFailures ) , 60 ) /* Delay exponentially until max 1 minute */
154- : 0 ) ; /* Do not delay if there are no failures */
129+ : 1000 ) ; /* Debounce for a second if there are no failures */
130+
131+ }
132+
133+ }
134+
135+ class AutoSync extends Disposable {
136+
137+ private static readonly INTERVAL_SYNCING = 'Interval' ;
138+
139+ private readonly intervalHandler = this . _register ( new MutableDisposable < IDisposable > ( ) ) ;
140+
141+ private readonly _onDidStartSync = this . _register ( new Emitter < void > ( ) ) ;
142+ readonly onDidStartSync = this . _onDidStartSync . event ;
143+
144+ private readonly _onDidFinishSync = this . _register ( new Emitter < Error | undefined > ( ) ) ;
145+ readonly onDidFinishSync = this . _onDidFinishSync . event ;
146+
147+ constructor (
148+ start : boolean ,
149+ private readonly interval : number /* in milliseconds */ ,
150+ private readonly userDataSyncService : IUserDataSyncService ,
151+ private readonly logService : IUserDataSyncLogService ,
152+ ) {
153+ super ( ) ;
154+ if ( start ) {
155+ this . _register ( this . onDidFinishSync ( ( ) => this . waitUntilNextIntervalAndSync ( ) ) ) ;
156+ this . _register ( toDisposable ( ( ) => {
157+ this . logService . info ( 'Auto Sync: Stopped' ) ;
158+ this . userDataSyncService . stop ( ) ;
159+ } ) ) ;
160+ this . logService . info ( 'Auto Sync: Started' ) ;
161+ this . sync ( AutoSync . INTERVAL_SYNCING ) ;
162+ }
163+ }
164+
165+ private waitUntilNextIntervalAndSync ( ) : void {
166+ this . intervalHandler . value = disposableTimeout ( ( ) => this . sync ( AutoSync . INTERVAL_SYNCING ) , this . interval ) ;
167+ }
168+
169+ async sync ( reason : string ) : Promise < void > {
170+ this . logService . info ( `Auto Sync: Triggered by ${ reason } ` ) ;
171+ this . _onDidStartSync . fire ( ) ;
172+ let error : Error | undefined ;
173+ try {
174+ await this . userDataSyncService . sync ( ) ;
175+ } catch ( e ) {
176+ error = e ;
177+ }
178+ this . _onDidFinishSync . fire ( error ) ;
179+ }
155180
181+ register < T extends IDisposable > ( t : T ) : T {
182+ return super . _register ( t ) ;
156183 }
157184
158185}
0 commit comments