enh: process killing enhancements

This commit is contained in:
Ben Allfree 2023-06-18 06:57:06 -07:00
parent 63b046a1e8
commit 671a69f943
2 changed files with 64 additions and 19 deletions

View File

@ -159,8 +159,8 @@ export const instanceService = mkSingleton(
) )
} }
})() })()
shutdownManager.add(() => { shutdownManager.add(async () => {
const res = childProcess.kill() const res = await childProcess.kill()
if (!res) { if (!res) {
error(`Expected child process to exit gracefully but got ${res}`) error(`Expected child process to exit gracefully but got ${res}`)
} }
@ -176,13 +176,6 @@ export const instanceService = mkSingleton(
assertTruthy(pid, `Expected PID here but got ${pid}`) assertTruthy(pid, `Expected PID here but got ${pid}`)
dbg(`PocketBase instance PID: ${pid}`) dbg(`PocketBase instance PID: ${pid}`)
if (!instance.isBackupAllowed) {
dbg(`Backups are now allowed`)
await clientLimiter.schedule(() =>
client.updateInstance(instance.id, { isBackupAllowed: true })
)
}
const invocation = await clientLimiter.schedule(() => const invocation = await clientLimiter.schedule(() =>
client.createInvocation(instance, pid) client.createInvocation(instance, pid)
) )

View File

@ -31,7 +31,11 @@ export type SpawnConfig = {
version?: string version?: string
port?: number port?: number
isMothership?: boolean isMothership?: boolean
onUnexpectedStop?: (code: number | null) => void onUnexpectedStop?: (
code: number | null,
stdout: string[],
stderr: string[]
) => void
} }
export type PocketbaseServiceApi = AsyncReturnType< export type PocketbaseServiceApi = AsyncReturnType<
typeof createPocketbaseService typeof createPocketbaseService
@ -45,7 +49,7 @@ export type PocketbaseServiceConfig = SingletonBaseConfig & {
export type PocketbaseProcess = { export type PocketbaseProcess = {
url: string url: string
pid: number | undefined pid: number | undefined
kill: () => boolean kill: () => Promise<boolean>
exited: Promise<number | null> exited: Promise<number | null>
} }
@ -59,6 +63,15 @@ export type Release = {
} }
export type Releases = Release[] export type Releases = Release[]
function pidIsRunning(pid: number) {
try {
process.kill(pid, 0)
return true
} catch (e) {
return false
}
}
export const createPocketbaseService = async ( export const createPocketbaseService = async (
config: PocketbaseServiceConfig config: PocketbaseServiceConfig
) => { ) => {
@ -184,14 +197,22 @@ export const createPocketbaseService = async (
dbg(`Spawning ${slug}`, { bin, args, cli: [bin, ...args].join(' ') }) dbg(`Spawning ${slug}`, { bin, args, cli: [bin, ...args].join(' ') })
const ls = spawn(bin, args) const ls = spawn(bin, args)
cm.add(() => ls.kill()) cm.add(() => {
ls.kill()
ls.stdout.on('data', (data) => {
dbg(`${slug} stdout: ${data}`)
}) })
ls.stderr.on('data', (data) => { const stdout: string[] = []
error(`${slug} stderr: ${data}`) ls.stdout.on('data', (data: Buffer) => {
dbg(`${slug} stdout: ${data}`)
stdout.push(data.toString())
if (stdout.length > 100) stdout.pop()
})
const stderr: string[] = []
ls.stderr.on('data', (data: Buffer) => {
warn(`${slug} stderr: ${data}`)
stderr.push(data.toString())
if (stderr.length > 100) stderr.pop()
}) })
ls.on('close', (code) => { ls.on('close', (code) => {
@ -202,7 +223,9 @@ export const createPocketbaseService = async (
ls.on('exit', (code) => { ls.on('exit', (code) => {
dbg(`${slug} exited with code ${code}`) dbg(`${slug} exited with code ${code}`)
isRunning = false isRunning = false
if (code) onUnexpectedStop?.(code) if (code || stderr.length > 0) {
onUnexpectedStop?.(code, stdout, stderr)
}
resolve(code) resolve(code)
}) })
}) })
@ -219,7 +242,36 @@ export const createPocketbaseService = async (
url, url,
exited, exited,
pid: ls.pid, pid: ls.pid,
kill: () => ls.kill(), kill: async () => {
const { pid } = ls
if (!pid) {
throw new Error(
`Attempt to kill a PocketBase process that was never running.`
)
}
const p = new Promise<boolean>((resolve, reject) => {
let cid: NodeJS.Timeout
const tid = setTimeout(() => {
clearTimeout(cid)
reject(new Error(`Timeout waiting for pid:${pid} to die`))
}, 1000)
const _check = () => {
dbg(`Checking to see if pid:${pid} is running`)
if (pidIsRunning(pid)) {
dbg(`pid:${pid} is still running`)
ls.kill()
cid = setTimeout(_check, 50)
} else {
dbg(`pid:${pid} is not running`)
clearTimeout(tid)
resolve(true)
}
}
_check()
})
return p
},
} }
return api return api
} }