From 94ce58a68f33b54d831ca89a73b19c6df8c875f0 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 13 Nov 2025 09:52:01 +0100 Subject: [PATCH] make restart and exit GradidoNode Process more robust, fix error in ensureCommunitiesAvailable --- .../client/GradidoNode/GradidoNodeProcess.ts | 16 +++++++++++--- .../src/client/GradidoNode/communities.ts | 10 +++++---- dlt-connector/src/client/hiero/HieroClient.ts | 21 ++++++++----------- dlt-connector/src/config/const.ts | 1 + .../sendToHiero/SendToHiero.context.ts | 2 +- dlt-connector/src/utils/time.ts | 4 ++++ 6 files changed, 34 insertions(+), 20 deletions(-) diff --git a/dlt-connector/src/client/GradidoNode/GradidoNodeProcess.ts b/dlt-connector/src/client/GradidoNode/GradidoNodeProcess.ts index bedbedf38..3980bb9f2 100644 --- a/dlt-connector/src/client/GradidoNode/GradidoNodeProcess.ts +++ b/dlt-connector/src/client/GradidoNode/GradidoNodeProcess.ts @@ -3,11 +3,13 @@ import { getLogger, Logger } from 'log4js' import { CONFIG } from '../../config' import { GRADIDO_NODE_KILL_TIMEOUT_MILLISECONDS, + GRADIDO_NODE_MIN_RUNTIME_BEFORE_EXIT_MILLISECONDS, GRADIDO_NODE_MIN_RUNTIME_BEFORE_RESTART_MILLISECONDS, GRADIDO_NODE_RUNTIME_PATH, LOG4JS_BASE_CATEGORY, } from '../../config/const' - +import { Mutex } from 'async-mutex' +import { delay } from '../../utils/time' /** * A Singleton class defines the `getInstance` method that lets clients access * the unique singleton instance. @@ -22,6 +24,7 @@ export class GradidoNodeProcess { private logger: Logger private lastStarted: Date | null = null private exitCalled: boolean = false + private restartMutex: Mutex = new Mutex() private constructor() { // constructor is private to prevent instantiation from outside @@ -55,7 +58,7 @@ export class GradidoNodeProcess { USERPROFILE: CONFIG.DLT_GRADIDO_NODE_SERVER_HOME_FOLDER, HOME: CONFIG.DLT_GRADIDO_NODE_SERVER_HOME_FOLDER, }, - onExit(proc, exitCode, signalCode, error) { + onExit(_proc, exitCode, signalCode, error) { logger.warn(`GradidoNodeProcess exited with code ${exitCode} and signalCode ${signalCode}`) if (error) { logger.error(`GradidoNodeProcess exit error: ${error}`) @@ -69,7 +72,6 @@ export class GradidoNodeProcess { }) }*/ } - logger.debug(`ressource usage: ${JSON.stringify(proc?.resourceUsage(), null, 2)}`) const gradidoNodeProcess = GradidoNodeProcess.getInstance() gradidoNodeProcess.proc = null if ( @@ -90,11 +92,16 @@ export class GradidoNodeProcess { } public async restart() { + const release = await this.restartMutex.acquire() + try { if (this.proc) { await this.exit() this.exitCalled = false this.start() } + } finally { + release() + } } public getLastStarted(): Date | null { @@ -104,6 +111,9 @@ export class GradidoNodeProcess { public async exit(): Promise { this.exitCalled = true if (this.proc) { + if (this.lastStarted && Date.now() - this.lastStarted.getTime() < GRADIDO_NODE_MIN_RUNTIME_BEFORE_EXIT_MILLISECONDS) { + await delay(GRADIDO_NODE_MIN_RUNTIME_BEFORE_EXIT_MILLISECONDS - Date.now() - this.lastStarted.getTime()) + } this.proc.kill('SIGTERM') const timeout = setTimeout(() => { this.logger.warn( diff --git a/dlt-connector/src/client/GradidoNode/communities.ts b/dlt-connector/src/client/GradidoNode/communities.ts index d25ed4dd4..76384f847 100644 --- a/dlt-connector/src/client/GradidoNode/communities.ts +++ b/dlt-connector/src/client/GradidoNode/communities.ts @@ -27,7 +27,8 @@ export async function ensureCommunitiesAvailable(communityTopicIds: HieroId[]): CONFIG.DLT_GRADIDO_NODE_SERVER_HOME_FOLDER, GRADIDO_NODE_HOME_FOLDER_NAME, ) - if (!checkCommunityAvailable(communityTopicIds, homeFolder)) { + const communityTopicIdsSet = new Set(communityTopicIds) + if (!checkCommunityAvailable(communityTopicIdsSet, homeFolder)) { await exportCommunities(homeFolder, BackendClient.getInstance()) return GradidoNodeProcess.getInstance().restart() } @@ -65,7 +66,7 @@ export async function exportCommunities(homeFolder: string, client: BackendClien logger.info(`exported ${communitiesForDltNodeServer.length} communities to ${communitiesPath}`) } -export function checkCommunityAvailable(communityTopicIds: HieroId[], homeFolder: string): boolean { +export function checkCommunityAvailable(communityTopicIds: Set, homeFolder: string): boolean { const communitiesPath = path.join(homeFolder, 'communities.json') if (!checkFileExist(communitiesPath)) { return false @@ -73,12 +74,13 @@ export function checkCommunityAvailable(communityTopicIds: HieroId[], homeFolder const communities = JSON.parse(fs.readFileSync(communitiesPath, 'utf-8')) let foundCount = 0 for (const community of communities) { - if (communityTopicIds.includes(community.hieroTopicId)) { + if (communityTopicIds.has(community.hieroTopicId)) { foundCount++ - if (foundCount >= communityTopicIds.length) { + if (foundCount >= communityTopicIds.size) { return true } } } + logger.debug(`community not found for topic ids: ${communityTopicIds}, communities: ${JSON.stringify(communities, null, 2)}`) return false } diff --git a/dlt-connector/src/client/hiero/HieroClient.ts b/dlt-connector/src/client/hiero/HieroClient.ts index 0ed375d62..f81da11c5 100644 --- a/dlt-connector/src/client/hiero/HieroClient.ts +++ b/dlt-connector/src/client/hiero/HieroClient.ts @@ -15,7 +15,7 @@ import { TransactionId, Wallet, } from '@hashgraph/sdk' -import { GradidoTransaction } from 'gradido-blockchain-js' +import { GradidoTransaction, Profiler } from 'gradido-blockchain-js' import { getLogger, Logger } from 'log4js' import * as v from 'valibot' import { CONFIG } from '../../config' @@ -24,8 +24,7 @@ import { HieroId, hieroIdSchema } from '../../schemas/typeGuard.schema' import { type TopicInfoOutput, topicInfoSchema } from './output.schema' import { GradidoNodeClient } from '../GradidoNode/GradidoNodeClient' import { GradidoNodeProcess } from '../GradidoNode/GradidoNodeProcess' -import { printTimeDuration } from '../../utils/time' - +import { durationInMinutesFromDates, printTimeDuration } from '../../utils/time' // https://docs.hedera.com/hedera/sdks-and-apis/hedera-api/consensus/consensusupdatetopic export const MIN_AUTORENEW_PERIOD = 6999999 //seconds export const MAX_AUTORENEW_PERIOD = 8000001 // seconds @@ -75,7 +74,7 @@ export class HieroClient { topicId: HieroId, transaction: GradidoTransaction, ): Promise { - const startTime = new Date() + const timeUsed = new Profiler() this.transactionInternNr++ const logger = getLogger(`${LOG4JS_BASE_CATEGORY}.client.HieroClient`) logger.addContext('trNr', this.transactionInternNr) @@ -96,11 +95,11 @@ export class HieroClient { .then(async (signedHieroTransaction) => { const sendResponse = await signedHieroTransaction.executeWithSigner(this.wallet) logger.info( - `message sent to topic ${topicId}, transaction id: ${sendResponse.transactionId.toString()}`, + `message sent to topic ${topicId}, transaction id: ${sendResponse.transactionId.toString()}, timeUsed: ${timeUsed.string()}`, ) // TODO: fix issue in GradidoNode // hot fix, when gradido node is running some time, the hiero listener stop working, so we check if our new transaction is received - // after 1 second, else restart GradidoNode + // after 10 seconds, else restart GradidoNode setTimeout(async () => { const transaction = await GradidoNodeClient.getInstance().getTransaction({ topic: topicId, @@ -110,7 +109,7 @@ export class HieroClient { const process = GradidoNodeProcess.getInstance() const lastStarted = process.getLastStarted() if (lastStarted) { - const serverRunTime = printTimeDuration(new Date().getTime() - lastStarted.getTime()) + const serverRunTime = printTimeDuration(durationInMinutesFromDates(lastStarted, new Date())) this.logger.error(`transaction not found, restart GradidoNode after ${serverRunTime}`) await GradidoNodeProcess.getInstance().restart() } else { @@ -118,7 +117,7 @@ export class HieroClient { GradidoNodeProcess.getInstance().start() } } - }, 1000) + }, 10000) if (logger.isInfoEnabled()) { // only for logging sendResponse.getReceiptWithSigner(this.wallet).then((receipt) => { @@ -127,9 +126,8 @@ export class HieroClient { // only for logging sendResponse.getRecordWithSigner(this.wallet).then((record) => { logger.info(`message sent, cost: ${record.transactionFee.toString()}`) - const localEndTime = new Date() logger.info( - `HieroClient.sendMessage used time (full process): ${localEndTime.getTime() - startTime.getTime()}ms`, + `HieroClient.sendMessage used time (full process): ${timeUsed.string()}`, ) }) } @@ -141,8 +139,7 @@ export class HieroClient { this.pendingPromises.splice(pendingPromiseIndex, 1) }), ) - const endTime = new Date() - logger.info(`HieroClient.sendMessage used time: ${endTime.getTime() - startTime.getTime()}ms`) + logger.debug(`create transactionId: ${hieroTransaction.transactionId?.toString()}, used time: ${timeUsed.string()}`) return hieroTransaction.transactionId } diff --git a/dlt-connector/src/config/const.ts b/dlt-connector/src/config/const.ts index dc4b0177c..5657f65ee 100644 --- a/dlt-connector/src/config/const.ts +++ b/dlt-connector/src/config/const.ts @@ -16,6 +16,7 @@ export const GRADIDO_NODE_RUNTIME_PATH = path.join( ) // if last start was less than this time, do not restart export const GRADIDO_NODE_MIN_RUNTIME_BEFORE_RESTART_MILLISECONDS = 1000 * 30 +export const GRADIDO_NODE_MIN_RUNTIME_BEFORE_EXIT_MILLISECONDS = 1000 * 2 export const GRADIDO_NODE_KILL_TIMEOUT_MILLISECONDS = 10000 // currently hard coded in gradido node, update in future export const GRADIDO_NODE_HOME_FOLDER_NAME = '.gradido' diff --git a/dlt-connector/src/interactions/sendToHiero/SendToHiero.context.ts b/dlt-connector/src/interactions/sendToHiero/SendToHiero.context.ts index 5ef946ec9..269bccada 100644 --- a/dlt-connector/src/interactions/sendToHiero/SendToHiero.context.ts +++ b/dlt-connector/src/interactions/sendToHiero/SendToHiero.context.ts @@ -104,7 +104,7 @@ async function sendViaHiero( if (!transactionId) { throw new Error('missing transaction id from hiero') } - logger.info('transmitted Gradido Transaction to Hiero', { + logger.debug('give Gradido Transaction to Hiero Client', { transactionId: transactionId.toString(), }) return v.parse(hieroTransactionIdStringSchema, transactionId.toString()) diff --git a/dlt-connector/src/utils/time.ts b/dlt-connector/src/utils/time.ts index ccbb91c07..4f69ebd98 100644 --- a/dlt-connector/src/utils/time.ts +++ b/dlt-connector/src/utils/time.ts @@ -1,3 +1,5 @@ +import { promisify } from 'node:util' + /** * @param {number} time - in minutes */ @@ -40,3 +42,5 @@ export const printTimeDuration = (duration: number): string => { } return result } + +export const delay = promisify(setTimeout) \ No newline at end of file