UNPKG

int-cli

Version:

INT is the new generation of bottom-up created system of IoT and blockchain

223 lines (222 loc) 8.89 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const fs = require("fs-extra"); const path = require("path"); const assert = require("assert"); const error_code_1 = require("../error_code"); const serializable_1 = require("../serializable"); const js_log_1 = require("./js_log"); const dump_snapshot_1 = require("./dump_snapshot"); const dump_snapshot_manager_1 = require("./dump_snapshot_manager"); class StorageLogSnapshotManager { constructor(options) { this.m_snapshots = new Map(); this.m_logPath = path.join(options.path, 'log'); if (options.dumpSnapshotManager) { this.m_dumpManager = options.dumpSnapshotManager; } else { this.m_dumpManager = new dump_snapshot_manager_1.StorageDumpSnapshotManager(options); } this.m_headerStorage = options.headerStorage; this.m_storageType = options.storageType; this.m_logger = options.logger; this.m_readonly = !!(options && options.readonly); } recycle() { this.m_logger.debug(`begin recycle snanshot`); let recycledMap = new Map(this.m_snapshots); for (let [blockHash, stub] of recycledMap.entries()) { if (!stub.ref) { this.m_logger.debug(`delete snapshot ${blockHash}`); const err = this.m_dumpManager.removeSnapshot(blockHash); if (!err) { this.m_snapshots.delete(blockHash); } } } } async init() { if (!this.m_readonly) { fs.ensureDirSync(this.m_logPath); } let err = await this.m_dumpManager.init(); if (err) { return err; } let snapshots = this.m_dumpManager.listSnapshots(); for (let ss of snapshots) { this.m_snapshots.set(ss.blockHash, { ref: 0 }); } return error_code_1.ErrorCode.RESULT_OK; } uninit() { this.m_dumpManager.uninit(); this.m_snapshots.clear(); } async createSnapshot(from, blockHash) { let csr = await this.m_dumpManager.createSnapshot(from, blockHash); if (csr.err) { return csr; } let logger = from.storageLogger; if (logger) { this.m_logger.debug(`begin write redo log ${blockHash}`); let writer = new serializable_1.BufferWriter(); logger.finish(); let err = logger.encode(writer); if (err) { this.m_logger.error(`encode redo logger failed `, blockHash); } fs.writeFileSync(this.getLogPath(blockHash), writer.render()); } else { this.m_logger.debug(`ignore write redo log ${blockHash} for redo log missing`); } this.m_snapshots.set(blockHash, { ref: 0 }); return csr; } getSnapshotFilePath(blockHash) { return this.m_dumpManager.getSnapshotFilePath(blockHash); } getLogPath(blockHash) { return path.join(this.m_logPath, blockHash + '.redo'); } hasRedoLog(blockHash) { return fs.existsSync(this.getLogPath(blockHash)); } getRedoLog(blockHash) { let redoLogRaw; try { redoLogRaw = fs.readFileSync(this.getLogPath(blockHash)); } catch (error) { this.m_logger.warn(`read log file ${this.getLogPath(blockHash)} failed.`); } if (!redoLogRaw) { this.m_logger.error(`get redo log ${blockHash} failed`); return undefined; } let redoLog = new js_log_1.JStorageLogger(); let err = redoLog.decode(new serializable_1.BufferReader(redoLogRaw)); if (err) { this.m_logger.error(`decode redo log ${blockHash} from storage failed`); return undefined; } return redoLog; } // 保存redolog文件 // 文件内容来源是 从其他节点请求来, 并不是本地节点自己运行的redolog writeRedoLog(blockHash, redoLog) { this.m_logger.debug(`write redo log ${blockHash}`); let filepath = this.getLogPath(blockHash); let writer = new serializable_1.BufferWriter(); let err = redoLog.encode(writer); if (err) { this.m_logger.error(`encode redo log failed `, redoLog); return err; } fs.writeFileSync(filepath, writer.render()); return error_code_1.ErrorCode.RESULT_OK; } async getSnapshot(blockHash) { this.m_logger.info(`getting snapshot ${blockHash}`); // 只能在storage manager 的实现中调用,在storage manager中保证不会以相同block hash重入 let ssr = await this.m_dumpManager.getSnapshot(blockHash); if (!ssr.err) { assert(this.m_snapshots.get(blockHash)); this.m_logger.info(`get snapshot ${blockHash} directly from dump`); ++this.m_snapshots.get(blockHash).ref; return ssr; } else if (ssr.err !== error_code_1.ErrorCode.RESULT_NOT_FOUND) { this.m_logger.error(`get snapshot ${blockHash} failed for dump manager get snapshot failed for ${ssr.err}`); return { err: ssr.err }; } let hr = await this.m_headerStorage.getHeader(blockHash); if (hr.err) { this.m_logger.error(`get snapshot ${blockHash} failed for load header failed ${hr.err}`); return { err: hr.err }; } let blockPath = []; let header = hr.header; let err = error_code_1.ErrorCode.RESULT_NOT_FOUND; let nearestSnapshot; this.m_logger.info(`================================getSnapshot need redo blockHash=${blockHash}`); do { let _ssr = await this.m_dumpManager.getSnapshot(header.hash); if (!_ssr.err) { nearestSnapshot = _ssr.snapshot; err = _ssr.err; break; } else if (_ssr.err !== error_code_1.ErrorCode.RESULT_NOT_FOUND) { this.m_logger.error(`get snapshot ${blockHash} failed for get dump ${header.hash} failed ${_ssr.err}`); err = _ssr.err; break; } blockPath.push(header.hash); let _hr = await this.m_headerStorage.getHeader(header.preBlockHash); if (_hr.err) { this.m_logger.error(`get snapshot ${blockHash} failed for get header ${header.preBlockHash} failed ${hr.err}`); err = error_code_1.ErrorCode.RESULT_INVALID_BLOCK; break; } header = _hr.header; } while (true); if (err) { this.m_logger.error(`get snapshot ${blockHash} failed for ${err}`); return { err }; } /** 这段代码要保证同步 start */ let storage = new this.m_storageType({ filePath: this.m_dumpManager.getSnapshotFilePath(blockHash), logger: this.m_logger }); fs.copyFileSync(nearestSnapshot.filePath, storage.filePath); /** 这段代码要保证同步 end */ err = await storage.init(); if (err) { this.m_logger.error(`get snapshot ${blockHash} failed for storage init failed for ${err}`); return { err }; } for (let _blockHash of blockPath.reverse()) { if (!fs.existsSync(this.getLogPath(_blockHash))) { this.m_logger.error(`get snapshot ${blockHash} failed for get redo log for ${_blockHash} failed for not exist`); err = error_code_1.ErrorCode.RESULT_NOT_FOUND; break; } let log; try { log = fs.readFileSync(this.getLogPath(_blockHash)); } catch (error) { this.m_logger.error(`read log file ${this.getLogPath(_blockHash)} failed.`); } err = await storage.redo(log); if (err) { this.m_logger.error(`get snapshot ${blockHash} failed for redo ${_blockHash} failed for ${err}`); break; } } await storage.uninit(); if (err) { await storage.remove(); this.m_logger.error(`get snapshot ${blockHash} failed for ${err}`); return { err }; } this.m_snapshots.set(blockHash, { ref: 1 }); return { err: error_code_1.ErrorCode.RESULT_OK, snapshot: new dump_snapshot_1.StorageDumpSnapshot(blockHash, storage.filePath) }; } releaseSnapshot(blockHash) { let stub = this.m_snapshots.get(blockHash); if (stub) { assert(stub.ref > 0); if (stub.ref > 0) { --stub.ref; } } } } exports.StorageLogSnapshotManager = StorageLogSnapshotManager;