diff --git a/test/test-cases/get-sync-progress-test-case.js b/test/test-cases/get-sync-progress-test-case.js index ff1b7566f..7457ce224 100644 --- a/test/test-cases/get-sync-progress-test-case.js +++ b/test/test-cases/get-sync-progress-test-case.js @@ -32,10 +32,21 @@ module.exports = ( assert.propertyVal(res.body, 'id', 5) assert.isObject(res.body.result) assert.isNumber(res.body.result.progress) - assert.isNumber(res.body.result.syncStartedAt) - assert.isNumber(res.body.result.spentTime) + + assert.isOk( + res.body.result.syncStartedAt === null || + Number.isInteger(res.body.result.syncStartedAt) + ) + assert.isOk( + res.body.result.spentTime === null || + Number.isInteger(res.body.result.spentTime) + ) + if (Number.isFinite(res.body.result.syncStartedAt)) { + Number.isInteger(res.body.result.spentTime) + } if ( + !Number.isFinite(res.body.result.syncStartedAt) || !Number.isFinite(res.body.result.progress) || res.body.result.progress <= 0 || res.body.result.progress > 100 diff --git a/workers/loc.api/sync/index.js b/workers/loc.api/sync/index.js index 4cfc9642d..cafcb8122 100644 --- a/workers/loc.api/sync/index.js +++ b/workers/loc.api/sync/index.js @@ -50,6 +50,8 @@ class Sync { } } + this.progress.deactivateSyncTimeEstimate() + try { await this.redirectRequestsToApi({ isRedirected: false }) } catch (err) { diff --git a/workers/loc.api/sync/progress/index.js b/workers/loc.api/sync/progress/index.js index 46f018559..7578a6bcb 100644 --- a/workers/loc.api/sync/progress/index.js +++ b/workers/loc.api/sync/progress/index.js @@ -33,6 +33,12 @@ class Progress extends EventEmitter { this.logger = logger this._syncStartedAt = null + this._progressEmitterInterval = null + this._progressEmitterIntervalMs = 1000 + this._progress = null + this._hasNotProgressChanged = true + this._leftTime = null + this._prevEstimatedLeftTime = Date.now() } async setProgress (progress) { @@ -43,18 +49,16 @@ class Progress extends EventEmitter { const _progress = isError ? progress.toString() : progress + this._hasNotProgressChanged = this._progress !== _progress + this._progress = _progress try { await this.dao.updateRecordOf( this.TABLES_NAMES.PROGRESS, { value: JSON.stringify(_progress) } ) - const estimatedSyncTime = this._estimateSyncTime({ - progress: _progress - }) - this.emit(estimatedSyncTime) - await this.wsEventEmitter.emitProgress(() => estimatedSyncTime) + await this._emitProgress() } catch (e) { this.logger.error( `PROGRESS:SYNC:SET: ${e.stack || e}` @@ -94,11 +98,13 @@ class Progress extends EventEmitter { activateSyncTimeEstimate () { this._syncStartedAt = Date.now() + this._launchProgressEmitterInterval() return this } deactivateSyncTimeEstimate () { + clearInterval(this._progressEmitterInterval) this._syncStartedAt = null return this @@ -106,7 +112,8 @@ class Progress extends EventEmitter { async _estimateSyncTime (params) { const { - progress + progress, + hasNotProgressChanged } = params ?? {} const syncStartedAt = this._getSyncStartedAt() @@ -124,7 +131,7 @@ class Progress extends EventEmitter { } } - const spentTime = nowMts - syncStartedAt + const spentTime = Math.floor(nowMts - syncStartedAt) if ( !Number.isFinite(progress) || @@ -139,7 +146,12 @@ class Progress extends EventEmitter { } } - const leftTime = (spentTime / progress) * (100 - progress) + const leftTime = this._calcLeftTime({ + progress, + nowMts, + spentTime, + hasNotProgressChanged + }) return { progress, @@ -149,9 +161,66 @@ class Progress extends EventEmitter { } } + _calcLeftTime (params) { + const { + progress, + nowMts, + spentTime, + hasNotProgressChanged + } = params ?? {} + + if (!hasNotProgressChanged) { + this._prevEstimatedLeftTime = nowMts + this._leftTime = Math.floor((spentTime / progress) * (100 - progress)) + + return this._leftTime + } + if (!Number.isFinite(this._leftTime)) { + this._prevEstimatedLeftTime = nowMts + + return this._leftTime + } + + const leftTime = Math.floor(this._leftTime - (nowMts - this._prevEstimatedLeftTime)) + this._prevEstimatedLeftTime = nowMts + this._leftTime = leftTime > 0 + ? leftTime + : null + + return this._leftTime + } + _getSyncStartedAt () { return this._syncStartedAt ?? null } + + _launchProgressEmitterInterval () { + clearInterval(this._progressEmitterInterval) + + this._progressEmitterInterval = setInterval(async () => { + await this._emitProgress({ + hasNotProgressChanged: this._hasNotProgressChanged + }) + }, this._progressEmitterIntervalMs).unref() + } + + async _emitProgress (opts) { + try { + const { hasNotProgressChanged } = opts ?? {} + + const estimatedSyncTime = this._estimateSyncTime({ + progress: this._progress, + hasNotProgressChanged + }) + + this.emit(estimatedSyncTime) + await this.wsEventEmitter.emitProgress(() => estimatedSyncTime) + } catch (e) { + this.logger.error( + `PROGRESS:SYNC:EMITTING: ${e.stack || e}` + ) + } + } } decorateInjectable(Progress, depsTypes)