diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index 755b3c3ad..e0d1cd9f0 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -520,6 +520,8 @@ class Blocks { } public async $updateBlocks() { + diskCache.lock(); + let fastForwarded = false; const blockHeightTip = await bitcoinApi.$getBlockHeightTip(); @@ -658,6 +660,8 @@ class Blocks { // wait for pending async callbacks to finish await Promise.all(callbackPromises); } + + diskCache.unlock(); } /** diff --git a/backend/src/api/disk-cache.ts b/backend/src/api/disk-cache.ts index d317036d8..2479e529b 100644 --- a/backend/src/api/disk-cache.ts +++ b/backend/src/api/disk-cache.ts @@ -18,6 +18,11 @@ class DiskCache { private static CHUNK_FILES = 25; private isWritingCache = false; + private semaphore: { resume: (() => void)[], locks: number } = { + resume: [], + locks: 0, + }; + constructor() { if (!cluster.isPrimary) { return; @@ -73,6 +78,7 @@ class DiskCache { fs.renameSync(DiskCache.TMP_FILE_NAMES.replace('{number}', i.toString()), DiskCache.FILE_NAMES.replace('{number}', i.toString())); } } else { + await this.$yield(); await fsPromises.writeFile(DiskCache.TMP_FILE_NAME, JSON.stringify({ network: config.MEMPOOL.NETWORK, cacheSchemaVersion: this.cacheSchemaVersion, @@ -82,6 +88,7 @@ class DiskCache { mempoolArray: mempoolArray.splice(0, chunkSize), }), { flag: 'w' }); for (let i = 1; i < DiskCache.CHUNK_FILES; i++) { + await this.$yield(); await fsPromises.writeFile(DiskCache.TMP_FILE_NAMES.replace('{number}', i.toString()), JSON.stringify({ mempool: {}, mempoolArray: mempoolArray.splice(0, chunkSize), @@ -175,6 +182,32 @@ class DiskCache { logger.warn('Failed to parse mempoool and blocks cache. Skipping. Reason: ' + (e instanceof Error ? e.message : e)); } } + + private $yield(): Promise { + if (this.semaphore.locks) { + logger.debug('Pause writing mempool and blocks data to disk cache (async)'); + return new Promise((resolve) => { + this.semaphore.resume.push(resolve); + }); + } else { + return Promise.resolve(); + } + } + + public lock(): void { + this.semaphore.locks++; + } + + public unlock(): void { + this.semaphore.locks = Math.max(0, this.semaphore.locks - 1); + if (!this.semaphore.locks && this.semaphore.resume.length) { + const nextResume = this.semaphore.resume.shift(); + if (nextResume) { + logger.debug('Resume writing mempool and blocks data to disk cache (async)'); + nextResume(); + } + } + } } export default new DiskCache(); diff --git a/backend/src/index.ts b/backend/src/index.ts index cbc9396c1..20fca4968 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -206,6 +206,8 @@ class Server { setTimeout(this.runMainUpdateLoop.bind(this), 1000 * this.currentBackendRetryInterval); this.currentBackendRetryInterval *= 2; this.currentBackendRetryInterval = Math.min(this.currentBackendRetryInterval, 60); + } finally { + diskCache.unlock(); } }