Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -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
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
14 changes: 11 additions & 3 deletions internal/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package internal

import (
"encoding/json"
"fmt"
"os"
"sync"

Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 {
Expand Down
27 changes: 27 additions & 0 deletions internal/protocol_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
Comment on lines +3 to +27
Loading