mirror of
https://github.com/IT4Change/gradido.git
synced 2026-04-06 01:25:28 +00:00
make restart and exit GradidoNode Process more robust, fix error in ensureCommunitiesAvailable
This commit is contained in:
parent
f66f33307d
commit
94ce58a68f
@ -3,11 +3,13 @@ import { getLogger, Logger } from 'log4js'
|
|||||||
import { CONFIG } from '../../config'
|
import { CONFIG } from '../../config'
|
||||||
import {
|
import {
|
||||||
GRADIDO_NODE_KILL_TIMEOUT_MILLISECONDS,
|
GRADIDO_NODE_KILL_TIMEOUT_MILLISECONDS,
|
||||||
|
GRADIDO_NODE_MIN_RUNTIME_BEFORE_EXIT_MILLISECONDS,
|
||||||
GRADIDO_NODE_MIN_RUNTIME_BEFORE_RESTART_MILLISECONDS,
|
GRADIDO_NODE_MIN_RUNTIME_BEFORE_RESTART_MILLISECONDS,
|
||||||
GRADIDO_NODE_RUNTIME_PATH,
|
GRADIDO_NODE_RUNTIME_PATH,
|
||||||
LOG4JS_BASE_CATEGORY,
|
LOG4JS_BASE_CATEGORY,
|
||||||
} from '../../config/const'
|
} from '../../config/const'
|
||||||
|
import { Mutex } from 'async-mutex'
|
||||||
|
import { delay } from '../../utils/time'
|
||||||
/**
|
/**
|
||||||
* A Singleton class defines the `getInstance` method that lets clients access
|
* A Singleton class defines the `getInstance` method that lets clients access
|
||||||
* the unique singleton instance.
|
* the unique singleton instance.
|
||||||
@ -22,6 +24,7 @@ export class GradidoNodeProcess {
|
|||||||
private logger: Logger
|
private logger: Logger
|
||||||
private lastStarted: Date | null = null
|
private lastStarted: Date | null = null
|
||||||
private exitCalled: boolean = false
|
private exitCalled: boolean = false
|
||||||
|
private restartMutex: Mutex = new Mutex()
|
||||||
|
|
||||||
private constructor() {
|
private constructor() {
|
||||||
// constructor is private to prevent instantiation from outside
|
// constructor is private to prevent instantiation from outside
|
||||||
@ -55,7 +58,7 @@ export class GradidoNodeProcess {
|
|||||||
USERPROFILE: CONFIG.DLT_GRADIDO_NODE_SERVER_HOME_FOLDER,
|
USERPROFILE: CONFIG.DLT_GRADIDO_NODE_SERVER_HOME_FOLDER,
|
||||||
HOME: 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}`)
|
logger.warn(`GradidoNodeProcess exited with code ${exitCode} and signalCode ${signalCode}`)
|
||||||
if (error) {
|
if (error) {
|
||||||
logger.error(`GradidoNodeProcess exit error: ${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()
|
const gradidoNodeProcess = GradidoNodeProcess.getInstance()
|
||||||
gradidoNodeProcess.proc = null
|
gradidoNodeProcess.proc = null
|
||||||
if (
|
if (
|
||||||
@ -90,11 +92,16 @@ export class GradidoNodeProcess {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async restart() {
|
public async restart() {
|
||||||
|
const release = await this.restartMutex.acquire()
|
||||||
|
try {
|
||||||
if (this.proc) {
|
if (this.proc) {
|
||||||
await this.exit()
|
await this.exit()
|
||||||
this.exitCalled = false
|
this.exitCalled = false
|
||||||
this.start()
|
this.start()
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
release()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getLastStarted(): Date | null {
|
public getLastStarted(): Date | null {
|
||||||
@ -104,6 +111,9 @@ export class GradidoNodeProcess {
|
|||||||
public async exit(): Promise<void> {
|
public async exit(): Promise<void> {
|
||||||
this.exitCalled = true
|
this.exitCalled = true
|
||||||
if (this.proc) {
|
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')
|
this.proc.kill('SIGTERM')
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
this.logger.warn(
|
this.logger.warn(
|
||||||
|
|||||||
@ -27,7 +27,8 @@ export async function ensureCommunitiesAvailable(communityTopicIds: HieroId[]):
|
|||||||
CONFIG.DLT_GRADIDO_NODE_SERVER_HOME_FOLDER,
|
CONFIG.DLT_GRADIDO_NODE_SERVER_HOME_FOLDER,
|
||||||
GRADIDO_NODE_HOME_FOLDER_NAME,
|
GRADIDO_NODE_HOME_FOLDER_NAME,
|
||||||
)
|
)
|
||||||
if (!checkCommunityAvailable(communityTopicIds, homeFolder)) {
|
const communityTopicIdsSet = new Set(communityTopicIds)
|
||||||
|
if (!checkCommunityAvailable(communityTopicIdsSet, homeFolder)) {
|
||||||
await exportCommunities(homeFolder, BackendClient.getInstance())
|
await exportCommunities(homeFolder, BackendClient.getInstance())
|
||||||
return GradidoNodeProcess.getInstance().restart()
|
return GradidoNodeProcess.getInstance().restart()
|
||||||
}
|
}
|
||||||
@ -65,7 +66,7 @@ export async function exportCommunities(homeFolder: string, client: BackendClien
|
|||||||
logger.info(`exported ${communitiesForDltNodeServer.length} communities to ${communitiesPath}`)
|
logger.info(`exported ${communitiesForDltNodeServer.length} communities to ${communitiesPath}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkCommunityAvailable(communityTopicIds: HieroId[], homeFolder: string): boolean {
|
export function checkCommunityAvailable(communityTopicIds: Set<HieroId>, homeFolder: string): boolean {
|
||||||
const communitiesPath = path.join(homeFolder, 'communities.json')
|
const communitiesPath = path.join(homeFolder, 'communities.json')
|
||||||
if (!checkFileExist(communitiesPath)) {
|
if (!checkFileExist(communitiesPath)) {
|
||||||
return false
|
return false
|
||||||
@ -73,12 +74,13 @@ export function checkCommunityAvailable(communityTopicIds: HieroId[], homeFolder
|
|||||||
const communities = JSON.parse(fs.readFileSync(communitiesPath, 'utf-8'))
|
const communities = JSON.parse(fs.readFileSync(communitiesPath, 'utf-8'))
|
||||||
let foundCount = 0
|
let foundCount = 0
|
||||||
for (const community of communities) {
|
for (const community of communities) {
|
||||||
if (communityTopicIds.includes(community.hieroTopicId)) {
|
if (communityTopicIds.has(community.hieroTopicId)) {
|
||||||
foundCount++
|
foundCount++
|
||||||
if (foundCount >= communityTopicIds.length) {
|
if (foundCount >= communityTopicIds.size) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
logger.debug(`community not found for topic ids: ${communityTopicIds}, communities: ${JSON.stringify(communities, null, 2)}`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import {
|
|||||||
TransactionId,
|
TransactionId,
|
||||||
Wallet,
|
Wallet,
|
||||||
} from '@hashgraph/sdk'
|
} from '@hashgraph/sdk'
|
||||||
import { GradidoTransaction } from 'gradido-blockchain-js'
|
import { GradidoTransaction, Profiler } from 'gradido-blockchain-js'
|
||||||
import { getLogger, Logger } from 'log4js'
|
import { getLogger, Logger } from 'log4js'
|
||||||
import * as v from 'valibot'
|
import * as v from 'valibot'
|
||||||
import { CONFIG } from '../../config'
|
import { CONFIG } from '../../config'
|
||||||
@ -24,8 +24,7 @@ import { HieroId, hieroIdSchema } from '../../schemas/typeGuard.schema'
|
|||||||
import { type TopicInfoOutput, topicInfoSchema } from './output.schema'
|
import { type TopicInfoOutput, topicInfoSchema } from './output.schema'
|
||||||
import { GradidoNodeClient } from '../GradidoNode/GradidoNodeClient'
|
import { GradidoNodeClient } from '../GradidoNode/GradidoNodeClient'
|
||||||
import { GradidoNodeProcess } from '../GradidoNode/GradidoNodeProcess'
|
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
|
// https://docs.hedera.com/hedera/sdks-and-apis/hedera-api/consensus/consensusupdatetopic
|
||||||
export const MIN_AUTORENEW_PERIOD = 6999999 //seconds
|
export const MIN_AUTORENEW_PERIOD = 6999999 //seconds
|
||||||
export const MAX_AUTORENEW_PERIOD = 8000001 // seconds
|
export const MAX_AUTORENEW_PERIOD = 8000001 // seconds
|
||||||
@ -75,7 +74,7 @@ export class HieroClient {
|
|||||||
topicId: HieroId,
|
topicId: HieroId,
|
||||||
transaction: GradidoTransaction,
|
transaction: GradidoTransaction,
|
||||||
): Promise<TransactionId | null> {
|
): Promise<TransactionId | null> {
|
||||||
const startTime = new Date()
|
const timeUsed = new Profiler()
|
||||||
this.transactionInternNr++
|
this.transactionInternNr++
|
||||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY}.client.HieroClient`)
|
const logger = getLogger(`${LOG4JS_BASE_CATEGORY}.client.HieroClient`)
|
||||||
logger.addContext('trNr', this.transactionInternNr)
|
logger.addContext('trNr', this.transactionInternNr)
|
||||||
@ -96,11 +95,11 @@ export class HieroClient {
|
|||||||
.then(async (signedHieroTransaction) => {
|
.then(async (signedHieroTransaction) => {
|
||||||
const sendResponse = await signedHieroTransaction.executeWithSigner(this.wallet)
|
const sendResponse = await signedHieroTransaction.executeWithSigner(this.wallet)
|
||||||
logger.info(
|
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
|
// 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
|
// 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 () => {
|
setTimeout(async () => {
|
||||||
const transaction = await GradidoNodeClient.getInstance().getTransaction({
|
const transaction = await GradidoNodeClient.getInstance().getTransaction({
|
||||||
topic: topicId,
|
topic: topicId,
|
||||||
@ -110,7 +109,7 @@ export class HieroClient {
|
|||||||
const process = GradidoNodeProcess.getInstance()
|
const process = GradidoNodeProcess.getInstance()
|
||||||
const lastStarted = process.getLastStarted()
|
const lastStarted = process.getLastStarted()
|
||||||
if (lastStarted) {
|
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}`)
|
this.logger.error(`transaction not found, restart GradidoNode after ${serverRunTime}`)
|
||||||
await GradidoNodeProcess.getInstance().restart()
|
await GradidoNodeProcess.getInstance().restart()
|
||||||
} else {
|
} else {
|
||||||
@ -118,7 +117,7 @@ export class HieroClient {
|
|||||||
GradidoNodeProcess.getInstance().start()
|
GradidoNodeProcess.getInstance().start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 1000)
|
}, 10000)
|
||||||
if (logger.isInfoEnabled()) {
|
if (logger.isInfoEnabled()) {
|
||||||
// only for logging
|
// only for logging
|
||||||
sendResponse.getReceiptWithSigner(this.wallet).then((receipt) => {
|
sendResponse.getReceiptWithSigner(this.wallet).then((receipt) => {
|
||||||
@ -127,9 +126,8 @@ export class HieroClient {
|
|||||||
// only for logging
|
// only for logging
|
||||||
sendResponse.getRecordWithSigner(this.wallet).then((record) => {
|
sendResponse.getRecordWithSigner(this.wallet).then((record) => {
|
||||||
logger.info(`message sent, cost: ${record.transactionFee.toString()}`)
|
logger.info(`message sent, cost: ${record.transactionFee.toString()}`)
|
||||||
const localEndTime = new Date()
|
|
||||||
logger.info(
|
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)
|
this.pendingPromises.splice(pendingPromiseIndex, 1)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
const endTime = new Date()
|
logger.debug(`create transactionId: ${hieroTransaction.transactionId?.toString()}, used time: ${timeUsed.string()}`)
|
||||||
logger.info(`HieroClient.sendMessage used time: ${endTime.getTime() - startTime.getTime()}ms`)
|
|
||||||
return hieroTransaction.transactionId
|
return hieroTransaction.transactionId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,7 @@ export const GRADIDO_NODE_RUNTIME_PATH = path.join(
|
|||||||
)
|
)
|
||||||
// if last start was less than this time, do not restart
|
// 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_RESTART_MILLISECONDS = 1000 * 30
|
||||||
|
export const GRADIDO_NODE_MIN_RUNTIME_BEFORE_EXIT_MILLISECONDS = 1000 * 2
|
||||||
export const GRADIDO_NODE_KILL_TIMEOUT_MILLISECONDS = 10000
|
export const GRADIDO_NODE_KILL_TIMEOUT_MILLISECONDS = 10000
|
||||||
// currently hard coded in gradido node, update in future
|
// currently hard coded in gradido node, update in future
|
||||||
export const GRADIDO_NODE_HOME_FOLDER_NAME = '.gradido'
|
export const GRADIDO_NODE_HOME_FOLDER_NAME = '.gradido'
|
||||||
|
|||||||
@ -104,7 +104,7 @@ async function sendViaHiero(
|
|||||||
if (!transactionId) {
|
if (!transactionId) {
|
||||||
throw new Error('missing transaction id from hiero')
|
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(),
|
transactionId: transactionId.toString(),
|
||||||
})
|
})
|
||||||
return v.parse(hieroTransactionIdStringSchema, transactionId.toString())
|
return v.parse(hieroTransactionIdStringSchema, transactionId.toString())
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import { promisify } from 'node:util'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} time - in minutes
|
* @param {number} time - in minutes
|
||||||
*/
|
*/
|
||||||
@ -40,3 +42,5 @@ export const printTimeDuration = (duration: number): string => {
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const delay = promisify(setTimeout)
|
||||||
Loading…
x
Reference in New Issue
Block a user