You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
| Planned |[#8324](https://github.com/akkadotnet/akka.net/pull/8324)|`Akka.Routing`| Behavior |`ConsistentHash<T>` no longer retains the `SortedDictionary` passed to its public constructor — the ring is snapshotted into internal sorted arrays. Mutating that dictionary after construction no longer affects the instance (previously the aliasing was inconsistent: `IsEmpty` and `operator +`/`-` read it live, while `NodeFor` froze it after the first lookup). A `null` dictionary now throws `ArgumentNullException` from the constructor instead of surfacing later as a `NullReferenceException`. No public API removed; the ring built by `ConsistentHash.Create` is byte-identical. (#8293) | None for normal use — `ConsistentHash.Create` already builds the dictionary fully before constructing, so routers/receptionists are unaffected. If you call the `ConsistentHash(SortedDictionary, int)` constructor directly, populate the dictionary before passing it and don't rely on post-construction mutation being visible. |
44
44
| Planned |`feature/default-bounded-shard-rebalancing`|`Akka.Cluster.Sharding`| Behavior | The default `rebalance-absolute-limit` is now `20`, selecting the bounded shard allocation strategy instead of the legacy threshold-based strategy. | To retain the legacy strategy temporarily, explicitly set `akka.cluster.sharding.least-shard-allocation-strategy.rebalance-absolute-limit = 0`. Review `rebalance-threshold` and `max-simultaneous-rebalance`, which do not apply while the bounded strategy is active. |
45
+
| Planned | `fix/artery-inbound-quarantine-check` | `Akka.Remote` (Artery) | Behavior | Quarantine is now enforced on the INBOUND path too. Previously Artery only gated outbound sends -- an envelope arriving FROM a uid this system has quarantined was still delivered, and the quarantined peer was only notified once, proactively, at the moment `Quarantine()` was called. A new `InboundQuarantineCheckStage`, woven into the inbound pipeline right after handshake, now drops every inbound envelope (ordinary or control, including system messages) whose origin uid is quarantined, and reactively re-sends a `Quarantined` control notice to the origin for each drop (except for a heartbeat or the peer's own `Quarantined` notice, to avoid a reply storm). The existing one-shot proactive notice in `Quarantine()` is unchanged. Additionally, an ordinary/large outbound stream that terminates while its association is quarantined no longer wedges permanently: the materialize-once gate is released (timer-driven auto-reconnect stays suppressed), so a quarantine-piercing `ActorSelection` send -- or any send after a new incarnation's handshake lifts the quarantine -- re-materializes the stream on demand and can reach a restarted peer at the same address. | No action required -- this is a bug fix restoring the documented "no further communication" guarantee of quarantine and the documented new-incarnation piercing behavior; code that (incorrectly) depended on a quarantined peer's replies still arriving is unsupported. |
45
46
| Planned | `fix/artery-daemonmsgcreate-control-stream` | `Akka.Remote` (Artery) | Behavior | Remote deployment's `DaemonMsgCreate` now travels over Artery's CONTROL stream (as a plain envelope, no delivery/ack sequencing) instead of the ordinary stream, ordering it ahead of the `Watch` that remote deployment sends immediately afterwards. Previously the two rode independent, unordered TCP connections and `Watch` systematically arrived first, so the receiver replied `DeathWatchNotification(existenceConfirmed: false)` for a not-yet-created actor and the deployer reaped the freshly-deployed routee before its `Supervise` registration landed, emptying cluster router pools. Additionally (Pekko parity), inbound ordinary messages addressed to a remote-deployed recipient that has not been created yet are no longer dead-lettered immediately: the resolve is retried on a bounded schedule (20 attempts x 50ms, buffered per recipient in FIFO order) so first messages that arrive ahead of the in-flight `DaemonMsgCreate` are delivered once the actor exists; paths that never resolve are banned and dead-letter as before. | No action required -- this is a bug fix restoring correct create-before-watch ordering; code that (incorrectly) depended on the old race is unsupported. |
46
47
| Planned |`feature/artery-test-mode`|`Akka.Remote`| Behavior | System UID generation (`AddressUidExtension` / `AddressUid`) now uses a cryptographic RNG instead of the `Environment.TickCount`-seeded `ThreadLocalRandom`. Previously, multiple processes started within the same millisecond tick (e.g. every node of a multi-node test spawning at once) could draw IDENTICAL system UIDs, silently corrupting Artery's uid-keyed identity (handshakes, quarantine, association reverse index). UIDs remain nonzero and in the legacy `[1, int.MaxValue]` range at the default (`use-64bit-system-uids = off`). | Nothing required -- UIDs were always documented as random; only code depending on the (buggy) time-seeded determinism could observe a difference. |
47
48
| Planned | `feature/artery-test-mode` | `Akka.Remote` (Artery) / `Akka.Remote.TestKit` | Behavior | New `akka.remote.artery.advanced.test-mode` setting (default `off`, Pekko key parity) enables failure-injection test stages in Artery's stream pipelines, and `ArteryRemoting.ManagementCommand` now honors `SetThrottle` with `Blackhole`/`Unthrottled` (returning `true`; previously always `false`) so TestConductor `Blackhole`/`PassThrough` work over Artery. Rate throttles (`TokenBucket`) and `ForceDisassociate` remain unsupported on Artery (still `false`, matching Pekko). `MultiNodeConfig.TestTransport = true` now also sets Artery test-mode on, and the TestKit `Player` now treats a `false` `ManagementCommand` result as a failure (previously reported success for a silently ignored command). | Nothing at the default (`test-mode = off` composes byte-identical pipelines). Test code that relied on `ManagementCommand` always returning `false` on Artery, or on the Player treating `false` as success, must account for the new semantics. |
**Association state machine:**`AssociationRegistry` keyed by remote **Address** (one Association per address, CAS-materialized) + an `association(uid)` reverse lookup (None until handshake completes). Per-association `AssociationState` (volatile, CAS-swapped) with `uniqueRemoteAddress`: **Associating** (UID unknown — gates OutboundHandshake) → **Associated** (`completeHandshake` sets it) → **Quarantined**. A **different** incoming UID (remote restart) → `newIncarnation` + atomic swap + clear outbound compression (UID-change → reset); the old UID is not auto-quarantined.
166
166
167
-
**Quarantine (UID-scoped):** acts only if the uid matches the current `uniqueRemoteAddress().uid` (stale-UID request ignored); swaps `newQuarantined`, emits `QuarantinedEvent`, clears compression, sends `ClearSystemMessageDelivery(incarnation)`. Only `ActorSelectionMessage` + `ClearSystemMessageDelivery` pierce. A **new incarnation re-associates** (keyed by Address; a new UID installs a fresh non-quarantined incarnation while the old UID stays quarantined). Prune after `remove-quarantined-association-after` = 1h.
167
+
**Quarantine (UID-scoped):** acts only if the uid matches the current `uniqueRemoteAddress().uid` (stale-UID request ignored); swaps `newQuarantined`, emits `QuarantinedEvent`, clears compression, sends `ClearSystemMessageDelivery(incarnation)`. Only `ActorSelectionMessage` + `ClearSystemMessageDelivery` pierce. The pierce is not uid-checked and may reach the quarantined incarnation itself, which processes it; the inbound quarantine check drops the reply, so the exchange is one-directional. This is deliberate: the sender cannot learn that the peer restarted without sending to it, and the not-restarted case is the price of that discovery. Classic remoting differs -- it refuses the quarantined uid at handshake (`HopelessAssociation` via `refuseUid`), so a pierce there can only ever reach a new incarnation. A **new incarnation re-associates** (keyed by Address; a new UID installs a fresh non-quarantined incarnation while the old UID stays quarantined). Prune after `remove-quarantined-association-after` = 1h. **Implementation gap:** the config key is not parsed and no pruning exists yet -- `QuarantinedUids` accumulates per association for the life of the process (classic bounds the same state with `prune-quarantine-marker-after = 5 d`, documented there as leak GC, not re-admission).
168
168
169
169
**Provider integration:** the `RemoteTransport` seam already exists — `RemoteActorRef.Tell → Remote.Send`; the provider creates refs via `new RemoteActorRef(Transport,…)`; `DefaultAddress` / `LocalAddressForRemote` / `Quarantine` all delegate to the transport. So `ArteryRemoting : RemoteTransport` implements **9 abstract members** (two `ManagementCommand` overloads — the design draft said 8) and needs **no change** to `RemoteActorRef` or the ref-creation path. **The one wiring change:**`RemoteActorRefProvider.CreateInternals()` hard-codes `new Remoting(…)` — add a config switch (`akka.remote.artery.enabled = on` → `ArteryRemoting`, else classic) by making it read `RemoteSettings` or overriding in a subclass. **Two nodes must run the same transport** (wire + scheme differ: classic `akka.tcp://`, Artery `akka://`) — homogeneous cluster; fail fast on a mixed config.
-**WHEN** an `ActorSelectionMessage` is sent toward a quarantined association
109
+
-**THEN** it SHALL be transmitted to the peer address, even if the peer is still running as the quarantined uid
110
+
111
+
#### Scenario: Inbound traffic from a quarantined uid is dropped
112
+
-**WHEN** an envelope arrives whose origin uid this system has quarantined
113
+
-**THEN** the envelope SHALL be dropped before dispatch, system messages included
114
+
-**AND** a quarantine control notice SHALL be sent to the origin, unless the dropped message is itself a quarantine notice or a heartbeat
115
+
116
+
#### Scenario: New incarnation lifts quarantine
117
+
-**WHEN** a completed handshake installs a different uid for a quarantined association
118
+
-**THEN** traffic to and from the new uid SHALL flow normally
119
+
-**AND** the old uid SHALL remain quarantined
120
+
121
+
#### Scenario: Quarantined peer does not retaliate
122
+
-**WHEN** a system receives notice that a peer has quarantined it
123
+
-**THEN** it SHALL publish `ThisActorSystemQuarantinedEvent` and SHALL NOT quarantine the peer in return, because a reciprocal quarantine can split a cluster
124
+
99
125
### Requirement: Outbound queues are bounded
100
126
101
127
Artery remoting SHALL use bounded outbound queues for user and control traffic.
0 commit comments