diff --git a/go.mod b/go.mod index 2304849..5a7c823 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/roadrunner-server/goridge/v4 v4.0.0-beta.2 github.com/shirou/gopsutil v3.21.11+incompatible github.com/stretchr/testify v1.11.1 - golang.org/x/sync v0.20.0 + golang.org/x/sync v0.21.0 ) require ( @@ -20,6 +20,6 @@ require ( github.com/tklauser/go-sysconf v0.4.0 // indirect github.com/tklauser/numcpus v0.12.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect - golang.org/x/sys v0.45.0 // indirect + golang.org/x/sys v0.46.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 82fc72a..c05f1c4 100644 --- a/go.sum +++ b/go.sum @@ -32,10 +32,14 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= +golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= +golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/protocol.go b/internal/protocol.go index bbc5186..6f66b87 100755 --- a/internal/protocol.go +++ b/internal/protocol.go @@ -2,6 +2,7 @@ package internal import ( "encoding/json" + "fmt" "os" "sync" @@ -56,6 +57,13 @@ func SendControl(rl relay.Relay, payload any) error { return nil } +// legacyHint annotates a failed pid exchange with its two likely causes — the +// worker crashed on startup, or it speaks the legacy goridge v3 protocol — so +// operators get an actionable message instead of a bare "Network: EOF". +func legacyHint(err error) error { + return fmt.Errorf("worker pid handshake failed: %w; the worker exited or replied with an unrecognized frame — check the worker logs for startup errors, and if the worker uses an SDK speaking the legacy goridge v3 protocol (e.g. spiral/roadrunner-worker v3.x for PHP), upgrade it to a goridge v4-compatible release", err) +} + func Pid(rl relay.Relay) (int64, error) { err := SendControl(rl, pidCommand{Pid: os.Getpid()}) if err != nil { @@ -67,7 +75,7 @@ func Pid(rl relay.Relay) (int64, error) { err = rl.Receive(fr) if err != nil { - return 0, err + return 0, legacyHint(err) } if fr == nil { @@ -77,13 +85,13 @@ func Pid(rl relay.Relay) (int64, error) { flags := fr.ReadFlags() if flags&frame.CONTROL == 0 { - return 0, errors.Str("unexpected response, header is missing, no CONTROL flag") + return 0, legacyHint(errors.Str("unexpected response, header is missing, no CONTROL flag")) } link := &pidCommand{} err = json.Unmarshal(fr.Payload(), link) if err != nil { - return 0, err + return 0, legacyHint(err) } if link.Pid <= 0 { diff --git a/internal/protocol_test.go b/internal/protocol_test.go new file mode 100644 index 0000000..5b1b7b8 --- /dev/null +++ b/internal/protocol_test.go @@ -0,0 +1,27 @@ +package internal + +import ( + "io" + "strings" + "testing" + + "github.com/roadrunner-server/goridge/v4/pkg/frame" +) + +// eofRelay accepts the outgoing control frame and then fails the read, +// mimicking a worker that exited on an unparseable (legacy-protocol) frame. +type eofRelay struct{} + +func (eofRelay) Send(*frame.Frame) error { return nil } +func (eofRelay) Receive(*frame.Frame) error { return io.EOF } +func (eofRelay) Close() error { return nil } + +func TestPidHandshakeHintsLegacyWorker(t *testing.T) { + _, err := Pid(eofRelay{}) + if err == nil { + t.Fatal("expected the pid handshake to fail") + } + if !strings.Contains(err.Error(), "goridge v3") { + t.Fatalf("handshake error should hint at the legacy protocol, got: %v", err) + } +}