0

I have long running go application, that I want to run from a node process.

package main

import (
    "fmt"
    "net/http"
)

func main() {
    fmt.Println("Running")
    http.ListenAndServe(":80", nil)
}

I set up my child process in node like:

async function application(){

    //const myProcess = exec("go run main.go"); 
    const myProcess = exec("./main"); 

    myProcess.stdout.on('data', (chunk) => {
        console.log(chunk);
    })
    
    return new Promise(res => {
        setTimeout(() => {
            myProcess.on("close", () => {
                console.log("close")
                res(); 
                
            }); 
            myProcess.on("exit", () => {
                console.log("exit")
            })

            myProcess.kill();
        }, 1000); 
    
    })
}

This all works fine if I'm running the compiled binary directly (./main).

I'll get an output of:

Running

exit
close
(process exits)

However if I try run with go run main.go my output is

Running

exit
(process continues)

Specifically what is happening here that the go run process will not close properly?

My understanding is that this is going to come down to the difference between 'close' and 'exit' events.

From the documentation:

The 'close' event is emitted after a process has ended and the stdio streams of a child process have been closed. This is distinct from the 'exit' event, since multiple processes might share the same stdio streams. The 'close' event will always emit after 'exit' was already emitted, or 'error' if the child failed to spawn.

Ok, so maybe something about go run leaving a stdio stream open? That's where my understanding is unclear.

For the problem I'm solving, just using the binary will work fine.

But I'm curious, if I did want to use the go run process - how would I close it properly?

Repro for this here: GitHub.

dwjohnston
  • 11,163
  • 32
  • 99
  • 194
  • This delves into the realms of process supervision, as `go run` appears to [not play nicely](https://stackoverflow.com/questions/54013010/how-do-i-send-a-signal-to-a-child-process). What operating systems do you want this to work on? – Matt Apr 19 '23 at 08:00
  • Ultimately macos and linux. (The real world use case is that I want to run a server from my node process and run tests against it, this would want to occur on both devs machines, as well as build servers). It's nice to know that this approach might encounter OS specific issues. – dwjohnston Apr 19 '23 at 08:02
  • @Matt But yeah, I guess I would like the high level overview of process supervision, I would have hoped that a child process can't spawn processes that makes its parent can't control. – dwjohnston Apr 19 '23 at 08:05

1 Answers1

2

The go run command builds the executable and then runs the executable in the background waiting for it. Which means that the process ID of go run is different than the PID of the executable.

You can use the -exec to tell go run to run another program (not go). For example, you can write run.sh

#!/bin/bash

# Run executable in background
$1&

# $! is child pid
echo PID: $!

# Wait for child to end
wait $!

And then execute go run -exec /path/to/run.sh main.go Tried this with the following main.go: package main

import (
    "fmt"
    "os"
)

func main() {
    fmt.Println(os.Getpid())
}

And got:

$ go run -exec /tmp/run.sh /tmp/main.go
PID: 13679
13679

In your node code you'll have to parse the output to get the PID.

Miki Tebeka
  • 13,428
  • 4
  • 37
  • 49
  • Any suggestions for, in the abstract, if you are creating a process from node, and that process may create a background process like we have in the initial question, how you could shut them all down from the parent process? Or is this simply a 'that's not possible scenario'? – dwjohnston Apr 19 '23 at 23:25
  • You can build to a temporary file and then run it instead of `go run`. It's one more command to execute but makes things simpler (that's when I do in tests). You might want to try and kill the `go run` process group ID, not sure if it'll work. See https://stackoverflow.com/questions/392022/whats-the-best-way-to-send-a-signal-to-all-members-of-a-process-group – Miki Tebeka Apr 20 '23 at 07:44