simplify communication on auto start chat process being launched

This commit is contained in:
geekgeekrun
2024-11-23 13:30:17 +08:00
parent 72e935643f
commit 803a3148b2
3 changed files with 23 additions and 81 deletions
@@ -11,6 +11,7 @@ import { initDb } from '@geekgeekrun/sqlite-plugin'
import { getPublicDbFilePath } from '@geekgeekrun/geek-auto-start-chat-with-boss/runtime-file-utils.mjs' import { getPublicDbFilePath } from '@geekgeekrun/geek-auto-start-chat-with-boss/runtime-file-utils.mjs'
import { AutoStartChatRunRecord } from '@geekgeekrun/sqlite-plugin/dist/entity/AutoStartChatRunRecord' import { AutoStartChatRunRecord } from '@geekgeekrun/sqlite-plugin/dist/entity/AutoStartChatRunRecord'
import minimist from 'minimist' import minimist from 'minimist'
import attachListenerForKillSelfOnParentExited from '../../utils/attachListenerForKillSelfOnParentExited'
const rerunInterval = (() => { const rerunInterval = (() => {
let v = Number(process.env.MAIN_BOSSGEEKGO_RERUN_INTERVAL) let v = Number(process.env.MAIN_BOSSGEEKGO_RERUN_INTERVAL)
@@ -20,7 +21,7 @@ const rerunInterval = (() => {
return v return v
})() })()
function runWithDaemon({ runRecordId, runMode }) { function runWithDaemon({ runRecordId, runMode, parentProcessPipe }) {
const subProcessOfCore = childProcess.spawn( const subProcessOfCore = childProcess.spawn(
process.argv[0], process.argv[0],
[...process.argv.slice(1), `--run-record-id=${runRecordId}`], [...process.argv.slice(1), `--run-record-id=${runRecordId}`],
@@ -36,11 +37,11 @@ function runWithDaemon({ runRecordId, runMode }) {
subProcessOfCore!.stdio[3]!.pipe(JSONStream.parse()).on('data', async (raw) => { subProcessOfCore!.stdio[3]!.pipe(JSONStream.parse()).on('data', async (raw) => {
const data = raw const data = raw
switch (data.type) { switch (data.type) {
case 'AUTO_START_CHAT_MAIN_PROCESS_STARTUP': { case 'GEEK_AUTO_START_CHAT_WITH_BOSS_STARTED': {
pipeWriteRegardlessError( pipeWriteRegardlessError(
subProcessOfCore!.stdio[3]! as WriteStream, parentProcessPipe as WriteStream,
JSON.stringify({ JSON.stringify({
type: 'GEEK_AUTO_START_CHAT_CAN_BE_RUN' type: data.type
}) })
) )
break break
@@ -67,23 +68,10 @@ function runWithDaemon({ runRecordId, runMode }) {
`[Run core daemon] Child process exit with code ${exitCode}, an internal error may not be caught, and will be restarted in ${rerunInterval}ms.` `[Run core daemon] Child process exit with code ${exitCode}, an internal error may not be caught, and will be restarted in ${rerunInterval}ms.`
) )
await sleep(rerunInterval) await sleep(rerunInterval)
runWithDaemon({ runRecordId, runMode }) runWithDaemon({ runRecordId, runMode, parentProcessPipe })
}) })
} }
// suicide timer for parent and child process don't have any communication after child process spawned.
let suicideTimer: NodeJS.Timeout | null = null
const setSuicideTimer = () =>
(suicideTimer = setTimeout(() => {
app.exit(AUTO_CHAT_ERROR_EXIT_CODE.AUTO_START_CHAT_DAEMON_PROCESS_SUICIDE)
}, 10000))
const clearSuicideTimer = () => {
if (suicideTimer) {
clearTimeout(suicideTimer)
}
suicideTimer = null
}
export async function runAutoChatWithDaemon() { export async function runAutoChatWithDaemon() {
const commandlineArgs = minimist(process.argv.slice(2)) const commandlineArgs = minimist(process.argv.slice(2))
if (!['geekAutoStartWithBossMain'].includes(commandlineArgs['mode-to-daemon'])) { if (!['geekAutoStartWithBossMain'].includes(commandlineArgs['mode-to-daemon'])) {
@@ -103,7 +91,6 @@ export async function runAutoChatWithDaemon() {
process.on('disconnect', () => { process.on('disconnect', () => {
app.exit() app.exit()
}) })
setSuicideTimer()
let pipe: null | fs.WriteStream = null let pipe: null | fs.WriteStream = null
try { try {
@@ -116,27 +103,21 @@ export async function runAutoChatWithDaemon() {
const disposePowerSaveBlocker = initPowerSaveBlocker() const disposePowerSaveBlocker = initPowerSaveBlocker()
app.once('quit', disposePowerSaveBlocker) app.once('quit', disposePowerSaveBlocker)
const pipeForRead: fs.ReadStream = fs.createReadStream(null, { fd: 3 })
const pipeForReadWithJsonParser = pipeForRead.pipe(JSONStream.parse())
pipeForReadWithJsonParser?.on('data', async function waitForCanRun(data) {
if (data.type === 'GEEK_AUTO_START_CHAT_CAN_BE_RUN') {
pipeForReadWithJsonParser.off('data', waitForCanRun)
clearSuicideTimer()
// if don't call close, when kill child process, child process will ANR.
pipeForRead.close()
const ds = await initDb(getPublicDbFilePath())
const autoStartChatRunRecord = new AutoStartChatRunRecord()
autoStartChatRunRecord.date = new Date()
const autoStartChatRunRecordRepository = ds.getRepository(AutoStartChatRunRecord)
const result = await autoStartChatRunRecordRepository.save(autoStartChatRunRecord)
runWithDaemon({ runRecordId: result.id, runMode })
}
})
process.on('SIGINT', () => { process.on('SIGINT', () => {
process.exit() process.exit()
}) })
const ds = await initDb(getPublicDbFilePath())
const autoStartChatRunRecord = new AutoStartChatRunRecord()
autoStartChatRunRecord.date = new Date()
const autoStartChatRunRecordRepository = ds.getRepository(AutoStartChatRunRecord)
const result = await autoStartChatRunRecordRepository.save(autoStartChatRunRecord)
runWithDaemon({
runRecordId: result.id,
runMode: commandlineArgs['mode-to-daemon'],
parentProcessPipe: pipe
})
pipeWriteRegardlessError( pipeWriteRegardlessError(
pipe, pipe,
JSON.stringify({ JSON.stringify({
@@ -146,3 +127,5 @@ export async function runAutoChatWithDaemon() {
gtag('run_auto_chat_with_boss_daemon_ready') gtag('run_auto_chat_with_boss_daemon_ready')
} }
attachListenerForKillSelfOnParentExited()
@@ -11,7 +11,7 @@ import { pipeWriteRegardlessError } from '../utils/pipe'
import { getAnyAvailablePuppeteerExecutable } from '../CHECK_AND_DOWNLOAD_DEPENDENCIES/utils/puppeteer-executable' import { getAnyAvailablePuppeteerExecutable } from '../CHECK_AND_DOWNLOAD_DEPENDENCIES/utils/puppeteer-executable'
import { sleep } from '@geekgeekrun/utils/sleep.mjs' import { sleep } from '@geekgeekrun/utils/sleep.mjs'
import { AUTO_CHAT_ERROR_EXIT_CODE } from '../../../common/enums/auto-start-chat' import { AUTO_CHAT_ERROR_EXIT_CODE } from '../../../common/enums/auto-start-chat'
import * as JSONStream from 'JSONStream' import attachListenerForKillSelfOnParentExited from '../../utils/attachListenerForKillSelfOnParentExited'
import SqlitePluginModule from '@geekgeekrun/sqlite-plugin' import SqlitePluginModule from '@geekgeekrun/sqlite-plugin'
import gtag from '../../utils/gtag' import gtag from '../../utils/gtag'
@@ -140,38 +140,8 @@ const runAutoChat = async () => {
} }
} }
} }
// suicide timer for parent and child process don't have any communication after child process spawned.
let suicideTimer: NodeJS.Timeout | null = null
const setSuicideTimer = () =>
(suicideTimer = setTimeout(() => {
app.exit(AUTO_CHAT_ERROR_EXIT_CODE.AUTO_START_CHAT_MAIN_PROCESS_SUICIDE)
}, 10000))
const clearSuicideTimer = () => {
if (suicideTimer) {
clearTimeout(suicideTimer)
}
suicideTimer = null
}
export const waitForProcessHandShakeAndRunAutoChat = () => { export const waitForProcessHandShakeAndRunAutoChat = () => {
setSuicideTimer()
const pipeForRead: fs.ReadStream = fs.createReadStream(null, { fd: 3 })
pipeForRead.on('error', () => {
return
})
const pipeForReadWithJsonParser = pipeForRead.pipe(JSONStream.parse())
pipeForReadWithJsonParser?.on('data', function waitForCanRun(data) {
if (data.type === 'GEEK_AUTO_START_CHAT_CAN_BE_RUN') {
pipeForReadWithJsonParser.off('data', waitForCanRun)
clearSuicideTimer()
runAutoChat()
// if don't call close, when kill child process, child process will ANR.
pipeForRead.close()
}
})
let pipe: null | fs.WriteStream = null let pipe: null | fs.WriteStream = null
try { try {
pipe = fs.createWriteStream(null, { fd: 3 }) pipe = fs.createWriteStream(null, { fd: 3 })
@@ -179,10 +149,7 @@ export const waitForProcessHandShakeAndRunAutoChat = () => {
console.error('pipe is not available') console.error('pipe is not available')
app.exit(1) app.exit(1)
} }
pipeWriteRegardlessError( runAutoChat()
pipe,
JSON.stringify({
type: 'AUTO_START_CHAT_MAIN_PROCESS_STARTUP'
})
)
} }
attachListenerForKillSelfOnParentExited()
@@ -117,14 +117,6 @@ export default function initIpc() {
subProcessOfPuppeteer!.stdio[3]!.pipe(JSONStream.parse()).on('data', async (raw) => { subProcessOfPuppeteer!.stdio[3]!.pipe(JSONStream.parse()).on('data', async (raw) => {
const data = raw const data = raw
switch (data.type) { switch (data.type) {
case 'AUTO_START_CHAT_DAEMON_PROCESS_STARTUP': {
subProcessOfPuppeteer!.stdio[3]!.write(
JSON.stringify({
type: 'GEEK_AUTO_START_CHAT_CAN_BE_RUN'
})
)
break
}
case 'GEEK_AUTO_START_CHAT_WITH_BOSS_STARTED': { case 'GEEK_AUTO_START_CHAT_WITH_BOSS_STARTED': {
resolve(data) resolve(data)
break break