From 796566e7aedcc926b872d7feef291554c4b8ce29 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 9 Mar 2023 19:47:54 -0600 Subject: [PATCH 1/2] Save cache to disk on SIGTERM/SIGINT --- backend/src/api/disk-cache.ts | 63 ++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/backend/src/api/disk-cache.ts b/backend/src/api/disk-cache.ts index 01bbb4d3f..584916563 100644 --- a/backend/src/api/disk-cache.ts +++ b/backend/src/api/disk-cache.ts @@ -16,14 +16,26 @@ class DiskCache { private static CHUNK_FILES = 25; private isWritingCache = false; - constructor() { } + constructor() { + if (!cluster.isMaster) { + return; + } + process.on('SIGINT', (e) => { + this.saveCacheToDiskSync(); + process.exit(2); + }); + process.on('SIGTERM', (e) => { + this.saveCacheToDiskSync(); + process.exit(2); + }); + } async $saveCacheToDisk(): Promise { if (!cluster.isPrimary) { return; } if (this.isWritingCache) { - logger.debug('Saving cache already in progress. Skipping.') + logger.debug('Saving cache already in progress. Skipping.'); return; } try { @@ -61,7 +73,50 @@ class DiskCache { } } - wipeCache() { + saveCacheToDiskSync(): void { + if (!cluster.isPrimary) { + return; + } + if (this.isWritingCache) { + logger.debug('Saving cache already in progress. Skipping.'); + return; + } + try { + logger.debug('Writing mempool and blocks data to disk cache (sync)...'); + this.isWritingCache = true; + + const mempool = memPool.getMempool(); + const mempoolArray: TransactionExtended[] = []; + for (const tx in mempool) { + mempoolArray.push(mempool[tx]); + } + + Common.shuffleArray(mempoolArray); + + const chunkSize = Math.floor(mempoolArray.length / DiskCache.CHUNK_FILES); + + fs.writeFileSync(DiskCache.FILE_NAME, JSON.stringify({ + cacheSchemaVersion: this.cacheSchemaVersion, + blocks: blocks.getBlocks(), + blockSummaries: blocks.getBlockSummaries(), + mempool: {}, + mempoolArray: mempoolArray.splice(0, chunkSize), + }), { flag: 'w' }); + for (let i = 1; i < DiskCache.CHUNK_FILES; i++) { + fs.writeFileSync(DiskCache.FILE_NAMES.replace('{number}', i.toString()), JSON.stringify({ + mempool: {}, + mempoolArray: mempoolArray.splice(0, chunkSize), + }), { flag: 'w' }); + } + logger.debug('Mempool and blocks data saved to disk cache'); + this.isWritingCache = false; + } catch (e) { + logger.warn('Error writing to cache file: ' + (e instanceof Error ? e.message : e)); + this.isWritingCache = false; + } + } + + wipeCache(): void { logger.notice(`Wiping nodejs backend cache/cache*.json files`); try { fs.unlinkSync(DiskCache.FILE_NAME); @@ -83,7 +138,7 @@ class DiskCache { } } - loadMempoolCache() { + loadMempoolCache(): void { if (!fs.existsSync(DiskCache.FILE_NAME)) { return; } From 46d89ac8379c04ceaa1c0988bab62211b4cf86de Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 9 Mar 2023 20:19:22 -0600 Subject: [PATCH 2/2] prevent disk cache file write corruption --- backend/src/api/disk-cache.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/backend/src/api/disk-cache.ts b/backend/src/api/disk-cache.ts index 584916563..af04d5acb 100644 --- a/backend/src/api/disk-cache.ts +++ b/backend/src/api/disk-cache.ts @@ -11,6 +11,8 @@ import { Common } from './common'; class DiskCache { private cacheSchemaVersion = 3; + private static TMP_FILE_NAME = config.MEMPOOL.CACHE_DIR + '/tmp-cache.json'; + private static TMP_FILE_NAMES = config.MEMPOOL.CACHE_DIR + '/tmp-cache{number}.json'; private static FILE_NAME = config.MEMPOOL.CACHE_DIR + '/cache.json'; private static FILE_NAMES = config.MEMPOOL.CACHE_DIR + '/cache{number}.json'; private static CHUNK_FILES = 25; @@ -95,7 +97,7 @@ class DiskCache { const chunkSize = Math.floor(mempoolArray.length / DiskCache.CHUNK_FILES); - fs.writeFileSync(DiskCache.FILE_NAME, JSON.stringify({ + fs.writeFileSync(DiskCache.TMP_FILE_NAME, JSON.stringify({ cacheSchemaVersion: this.cacheSchemaVersion, blocks: blocks.getBlocks(), blockSummaries: blocks.getBlockSummaries(), @@ -103,11 +105,17 @@ class DiskCache { mempoolArray: mempoolArray.splice(0, chunkSize), }), { flag: 'w' }); for (let i = 1; i < DiskCache.CHUNK_FILES; i++) { - fs.writeFileSync(DiskCache.FILE_NAMES.replace('{number}', i.toString()), JSON.stringify({ + fs.writeFileSync(DiskCache.TMP_FILE_NAMES.replace('{number}', i.toString()), JSON.stringify({ mempool: {}, mempoolArray: mempoolArray.splice(0, chunkSize), }), { flag: 'w' }); } + + fs.renameSync(DiskCache.TMP_FILE_NAME, DiskCache.FILE_NAME); + for (let i = 1; i < DiskCache.CHUNK_FILES; i++) { + fs.renameSync(DiskCache.TMP_FILE_NAMES.replace('{number}', i.toString()), DiskCache.FILE_NAMES.replace('{number}', i.toString())); + } + logger.debug('Mempool and blocks data saved to disk cache'); this.isWritingCache = false; } catch (e) {