From facadf4167250e984aabff33f2b9ee93d3bb4658 Mon Sep 17 00:00:00 2001 From: ignoxx Date: Mon, 19 Jan 2026 19:59:16 +0100 Subject: [PATCH 1/3] fix: allow Child() to accept relative 'kind/id' path Previously, ctx.Child() required the full PID.ID to find a child, which was counter-intuitive. This change allows ctx.Child('kind/id') to work as expected while maintaining backward compatibility with full PID.ID lookups. --- actor/context.go | 9 ++++++++- actor/context_test.go | 5 +++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/actor/context.go b/actor/context.go index 4f6a760f..78576866 100644 --- a/actor/context.go +++ b/actor/context.go @@ -139,7 +139,14 @@ func (c *Context) Parent() *PID { // Child will return the PID of the child (if any) by the given name/id. // PID will be nil if it could not find it. func (c *Context) Child(id string) *PID { - pid, _ := c.children.Get(id) + // First try exact match (backward compatibility) + if pid, ok := c.children.Get(id); ok { + return pid + } + + // Then try with parent prefix + fullKey := c.pid.ID + pidSeparator + id + pid, _ := c.children.Get(fullKey) return pid } diff --git a/actor/context_test.go b/actor/context_test.go index 3f6ffe9c..5b0efc74 100644 --- a/actor/context_test.go +++ b/actor/context_test.go @@ -66,6 +66,11 @@ func TestSpawnChildPID(t *testing.T) { case Started: pid := c.SpawnChildFunc(childfn, "child", WithID("1")) assert.True(t, expectedPID.Equals(pid)) + + pid = c.Child("child/1") + assert.NotNil(t, pid) + assert.True(t, expectedPID.Equals(pid)) + wg.Done() case Stopped: } From 8de2c41cd5ffd8ef74fdc04e2dbdeb2dc7268914 Mon Sep 17 00:00:00 2001 From: ignoxx Date: Tue, 20 Jan 2026 12:22:07 +0100 Subject: [PATCH 2/3] add: test cluster child lookup --- cluster/cluster_test.go | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/cluster/cluster_test.go b/cluster/cluster_test.go index 20a8c06d..e4332de1 100644 --- a/cluster/cluster_test.go +++ b/cluster/cluster_test.go @@ -410,6 +410,41 @@ func TestCannotDuplicateActor(t *testing.T) { c2.Stop() } +func TestClusterChildLookup(t *testing.T) { + var ( + c1Addr = getRandomLocalhostAddr() + c1 = makeCluster(t, c1Addr, "A", "eu-west") + wg = sync.WaitGroup{} + ) + + wg.Add(1) + c1.engine.SpawnFunc(func(c *actor.Context) { + switch c.Message().(type) { + case actor.Started: + childPID := c.SpawnChildFunc(func(_ *actor.Context) {}, "worker", actor.WithID("1")) + + // Test relative path lookup (new behavior) + found := c.Child("worker/1") + assert.NotNil(t, found) + assert.True(t, childPID.Equals(found)) + + // Test full path lookup (backward compatible) + fullPath := c.PID().ID + "/worker/1" + foundFull := c.Child(fullPath) + assert.NotNil(t, foundFull) + assert.True(t, childPID.Equals(foundFull)) + + assert.Equal(t, c1Addr, childPID.Address) + + wg.Done() + } + }, "parent", actor.WithID("1")) + + c1.Start() + wg.Wait() + c1.Stop() +} + func makeCluster(t *testing.T, addr, id, region string) *Cluster { config := NewConfig(). WithID(id). From 146d0c9774ef5f59aec3f4a77b0a53f16a18bae4 Mon Sep 17 00:00:00 2001 From: ignoxx Date: Tue, 20 Jan 2026 12:22:26 +0100 Subject: [PATCH 3/3] update: doc --- actor/context.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/actor/context.go b/actor/context.go index 78576866..a6bb5b83 100644 --- a/actor/context.go +++ b/actor/context.go @@ -136,8 +136,9 @@ func (c *Context) Parent() *PID { return nil } -// Child will return the PID of the child (if any) by the given name/id. -// PID will be nil if it could not find it. +// Child will return the PID of the child (if any) by the given id. +// The id can be a relative path (e.g., "child/1") or a full path (e.g., "parent/1/child/1"). +// Returns nil if the child could not be found. func (c *Context) Child(id string) *PID { // First try exact match (backward compatibility) if pid, ok := c.children.Get(id); ok {