feat(main): ignoring SIGTERM when managed by PM2
Some checks are pending
Build / Goreleaser (push) Waiting to run
Build / Upload artifacts-${{ matrix.os }}-${{ matrix.arch }} (amd64_v3, darwin) (push) Blocked by required conditions
Build / Upload artifacts-${{ matrix.os }}-${{ matrix.arch }} (amd64_v3, linux) (push) Blocked by required conditions
Build / Upload artifacts-${{ matrix.os }}-${{ matrix.arch }} (amd64_v3, windows) (push) Blocked by required conditions
Build / Upload artifacts-${{ matrix.os }}-${{ matrix.arch }} (arm64_v8.0, darwin) (push) Blocked by required conditions
Build / Upload artifacts-${{ matrix.os }}-${{ matrix.arch }} (arm64_v8.0, linux) (push) Blocked by required conditions
Build / Upload artifacts-${{ matrix.os }}-${{ matrix.arch }} (arm64_v8.0, windows) (push) Blocked by required conditions

- there is a race condition during shutting down when go-judge receives SIGTERM from systemd and PM2 dumps a STOPPED status, resulting go-judge not auto-restart after a reboot
- PM2 uses SIGINT for stop signal so that we can distinguish different stop signal sent by different process manager
- So when detected that we are manged by PM2, we ignore the SIGTERM signal from the systemd and wait for PM2's SIGINT to avoid race condition

Unitech/pm2#6036
This commit is contained in:
criyle 2025-10-28 16:27:13 +00:00
parent 565f302e52
commit bf22d6f479
2 changed files with 32 additions and 3 deletions

View File

@ -16,6 +16,7 @@ import (
"runtime"
"runtime/debug"
"strings"
"syscall"
"time"
"github.com/criyle/go-judge/cmd/go-judge/config"
@ -106,9 +107,21 @@ func main() {
newForceGCWorker(conf)
// Graceful shutdown...
signal.Notify(sig, os.Interrupt)
<-sig
signal.Reset(os.Interrupt)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
loop:
for s := range sig {
switch s {
case syscall.SIGINT:
break loop
case syscall.SIGTERM:
if isManagedByPM2() {
logger.Info("running with PM2, received SIGTERM (from systemd), ignoring")
} else {
break loop
}
}
}
signal.Reset(syscall.SIGINT, syscall.SIGTERM)
logger.Info("Shutting Down...")
@ -591,3 +604,18 @@ func generateHandleConfig(conf *config.Config, builderParam map[string]any) func
})
}
}
func isManagedByPM2() bool {
// List of environment variables that pm2 typically sets.
pm2EnvVars := []string{
"PM2_HOME",
"PM2_JSON_PROCESSING",
"NODE_APP_INSTANCE",
}
for _, v := range pm2EnvVars {
if os.Getenv(v) != "" {
return true
}
}
return false
}

View File

@ -76,6 +76,7 @@ func startTransientUnit(conn *dbus.Conn, scopeName string, logger *zap.Logger) e
dbus.PropWants(scopeName),
dbus.PropPids(uint32(os.Getpid())),
newSystemdProperty("Delegate", true),
newSystemdProperty("KillMode", "process"),
}
ch := make(chan string, 1)
if _, err := conn.StartTransientUnitContext(context.TODO(), scopeName, "replace", properties, ch); err != nil {