Skip to content

Commit 8fd0257

Browse files
authored
fix: release cluster membership events on routing table convergence (#1332)
1 parent 320ad1b commit 8fd0257

9 files changed

Lines changed: 935 additions & 485 deletions

File tree

changelogs/unreleased.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
11
# Unreleased
2+
3+
## 🔧 Fixes
4+
5+
- **Cluster membership events follow routing table convergence** ([#1331](https://github.com/Tochemey/goakt/issues/1331)). `NodeLeft` and `NodeJoined` are now published once the cluster's routing table has converged on the membership change, using the member set olric announces with each convergence, instead of waiting for the single rebalance epoch started for the change. A departure whose epoch was superseded by a later routing table push used to surface only through the 30s fallback timer, and a join in the same situation could go unreported until the next join. A node that joins and departs, or departs and restarts, before the table converges is announced in the order the converged member set implies. A member that departs, restarts at the same address and departs again is announced each time, where the second departure was previously lost. A join whose convergence never comes is announced after the same 30s bound as a departure, and that wait is cancelled as soon as the event is announced or the actor system stops.

docs/actor/relocation.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ other nodes so the cluster remains available.
2020
Relocation is triggered when:
2121

2222
1. **A node leaves the cluster**: The cluster membership layer (Hashicorp Memberlist) detects the departure and emits a
23-
`NodeLeft` event.
23+
`NodeLeft` event. The event is published once the cluster's routing table has converged on the departure, bounded by
24+
a 30 second fallback, so relocation runs against a table that no longer routes to the departed node.
2425
2. **The departed node had relocatable actors or grains**: Only actors spawned with relocation enabled (the default) and
2526
grains without `WithGrainDisableRelocation` are eligible.
2627

docs/advanced/event-streams.mdx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,10 @@ that reads from the channel.
9393
- The event stream is backed by the `eventstream` package. The actor system creates an `EventsStream` at startup and
9494
subscribes callers to the internal topic `topic.events`.
9595
- Lifecycle events (ActorStarted, ActorStopped, etc.) are published by the PID and death-watch logic. Cluster events
96-
(NodeJoined, NodeLeft) are published by the cluster events loop when membership changes. `LeaderChanged` is published
96+
(NodeJoined, NodeLeft) are published by the cluster events loop once the cluster's routing table has converged on the
97+
membership change, that is once the coordinator has redistributed partitions for the new member set, so subscribers
98+
and relocation act on a stable table. If no convergence is announced within 30 seconds, for example because a member
99+
fails to acknowledge the rebalance, the event is published anyway. `LeaderChanged` is published
97100
from the same loop, on every node, whenever a topology event reveals that the coordinator moved to a different node;
98101
the initial coordinator election on startup is not published, only subsequent transitions.
99102
- `RelocationFailed` is published by the relocator when a departed node's actors or grains cannot be re-deployed. Each

go.mod

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ require (
1515
github.com/hashicorp/go-sockaddr v1.0.7
1616
github.com/hashicorp/memberlist v0.6.0
1717
github.com/kapetan-io/tackle v0.15.0
18-
github.com/klauspost/compress v1.19.2
18+
github.com/klauspost/compress v1.20.0
1919
github.com/nats-io/nats-server/v2 v2.14.6
2020
github.com/nats-io/nats.go v1.53.1
2121
github.com/redis/go-redis/v9 v9.22.0
@@ -24,7 +24,7 @@ require (
2424
github.com/testcontainers/testcontainers-go v0.44.0
2525
github.com/testcontainers/testcontainers-go/modules/consul v0.44.0
2626
github.com/testcontainers/testcontainers-go/modules/etcd v0.44.0
27-
github.com/tochemey/olric v0.3.18
27+
github.com/tochemey/olric v0.3.19
2828
github.com/zeebo/xxh3 v1.1.0
2929
go.etcd.io/bbolt v1.5.0
3030
go.etcd.io/etcd/api/v3 v3.7.1
@@ -48,7 +48,7 @@ require (
4848
dario.cat/mergo v1.0.2 // indirect
4949
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
5050
github.com/Microsoft/go-winio v0.6.2 // indirect
51-
github.com/RoaringBitmap/roaring/v2 v2.26.0 // indirect
51+
github.com/RoaringBitmap/roaring/v2 v2.27.0 // indirect
5252
github.com/antithesishq/antithesis-sdk-go v0.8.0 // indirect
5353
github.com/armon/go-metrics v0.4.1 // indirect
5454
github.com/bits-and-blooms/bitset v1.25.0 // indirect
@@ -158,7 +158,7 @@ require (
158158
go.yaml.in/yaml/v2 v2.4.4 // indirect
159159
go.yaml.in/yaml/v3 v3.0.5 // indirect
160160
golang.org/x/arch v0.30.0 // indirect
161-
golang.org/x/crypto v0.55.0 // indirect
161+
golang.org/x/crypto v0.56.0 // indirect
162162
golang.org/x/exp v0.0.0-20260824195058-e88cd73687aa // indirect
163163
golang.org/x/oauth2 v0.36.0 // indirect
164164
golang.org/x/term v0.45.0 // indirect

go.sum

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg6
77
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
88
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
99
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
10-
github.com/RoaringBitmap/roaring/v2 v2.26.0 h1:K30ZxF4vZcIKvJsbmgfiep2K64f+dILJqkYGoj4xnwU=
11-
github.com/RoaringBitmap/roaring/v2 v2.26.0/go.mod h1:BZufmFbox589n3j5eOmyTaLSGXbRLc2LmQvjKjzSEGU=
10+
github.com/RoaringBitmap/roaring/v2 v2.27.0 h1:rTzs+cuH9YBj6j4L9GF2DcK2NmOfdUvLI+ZFhyLy8g8=
11+
github.com/RoaringBitmap/roaring/v2 v2.27.0/go.mod h1:BZufmFbox589n3j5eOmyTaLSGXbRLc2LmQvjKjzSEGU=
1212
github.com/Workiva/go-datastructures v1.1.7 h1:q5RXlAeKm3zDpZTbYXwdMb1gN9RtGSvOCtPXGJJL6Cs=
1313
github.com/Workiva/go-datastructures v1.1.7/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A=
1414
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -179,7 +179,6 @@ github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJ
179179
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
180180
github.com/hashicorp/go-metrics v0.6.1 h1:V7j9cgHTXl4OebnX3y0l6ZSoO5dTp0VI0K8Y7JfRsS4=
181181
github.com/hashicorp/go-metrics v0.6.1/go.mod h1:XOozbQKeJz12GG8cCcQb/E5vNVeTdJt0aHbM+uHnL1M=
182-
github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
183182
github.com/hashicorp/go-msgpack/v2 v2.1.5 h1:Ue879bPnutj/hXfmUk6s/jtIK90XxgiUIcXRl656T44=
184183
github.com/hashicorp/go-msgpack/v2 v2.1.5/go.mod h1:bjCsRXpZ7NsJdk45PoCQnzRGDaK8TKm5ZnDI/9y3J4M=
185184
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
@@ -210,8 +209,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
210209
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
211210
github.com/kapetan-io/tackle v0.15.0 h1:J+D04RuxEKtybzCjuzgcmQuFBiEpYa+5vPU2mAgAxTs=
212211
github.com/kapetan-io/tackle v0.15.0/go.mod h1:pDr4mjpo2RQO/q/je1dGuGwnBVwZcsRp60wgDV2hA3c=
213-
github.com/klauspost/compress v1.19.2 h1:hMRETovs/pu/dVWN7zIT1PGG8t509MwT6bO7XSi26R8=
214-
github.com/klauspost/compress v1.19.2/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
212+
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
213+
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
215214
github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw=
216215
github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU=
217216
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -362,8 +361,8 @@ github.com/tklauser/go-sysconf v0.4.0 h1:7H0uAN+7RkwWRaxhYXDLqa5V3LPrJeV8wmD9dRU
362361
github.com/tklauser/go-sysconf v0.4.0/go.mod h1:8mTNWyog7H+MpKijp4VmKJAd2bbYQ2zuUwkYRbUArPI=
363362
github.com/tklauser/numcpus v0.12.0 h1:NR85qdvHA9pFse3x3weVZ0r0ST8R6l5RHbZrlRaqob4=
364363
github.com/tklauser/numcpus v0.12.0/go.mod h1:ABHeXzJnr/qqwguhClkZKT1/8VABcYrsyUiUGobwWJg=
365-
github.com/tochemey/olric v0.3.18 h1:i/1llTZCXqomeS257/xdIXDlDRH0w0yBuZsKiVjEzZY=
366-
github.com/tochemey/olric v0.3.18/go.mod h1:U+htGNuLQboPKwnSPFOOSao7gM+YgL41vgXoE1HLoLI=
364+
github.com/tochemey/olric v0.3.19 h1:aJgHzYXaX0/77mzndF+ut8YI2jgn2oAn+omma1s7bG4=
365+
github.com/tochemey/olric v0.3.19/go.mod h1:Ok8mMnA1v3lZr3QZMXBKoD+ETqI/N/ZQ6x+mgn8qock=
367366
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q=
368367
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
369368
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
@@ -425,8 +424,8 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
425424
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
426425
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
427426
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
428-
golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M=
429-
golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis=
427+
golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y=
428+
golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I=
430429
golang.org/x/exp v0.0.0-20260824195058-e88cd73687aa h1:QSyA8ishJCyT21kER9KwNt0b7BM3iRK4x9QXhjN5Fdk=
431430
golang.org/x/exp v0.0.0-20260824195058-e88cd73687aa/go.mod h1:zeBbvyFKDaLwa7CH/zI8KXt7gTl14SF7sO08Pl5jBCM=
432431
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=

0 commit comments

Comments
 (0)