diff --git a/Sources/ActorSingletonPlugin/ActorSingleton.swift b/Sources/ActorSingletonPlugin/ActorSingleton.swift index 684913335..7fe1371c2 100644 --- a/Sources/ActorSingletonPlugin/ActorSingleton.swift +++ b/Sources/ActorSingletonPlugin/ActorSingleton.swift @@ -18,7 +18,7 @@ import DistributedActorsConcurrencyHelpers // ==== ---------------------------------------------------------------------------------------------------------------- // MARK: Actor singleton -internal final class ActorSingleton { +internal final class ActorSingleton { /// Settings for the `ActorSingleton` let settings: ActorSingletonSettings diff --git a/Sources/ActorSingletonPlugin/ActorSingletonManager.swift b/Sources/ActorSingletonPlugin/ActorSingletonManager.swift index 4dd02e12d..cb4ca7efd 100644 --- a/Sources/ActorSingletonPlugin/ActorSingletonManager.swift +++ b/Sources/ActorSingletonPlugin/ActorSingletonManager.swift @@ -20,7 +20,7 @@ import Logging /// Spawned as a system actor on the node where the singleton is supposed to run, `ActorSingletonManager` manages /// the singleton's lifecycle and stops itself after handing over the singleton. -internal class ActorSingletonManager { +internal class ActorSingletonManager { /// Settings for the `ActorSingleton` private let settings: ActorSingletonSettings @@ -71,7 +71,7 @@ internal class ActorSingletonManager { try context.stop(child: singleton) } - internal enum Directive: NonTransportableActorMessage { + internal enum Directive: _NotActuallyCodableMessage { case takeOver(from: UniqueNode?, replyTo: _ActorRef<_ActorRef?>) case handOver(to: UniqueNode?) case stop diff --git a/Sources/ActorSingletonPlugin/ActorSingletonPlugin.swift b/Sources/ActorSingletonPlugin/ActorSingletonPlugin.swift index 89914ca65..111a6cb96 100644 --- a/Sources/ActorSingletonPlugin/ActorSingletonPlugin.swift +++ b/Sources/ActorSingletonPlugin/ActorSingletonPlugin.swift @@ -37,7 +37,7 @@ public final class ActorSingletonPlugin { public init() {} // FIXME: document that may crash, it may right? - func ref(of type: Message.Type, settings: ActorSingletonSettings, system: ClusterSystem, props: _Props? = nil, _ behavior: _Behavior? = nil) throws -> _ActorRef { + func ref(of type: Message.Type, settings: ActorSingletonSettings, system: ClusterSystem, props: _Props? = nil, _ behavior: _Behavior? = nil) throws -> _ActorRef { try self.singletonsLock.withLock { if let existing = self.singletons[settings.name] { guard let proxy = existing.unsafeUnwrapAs(Message.self).proxy else { diff --git a/Sources/ActorSingletonPlugin/ActorSingletonProxy.swift b/Sources/ActorSingletonPlugin/ActorSingletonProxy.swift index 14390f617..109334716 100644 --- a/Sources/ActorSingletonPlugin/ActorSingletonProxy.swift +++ b/Sources/ActorSingletonPlugin/ActorSingletonProxy.swift @@ -31,7 +31,7 @@ import Logging /// singleton runs on. If the singleton falls on *this* node, the proxy will spawn a `ActorSingletonManager`, /// which manages the actual singleton actor, and obtain the ref from it. The proxy instructs the /// `ActorSingletonManager` to hand over the singleton whenever the node changes. -internal class ActorSingletonProxy { +internal class ActorSingletonProxy { /// Settings for the `ActorSingleton` private let settings: ActorSingletonSettings diff --git a/Sources/DistributedActors/ActorAddress.swift b/Sources/DistributedActors/ActorAddress.swift index db7faab62..3daa6bf07 100644 --- a/Sources/DistributedActors/ActorAddress.swift +++ b/Sources/DistributedActors/ActorAddress.swift @@ -20,9 +20,6 @@ import Distributed /// Convenience alias for ``ClusterSystem/ActorID``. public typealias ActorID = ClusterSystem.ActorID -@available(*, deprecated, message: "Old name of this type, to be replaced by ActorID") -public typealias ActorAddress = ActorID - extension ClusterSystem { /// Uniquely identifies a DistributedActor within the cluster. /// diff --git a/Sources/DistributedActors/ActorContext.swift b/Sources/DistributedActors/ActorContext.swift index 432cb615d..3aef29a09 100644 --- a/Sources/DistributedActors/ActorContext.swift +++ b/Sources/DistributedActors/ActorContext.swift @@ -19,7 +19,7 @@ import Logging /// - Warning: /// - It MUST only ever be accessed from its own Actor. It is fine though to close over it in the actors behaviours. /// - It MUST NOT be shared to other actors, and MUST NOT be accessed concurrently (e.g. from outside the actor). -public class _ActorContext /* TODO(sendable): NOTSendable*/ { +public class _ActorContext /* TODO(sendable): NOTSendable*/ { public typealias Myself = _ActorRef /// Returns `ClusterSystem` which this context belongs to. @@ -126,7 +126,7 @@ public class _ActorContext /* TODO(sendable): NOTSendable file: String = #file, line: UInt = #line, _ behavior: _Behavior ) throws -> _ActorRef - where M: ActorMessage + where M: Codable { _undefined() } @@ -145,7 +145,7 @@ public class _ActorContext /* TODO(sendable): NOTSendable file: String = #file, line: UInt = #line, _ behavior: _Behavior ) throws -> _ActorRef - where M: ActorMessage + where M: Codable { _undefined() } @@ -174,7 +174,7 @@ public class _ActorContext /* TODO(sendable): NOTSendable /// - Throws: an `_ActorContextError` when an actor ref is passed in that is NOT a child of the current actor. /// An actor may not terminate another's child actors. Attempting to stop `myself` using this method will /// also throw, as the proper way of stopping oneself is returning a `_Behavior.stop`. - public func stop(child ref: _ActorRef) throws where M: ActorMessage { + public func stop(child ref: _ActorRef) throws where M: Codable { return _undefined() } @@ -332,7 +332,7 @@ public class _ActorContext /* TODO(sendable): NOTSendable /// being silently dropped. This can be useful when not all messages `From` have a valid representation in /// `Message`, or if not all `From` messages are of interest for this particular actor. public final func messageAdapter(_ adapt: @escaping (From) -> Message?) -> _ActorRef - where From: ActorMessage + where From: Codable { return self.messageAdapter(from: From.self, adapt: adapt) } @@ -349,7 +349,7 @@ public class _ActorContext /* TODO(sendable): NOTSendable /// being silently dropped. This can be useful when not all messages `From` have a valid representation in /// `Message`, or if not all `From` messages are of interest for this particular actor. public func messageAdapter(from type: From.Type, adapt: @escaping (From) -> Message?) -> _ActorRef - where From: ActorMessage + where From: Codable { return _undefined() } @@ -365,7 +365,7 @@ public class _ActorContext /* TODO(sendable): NOTSendable /// with an existing `_SubReceiveId`, it replaces the old one. All references will remain valid and point to /// the new behavior. public func subReceive(_: _SubReceiveId, _: SubMessage.Type, _: @escaping (SubMessage) throws -> Void) -> _ActorRef - where SubMessage: ActorMessage + where SubMessage: Codable { return _undefined() } diff --git a/Sources/DistributedActors/ActorMessage+Protobuf.swift b/Sources/DistributedActors/ActorMessage+Protobuf.swift index c8f6e633f..2d8779181 100644 --- a/Sources/DistributedActors/ActorMessage+Protobuf.swift +++ b/Sources/DistributedActors/ActorMessage+Protobuf.swift @@ -21,7 +21,7 @@ import SwiftProtobuf // ==== ---------------------------------------------------------------------------------------------------------------- // MARK: Protobuf representations -public protocol _AnyProtobufRepresentable: ActorMessage, SerializationRepresentable {} +public protocol _AnyProtobufRepresentable: Codable, SerializationRepresentable {} extension _AnyProtobufRepresentable { public static var defaultSerializerID: Serialization.SerializerID? { @@ -32,8 +32,6 @@ extension _AnyProtobufRepresentable { public protocol _AnyPublicProtobufRepresentable: _AnyProtobufRepresentable {} /// A protocol that facilitates conversion between Swift and protobuf messages. -/// -/// - SeeAlso: `ActorMessage` public protocol _ProtobufRepresentable: _AnyPublicProtobufRepresentable { associatedtype ProtobufRepresentation: SwiftProtobuf.Message diff --git a/Sources/DistributedActors/ActorMessages.swift b/Sources/DistributedActors/ActorMessages.swift index 338eb9182..33d6b6ad8 100644 --- a/Sources/DistributedActors/ActorMessages.swift +++ b/Sources/DistributedActors/ActorMessages.swift @@ -15,25 +15,14 @@ import struct Foundation.Data import NIO -// ==== ---------------------------------------------------------------------------------------------------------------- -// MARK: Actor Message - -/// An Actor message is simply a Codable type. -/// -/// Any Codable it able to be sent as an actor message. -/// -/// You can customize which coder/decoder should be used by registering specialized manifests for the message type, -/// or having the type conform to one of the special `...Representable` (e.g. `_ProtobufRepresentable`) protocols. -public typealias ActorMessage = Codable // FIXME: MAKE THIS SENDABLE: & Sendable - /// A `Never` can never be sent as message, even more so over the wire. -extension Never: NonTransportableActorMessage {} +extension Never: _NotActuallyCodableMessage {} // ==== ---------------------------------------------------------------------------------------------------------------- // MARK: Common utility messages // FIXME: we should not add Codable conformance onto a stdlib type, but rather fix this in stdlib -extension Result: ActorMessage where Success: ActorMessage, Failure: ActorMessage { +extension Result: Codable where Success: Codable, Failure: Codable { public enum DiscriminatorKeys: String, Codable { case success case failure @@ -69,7 +58,7 @@ extension Result: ActorMessage where Success: ActorMessage, Failure: ActorMessag } /// Generic transportable Error type, can be used to wrap error types and represent them as best as possible for transporting. -public struct ErrorEnvelope: Error, ActorMessage { +public struct ErrorEnvelope: Error, Codable { public typealias CodableError = Error & Codable private let codableError: CodableError @@ -139,7 +128,7 @@ public struct BestEffortStringError: Error, Codable, Equatable, CustomStringConv } /// Useful error wrapper which performs an best effort Error serialization as configured by the actor system. -public struct NonTransportableAnyError: Error, NonTransportableActorMessage { +public struct NonTransportableAnyError: Error, _NotActuallyCodableMessage { public let failure: Error public init(_ failure: Failure) { @@ -150,8 +139,7 @@ public struct NonTransportableAnyError: Error, NonTransportableActorMessage { // ==== ---------------------------------------------------------------------------------------------------------------- // MARK: Not Transportable Actor Message (i.e. "local only") -/// Marks a type as `ActorMessage` however -/// Attempting to send such message to a remote actor WILL FAIL and log an error. +/// Marks a type as `Codable` however attempting to send such message to a remote actor WILL FAIL and log an error. /// /// Use this with great caution and only for messages which are specifically designed to utilize the local assumption. /// @@ -162,22 +150,22 @@ public struct NonTransportableAnyError: Error, NonTransportableActorMessage { /// No serializer is expected to be registered for such types. /// /// - Warning: Attempting to send such message over the network will fail at runtime (and log an error or warning). -public protocol NonTransportableActorMessage: ActorMessage {} +public protocol _NotActuallyCodableMessage: Codable {} -extension NonTransportableActorMessage { +extension _NotActuallyCodableMessage { public init(from decoder: Swift.Decoder) throws { - fatalError("Attempted to decode NonTransportableActorMessage message: \(Self.self)! This should never happen.") + fatalError("Attempted to decode _NotActuallyCodableMessage message: \(Self.self)! This should never happen.") } public func encode(to encoder: Swift.Encoder) throws { - fatalError("Attempted to encode NonTransportableActorMessage message: \(Self.self)! This should never happen.") + fatalError("Attempted to encode _NotActuallyCodableMessage message: \(Self.self)! This should never happen.") } public init(context: Serialization.Context, from buffer: inout ByteBuffer, using manifest: Serialization.Manifest) throws { - fatalError("Attempted to deserialize NonTransportableActorMessage message: \(Self.self)! This should never happen.") + fatalError("Attempted to deserialize _NotActuallyCodableMessage message: \(Self.self)! This should never happen.") } public func serialize(context: Serialization.Context, to bytes: inout ByteBuffer) throws { - fatalError("Attempted to serialize NonTransportableActorMessage message: \(Self.self)! This should never happen.") + fatalError("Attempted to serialize _NotActuallyCodableMessage message: \(Self.self)! This should never happen.") } } diff --git a/Sources/DistributedActors/ActorRef+Ask.swift b/Sources/DistributedActors/ActorRef+Ask.swift index 922975ff1..f9ce68865 100644 --- a/Sources/DistributedActors/ActorRef+Ask.swift +++ b/Sources/DistributedActors/ActorRef+Ask.swift @@ -86,12 +86,8 @@ extension _ActorRef: ReceivesQuestions { let promise = system._eventLoopGroup.next().makePromise(of: answerType) - // TODO: maybe a specialized one... for ask? - let instrumentation = system.settings.instrumentation.makeActorInstrumentation(promise.futureResult, self.id) - do { - // TODO: implement special actor ref instead of using real actor - let askRef = try system._spawn( + let askRef = try system._spawn( // TODO: "ask" is going away in favor of raw "remoteCalls" .ask, AskActor.behavior( promise, @@ -105,18 +101,7 @@ extension _ActorRef: ReceivesQuestions { let message = makeQuestion(askRef) self.tell(message, file: file, line: line) - - instrumentation.actorAsked(message: message, from: askRef.id) - promise.futureResult.whenComplete { - switch $0 { - case .success(let answer): - instrumentation.actorAskReplied(reply: answer, error: nil) - case .failure(let error): - instrumentation.actorAskReplied(reply: nil, error: error) - } - } } catch { - instrumentation.actorAskReplied(reply: nil, error: error) promise.fail(error) } @@ -253,7 +238,7 @@ extension AskResponse { /// // TODO: replace with a special minimal `_ActorRef` that does not require spawning or scheduling. internal enum AskActor { - enum Event: NonTransportableActorMessage { + enum Event: _NotActuallyCodableMessage { case timeout } diff --git a/Sources/DistributedActors/ActorRefFactory.swift b/Sources/DistributedActors/ActorRefFactory.swift index 00d6a6466..d81dc983d 100644 --- a/Sources/DistributedActors/ActorRefFactory.swift +++ b/Sources/DistributedActors/ActorRefFactory.swift @@ -54,7 +54,7 @@ public protocol _ActorRefFactory { props: _Props, file: String, line: UInt, _ behavior: _Behavior - ) throws -> _ActorRef where Message: ActorMessage + ) throws -> _ActorRef where Message: Codable } // ==== ---------------------------------------------------------------------------------------------------------------- @@ -64,7 +64,7 @@ public protocol _ChildActorRefFactory: _ActorRefFactory { var children: _Children { get set } // lock-protected func stop(child ref: _ActorRef) throws - where Message: ActorMessage + where Message: Codable } // ==== ---------------------------------------------------------------------------------------------------------------- diff --git a/Sources/DistributedActors/ActorRefProvider.swift b/Sources/DistributedActors/ActorRefProvider.swift index 985f4eaf4..e26f08320 100644 --- a/Sources/DistributedActors/ActorRefProvider.swift +++ b/Sources/DistributedActors/ActorRefProvider.swift @@ -36,7 +36,7 @@ internal protocol _ActorRefProvider: _ActorTreeTraversable { dispatcher: MessageDispatcher, props: _Props, startImmediately: Bool ) throws -> _ActorRef - where Message: ActorMessage + where Message: Codable /// Stops all actors created by this `_ActorRefProvider` and blocks until they have all stopped. func stopAll() @@ -81,7 +81,7 @@ extension RemoteActorRefProvider { dispatcher: MessageDispatcher, props: _Props, startImmediately: Bool ) throws -> _ActorRef - where Message: ActorMessage + where Message: Codable { // spawn is always local, thus we delegate to the underlying provider return try self.localProvider._spawn(system: system, behavior: behavior, id: id, dispatcher: dispatcher, props: props, startImmediately: startImmediately) @@ -91,7 +91,7 @@ extension RemoteActorRefProvider { self.localProvider.stopAll() } - public func _traverse(context: _TraversalContext, _ visit: (_TraversalContext, AddressableActorRef) -> _TraversalDirective) -> _TraversalResult { + public func _traverse(context: _TraversalContext, _ visit: (_TraversalContext, _AddressableActorRef) -> _TraversalDirective) -> _TraversalResult { self.localProvider._traverse(context: context, visit) } } @@ -113,11 +113,11 @@ extension RemoteActorRefProvider { } } - public func _resolveUntyped(context: ResolveContext) -> AddressableActorRef { + public func _resolveUntyped(context: ResolveContext) -> _AddressableActorRef { if self.localNode == context.id.uniqueNode { return self.localProvider._resolveUntyped(context: context) } else { - return AddressableActorRef(self._resolveAsRemoteRef(context, remoteAddress: context.id)) + return _AddressableActorRef(self._resolveAsRemoteRef(context, remoteAddress: context.id)) } } @@ -146,13 +146,13 @@ internal struct LocalActorRefProvider: _ActorRefProvider { dispatcher: MessageDispatcher, props: _Props, startImmediately: Bool ) throws -> _ActorRef - where Message: ActorMessage + where Message: Codable { return try self.root.makeChild(path: id.path) { // the cell that holds the actual "actor", though one could say the cell *is* the actor... let actor: _ActorShell = _ActorShell( system: system, - parent: AddressableActorRef(root.ref), + parent: _AddressableActorRef(root.ref), behavior: behavior, id: id, props: props, @@ -181,7 +181,7 @@ internal struct LocalActorRefProvider: _ActorRefProvider { } extension LocalActorRefProvider { - public func _traverse(context: _TraversalContext, _ visit: (_TraversalContext, AddressableActorRef) -> _TraversalDirective) -> _TraversalResult { + public func _traverse(context: _TraversalContext, _ visit: (_TraversalContext, _AddressableActorRef) -> _TraversalDirective) -> _TraversalResult { self.root._traverse(context: context.deeper, visit) } @@ -189,28 +189,28 @@ extension LocalActorRefProvider { self.root._resolve(context: context) } - public func _resolveUntyped(context: ResolveContext) -> AddressableActorRef { + public func _resolveUntyped(context: ResolveContext) -> _AddressableActorRef { self.root._resolveUntyped(context: context) } } /// INTERNAL API public protocol _ActorTreeTraversable { - func _traverse(context: _TraversalContext, _ visit: (_TraversalContext, AddressableActorRef) -> _TraversalDirective) -> _TraversalResult + func _traverse(context: _TraversalContext, _ visit: (_TraversalContext, _AddressableActorRef) -> _TraversalDirective) -> _TraversalResult /// Resolves the given actor path against the underlying actor tree. /// /// Depending on the underlying implementation, the returned ref MAY be a remote one. /// /// - Returns: `deadLetters` if actor path resolves to no live actor, a valid `_ActorRef` otherwise. - func _resolve(context: ResolveContext) -> _ActorRef where Message: ActorMessage + func _resolve(context: ResolveContext) -> _ActorRef where Message: Codable /// Resolves the given actor path against the underlying actor tree. /// /// Depending on the underlying implementation, the returned ref MAY be a remote one. /// /// - Returns: `deadLetters` if actor path resolves to no live actor, a valid `_ActorRef` otherwise. - func _resolveUntyped(context: ResolveContext) -> AddressableActorRef + func _resolveUntyped(context: ResolveContext) -> _AddressableActorRef } // TODO: Would be nice to not need this type at all; though initialization dance prohibiting self access makes this a bit hard @@ -228,7 +228,7 @@ internal struct CompositeActorTreeTraversable: _ActorTreeTraversable { // TODO: duplicates some logic from _traverse implementation on Actor system (due to initialization dances), see if we can remove the duplication of this // TODO: we may be able to pull this off by implementing the "root" as traversable and then we expose it to the Serialization() impl - public func _traverse(context: _TraversalContext, _ visit: (_TraversalContext, AddressableActorRef) -> _TraversalDirective) -> _TraversalResult { + public func _traverse(context: _TraversalContext, _ visit: (_TraversalContext, _AddressableActorRef) -> _TraversalDirective) -> _TraversalResult { let systemTraversed = self.systemTree._traverse(context: context, visit) switch systemTraversed { @@ -262,7 +262,7 @@ internal struct CompositeActorTreeTraversable: _ActorTreeTraversable { } } - public func _resolveUntyped(context: ResolveContext) -> AddressableActorRef { + public func _resolveUntyped(context: ResolveContext) -> _AddressableActorRef { guard let selector = context.selectorSegments.first else { return context.personalDeadLetters.asAddressable // i.e. we resolved a "dead reference" as it points to nothing } @@ -326,7 +326,7 @@ public struct _TraversalContext { } /// INTERNAL API: May change without any prior notice. -public struct ResolveContext { +public struct ResolveContext { /// The "remaining path" of the resolve being performed public var selectorSegments: ArraySlice diff --git a/Sources/DistributedActors/ActorShell+Children.swift b/Sources/DistributedActors/ActorShell+Children.swift index 6c033271c..1e2847b35 100644 --- a/Sources/DistributedActors/ActorShell+Children.swift +++ b/Sources/DistributedActors/ActorShell+Children.swift @@ -158,7 +158,7 @@ public class _Children { } @usableFromInline - internal func forEach(_ body: (AddressableActorRef) throws -> Void) rethrows { + internal func forEach(_ body: (_AddressableActorRef) throws -> Void) rethrows { try self.rwLock.withReaderLock { try self.container.values.forEach { switch $0 { @@ -186,7 +186,7 @@ public class _Children { // MARK: Traversal extension _Children: _ActorTreeTraversable { - public func _traverse(context: _TraversalContext, _ visit: (_TraversalContext, AddressableActorRef) -> _TraversalDirective) -> _TraversalResult { + public func _traverse(context: _TraversalContext, _ visit: (_TraversalContext, _AddressableActorRef) -> _TraversalDirective) -> _TraversalResult { var c = context.deeper let children = self.rwLock.withReaderLock { @@ -242,7 +242,7 @@ extension _Children: _ActorTreeTraversable { } } - public func _resolveUntyped(context: ResolveContext) -> AddressableActorRef { + public func _resolveUntyped(context: ResolveContext) -> _AddressableActorRef { guard let selector = context.selectorSegments.first else { // no selector, we should not be in this place! fatalError("Resolve should have stopped before stepping into children._resolve, this is a bug!") @@ -385,11 +385,11 @@ public enum _ActorContextError: Error { /// as the actor would continue running until it receives the stop message. Rather, to stop the current actor /// it should return `_Behavior.stop` from its receive block, which will cause it to immediately stop processing /// any further messages. - case attemptedStoppingMyselfUsingContext(ref: AddressableActorRef) + case attemptedStoppingMyselfUsingContext(ref: _AddressableActorRef) /// Only the parent actor is allowed to stop its children. This is to avoid mistakes in which one part of the system /// can stop arbitrary actors of another part of the system which was programmed under the assumption such actor would /// wellKnownly exist. - case attemptedStoppingNonChildActor(ref: AddressableActorRef) + case attemptedStoppingNonChildActor(ref: _AddressableActorRef) /// It is not allowed to spawn case duplicateActorPath(path: ActorPath) /// It is not allowed to spawn new actors when the system is stopping diff --git a/Sources/DistributedActors/Adapters.swift b/Sources/DistributedActors/Adapters.swift index 5c688eb71..a07df8abf 100644 --- a/Sources/DistributedActors/Adapters.swift +++ b/Sources/DistributedActors/Adapters.swift @@ -40,11 +40,11 @@ public protocol _AbstractAdapter: _ActorTreeTraversable { /// The adapter can be watched and shares the lifecycle with the adapted actor, /// meaning that it will terminate when the actor terminates. It will survive /// restarts after failures. -internal final class _ActorRefAdapter: _AbstractAdapter { +internal final class _ActorRefAdapter: _AbstractAdapter { public let fromType: Any.Type private let target: _ActorRef let id: ActorID - private var watchers: Set? + private var watchers: Set<_AddressableActorRef>? private let lock = _Mutex() let deadLetters: _ActorRef @@ -60,7 +60,7 @@ internal final class _ActorRefAdapter: _AbstractAdapter { self.deadLetters = self.target._deadLetters } - private var myselfAddressable: AddressableActorRef { + private var myselfAddressable: _AddressableActorRef { _ActorRef(.adapter(self)).asAddressable } @@ -86,7 +86,7 @@ internal final class _ActorRefAdapter: _AbstractAdapter { self.target._unsafeUnwrapCell.sendAdaptedMessage(message, file: file, line: line) } - private func addWatcher(watchee: AddressableActorRef, watcher: AddressableActorRef) { + private func addWatcher(watchee: _AddressableActorRef, watcher: _AddressableActorRef) { assert(watchee.id == self.id && watcher.id != self.id, "Illegal watch received. Watchee: [\(watchee)], watcher: [\(watcher)]") self.lock.synchronized { @@ -104,7 +104,7 @@ internal final class _ActorRefAdapter: _AbstractAdapter { } } - private func removeWatcher(watchee: AddressableActorRef, watcher: AddressableActorRef) { + private func removeWatcher(watchee: _AddressableActorRef, watcher: _AddressableActorRef) { assert(watchee.id == self.id && watcher.id != self.id, "Illegal unwatch received. Watchee: [\(watchee)], watcher: [\(watcher)]") self.lock.synchronized { @@ -116,20 +116,20 @@ internal final class _ActorRefAdapter: _AbstractAdapter { } } - private func watch(_ watchee: AddressableActorRef) { + private func watch(_ watchee: _AddressableActorRef) { watchee._sendSystemMessage(.watch(watchee: watchee, watcher: self.myselfAddressable)) } - private func unwatch(_ watchee: AddressableActorRef) { + private func unwatch(_ watchee: _AddressableActorRef) { watchee._sendSystemMessage(.unwatch(watchee: watchee, watcher: self.myselfAddressable)) } - private func sendTerminated(_ ref: AddressableActorRef) { + private func sendTerminated(_ ref: _AddressableActorRef) { ref._sendSystemMessage(.terminated(ref: self.myselfAddressable, existenceConfirmed: true, idTerminated: false)) } func stop() { - var localWatchers: Set = [] + var localWatchers: Set<_AddressableActorRef> = [] self.lock.synchronized { guard self.watchers != nil else { return @@ -147,7 +147,7 @@ internal final class _ActorRefAdapter: _AbstractAdapter { } extension _ActorRefAdapter { - public func _traverse(context: _TraversalContext, _ visit: (_TraversalContext, AddressableActorRef) -> _TraversalDirective) -> _TraversalResult { + public func _traverse(context: _TraversalContext, _ visit: (_TraversalContext, _AddressableActorRef) -> _TraversalDirective) -> _TraversalResult { var c = context.deeper switch visit(context, self.myselfAddressable) { case .continue: @@ -173,7 +173,7 @@ extension _ActorRefAdapter { return .init(.adapter(self)) } - public func _resolveUntyped(context: ResolveContext) -> AddressableActorRef { + public func _resolveUntyped(context: ResolveContext) -> _AddressableActorRef { guard context.selectorSegments.first == nil, self.id.incarnation == context.id.incarnation else { return context.personalDeadLetters.asAddressable } @@ -219,7 +219,7 @@ internal final class _DeadLetterAdapterPersonality: _AbstractAdapter { // nothing to stop, a dead letters adapter is special } - public func _traverse(context: _TraversalContext, _ visit: (_TraversalContext, AddressableActorRef) -> _TraversalDirective) -> _TraversalResult { + public func _traverse(context: _TraversalContext, _ visit: (_TraversalContext, _AddressableActorRef) -> _TraversalDirective) -> _TraversalResult { .completed } @@ -227,7 +227,7 @@ internal final class _DeadLetterAdapterPersonality: _AbstractAdapter { self.deadLetters.adapted() } - public func _resolveUntyped(context: ResolveContext) -> AddressableActorRef { + public func _resolveUntyped(context: ResolveContext) -> _AddressableActorRef { self.deadLetters.asAddressable } } @@ -235,13 +235,13 @@ internal final class _DeadLetterAdapterPersonality: _AbstractAdapter { // ==== ---------------------------------------------------------------------------------------------------------------- // MARK: SubReceiveAdapter -internal final class SubReceiveAdapter: _AbstractAdapter { +internal final class SubReceiveAdapter: _AbstractAdapter { internal let fromType: Any.Type private let target: _ActorRef private let identifier: _AnySubReceiveId private let adapterAddress: ActorID - private var watchers: Set? + private var watchers: Set<_AddressableActorRef>? private let lock = _Mutex() var id: ActorID { @@ -303,7 +303,7 @@ internal final class SubReceiveAdapter = [] + var localWatchers: Set<_AddressableActorRef> = [] self.lock.synchronized { guard self.watchers != nil else { return @@ -364,7 +364,7 @@ internal final class SubReceiveAdapter(context: _TraversalContext, _ visit: (_TraversalContext, AddressableActorRef) -> _TraversalDirective) -> _TraversalResult { + public func _traverse(context: _TraversalContext, _ visit: (_TraversalContext, _AddressableActorRef) -> _TraversalDirective) -> _TraversalResult { var c = context.deeper switch visit(context, self.myself.asAddressable) { case .continue: @@ -395,7 +395,7 @@ extension SubReceiveAdapter { } } - public func _resolveUntyped(context: ResolveContext) -> AddressableActorRef { + public func _resolveUntyped(context: ResolveContext) -> _AddressableActorRef { guard context.selectorSegments.first == nil, self.id.incarnation == context.id.incarnation else { return context.personalDeadLetters.asAddressable } diff --git a/Sources/DistributedActors/Behaviors.swift b/Sources/DistributedActors/Behaviors.swift index 047ba3697..e16373af4 100644 --- a/Sources/DistributedActors/Behaviors.swift +++ b/Sources/DistributedActors/Behaviors.swift @@ -16,7 +16,7 @@ /// /// The most important behavior is `_Behavior.receive` since it allows handling incoming messages with a simple block. /// Various other predefined behaviors exist, such as "stopping" or "ignoring" a message. -public struct _Behavior: @unchecked Sendable { +public struct _Behavior: @unchecked Sendable { @usableFromInline let underlying: __Behavior @@ -459,7 +459,7 @@ extension _Behavior { } @usableFromInline -internal enum __Behavior { +internal enum __Behavior { case setup(_ onStart: (_ActorContext) throws -> _Behavior) case receive(_ handle: (_ActorContext, Message) throws -> _Behavior) @@ -501,7 +501,7 @@ internal enum StopReason { case failure(_Supervision.Failure) } -public enum IllegalBehaviorError: Error { +public enum IllegalBehaviorError: Error { /// Some behaviors, like `.same` and `.unhandled` are not allowed to be used as initial behaviors. /// See their individual documentation for the rationale why that is so. case notAllowedAsInitial(_ behavior: _Behavior) @@ -535,7 +535,7 @@ extension _Behavior { } /// Used in combination with `_Behavior.intercept` to intercept messages and signals delivered to a behavior. -open class _Interceptor { +open class _Interceptor { public init() {} @inlinable diff --git a/Sources/DistributedActors/Clocks/LamportClock.swift b/Sources/DistributedActors/Clocks/LamportClock.swift deleted file mode 100644 index ee8d20ccf..000000000 --- a/Sources/DistributedActors/Clocks/LamportClock.swift +++ /dev/null @@ -1,51 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Distributed Actors open source project -// -// Copyright (c) 2018-2019 Apple Inc. and the Swift Distributed Actors project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.md for the list of Swift Distributed Actors project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -public typealias LamportTime = UInt64 - -/// A Lamport clock. -/// -/// Each node or entity owns its own clock, and updates it each time -/// -/// NOTE: Unless two events are causally related, lamport clocks are not able to guarantee causality, -/// and thus strict happens-before relationships. -/// -/// - SeeAlso: -/// Time, Clocks, and the Ordering of Events in a Distributed System, Leslie Lamport, 1978 -public struct LamportClock: Codable, ExpressibleByIntegerLiteral { - public typealias IntegerLiteralType = UInt64 - - private var _time: LamportTime - - public init() { - self._time = 0 - } - - public init(integerLiteral value: IntegerLiteralType) { - self._time = LamportTime(value) - } - - public var time: LamportTime { - self._time - } - - public mutating func increment() { - self._time += 1 - } - - /// Witnessing time may only move the clock *forward*. - public mutating func witness(_ other: LamportClock) { - self._time = max(self._time, other._time) - } -} diff --git a/Sources/DistributedActors/Cluster/Cluster+Event.swift b/Sources/DistributedActors/Cluster/Cluster+Event.swift index 091420063..560d560a5 100644 --- a/Sources/DistributedActors/Cluster/Cluster+Event.swift +++ b/Sources/DistributedActors/Cluster/Cluster+Event.swift @@ -19,7 +19,7 @@ extension Cluster { /// Represents cluster events, most notably regarding membership and reachability of other members of the cluster. /// /// Inspect them directly, or `apply` to a `Membership` copy in order to be able to react to membership state of the cluster. - public enum Event: ActorMessage, Equatable { + public enum Event: Codable, Equatable { case snapshot(Membership) case membershipChange(MembershipChange) case reachabilityChange(ReachabilityChange) diff --git a/Sources/DistributedActors/Cluster/Cluster+Membership.swift b/Sources/DistributedActors/Cluster/Cluster+Membership.swift index d8613d893..5d49fe4cc 100644 --- a/Sources/DistributedActors/Cluster/Cluster+Membership.swift +++ b/Sources/DistributedActors/Cluster/Cluster+Membership.swift @@ -21,7 +21,7 @@ extension Cluster { /// Represents the set of members of this cluster. /// /// Membership changes are driven by nodes joining and leaving the cluster. - /// Leaving the cluster may be graceful or triggered by a ``FailureDetector``. + /// Leaving the cluster may be graceful or triggered by a failure detector. /// /// ### Replacement (Unique)Nodes /// A node (or member) is referred to as a "replacement" if it shares _the same_ protocol+host+address (i.e. ``Node``), diff --git a/Sources/DistributedActors/Cluster/ClusterShell.swift b/Sources/DistributedActors/Cluster/ClusterShell.swift index 497946957..32c65366e 100644 --- a/Sources/DistributedActors/Cluster/ClusterShell.swift +++ b/Sources/DistributedActors/Cluster/ClusterShell.swift @@ -303,7 +303,7 @@ internal class ClusterShell { } // Due to lack of Union Types, we have to emulate them - enum Message: ActorMessage { + enum Message: Codable { // The external API, exposed to users of the ClusterShell case command(CommandMessage) // The external API, exposed to users of the ClusterShell to query for state @@ -324,7 +324,7 @@ internal class ClusterShell { } // this is basically our API internally for this system - enum CommandMessage: NonTransportableActorMessage, SilentDeadLetter { + enum CommandMessage: _NotActuallyCodableMessage, SilentDeadLetter { /// Connect and handshake with remote `Node`, obtaining an `UniqueNode` in the process. /// Once the handshake is completed, reply to `replyTo` with the handshake result, and also mark the unique node as `.joining`. /// @@ -343,7 +343,7 @@ internal class ClusterShell { case cleanUpAssociationTombstones } - enum QueryMessage: NonTransportableActorMessage { + enum QueryMessage: _NotActuallyCodableMessage { case associatedNodes(_ActorRef>) // TODO: better type here case currentMembership(_ActorRef) } @@ -362,7 +362,7 @@ internal class ClusterShell { } // TODO: reformulate as Wire.accept / reject? - internal enum HandshakeResult: Equatable, NonTransportableActorMessage { + internal enum HandshakeResult: Equatable, _NotActuallyCodableMessage { case success(UniqueNode) case failure(HandshakeStateMachine.HandshakeConnectionError) } diff --git a/Sources/DistributedActors/Cluster/DiscoveryShell.swift b/Sources/DistributedActors/Cluster/DiscoveryShell.swift index fd3c8bb2b..71c9aa1e8 100644 --- a/Sources/DistributedActors/Cluster/DiscoveryShell.swift +++ b/Sources/DistributedActors/Cluster/DiscoveryShell.swift @@ -16,7 +16,7 @@ import Logging import ServiceDiscovery final class DiscoveryShell { - enum Message: NonTransportableActorMessage { + enum Message: _NotActuallyCodableMessage { case listing(Set) case stop(CompletionReason?) } diff --git a/Sources/DistributedActors/Cluster/Downing/DowningSettings.swift b/Sources/DistributedActors/Cluster/Downing/DowningSettings.swift index 64aa90a50..92269d73e 100644 --- a/Sources/DistributedActors/Cluster/Downing/DowningSettings.swift +++ b/Sources/DistributedActors/Cluster/Downing/DowningSettings.swift @@ -57,7 +57,9 @@ public enum OnDownActionStrategySettings { .setup { context in guard .milliseconds(0) < shutdownDelay else { context.log.warning("This node was marked as [.down], delay is immediate. Shutting down the system immediately!") - system.shutdown() + Task { + system.shutdown() + } return .stop } @@ -66,7 +68,9 @@ public enum OnDownActionStrategySettings { return .receiveMessage { _ in system.log.warning("Shutting down...") - system.shutdown() + Task { + system.shutdown() + } return .stop } } diff --git a/Sources/DistributedActors/Cluster/Downing/DowningStrategy.swift b/Sources/DistributedActors/Cluster/Downing/DowningStrategy.swift index ae527cead..153731a44 100644 --- a/Sources/DistributedActors/Cluster/Downing/DowningStrategy.swift +++ b/Sources/DistributedActors/Cluster/Downing/DowningStrategy.swift @@ -15,15 +15,13 @@ import Distributed import Logging -public struct DowningStrategies {} - /// Allows implementing downing strategies, without having to re-implement and reinvent logging and subscription logic. /// /// Downing strategies can focus on inspecting the membership and issuing timers if needed. public protocol DowningStrategy { /// Invoked whenever the cluster emits an event. /// - /// - Parameter event: cluster event that just ocurred + /// - Parameter event: cluster event that just occurred /// - Returns: directive, instructing the cluster to take some specific action. /// - Throws: If unable to handle the event for some reason; the failure will be logged and ignored. func onClusterEvent(event: Cluster.Event) throws -> DowningStrategyDirective @@ -31,14 +29,38 @@ public protocol DowningStrategy { func onTimeout(_ member: Cluster.Member) -> DowningStrategyDirective } -public enum DowningStrategyDirective { - case none - case markAsDown(Set) - case startTimer(key: TimerKey, member: Cluster.Member, delay: TimeAmount) - case cancelTimer(key: TimerKey) +/// Return to instruct the downing shell how to react. +public struct DowningStrategyDirective { + internal let underlying: Repr + internal enum Repr { + case none + case markAsDown(Set) + case startTimer(key: TimerKey, member: Cluster.Member, delay: TimeAmount) + case cancelTimer(key: TimerKey) + } + + internal init(_ underlying: Repr) { + self.underlying = underlying + } + + public static var none: Self { + .init(.none) + } + + public static func startTimer(key: TimerKey, member: Cluster.Member, delay: TimeAmount) -> Self { + .init(.startTimer(key: key, member: member, delay: delay)) + } + + public static func cancelTimer(key: TimerKey) -> Self { + .init(.cancelTimer(key: key)) + } + + public static func markAsDown(members: Set) -> Self { + .init(.markAsDown(members)) + } - static func markAsDown(_ member: Cluster.Member) -> Self { - Self.markAsDown([member]) + public static func markAsDown(_ member: Cluster.Member) -> Self { + .init(.markAsDown([member])) } } @@ -87,7 +109,7 @@ internal distributed actor DowningStrategyShell { } func interpret(directive: DowningStrategyDirective) { - switch directive { + switch directive.underlying { case .markAsDown(let members): self.markAsDown(members: members) diff --git a/Sources/DistributedActors/Cluster/Downing/TimeoutBasedDowningStrategy.swift b/Sources/DistributedActors/Cluster/Downing/TimeoutBasedDowningStrategy.swift index e662cee47..91eb26b7d 100644 --- a/Sources/DistributedActors/Cluster/Downing/TimeoutBasedDowningStrategy.swift +++ b/Sources/DistributedActors/Cluster/Downing/TimeoutBasedDowningStrategy.swift @@ -60,7 +60,7 @@ public final class TimeoutBasedDowningStrategy: DowningStrategy { } else if let replaced = change.replaced { _ = self._markAsDown.remove(replaced) _ = self._unreachable.remove(replaced) - return .markAsDown([replaced]) + return .markAsDown(members: [replaced]) } return .none @@ -82,7 +82,7 @@ public final class TimeoutBasedDowningStrategy: DowningStrategy { return .none // perhaps we removed it already for other reasons (e.g. node replacement) } if self.isLeader { - return .markAsDown([nodeToDown]) + return .markAsDown(members: [nodeToDown]) } else { self._markAsDown.insert(nodeToDown) return .none @@ -119,7 +119,7 @@ public final class TimeoutBasedDowningStrategy: DowningStrategy { if self.isLeader, !self._markAsDown.isEmpty { defer { self._markAsDown = [] } - return .markAsDown(self._markAsDown) + return .markAsDown(members: self._markAsDown) } else { return .none } diff --git a/Sources/DistributedActors/Cluster/HandshakeStateMachine.swift b/Sources/DistributedActors/Cluster/HandshakeStateMachine.swift index 25bbd1e2f..17c813e54 100644 --- a/Sources/DistributedActors/Cluster/HandshakeStateMachine.swift +++ b/Sources/DistributedActors/Cluster/HandshakeStateMachine.swift @@ -17,14 +17,19 @@ import NIO // ==== ---------------------------------------------------------------------------------------------------------------- // MARK: Protocol version -/// Wire Protocol version of this Swift Distributed Actors build. -public let DistributedActorsProtocolVersion: DistributedActors.Version = Version(reserved: 0, major: 0, minor: 0, patch: 1) +extension ClusterSystem { + /// Wire protocol version of this `ClusterSystem`. + /// + /// This version does not have to match the project version, i.e. a library version `1.5.0` may still be using the protocol version `1.0.0`, + /// as this version number is more about the _wire_ compatibility of the underlying protocol, rather than the library capabilities + public static let protocolVersion = ClusterSystem.Version(reserved: 0, major: 1, minor: 0, patch: 0) +} // ==== ---------------------------------------------------------------------------------------------------------------- // MARK: Constants for Cluster /// Magic 2 byte value for use as initial bytes in connections (before handshake). -/// Reads as: `5AC7 == SACT == S Act == Swift/Swift Distributed Actors Act == Swift/Swift Distributed Actors` +/// Reads as: `5AC7 == SACT == S Act == Swift/ (Distributed) Actors internal let HandshakeMagicBytes: UInt16 = 0x5AC7 // ==== ---------------------------------------------------------------------------------------------------------------- @@ -63,7 +68,7 @@ internal struct HandshakeStateMachine { internal struct InitiatedState: Swift.CustomStringConvertible { let settings: ClusterSystemSettings - var protocolVersion: Version { + var protocolVersion: ClusterSystem.Version { self.settings.protocolVersion } @@ -150,7 +155,7 @@ internal struct HandshakeStateMachine { self.state.selfNode } - var protocolVersion: DistributedActors.Version { + var protocolVersion: ClusterSystem.Version { self.state.settings.protocolVersion } @@ -185,7 +190,7 @@ internal struct HandshakeStateMachine { return .acceptAndAssociate(completed) } - func negotiateVersion(local: DistributedActors.Version, remote: DistributedActors.Version) -> RejectedState? { + func negotiateVersion(local: ClusterSystem.Version, remote: ClusterSystem.Version) -> RejectedState? { guard local.major == remote.major else { let error = HandshakeError.incompatibleProtocolVersion( local: self.protocolVersion, remote: self.offer.version @@ -203,7 +208,7 @@ internal struct HandshakeStateMachine { /// State reached once we have received a `HandshakeAccepted` and are ready to create an association. /// This state is used to unlock creating a completed Association. internal struct CompletedState { - let protocolVersion: Version + let protocolVersion: ClusterSystem.Version var remoteNode: UniqueNode var localNode: UniqueNode // let whenCompleted: EventLoopPromise @@ -241,7 +246,7 @@ internal struct HandshakeStateMachine { } internal struct RejectedState { - let protocolVersion: Version + let protocolVersion: ClusterSystem.Version let localNode: UniqueNode let remoteNode: UniqueNode let error: HandshakeError @@ -298,7 +303,7 @@ enum HandshakeError: Error { case targetHandshakeAddressMismatch(Wire.HandshakeOffer, selfNode: UniqueNode) /// Returned when an incoming handshake protocol version does not match what this node can understand. - case incompatibleProtocolVersion(local: DistributedActors.Version, remote: DistributedActors.Version) + case incompatibleProtocolVersion(local: ClusterSystem.Version, remote: ClusterSystem.Version) case targetRejectedHandshake(selfNode: UniqueNode, remoteNode: UniqueNode, message: String) diff --git a/Sources/DistributedActors/Cluster/MembershipGossip/Cluster+MembershipGossip.swift b/Sources/DistributedActors/Cluster/MembershipGossip/Cluster+MembershipGossip.swift index 6f4bf7570..44142bc0c 100644 --- a/Sources/DistributedActors/Cluster/MembershipGossip/Cluster+MembershipGossip.swift +++ b/Sources/DistributedActors/Cluster/MembershipGossip/Cluster+MembershipGossip.swift @@ -19,7 +19,7 @@ extension Cluster { /// Gossip payload about members in the cluster. /// /// Used to guarantee phrases like "all nodes have seen a node A in status S", upon which the Leader may act. - struct MembershipGossip: ActorMessage, Equatable { + struct MembershipGossip: Codable, Equatable { let owner: UniqueNode /// A table maintaining our perception of other nodes views on the version of membership. /// Each row in the table represents what versionVector we know the given node has observed recently. diff --git a/Sources/DistributedActors/Cluster/MembershipGossip/Cluster+MembershipGossipLogic.swift b/Sources/DistributedActors/Cluster/MembershipGossip/Cluster+MembershipGossipLogic.swift index 1aad2bd29..60b7fa229 100644 --- a/Sources/DistributedActors/Cluster/MembershipGossip/Cluster+MembershipGossipLogic.swift +++ b/Sources/DistributedActors/Cluster/MembershipGossip/Cluster+MembershipGossipLogic.swift @@ -33,7 +33,7 @@ final class MembershipGossipLogic: GossipLogic, CustomStringConvertible { /// We store and use a shuffled yet stable order for gossiping peers. /// See `updateActivePeers` for details. - private var peers: [AddressableActorRef] = [] + private var peers: [_AddressableActorRef] = [] /// Constantly mutated by `nextPeerToGossipWith` in an effort to keep order in which we gossip with nodes evenly distributed. /// This follows our logic in SWIM, and has the benefit that we never get too chatty with one specific node (as in the worst case it may be unreachable or down already). private var _peerToGossipWithIndex: Int = 0 @@ -42,7 +42,7 @@ final class MembershipGossipLogic: GossipLogic, CustomStringConvertible { /// /// See `updateActivePeers` and `receiveGossip` for details. // TODO: This can be optimized and it's enough if we keep a digest of the gossips; this way ACKs can just send the digest as well saving space. - private var lastGossipFrom: [AddressableActorRef: Cluster.MembershipGossip] = [:] + private var lastGossipFrom: [_AddressableActorRef: Cluster.MembershipGossip] = [:] init(_ context: Context, notifyOnGossipRef: _ActorRef) { self.context = context @@ -53,13 +53,13 @@ final class MembershipGossipLogic: GossipLogic, CustomStringConvertible { // ==== ------------------------------------------------------------------------------------------------------------ // MARK: Spreading gossip - func selectPeers(_ peers: [AddressableActorRef]) -> [AddressableActorRef] { + func selectPeers(_ peers: [_AddressableActorRef]) -> [_AddressableActorRef] { // how many peers we select in each gossip round, // we could for example be dynamic and notice if we have 10+ nodes, we pick 2 members to speed up the dissemination etc. let n = 1 self.updateActivePeers(peers) - var selectedPeers: [AddressableActorRef] = [] + var selectedPeers: [_AddressableActorRef] = [] selectedPeers.reserveCapacity(min(n, self.peers.count)) /// Trust the order of peers in gossipPeers for the selection; see `updateActivePeers` for logic of the ordering. @@ -72,7 +72,7 @@ final class MembershipGossipLogic: GossipLogic, CustomStringConvertible { return selectedPeers } - private func updateActivePeers(_ peers: [AddressableActorRef]) { + private func updateActivePeers(_ peers: [_AddressableActorRef]) { if let changed = Self.peersChanged(known: self.peers, current: peers) { // 1) remove any peers which are no longer active // - from the peers list @@ -99,7 +99,7 @@ final class MembershipGossipLogic: GossipLogic, CustomStringConvertible { } } - func makePayload(target: AddressableActorRef) -> Cluster.MembershipGossip? { + func makePayload(target: _AddressableActorRef) -> Cluster.MembershipGossip? { // today we don't trim payloads at all // TODO: trim some information? self.latestGossip @@ -109,7 +109,7 @@ final class MembershipGossipLogic: GossipLogic, CustomStringConvertible { // TODO: Implement stricter-round robin, the same way as our SWIM impl does, see `nextMemberToPing` // This hardens the implementation against gossiping with the same node multiple times in a row. // Note that we do NOT need to worry about filtering out dead peers as this is automatically handled by the gossip shell. - private func shouldGossipWith(_ peer: AddressableActorRef) -> Bool { + private func shouldGossipWith(_ peer: _AddressableActorRef) -> Bool { guard let lastSeenGossipFromPeer = self.lastGossipFrom[peer] else { // it's a peer we have not gotten any gossip from yet return true @@ -119,7 +119,7 @@ final class MembershipGossipLogic: GossipLogic, CustomStringConvertible { } // TODO: may also want to return "these were removed" if we need to make any internal cleanup - static func peersChanged(known: [AddressableActorRef], current: [AddressableActorRef]) -> PeersChanged? { + static func peersChanged(known: [_AddressableActorRef], current: [_AddressableActorRef]) -> PeersChanged? { // TODO: a bit lazy implementation let knownSet = Set(known) let currentSet = Set(current) @@ -138,10 +138,10 @@ final class MembershipGossipLogic: GossipLogic, CustomStringConvertible { } struct PeersChanged { - let added: Set - let removed: Set + let added: Set<_AddressableActorRef> + let removed: Set<_AddressableActorRef> - init(added: Set, removed: Set) { + init(added: Set<_AddressableActorRef>, removed: Set<_AddressableActorRef>) { assert(!added.isEmpty || !removed.isEmpty, "PeersChanged yet both added/removed are empty!") self.added = added self.removed = removed @@ -151,7 +151,7 @@ final class MembershipGossipLogic: GossipLogic, CustomStringConvertible { // ==== ------------------------------------------------------------------------------------------------------------ // MARK: Receiving gossip - func receiveGossip(_ gossip: Gossip, from peer: AddressableActorRef) -> Acknowledgement? { + func receiveGossip(_ gossip: Gossip, from peer: _AddressableActorRef) -> Acknowledgement? { // 1) mark that from that specific peer, we know it observed at least that version self.lastGossipFrom[peer] = gossip @@ -166,7 +166,7 @@ final class MembershipGossipLogic: GossipLogic, CustomStringConvertible { return self.latestGossip } - func receiveAcknowledgement(_ acknowledgement: Acknowledgement, from peer: AddressableActorRef, confirming gossip: Cluster.MembershipGossip) { + func receiveAcknowledgement(_ acknowledgement: Acknowledgement, from peer: _AddressableActorRef, confirming gossip: Cluster.MembershipGossip) { // 1) store the direct gossip we got from this peer; we can use this to know if there's no need to gossip to that peer by inspecting seen table equality self.lastGossipFrom[peer] = acknowledgement diff --git a/Sources/DistributedActors/Cluster/NodeDeathWatcher.swift b/Sources/DistributedActors/Cluster/NodeDeathWatcher.swift index 00d1da287..3a606b135 100644 --- a/Sources/DistributedActors/Cluster/NodeDeathWatcher.swift +++ b/Sources/DistributedActors/Cluster/NodeDeathWatcher.swift @@ -55,7 +55,7 @@ internal final class NodeDeathWatcherInstance: NodeDeathWatcher { } /// Mapping between remote node, and actors which have watched some actors on given remote node. - private var remoteWatchers: [UniqueNode: Set] = [:] + private var remoteWatchers: [UniqueNode: Set<_AddressableActorRef>] = [:] private var remoteWatchCallbacks: [UniqueNode: Set] = [:] init(selfNode: UniqueNode) { @@ -64,7 +64,7 @@ internal final class NodeDeathWatcherInstance: NodeDeathWatcher { } @available(*, deprecated, message: "will be replaced by distributed actor / closure version") - func onActorWatched(by watcher: AddressableActorRef, remoteNode: UniqueNode) { + func onActorWatched(by watcher: _AddressableActorRef, remoteNode: UniqueNode) { guard !self.nodeTombstones.contains(remoteNode) else { // the system the watcher is attempting to watch has terminated before the watch has been processed, // thus we have to immediately reply with a termination system message, as otherwise it would never receive one @@ -149,7 +149,7 @@ internal protocol NodeDeathWatcher { /// Called when the `watcher` watches a remote actor which resides on the `remoteNode`. /// A failure detector may have to start monitoring this node using some internal mechanism, /// in order to be able to signal the watcher in case the node terminates (e.g. the node crashes). - func onActorWatched(by watcher: AddressableActorRef, remoteNode: UniqueNode) + func onActorWatched(by watcher: _AddressableActorRef, remoteNode: UniqueNode) /// Called when the cluster membership changes. /// @@ -171,8 +171,8 @@ enum NodeDeathWatcherShell { /// Message protocol for interacting with the failure detector. /// By default, the `FailureDetectorShell` handles these messages by interpreting them with an underlying `FailureDetector`, /// it would be possible however to allow implementing the raw protocol by user actors if we ever see the need for it. - internal enum Message: NonTransportableActorMessage { - case remoteActorWatched(watcher: AddressableActorRef, remoteNode: UniqueNode) + internal enum Message: _NotActuallyCodableMessage { + case remoteActorWatched(watcher: _AddressableActorRef, remoteNode: UniqueNode) case remoteDistributedActorWatched(remoteNode: UniqueNode, watcherID: ClusterSystem.ActorID, nodeTerminated: @Sendable (UniqueNode) async -> Void) case removeWatcher(watcherID: ClusterSystem.ActorID) case membershipSnapshot(Cluster.Membership) diff --git a/Sources/DistributedActors/Cluster/Reception/OperationLogDistributedReceptionist.swift b/Sources/DistributedActors/Cluster/Reception/OperationLogDistributedReceptionist.swift index 0c2384440..903d48eda 100644 --- a/Sources/DistributedActors/Cluster/Reception/OperationLogDistributedReceptionist.swift +++ b/Sources/DistributedActors/Cluster/Reception/OperationLogDistributedReceptionist.swift @@ -85,7 +85,6 @@ import Logging /// number observations. /// /// #### Optimization: "Blip" Registration Replication Avoidance -// TODO: This is done "automatically" once we do log compaction /// We define a "blip registration" as a registration of an actor, which immediately (or very quickly) after registering /// terminates. It can be argued it is NOT useful to replicate the very existence of such short lived actor to other peers, /// as even if they'd act on the `register`, it'd be immediately followed by `remove` and/or a termination signal. @@ -139,22 +138,23 @@ import Logging /// (Note that we simply always `ack(latest)` and if in the meantime the pusher got more updates, it'll push those to us as well. /// /// - SeeAlso: [Wikipedia: Atomic broadcast](https://en.wikipedia.org/wiki/Atomic_broadcast) -// TODO: compact the log whenever we know all members of the cluster have seen -// TODO: Optimization: gap collapsing: [+a,+b,+c,-c] -> [+a,+b,gap(until:4)] -// TODO: Optimization: head collapsing: [+a,+b,+c,-b,-a] -> [gap(until:2),+c,-b] -// -// TODO: slow/fast ticks: When we know there's nothing new to share with others, we use the slow tick (which should be increased to 5 seconds or less) -// when we received a register() or observed an "ahead" receptionist, we should schedule a "fast tick" in order to more quickly spread this information -// This should still be done on a delay, e.g. if we are receiving many registrations, we want to get the benefit of batching them up before sending after all -// The fast tick could be 1s or 0.5s for example as a default. public distributed actor OpLogDistributedReceptionist: DistributedReceptionist, CustomStringConvertible { + // TODO: compact the log whenever we know all members of the cluster have seen + // TODO: Optimization: gap collapsing: [+a,+b,+c,-c] -> [+a,+b,gap(until:4)] + // TODO: Optimization: head collapsing: [+a,+b,+c,-b,-a] -> [gap(until:2),+c,-b] + // + // TODO: slow/fast ticks: When we know there's nothing new to share with others, we use the slow tick (which should be increased to 5 seconds or less) + // when we received a register() or observed an "ahead" receptionist, we should schedule a "fast tick" in order to more quickly spread this information + // This should still be done on a delay, e.g. if we are receiving many registrations, we want to get the benefit of batching them up before sending after all + // The fast tick could be 1s or 0.5s for example as a default. + public typealias ActorSystem = ClusterSystem // TODO: remove this typealias ReceptionistRef = OpLogDistributedReceptionist typealias Key = DistributedReception.Key where Guest.ActorSystem == ClusterSystem - internal let instrumentation: ReceptionistInstrumentation + internal let instrumentation: _ReceptionistInstrumentation /// Stores the actual mappings of keys to actors. let storage = DistributedReceptionistStorage() @@ -1028,7 +1028,7 @@ extension OpLogDistributedReceptionist { } } - final class PublishLocalListingsTrigger: Receptionist.Message, NonTransportableActorMessage, CustomStringConvertible { + final class PublishLocalListingsTrigger: Receptionist.Message, _NotActuallyCodableMessage, CustomStringConvertible { override init() { super.init() } diff --git a/Sources/DistributedActors/Cluster/Reception/_OperationLogClusterReceptionistBehavior.swift b/Sources/DistributedActors/Cluster/Reception/_OperationLogClusterReceptionistBehavior.swift index 11ea875a6..400a660d7 100644 --- a/Sources/DistributedActors/Cluster/Reception/_OperationLogClusterReceptionistBehavior.swift +++ b/Sources/DistributedActors/Cluster/Reception/_OperationLogClusterReceptionistBehavior.swift @@ -17,14 +17,11 @@ import Logging // ==== ---------------------------------------------------------------------------------------------------------------- // MARK: Cluster (OpLog) Receptionist -/// ClusterReceptionist namespace -public enum ClusterReceptionist {} - public final class _OperationLogClusterReceptionist { typealias Message = Receptionist.Message typealias ReceptionistRef = _ActorRef - internal let instrumentation: ReceptionistInstrumentation + internal let instrumentation: _ReceptionistInstrumentation // ==== ------------------------------------------------------------------------------------------------------------ // MARK: Timer Keys @@ -80,7 +77,7 @@ public final class _OperationLogClusterReceptionist { /// gives a good idea how far "behind" we are with regards to changed performed at that peer. var appliedSequenceNrs: VersionVector - internal init(settings: ReceptionistSettings, instrumentation: ReceptionistInstrumentation = NoopReceptionistInstrumentation()) { + internal init(settings: ReceptionistSettings, instrumentation: _ReceptionistInstrumentation = NoopReceptionistInstrumentation()) { self.instrumentation = instrumentation self.ops = .init(batchSize: settings.syncBatchSize) @@ -253,7 +250,7 @@ extension _OperationLogClusterReceptionist { } func notifySubscribers(of key: AnyReceptionKey) { - let registrations: Set = self.storage.registrations(forKey: key) ?? [] + let registrations: Set<_AddressableActorRef> = self.storage.registrations(forKey: key) ?? [] guard let subscriptions = self.storage.subscriptions(forKey: key) else { self.instrumentation.listingPublished(key: key, subscribers: 0, registrations: registrations.count) @@ -777,7 +774,7 @@ extension _OperationLogClusterReceptionist { } } - class PeriodicAckTick: Receptionist.Message, NonTransportableActorMessage, CustomStringConvertible { + class PeriodicAckTick: Receptionist.Message, _NotActuallyCodableMessage, CustomStringConvertible { override init() { super.init() } @@ -791,7 +788,7 @@ extension _OperationLogClusterReceptionist { } } - class PublishLocalListingsTrigger: Receptionist.Message, NonTransportableActorMessage, CustomStringConvertible { + class PublishLocalListingsTrigger: Receptionist.Message, _NotActuallyCodableMessage, CustomStringConvertible { override init() { super.init() } diff --git a/Sources/DistributedActors/Cluster/SWIM/SWIM+Messages.swift b/Sources/DistributedActors/Cluster/SWIM/SWIM+Messages.swift index 047ca0cf2..723a12ad1 100644 --- a/Sources/DistributedActors/Cluster/SWIM/SWIM+Messages.swift +++ b/Sources/DistributedActors/Cluster/SWIM/SWIM+Messages.swift @@ -16,13 +16,13 @@ import ClusterMembership import SWIM extension SWIM { - public enum Message: ActorMessage { + public enum Message: Codable { case remote(SWIM.RemoteMessage) case local(SWIM.LocalMessage) case _testing(SWIM._TestingMessage) } - public enum RemoteMessage: ActorMessage { + public enum RemoteMessage: Codable { case ping(pingOrigin: SWIM.PingOriginRef, payload: SWIM.GossipPayload, sequenceNumber: SWIM.SequenceNumber) /// "Ping Request" requests a SWIM probe. @@ -31,7 +31,7 @@ extension SWIM { case pingResponse(SWIM.PingResponse) } - public enum LocalMessage: NonTransportableActorMessage { + public enum LocalMessage: _NotActuallyCodableMessage { /// Periodic message used to wake up SWIM and perform a random ping probe among its members. case protocolPeriodTick @@ -75,7 +75,7 @@ extension SWIM { case confirmDead(UniqueNode) } - public enum _TestingMessage: NonTransportableActorMessage { + public enum _TestingMessage: _NotActuallyCodableMessage { /// FOR TESTING: Expose the entire membership state case _getMembershipState(replyTo: _ActorRef<[SWIM.Member]>) } diff --git a/Sources/DistributedActors/Cluster/Transport/RemoteClusterActorPersonality.swift b/Sources/DistributedActors/Cluster/Transport/RemoteClusterActorPersonality.swift index 531697e53..50efcec18 100644 --- a/Sources/DistributedActors/Cluster/Transport/RemoteClusterActorPersonality.swift +++ b/Sources/DistributedActors/Cluster/Transport/RemoteClusterActorPersonality.swift @@ -23,7 +23,7 @@ import Atomics /// by being sent from a remote note, one can safely assume that the actor _existed_, however nothing /// is clear about its current lifecycle state (it may have already terminated the moment the message was sent, /// or even before then). To obtain lifecycle status of this actor the usual strategy of watching it needs to be employed. -public final class _RemoteClusterActorPersonality { +public final class _RemoteClusterActorPersonality { let id: ActorID let clusterShell: ClusterShell @@ -68,10 +68,6 @@ public final class _RemoteClusterActorPersonality { // we only pay the cost of this atomic read, rather than the expensive hashmap lookup and locking! private var _cachedAssociation: ManagedAtomicLazyReference - // TODO: move instrumentation into the transport? - @usableFromInline - internal var instrumentation: ActorInstrumentation! - init(shell: ClusterShell, id: ActorID, system: ClusterSystem) { precondition(id._isRemote, "RemoteActorRef MUST be remote. ActorID was: \(String(reflecting: id))") @@ -84,8 +80,6 @@ public final class _RemoteClusterActorPersonality { self.clusterShell = shell self.system = system - - self.instrumentation = system.settings.instrumentation.makeActorInstrumentation(self, id) // TODO: could be in association, per node } @usableFromInline @@ -95,7 +89,6 @@ public final class _RemoteClusterActorPersonality { switch self.association { case .association(let association): association.sendUserMessage(envelope: Payload(payload: .message(message)), recipient: self.id) - self.instrumentation.actorTold(message: message, from: nil) case .tombstone: // TODO: metric for dead letter: self.instrumentation.deadLetter(message: message, from: nil) self.deadLetters.tell(message, file: file, line: line) @@ -109,7 +102,6 @@ public final class _RemoteClusterActorPersonality { switch self.association { case .association(let association): association.sendInvocation(invocation, recipient: self.id) - self.instrumentation.actorTold(message: invocation, from: nil) case .tombstone: // TODO: metric for dead letter: self.instrumentation.deadLetter(message: message, from: nil) self.system.deadLetters.tell(DeadLetter(invocation, recipient: self.id), file: file, line: line) @@ -125,7 +117,6 @@ public final class _RemoteClusterActorPersonality { switch self.association { case .association(let association): association.sendSystemMessage(message, recipient: self.id) - self.instrumentation.actorTold(message: message, from: nil) case .tombstone: // TODO: metric for dead letter: self.instrumentation.deadLetter(message: message, from: nil) self.system.personalDeadLetters(recipient: self.id).tell(message, file: file, line: line) @@ -146,7 +137,7 @@ public final class _RemoteClusterActorPersonality { } } - func _unsafeAssumeCast(to: NewMessage.Type) -> _RemoteClusterActorPersonality { + func _unsafeAssumeCast(to: NewMessage.Type) -> _RemoteClusterActorPersonality { _RemoteClusterActorPersonality(shell: self.clusterShell, id: self.id, system: self.system) } } diff --git a/Sources/DistributedActors/Cluster/Transport/TransportPipelines.swift b/Sources/DistributedActors/Cluster/Transport/TransportPipelines.swift index 3257fd164..18ee67964 100644 --- a/Sources/DistributedActors/Cluster/Transport/TransportPipelines.swift +++ b/Sources/DistributedActors/Cluster/Transport/TransportPipelines.swift @@ -321,7 +321,6 @@ final class OutboundSerializationHandler: ChannelOutboundHandler { serializationPromise.futureResult.whenComplete { switch $0 { case .success(let serialized): - // force unwrapping here is safe because we read exactly the amount of readable bytes let wireEnvelope = Wire.Envelope( recipient: transportEnvelope.recipient, payload: serialized.buffer, diff --git a/Sources/DistributedActors/Cluster/Transport/WireMessages.swift b/Sources/DistributedActors/Cluster/Transport/WireMessages.swift index 48a802696..c6a2ddc54 100644 --- a/Sources/DistributedActors/Cluster/Transport/WireMessages.swift +++ b/Sources/DistributedActors/Cluster/Transport/WireMessages.swift @@ -22,7 +22,7 @@ internal enum Wire { typealias Message = WireMessage /// The wire protocol version is the DistributedActors version (at least now) - public typealias Version = DistributedActors.Version + public typealias Version = ClusterSystem.Version /// Envelope type carrying messages over the network. struct Envelope: Codable { diff --git a/Sources/DistributedActors/ClusterSystem.swift b/Sources/DistributedActors/ClusterSystem.swift index c25f51c7f..dbaed58fb 100644 --- a/Sources/DistributedActors/ClusterSystem.swift +++ b/Sources/DistributedActors/ClusterSystem.swift @@ -184,7 +184,7 @@ public class ClusterSystem: DistributedActorSystem, @unchecked Sendable { // ==== ---------------------------------------------------------------------------------------------------------------- // MARK: Shutdown private var shutdownReceptacle = BlockingReceptacle() - private let shutdownLock = Lock() + internal let shutdownLock = Lock() /// Greater than 0 shutdown has been initiated / is in progress. private let shutdownFlag: ManagedAtomic = .init(0) @@ -488,6 +488,8 @@ public class ClusterSystem: DistributedActorSystem, @unchecked Sendable { return Shutdown(receptacle: self.shutdownReceptacle) } + self.shutdownLock.lock() + /// Down this member as part of shutting down; it may have enough time to notify other nodes on an best effort basis. if let myselfMember = self.cluster.membershipSnapshot.uniqueMember(self.cluster.uniqueNode) { self.cluster.down(member: myselfMember) @@ -495,42 +497,44 @@ public class ClusterSystem: DistributedActorSystem, @unchecked Sendable { self.settings.plugins.stopAll(self) - queue.async { - self.log.log(level: .debug, "Shutting down actor system [\(self.name)]. All actors will be stopped.", file: #file, function: #function, line: #line) - if let cluster = self._cluster { - let receptacle = BlockingReceptacle() - cluster.ref.tell(.command(.shutdown(receptacle))) - receptacle.wait() - } - self.userProvider.stopAll() - self.systemProvider.stopAll() - self.dispatcher.shutdown() - self.downing = nil - - self._associationTombstoneCleanupTask?.cancel() - self._associationTombstoneCleanupTask = nil + self.log.log(level: .debug, "Shutting down actor system [\(self.name)]. All actors will be stopped.", file: #file, function: #function, line: #line) + defer { + self.shutdownLock.unlock() + } - do { - try self._eventLoopGroup.syncShutdownGracefully() - // self._receptionistRef = self.deadLetters.adapted() - } catch { - self.shutdownReceptacle.offerOnce(error) - afterShutdownCompleted(error) - } + if let cluster = self._cluster { + let receptacle = BlockingReceptacle() + cluster.ref.tell(.command(.shutdown(receptacle))) + receptacle.wait() + } + self.userProvider.stopAll() + self.systemProvider.stopAll() + self.dispatcher.shutdown() + self.downing = nil - /// Only once we've shutdown all dispatchers and loops, we clear cycles between the serialization and system, - /// as they should never be invoked anymore. - /* - self.lazyInitializationLock.withWriterLockVoid { - // self._serialization = nil // FIXME: need to release serialization - } - */ - _ = self._clusterStore.storeIfNilThenLoad(Box(nil)) + self._associationTombstoneCleanupTask?.cancel() + self._associationTombstoneCleanupTask = nil - self.shutdownReceptacle.offerOnce(nil) - afterShutdownCompleted(nil) + do { + try self._eventLoopGroup.syncShutdownGracefully() + // self._receptionistRef = self.deadLetters.adapted() + } catch { + self.shutdownReceptacle.offerOnce(error) + afterShutdownCompleted(error) } + /// Only once we've shutdown all dispatchers and loops, we clear cycles between the serialization and system, + /// as they should never be invoked anymore. + /* + self.lazyInitializationLock.withWriterLockVoid { + // self._serialization = nil // FIXME: need to release serialization + } + */ + _ = self._clusterStore.storeIfNilThenLoad(Box(nil)) + + self.shutdownReceptacle.offerOnce(nil) + afterShutdownCompleted(nil) + return Shutdown(receptacle: self.shutdownReceptacle) } } @@ -562,7 +566,7 @@ extension ClusterSystem: _ActorRefFactory { _ naming: _ActorNaming, of type: Message.Type = Message.self, props: _Props = _Props(), file: String = #file, line: UInt = #line, _ behavior: _Behavior - ) throws -> _ActorRef where Message: ActorMessage { + ) throws -> _ActorRef where Message: Codable { try self.serialization._ensureSerializer(type, file: file, line: line) return try self._spawn(using: self.userProvider, behavior, name: naming, props: props) } @@ -577,7 +581,7 @@ extension ClusterSystem: _ActorRefFactory { public func _spawnSystemActor( _ naming: _ActorNaming, _ behavior: _Behavior, props: _Props = _Props() ) throws -> _ActorRef - where Message: ActorMessage + where Message: Codable { try self.serialization._ensureSerializer(Message.self) return try self._spawn(using: self.systemProvider, behavior, name: naming, props: props) @@ -596,7 +600,7 @@ extension ClusterSystem: _ActorRefFactory { internal func _prepareSystemActor( _ naming: _ActorNaming, _ behavior: _Behavior, props: _Props = _Props() ) throws -> LazyStart - where Message: ActorMessage + where Message: Codable { // try self._serialization._ensureSerializer(Message.self) let ref = try self._spawn(using: self.systemProvider, behavior, name: naming, props: props, startImmediately: false) @@ -609,7 +613,7 @@ extension ClusterSystem: _ActorRefFactory { _ behavior: _Behavior, name naming: _ActorNaming, props: _Props = _Props(), startImmediately: Bool = true ) throws -> _ActorRef - where Message: ActorMessage + where Message: Codable { try behavior.validateAsInitial() @@ -653,7 +657,7 @@ extension ClusterSystem: _ActorRefFactory { _ behavior: _Behavior, id: ActorID, props: _Props = _Props(), startImmediately: Bool = true ) throws -> _ActorRef - where Message: ActorMessage + where Message: Codable { try behavior.validateAsInitial() @@ -709,7 +713,7 @@ extension ClusterSystem: _ActorRefFactory { public func _spawnDistributedActor( _ behavior: _Behavior, identifiedBy id: ClusterSystem.ActorID - ) -> _ActorRef where Message: ActorMessage { + ) -> _ActorRef where Message: Codable { var props = _Props.forSpawn props._distributedActor = true @@ -739,7 +743,7 @@ extension ClusterSystem: _ActorTreeTraversable { } } - public func _traverse(context: _TraversalContext, _ visit: (_TraversalContext, AddressableActorRef) -> _TraversalDirective) -> _TraversalResult { + public func _traverse(context: _TraversalContext, _ visit: (_TraversalContext, _AddressableActorRef) -> _TraversalDirective) -> _TraversalResult { let systemTraversed: _TraversalResult = self.systemProvider._traverse(context: context, visit) switch systemTraversed { @@ -760,18 +764,18 @@ extension ClusterSystem: _ActorTreeTraversable { } } - internal func _traverseAll(_ visit: (_TraversalContext, AddressableActorRef) -> _TraversalDirective) -> _TraversalResult { + internal func _traverseAll(_ visit: (_TraversalContext, _AddressableActorRef) -> _TraversalDirective) -> _TraversalResult { let context = _TraversalContext() return self._traverse(context: context, visit) } @discardableResult - internal func _traverseAllVoid(_ visit: (_TraversalContext, AddressableActorRef) -> _TraversalDirective) -> _TraversalResult { + internal func _traverseAllVoid(_ visit: (_TraversalContext, _AddressableActorRef) -> _TraversalDirective) -> _TraversalResult { self._traverseAll(visit) } /// INTERNAL API: Not intended to be used by end users. - public func _resolve(context: ResolveContext) -> _ActorRef { + public func _resolve(context: ResolveContext) -> _ActorRef { do { try context.system.serialization._ensureSerializer(Message.self) } catch { @@ -800,7 +804,7 @@ extension ClusterSystem: _ActorTreeTraversable { } } - public func _resolveUntyped(id: ActorID) -> AddressableActorRef { + public func _resolveUntyped(id: ActorID) -> _AddressableActorRef { return self._resolveUntyped(context: .init(id: id, system: self)) } @@ -808,12 +812,12 @@ extension ClusterSystem: _ActorTreeTraversable { return try StubDistributedActor.resolve(id: identity, using: self) } - public func _resolveUntyped(context: ResolveContext) -> AddressableActorRef { + public func _resolveUntyped(context: ResolveContext) -> _AddressableActorRef { guard let selector = context.selectorSegments.first else { return context.personalDeadLetters.asAddressable } - var resolved: AddressableActorRef? + var resolved: _AddressableActorRef? // TODO: The looping through transports could be ineffective... but realistically we dont have many // TODO: realistically we ARE becoming a transport and thus should be able to remove 'transports' entirely for transport in context.system.settings.transports { @@ -983,11 +987,14 @@ extension ClusterSystem { guard let clusterShell = _cluster else { throw RemoteCallError.clusterAlreadyShutDown } + guard self.shutdownFlag.load(ordering: .relaxed) == 0 else { + throw RemoteCallError.clusterAlreadyShutDown + } let recipient = _RemoteClusterActorPersonality(shell: clusterShell, id: actor.id._asRemote, system: self) let arguments = invocation.arguments - let reply: RemoteCallReply = try await self.withCallID { callID in + let reply: RemoteCallReply = try await self.withCallID(on: actor.id, target: target) { callID in let invocation = InvocationMessage( callID: callID, targetIdentifier: target.identifier, @@ -1018,11 +1025,14 @@ extension ClusterSystem { guard let clusterShell = self._cluster else { throw RemoteCallError.clusterAlreadyShutDown } + guard self.shutdownFlag.load(ordering: .relaxed) == 0 else { + throw RemoteCallError.clusterAlreadyShutDown + } let recipient = _RemoteClusterActorPersonality(shell: clusterShell, id: actor.id._asRemote, system: self) let arguments = invocation.arguments - let reply: RemoteCallReply<_Done> = try await self.withCallID { callID in + let reply: RemoteCallReply<_Done> = try await self.withCallID(on: actor.id, target: target) { callID in let invocation = InvocationMessage( callID: callID, targetIdentifier: target.identifier, @@ -1037,6 +1047,8 @@ extension ClusterSystem { } private func withCallID( + on actorID: ActorID, + target: RemoteCallTarget, body: (CallID) -> Void ) async throws -> Reply where Reply: AnyRemoteCallReply @@ -1044,15 +1056,35 @@ extension ClusterSystem { let callID = UUID() let timeout = RemoteCall.timeout ?? self.settings.defaultRemoteCallTimeout - let timeoutTask: Task = Task { + let timeoutTask: Task = Task.detached { await Task.sleep(UInt64(timeout.nanoseconds)) guard !Task.isCancelled else { return } + self.inFlightCallLock.withLockVoid { - if let continuation = self._inFlightCalls.removeValue(forKey: callID) { - continuation.resume(throwing: RemoteCallError.timedOut(TimeoutError(message: "Remote call timed out", timeout: timeout))) + guard let continuation = self._inFlightCalls.removeValue(forKey: callID) else { + // remoteCall was already completed successfully, nothing to do here + return } + + let error: Error + if self.isShuttingDown { + // If the system is shutting down, we should offer a more specific error; + // + // We may not be getting responses simply because we've shut down associations + // and cannot receive them anymore. + // Some subsystems may ignore those errors, since they are "expected". + // + // If we're shutting down, it is okay to not get acknowledgements to calls for example, + // and we don't care about them missing -- we're shutting down anyway. + error = RemoteCallError.clusterAlreadyShutDown + } else { + error = RemoteCallError.timedOut( + TimeoutError(message: "Remote call [\(callID)] to [\(target)](\(actorID)) timed out", timeout: timeout)) + } + + continuation.resume(throwing: error) } } defer { @@ -1283,7 +1315,7 @@ public enum ResolveError: DistributedActorSystemError { /// **CAUTION** Not calling `wakeUp` will prevent the actor from ever running /// and can cause leaks. Also `wakeUp` MUST NOT be called more than once, /// as that would violate the single-threaded execution guaranteed of actors. -internal struct LazyStart { +internal struct LazyStart { let ref: _ActorRef init(ref: _ActorRef) { diff --git a/Sources/DistributedActors/ClusterSystemSettings.swift b/Sources/DistributedActors/ClusterSystemSettings.swift index 6354b8059..c0064ef42 100644 --- a/Sources/DistributedActors/ClusterSystemSettings.swift +++ b/Sources/DistributedActors/ClusterSystemSettings.swift @@ -40,7 +40,7 @@ public struct ClusterSystemSettings { public var receptionist: ReceptionistSettings = .default - public var transports: [_InternalActorTransport] = [] + internal var transports: [_InternalActorTransport] = [] public var serialization: Serialization.Settings = .default // ==== ------------------------------------------------------------------------------------------------------------ @@ -124,6 +124,7 @@ public struct ClusterSystemSettings { ) /// Defines the Time-To-Live of an association, i.e. when it shall be completely dropped from the tombstones list. + /// /// An association ("unique connection identifier between two nodes") is kept as tombstone when severing a connection between nodes, /// in order to avoid accidental re-connections to given node. Once a node has been downed, removed, and disassociated, it MUST NOT be /// communicated with again. Tombstones are used to ensure this, even if the downed ("zombie") node, attempts to reconnect. @@ -136,12 +137,12 @@ public struct ClusterSystemSettings { // MARK: Cluster protocol versioning /// `ProtocolVersion` to be used when exposing `UniqueNode` for node configured by using these settings. - public var protocolVersion: DistributedActors.Version { + public var protocolVersion: ClusterSystem.Version { self._protocolVersion } /// FOR TESTING ONLY: Exposed for testing handshake negotiation while joining nodes of different versions. - internal var _protocolVersion: DistributedActors.Version = DistributedActorsProtocolVersion + internal var _protocolVersion: ClusterSystem.Version = ClusterSystem.protocolVersion // ==== ------------------------------------------------------------------------------------------------------------ // MARK: Cluster.Membership Gossip @@ -230,10 +231,13 @@ public struct ClusterSystemSettings { } public var metrics: MetricsSettings = .default(rootName: nil) - public var instrumentation: InstrumentationSettings = .default + + /// Internal only; Instrumentation configuration, allowing to set instrumentation objects to be called by actor system internals, for purpose of metrics collection etc. + internal var instrumentation: InstrumentationSettings = .default /// Installs a global backtrace (on fault) pretty-print facility upon actor system start. - public var installSwiftBacktrace: Bool = true + @available(*, deprecated, message: "Backtrace will not longer be offered by the actor system by default, and has to be depended on by end-users") + internal var installSwiftBacktrace: Bool = true // FIXME: should have more proper config section public var threadPoolSize: Int = ProcessInfo.processInfo.activeProcessorCount @@ -267,9 +271,8 @@ extension Array where Element == _InternalActorTransport { // ==== ---------------------------------------------------------------------------------------------------------------- // MARK: Logging Settings -/// Note that some of these settings would be obsolete if we had a nice configurable LogHandler which could selectively -/// log some labelled loggers and some not. Until we land such log handler we have to manually in-project opt-in/-out -/// of logging some subsystems. +/// Settings which allow customizing logging behavior by various sub-systems of the cluster system. +/// public struct LoggingSettings { public static var `default`: LoggingSettings { .init() @@ -308,23 +311,18 @@ public struct LoggingSettings { } internal var customizedLogger: Bool = false - internal var _logger: Logger = LoggingSettings.makeDefaultLogger() - static func makeDefaultLogger() -> Logger { - Logger(label: "ClusterSystem-initializing") // replaced by specific system name during startup - } + internal var _logger = Logger(label: "ClusterSystem-initializing") // replaced by specific system name during startup - // TODO: hope to remove this once a StdOutLogHandler lands that has formatting support; - // logs are hard to follow with not consistent order of metadata etc (like system address etc). - public var useBuiltInFormatter: Bool = true + internal var useBuiltInFormatter: Bool = true // ==== ------------------------------------------------------------------------------------------------------------ // MARK: Verbose logging of sub-systems (again, would not be needed with configurable appenders) /// Log all detailed timer start/cancel events - public var verboseTimers = false + internal var verboseTimers = false /// Log all actor `spawn` events - public var verboseSpawning = false + internal var verboseSpawning = false } // ==== ---------------------------------------------------------------------------------------------------------------- @@ -345,41 +343,36 @@ extension ClusterSystemSettings { // MARK: Instrumentation Settings extension ClusterSystemSettings { - public struct InstrumentationSettings { + /// Prototype instrumentation mechanism settings. + /// + /// These can be used to inject implementations of instrumentation into the cluster system, + /// which are invoked at apropriate times and can be uset to emit metrics about a specific `ClusterSystem` sub-system. + struct InstrumentationSettings { /// Default set of enabled instrumentations, based on current operating system. /// /// On Apple platforms, this includes the `OSSignpostInstrumentationProvider` provided instrumentations, /// as they carry only minimal overhead in release builds when the signposts are not active. /// /// You may easily installing any kind of instrumentations, regardless of platform, by using `.none` instead of `.default`. - public static var `default`: InstrumentationSettings { + static var `default`: InstrumentationSettings { .init() } - public static var none: InstrumentationSettings { + static var none: InstrumentationSettings { InstrumentationSettings() } - /// - SeeAlso: `ActorInstrumentation` - public var makeActorInstrumentation: (AnyObject, ActorID) -> ActorInstrumentation = { id, actorID in - NoopActorInstrumentation(id: id, actorID: actorID) - } - /// - SeeAlso: `_InternalActorTransportInstrumentation` public var makeInternalActorTransportInstrumentation: () -> _InternalActorTransportInstrumentation = { () in Noop_InternalActorTransportInstrumentation() } /// - SeeAlso: `ReceptionistInstrumentation` - public var makeReceptionistInstrumentation: () -> ReceptionistInstrumentation = { () in + var makeReceptionistInstrumentation: () -> _ReceptionistInstrumentation = { () in NoopReceptionistInstrumentation() } - public mutating func configure(with provider: ClusterSystemInstrumentationProvider) { - if let instrumentFactory = provider.actorInstrumentation { - self.makeActorInstrumentation = instrumentFactory - } - + mutating func configure(with provider: ClusterSystemInstrumentationProvider) { if let instrumentFactory = provider.actorTransportInstrumentation { self.makeInternalActorTransportInstrumentation = instrumentFactory } @@ -391,16 +384,19 @@ extension ClusterSystemSettings { } } -public protocol ClusterSystemInstrumentationProvider { - var actorInstrumentation: ((AnyObject, ActorID) -> ActorInstrumentation)? { get } +protocol ClusterSystemInstrumentationProvider { var actorTransportInstrumentation: (() -> _InternalActorTransportInstrumentation)? { get } - var receptionistInstrumentation: (() -> ReceptionistInstrumentation)? { get } + var receptionistInstrumentation: (() -> _ReceptionistInstrumentation)? { get } } // ==== ---------------------------------------------------------------------------------------------------------------- // MARK: Cluster Service Discovery Settings /// Configure initial contact point discovery to use a `ServiceDiscovery` implementation. +/// +/// By providing a service discovery implementation, the `ClusterSystem` will reach out to nodes discovered using this mechanism, +/// in an attempt to form (or join) a cluster with those nodes. These typically should include a few initial contact points, but can also include +/// all the nodes of an existing cluster. public struct ServiceDiscoverySettings { internal let implementation: AnyServiceDiscovery private let _subscribe: (@escaping (Result<[Node], Error>) -> Void, @escaping (CompletionReason) -> Void) -> CancellationToken diff --git a/Sources/DistributedActors/DeadLetters.swift b/Sources/DistributedActors/DeadLetters.swift index 4f2da6921..c2c21c5e1 100644 --- a/Sources/DistributedActors/DeadLetters.swift +++ b/Sources/DistributedActors/DeadLetters.swift @@ -36,7 +36,7 @@ import Logging /// mail would be called such, and shipped to one specific place to deal with these letters. /// /// - SeeAlso: [Dead letter office](https://en.wikipedia.org/wiki/Dead_letter_office) on Wikipedia. -public struct DeadLetter: NonTransportableActorMessage { +public struct DeadLetter: _NotActuallyCodableMessage { let message: Any let recipient: ActorID? @@ -58,7 +58,7 @@ public struct DeadLetter: NonTransportableActorMessage { extension ClusterSystem { /// Dead letters reference dedicated to a specific address. - public func personalDeadLetters(type: Message.Type = Message.self, recipient: ActorID) -> _ActorRef { + public func personalDeadLetters(type: Message.Type = Message.self, recipient: ActorID) -> _ActorRef { // TODO: rather could we send messages to self._deadLetters with enough info so it handles properly? guard recipient.uniqueNode == self.settings.uniqueBindNode else { diff --git a/Sources/DistributedActors/DistributedActor+Messages.swift b/Sources/DistributedActors/DistributedActor+Messages.swift index 5c01b595c..373fe148c 100644 --- a/Sources/DistributedActors/DistributedActor+Messages.swift +++ b/Sources/DistributedActors/DistributedActor+Messages.swift @@ -18,13 +18,13 @@ import struct Foundation.Data // FIXME(distributed): we need to get rid of this all of this... probably means having to remove the entire Ref based infrastructure /// `Void` equivalent but `Codable`. -public enum _Done: String, ActorMessage { +public enum _Done: String, Codable { case done } //// TODO(distributed): remove this, actually system._spawn the underlying reference for the reserved address // public protocol __AnyDistributedClusterActor { -// static func _spawnAny(instance: Self, on system: ClusterSystem) throws -> AddressableActorRef +// static func _spawnAny(instance: Self, on system: ClusterSystem) throws -> _AddressableActorRef // } // FIXME: workaround (!) @@ -38,7 +38,7 @@ extension DistributedActor where ActorSystem == ClusterSystem { } } - static func _spawnAny(instance: Self, on system: ActorSystem) throws -> AddressableActorRef { + static func _spawnAny(instance: Self, on system: ActorSystem) throws -> _AddressableActorRef { self._spawn(instance: instance, on: system).asAddressable } } diff --git a/Sources/DistributedActors/DistributedActors.docc/Clustering.md b/Sources/DistributedActors/DistributedActors.docc/Clustering.md index 94f56150c..e3fe9a35b 100644 --- a/Sources/DistributedActors/DistributedActors.docc/Clustering.md +++ b/Sources/DistributedActors/DistributedActors.docc/Clustering.md @@ -268,3 +268,24 @@ distributed actor Boss: LifecycleWatch { } } ``` + +## Customizing Remote Calls + +Remote calls are at the heart of what makes distributed actors actually distributed. + +A call made on a remote distributed actor reference, will cross network boundaries, and therefore may way due to +network issues, message loss, serialization errors, or other reasons such as the recipient node crashing as it +processes the message. Even replies to remote calls could sometimes fail being delivered, so you might need to +design your distributed actors with idempotency (the resilience of a method being called more than once, e.g. due to a retry) in mind. + +By default, to avoid "hanging" a remote caller forever on a suspended remote call as the recipient node fails to reply to it, +for example because it (or the network itself), are currently unresponsive, remote calls have a default timeout configured, +and if no reply is received within this duration, the call will fail with a ``RemoteCallError``. + +You can configure the default timeout used by the cluster system during its initialization: + +```swift +ClusterSystem() { settings in + settings. +} +``` diff --git a/Sources/DistributedActors/DistributedActors.docc/DistributedActors.md b/Sources/DistributedActors/DistributedActors.docc/DistributedActors.md index 03b5df126..970089550 100644 --- a/Sources/DistributedActors/DistributedActors.docc/DistributedActors.md +++ b/Sources/DistributedActors/DistributedActors.docc/DistributedActors.md @@ -69,5 +69,5 @@ Please note that this project requires the latest **Swift 5.7** language feature ### Utilities - ``ExponentialBackoffStrategy`` -- ``VersionVector`` - ``Backoff`` +- ``VersionVector`` diff --git a/Sources/DistributedActors/EventStream.swift b/Sources/DistributedActors/EventStream.swift index f9e4f05b5..fa34e6232 100644 --- a/Sources/DistributedActors/EventStream.swift +++ b/Sources/DistributedActors/EventStream.swift @@ -20,7 +20,7 @@ import Atomics /// they terminate. /// /// `EventStream` is only meant to be used locally and does not buffer or redeliver messages. -public struct EventStream: AsyncSequence { +public struct EventStream: AsyncSequence { public typealias Element = Event internal let ref: _ActorRef> @@ -100,7 +100,7 @@ public struct EventStream: AsyncSequence { } internal enum EventStreamShell { - enum Message: NonTransportableActorMessage { // TODO: make it codable, transportability depends on the Event really + enum Message: _NotActuallyCodableMessage { // TODO: make it codable, transportability depends on the Event really /// Subscribe to receive events case subscribe(_ActorRef) /// Unsubscribe from receiving events diff --git a/Sources/DistributedActors/Gossip/Gossip+Settings.swift b/Sources/DistributedActors/Gossip/Gossip+Settings.swift index ebae348db..15035fa72 100644 --- a/Sources/DistributedActors/Gossip/Gossip+Settings.swift +++ b/Sources/DistributedActors/Gossip/Gossip+Settings.swift @@ -62,14 +62,14 @@ extension Gossiper { case manuallyIntroduced /// Automatically register this gossiper and subscribe for any others identifying under the same - /// `Reception.Key.Message>(id)`. + /// `_Reception.Key.Message>(id)`. case fromReceptionistListing(id: String) /// Automatically discover and add cluster members to the gossip group when they become reachable in `atLeast` status. /// /// Note that by default `.leaving`, `.down` and `.removed` members are NOT added to the gossip group, /// even if they were never contacted by this gossiper before. - case onClusterMember(atLeast: Cluster.MemberStatus, resolve: (Cluster.Member) -> AddressableActorRef) + case onClusterMember(atLeast: Cluster.MemberStatus, resolve: (Cluster.Member) -> _AddressableActorRef) } } } diff --git a/Sources/DistributedActors/Gossip/GossipLogic.swift b/Sources/DistributedActors/Gossip/GossipLogic.swift index 6b1cfecea..8207a7a80 100644 --- a/Sources/DistributedActors/Gossip/GossipLogic.swift +++ b/Sources/DistributedActors/Gossip/GossipLogic.swift @@ -50,7 +50,7 @@ protocol GossipLogic { /// /// Useful to implement using `PeerSelection` // TODO: OrderedSet would be the right thing here to be honest... - mutating func selectPeers(_ peers: [AddressableActorRef]) -> [AddressableActorRef] + mutating func selectPeers(_ peers: [_AddressableActorRef]) -> [_AddressableActorRef] // TODO: make a directive here /// Allows for customizing the payload for each of the selected peers. @@ -59,7 +59,7 @@ protocol GossipLogic { /// e.g. by excluding information the peer is already aware of or similar. /// /// Returning `nil` means that the peer will be skipped in this gossip round, even though it was a candidate selected by peer selection. - mutating func makePayload(target: AddressableActorRef) -> Gossip? + mutating func makePayload(target: _AddressableActorRef) -> Gossip? // ==== ------------------------------------------------------------------------------------------------------------ // MARK: Receiving gossip @@ -69,7 +69,7 @@ protocol GossipLogic { /// Note that a single gossiper instance may create _multiple_ `GossipLogic` instances, /// one for each `GossipIdentifier` it is managing. This function is guaranteed to be invoked with the /// gossip targeted to the same gossip identity as the logic's context - mutating func receiveGossip(_ gossip: Gossip, from peer: AddressableActorRef) -> Acknowledgement? + mutating func receiveGossip(_ gossip: Gossip, from peer: _AddressableActorRef) -> Acknowledgement? /// Invoked when the specific gossiped payload is acknowledged by the target. /// @@ -81,7 +81,7 @@ protocol GossipLogic { /// - peer: The target which has acknowledged the gossiped payload. /// It corresponds to the parameter that was passed to the `makePayload(target:)` which created this gossip payload. /// - gossip: - mutating func receiveAcknowledgement(_ acknowledgement: Acknowledgement, from peer: AddressableActorRef, confirming gossip: Gossip) + mutating func receiveAcknowledgement(_ acknowledgement: Acknowledgement, from peer: _AddressableActorRef, confirming gossip: Gossip) // ==== ---------------------------------------------------------------------------------------------------------------- // MARK: Local interactions / control messages @@ -93,7 +93,7 @@ protocol GossipLogic { } extension GossipLogic { - mutating func receiveAcknowledgement(_ acknowledgement: Acknowledgement, from peer: AddressableActorRef, confirming gossip: Gossip) { + mutating func receiveAcknowledgement(_ acknowledgement: Acknowledgement, from peer: _AddressableActorRef, confirming gossip: Gossip) { // ignore by default } @@ -140,13 +140,13 @@ struct GossipLogicContext { struct AnyGossipLogic: GossipLogic, CustomStringConvertible { @usableFromInline - let _selectPeers: ([AddressableActorRef]) -> [AddressableActorRef] + let _selectPeers: ([_AddressableActorRef]) -> [_AddressableActorRef] @usableFromInline - let _makePayload: (AddressableActorRef) -> Gossip? + let _makePayload: (_AddressableActorRef) -> Gossip? @usableFromInline - let _receiveGossip: (Gossip, AddressableActorRef) -> Acknowledgement? + let _receiveGossip: (Gossip, _AddressableActorRef) -> Acknowledgement? @usableFromInline - let _receiveAcknowledgement: (Acknowledgement, AddressableActorRef, Gossip) -> Void + let _receiveAcknowledgement: (Acknowledgement, _AddressableActorRef, Gossip) -> Void @usableFromInline let _receiveLocalGossipUpdate: (Gossip) -> Void @@ -171,19 +171,19 @@ struct AnyGossipLogic: GossipLogic, C self._receiveSideChannelMessage = { try l.receiveSideChannelMessage($0) } } - func selectPeers(_ peers: [AddressableActorRef]) -> [AddressableActorRef] { + func selectPeers(_ peers: [_AddressableActorRef]) -> [_AddressableActorRef] { self._selectPeers(peers) } - func makePayload(target: AddressableActorRef) -> Gossip? { + func makePayload(target: _AddressableActorRef) -> Gossip? { self._makePayload(target) } - func receiveGossip(_ gossip: Gossip, from peer: AddressableActorRef) -> Acknowledgement? { + func receiveGossip(_ gossip: Gossip, from peer: _AddressableActorRef) -> Acknowledgement? { self._receiveGossip(gossip, peer) } - func receiveAcknowledgement(_ acknowledgement: Acknowledgement, from peer: AddressableActorRef, confirming gossip: Gossip) { + func receiveAcknowledgement(_ acknowledgement: Acknowledgement, from peer: _AddressableActorRef, confirming gossip: Gossip) { self._receiveAcknowledgement(acknowledgement, peer, gossip) } diff --git a/Sources/DistributedActors/Gossip/Gossiper+Shell.swift b/Sources/DistributedActors/Gossip/Gossiper+Shell.swift index 07cd74733..d2c71a7f7 100644 --- a/Sources/DistributedActors/Gossip/Gossiper+Shell.swift +++ b/Sources/DistributedActors/Gossip/Gossiper+Shell.swift @@ -181,7 +181,7 @@ internal final class GossipShell { self.ensureNextGossipRound(context) } - let allPeers: [AddressableActorRef] = Array(self.peers).map(\.asAddressable) // TODO: some protocol Addressable so we can avoid this mapping? + let allPeers: [_AddressableActorRef] = Array(self.peers).map(\.asAddressable) // TODO: some protocol Addressable so we can avoid this mapping? guard !allPeers.isEmpty else { // no members to gossip with, skip this round @@ -280,8 +280,8 @@ internal final class GossipShell { // MARK: ConvergentGossip: Peer Discovery extension GossipShell { - static func receptionKey(id: String) -> Reception.Key<_ActorRef> { - Reception.Key(id: id) + static func receptionKey(id: String) -> _Reception.Key<_ActorRef> { + _Reception.Key(id: id) } private func initPeerDiscovery(_ context: _ActorContext) { @@ -299,7 +299,7 @@ extension GossipShell { return // too "early" status of the member } - let resolved: AddressableActorRef = resolvePeerOn(member) + let resolved: _AddressableActorRef = resolvePeerOn(member) if let peer = resolved.ref as? PeerRef { // We MUST always watch all peers we gossip with, as if they (or their nodes) were to terminate // they MUST be removed from the peer list we offer to gossip logics. Otherwise a naive gossip logic @@ -333,7 +333,7 @@ extension GossipShell { context.system.cluster.events.subscribe(onClusterEventRef) case .fromReceptionistListing(let id): - let key = Reception.Key(_ActorRef.self, id: id) + let key = _Reception.Key(_ActorRef.self, id: id) context.receptionist.registerMyself(with: key) context.log.debug("Registered with receptionist key: \(key)") diff --git a/Sources/DistributedActors/Gossip/PeerSelection.swift b/Sources/DistributedActors/Gossip/PeerSelection.swift deleted file mode 100644 index 3800b5d6c..000000000 --- a/Sources/DistributedActors/Gossip/PeerSelection.swift +++ /dev/null @@ -1,31 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Distributed Actors open source project -// -// Copyright (c) 2020 Apple Inc. and the Swift Distributed Actors project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.md for the list of Swift Distributed Actors project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import OrderedCollections - -/// Peer Selection is a common step in various gossip protocols. -/// -/// Selecting a peer may be as trivial as randomly selecting one among known actors or nodes, -/// round-robin cycling through peers or a mix thereof in which the order is randomized _but stable_ -/// which reduces the risk of gossiping to the same or starving certain peers in situations when many -/// new peers are added to the group. -/// // TODO: implement SWIMs selection in terms of this -public protocol PeerSelection { - associatedtype Peer: Hashable - - func onMembershipEvent(event: Cluster.Event) - - /// Invoked to select a list of peers - func select() -> OrderedSet -} diff --git a/Sources/DistributedActors/Instrumentation/ActorInstrumentation.swift b/Sources/DistributedActors/Instrumentation/ActorInstrumentation.swift deleted file mode 100644 index 21317e756..000000000 --- a/Sources/DistributedActors/Instrumentation/ActorInstrumentation.swift +++ /dev/null @@ -1,62 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Distributed Actors open source project -// -// Copyright (c) 2020 Apple Inc. and the Swift Distributed Actors project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.md for the list of Swift Distributed Actors project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -// ==== ---------------------------------------------------------------------------------------------------------------- -// MARK: ActorInstrumentation - -// TODO: all these to accept trace context or something similar -public protocol ActorInstrumentation { - init(id: AnyObject, actorID: ActorID) - - func actorSpawned() - func actorStopped() - func actorFailed(failure: _Supervision.Failure) - - func actorTold(message: Any, from: ActorID?) - - // TODO: Those read bad, make one that is from/to in params? - func actorAsked(message: Any, from: ActorID?) - func actorAskReplied(reply: Any?, error: Error?) - - func actorReceivedStart(message: Any, from: ActorID?) - func actorReceivedEnd(error: Error?) - - func actorWatchReceived(watchee: ActorID, watcher: ActorID) - func actorUnwatchReceived(watchee: ActorID, watcher: ActorID) -} - -// ==== ---------------------------------------------------------------------------------------------------------------- -// MARK: Noop ActorInstrumentation - -struct NoopActorInstrumentation: ActorInstrumentation { - public init(id: AnyObject, actorID: ActorID) {} - - public func actorSpawned() {} - public func actorStopped() {} - public func actorFailed(failure: _Supervision.Failure) {} - - public func actorMailboxRunStarted(mailboxCount: Int) {} - public func actorMailboxRunCompleted(processed: Int) {} - - public func actorTold(message: Any, from: ActorID?) {} - - public func actorAsked(message: Any, from: ActorID?) {} - public func actorAskReplied(reply: Any?, error: Error?) {} - - public func actorReceivedStart(message: Any, from: ActorID?) {} - public func actorReceivedEnd(error: Error?) {} - - func actorWatchReceived(watchee: ActorID, watcher: ActorID) {} - func actorUnwatchReceived(watchee: ActorID, watcher: ActorID) {} -} diff --git a/Sources/DistributedActors/Instrumentation/ActorMailboxInstrumentation.swift b/Sources/DistributedActors/Instrumentation/ActorMailboxInstrumentation.swift deleted file mode 100644 index 76575e106..000000000 --- a/Sources/DistributedActors/Instrumentation/ActorMailboxInstrumentation.swift +++ /dev/null @@ -1,35 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Distributed Actors open source project -// -// Copyright (c) 2020 Apple Inc. and the Swift Distributed Actors project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.md for the list of Swift Distributed Actors project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -// ==== ---------------------------------------------------------------------------------------------------------------- -// MARK: _ActorMailboxInstrumentation - -// TODO: all these to accept trace context or something similar -public protocol _ActorMailboxInstrumentation { - init(id: AnyObject, actorID: ActorID) - - func actorMailboxRunStarted(mailboxCount: Int) - func actorMailboxRunCompleted(processed: Int, error: Error?) -} - -// ==== ---------------------------------------------------------------------------------------------------------------- -// MARK: Noop _ActorMailboxInstrumentation - -struct NoopActorMailboxInstrumentation: _ActorMailboxInstrumentation { - public init(id: AnyObject, actorID: ActorID) {} - - public func actorMailboxRunStarted(mailboxCount: Int) {} - - public func actorMailboxRunCompleted(processed: Int, error: Error?) {} -} diff --git a/Sources/DistributedActors/Instrumentation/ReceptionistInstrumentation.swift b/Sources/DistributedActors/Instrumentation/ReceptionistInstrumentation.swift index fac645186..f8f0a12a7 100644 --- a/Sources/DistributedActors/Instrumentation/ReceptionistInstrumentation.swift +++ b/Sources/DistributedActors/Instrumentation/ReceptionistInstrumentation.swift @@ -13,9 +13,9 @@ //===----------------------------------------------------------------------===// // ==== ---------------------------------------------------------------------------------------------------------------- -// MARK: ReceptionistInstrumentation +// MARK: _ReceptionistInstrumentation -public protocol ReceptionistInstrumentation: Sendable { +protocol _ReceptionistInstrumentation: Sendable { init() func actorSubscribed(key: AnyReceptionKey, id: ActorID) @@ -30,7 +30,7 @@ public protocol ReceptionistInstrumentation: Sendable { // ==== ---------------------------------------------------------------------------------------------------------------- // MARK: Noop ReceptionistInstrumentation -struct NoopReceptionistInstrumentation: ReceptionistInstrumentation { +struct NoopReceptionistInstrumentation: _ReceptionistInstrumentation { public init() {} func actorSubscribed(key: AnyReceptionKey, id: ActorID) {} diff --git a/Sources/DistributedActors/Instrumentation/os_signpost/ActorInstrumentation+os_signpost.swift b/Sources/DistributedActors/Instrumentation/os_signpost/ActorInstrumentation+os_signpost.swift deleted file mode 100644 index 383a6faba..000000000 --- a/Sources/DistributedActors/Instrumentation/os_signpost/ActorInstrumentation+os_signpost.swift +++ /dev/null @@ -1,352 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Distributed Actors open source project -// -// Copyright (c) 2020 Apple Inc. and the Swift Distributed Actors project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.md for the list of Swift Distributed Actors project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -#if os(macOS) || os(tvOS) || os(iOS) || os(watchOS) -import Foundation -import os.log -import os.signpost - -@available(OSX 10.14, *) -@available(iOS 12.0, *) -@available(tvOS 12.0, *) -@available(watchOS 3.0, *) -public struct OSSignpostActorInstrumentation: ActorInstrumentation { - static let subsystem: StaticString = "com.apple.actors" - - static let categoryLifecycle: StaticString = "Lifecycle" - static let categoryMessages: StaticString = "Messages" - static let categorySystemMessages: StaticString = "System Messages" - - static let logLifecycle = OSLog(subsystem: "\(Self.subsystem)", category: "\(Self.categoryLifecycle)") - static let logMessages = OSLog(subsystem: "\(Self.subsystem)", category: "\(Self.categoryMessages)") - - static let logSystemMessages = OSLog(subsystem: "\(Self.subsystem)", category: "\(Self.categorySystemMessages)") - - let actorID: ActorID - let signpostID: OSSignpostID - - public init(id: AnyObject, actorID: ActorID) { - self.actorID = actorID - self.signpostID = OSSignpostID( - log: OSSignpostActorInstrumentation.logMessages, - object: id - ) - } -} - -// ==== ---------------------------------------------------------------------------------------------------------------- -// MARK: Instrumentation: Actor Lifecycle - -@available(OSX 10.14, *) -@available(iOS 12.0, *) -@available(tvOS 12.0, *) -@available(watchOS 3.0, *) -extension OSSignpostActorInstrumentation { - internal static let actorSpawnedStartFormat: StaticString = - """ - spawned;\ - node:%{public}s;\ - path:%{public}s - """ - internal static let actorSpawnedEndFormat: StaticString = - """ - stopped;\ - reason:%{public}s - """ - - public func actorSpawned() { - guard OSSignpostActorInstrumentation.logLifecycle.signpostsEnabled else { - return - } - - guard !self.actorID.name.hasPrefix("$ask") else { - // don't track ask actor's int spawned etc, since they should eventually go away - // ask timings are to be found in the Asks instrument - return - } - - os_signpost( - .event, - log: OSSignpostActorInstrumentation.logLifecycle, - name: "Actor Lifecycle", - signpostID: self.signpostID, - Self.actorSpawnedStartFormat, - "\(self.actorID.uniqueNode)", "\(self.actorID.path)" - ) - - os_signpost( - .begin, - log: OSSignpostActorInstrumentation.logLifecycle, - name: "Actor Lifecycle", - signpostID: self.signpostID, - Self.actorSpawnedStartFormat, - "\(self.actorID.uniqueNode)", "\(self.actorID.path)" - ) - } - - public func actorStopped() { - guard OSSignpostActorInstrumentation.logLifecycle.signpostsEnabled else { - return - } - - guard !self.actorID.name.hasPrefix("$ask") else { - // don't track ask actor's int spawned etc, since they should eventually go away - // ask timings are to be found in the Asks instrument - return - } - - os_signpost( - .end, - log: OSSignpostActorInstrumentation.logLifecycle, - name: "Actor Lifecycle", - signpostID: self.signpostID, - Self.actorSpawnedEndFormat, - "stop" - ) - } - - public func actorFailed(failure: _Supervision.Failure) { - guard OSSignpostActorInstrumentation.logLifecycle.signpostsEnabled else { - return - } - - guard !self.actorID.name.hasPrefix("$ask") else { - // don't track ask actor's int spawned etc, since they should eventually go away - // ask timings are to be found in the Asks instrument - return - } - - os_signpost( - .end, - log: OSSignpostActorInstrumentation.logLifecycle, - name: "Actor Lifecycle", - signpostID: self.signpostID, - Self.actorSpawnedEndFormat, - "\(failure)" - ) - } -} - -// ==== ---------------------------------------------------------------------------------------------------------------- -// MARK: Instrumentation: Actor Messages - -@available(OSX 10.14, *) -@available(iOS 12.0, *) -@available(tvOS 12.0, *) -@available(watchOS 3.0, *) -extension OSSignpostActorInstrumentation { - // ==== ---------------------------------------------------------------------------------------------------------------- - // MARK: Mailbox - - public func actorMailboxRunStarted(mailboxCount: Int) {} - - public func actorMailboxRunCompleted(processed: Int) {} - - // ==== ---------------------------------------------------------------------------------------------------------------- - // MARK: Actor Messages: Tell - - internal static let actorToldEventPattern: StaticString = - """ - actor-message-told;\ - recipient-node:%{public}s;\ - recipient-path:%{public}s;\ - sender-node:%{public}s;\ - sender-path:%{public}s;\ - message:%{public}s;\ - message-type:%{public}s - """ - - // FIXME: we need the sender() to attach properly - public func actorTold(message: Any, from: ActorID?) { - guard OSSignpostActorInstrumentation.logMessages.signpostsEnabled else { - return - } - - os_signpost( - .event, - log: OSSignpostActorInstrumentation.logMessages, - name: "Actor Message (Tell)", - signpostID: self.signpostID, - Self.actorToldEventPattern, - "\(self.actorID.uniqueNode)", "\(self.actorID.path)", - "\(from?.uniqueNode.description ?? "")", "\(from?.path.description ?? "")", - "\(message)", String(reflecting: type(of: message)) - ) - } - - // ==== ---------------------------------------------------------------------------------------------------------------- - // MARK: Actor Messages: Ask - - internal static let signpostNameActorAsk: StaticString = - "Actor Message (Ask)" - - internal static let actorAskedEventPattern: StaticString = - """ - actor-message-asked;\ - recipient-node:%{public}s;\ - recipient-path:%{public}s;\ - sender-node:%{public}s;\ - sender-path:%{public}s;\ - question:%{public}s;\ - question-type:%{public}s - """ - - internal static let actorAskRepliedEventPattern: StaticString = - """ - actor-message-ask-answered;\ - answer:%{public}s;\ - answer-type:%{public}s;\ - error:%{public}s;\ - error-type:%{public}s - """ - - public func actorAsked(message: Any, from: ActorID?) { - guard OSSignpostActorInstrumentation.logMessages.signpostsEnabled else { - return - } - - os_signpost( - .begin, - log: OSSignpostActorInstrumentation.logMessages, - name: "Actor Message (Ask)", - signpostID: self.signpostID, - Self.actorAskedEventPattern, - "\(self.actorID.uniqueNode)", "\(self.actorID.path)", - "\(from?.uniqueNode.description ?? "")", "\(from?.path.description ?? "")", - "\(message)", String(reflecting: type(of: message)) - ) - } - - public func actorAskReplied(reply: Any?, error: Error?) { - guard OSSignpostActorInstrumentation.logMessages.signpostsEnabled else { - return - } - - if let error = error { - os_signpost( - .end, - log: OSSignpostActorInstrumentation.logMessages, - name: Self.signpostNameActorAsk, - signpostID: self.signpostID, - Self.actorAskRepliedEventPattern, - "", "", "\(error)", String(reflecting: type(of: error)) - ) - return - } - - guard let message = reply else { - os_signpost( - .end, - log: OSSignpostActorInstrumentation.logMessages, - name: Self.signpostNameActorAsk, - signpostID: self.signpostID, - Self.actorAskRepliedEventPattern, - "", "", "", "" - ) - return - } - - os_signpost( - .end, - log: OSSignpostActorInstrumentation.logMessages, - name: Self.signpostNameActorAsk, - signpostID: self.signpostID, - Self.actorAskRepliedEventPattern, - "\(message)", String(reflecting: type(of: message)), "", "" - ) - } - - // ==== ------------------------------------------------------------------------------------------------------------ - // MARK: Actor Messages: Receive - - public static let actorReceivedEventPattern: StaticString = - """ - actor-message-received;\ - recipient-node:%{public}s;\ - recipient-path:%{public}s;\ - sender-node:%{public}s;\ - sender-path:%{public}s;\ - message:%{public}s;\ - message-type:%{public}s - """ - - public func actorReceivedStart(message: Any, from: ActorID?) { - guard OSSignpostActorInstrumentation.logMessages.signpostsEnabled else { - return - } - - os_signpost( - .event, - log: OSSignpostActorInstrumentation.logMessages, - name: "Actor Message (Received)", - signpostID: self.signpostID, - Self.actorReceivedEventPattern, - "\(self.actorID.uniqueNode.description)", - "\(self.actorID.path)", - "\(from?.uniqueNode.description ?? "")", - "\(from?.path.description ?? "")", - "\(message)", - String(reflecting: type(of: message)) - ) - } - - public func actorReceivedEnd(error: Error?) { - // TODO: make interval so we know the length of how long an actor processes a message - } - - // ==== ------------------------------------------------------------------------------------------------------------ - // MARK: Watch signals - internal static let signpostNameActorWatches: StaticString = - "System Messages (Watch)" - - public static let actorReceivedWatchesPattern: StaticString = - """ - watch;\ - action:%{public}s;\ - watchee:%{public}s;\ - watcher:%{public}s - """ - - public func actorWatchReceived(watchee: ActorID, watcher: ActorID) { - guard Self.logSystemMessages.signpostsEnabled else { - return - } - - os_signpost( - .event, - log: Self.logSystemMessages, - name: Self.signpostNameActorWatches, - signpostID: self.signpostID, - Self.actorReceivedWatchesPattern, - "watch", "\(watchee)", "\(watcher)" - ) - } - - public func actorUnwatchReceived(watchee: ActorID, watcher: ActorID) { - guard Self.logSystemMessages.signpostsEnabled else { - return - } - - os_signpost( - .event, - log: Self.logSystemMessages, - name: Self.signpostNameActorWatches, - signpostID: self.signpostID, - Self.actorReceivedWatchesPattern, - "unwatch", "\(watchee)", "\(watcher)" - ) - } -} - -#endif diff --git a/Sources/DistributedActors/Instrumentation/os_signpost/ActorTransportInstrumentation+os_signpost.swift b/Sources/DistributedActors/Instrumentation/os_signpost/ActorTransportInstrumentation+os_signpost.swift index 3395adfcb..48632a475 100644 --- a/Sources/DistributedActors/Instrumentation/os_signpost/ActorTransportInstrumentation+os_signpost.swift +++ b/Sources/DistributedActors/Instrumentation/os_signpost/ActorTransportInstrumentation+os_signpost.swift @@ -24,7 +24,7 @@ import os.signpost @available(iOS 12.0, *) @available(tvOS 12.0, *) @available(watchOS 3.0, *) -public struct OSSignpost_InternalActorTransportInstrumentation: _InternalActorTransportInstrumentation { +internal struct OSSignpost_InternalActorTransportInstrumentation: _InternalActorTransportInstrumentation { static let subsystem: StaticString = "com.apple.actors" static let category: StaticString = "Serialization" @@ -35,7 +35,7 @@ public struct OSSignpost_InternalActorTransportInstrumentation: _InternalActorTr let signpostID: OSSignpostID - public init() { + init() { self.signpostID = OSSignpostID( log: OSSignpost_InternalActorTransportInstrumentation.logTransportSerialization ) @@ -78,7 +78,7 @@ extension OSSignpost_InternalActorTransportInstrumentation { message-type:%{public}s """ - public func remoteActorMessageSerializeStart(id: AnyObject, recipient: ActorPath, message: Any) { + func remoteActorMessageSerializeStart(id: AnyObject, recipient: ActorPath, message: Any) { guard OSSignpost_InternalActorTransportInstrumentation.logTransportSerialization.signpostsEnabled else { return } @@ -93,7 +93,7 @@ extension OSSignpost_InternalActorTransportInstrumentation { ) } - public func remoteActorMessageSerializeEnd(id: AnyObject, bytes: Int) { + func remoteActorMessageSerializeEnd(id: AnyObject, bytes: Int) { guard OSSignpost_InternalActorTransportInstrumentation.logTransportSerialization.signpostsEnabled else { return } @@ -108,7 +108,7 @@ extension OSSignpost_InternalActorTransportInstrumentation { ) } - public func remoteActorMessageDeserializeStart(id: AnyObject, recipient: ActorPath, bytes: Int) { + func remoteActorMessageDeserializeStart(id: AnyObject, recipient: ActorPath, bytes: Int) { guard OSSignpost_InternalActorTransportInstrumentation.logTransportSerialization.signpostsEnabled else { return } @@ -123,7 +123,7 @@ extension OSSignpost_InternalActorTransportInstrumentation { ) } - public func remoteActorMessageDeserializeEnd(id: AnyObject, message: Any?) { + func remoteActorMessageDeserializeEnd(id: AnyObject, message: Any?) { guard OSSignpost_InternalActorTransportInstrumentation.logTransportSerialization.signpostsEnabled else { return } diff --git a/Sources/DistributedActors/Instrumentation/os_signpost/InstrumentationProvider+os_signpost.swift b/Sources/DistributedActors/Instrumentation/os_signpost/InstrumentationProvider+os_signpost.swift index d3cc20839..fd91e02a4 100644 --- a/Sources/DistributedActors/Instrumentation/os_signpost/InstrumentationProvider+os_signpost.swift +++ b/Sources/DistributedActors/Instrumentation/os_signpost/InstrumentationProvider+os_signpost.swift @@ -14,19 +14,10 @@ #if os(macOS) || os(tvOS) || os(iOS) || os(watchOS) /// Provider of Instrumentation instances which use `os_signpost`. -public struct OSSignpostInstrumentationProvider: ClusterSystemInstrumentationProvider { - public init() {} +internal struct OSSignpostInstrumentationProvider: ClusterSystemInstrumentationProvider { + init() {} - public var actorInstrumentation: ((AnyObject, ActorID) -> ActorInstrumentation)? { - if #available(OSX 10.14, iOS 12.0, *) { - // TODO: how to guard in iOS etc here? - return OSSignpostActorInstrumentation.init - } else { - return nil - } - } - - public var actorTransportInstrumentation: (() -> _InternalActorTransportInstrumentation)? { + var actorTransportInstrumentation: (() -> _InternalActorTransportInstrumentation)? { if #available(OSX 10.14, iOS 12.0, *) { return OSSignpost_InternalActorTransportInstrumentation.init } else { @@ -34,7 +25,7 @@ public struct OSSignpostInstrumentationProvider: ClusterSystemInstrumentationPro } } - public var receptionistInstrumentation: (() -> ReceptionistInstrumentation)? { + var receptionistInstrumentation: (() -> _ReceptionistInstrumentation)? { if #available(OSX 10.14, iOS 12.0, *) { return OSSignpostReceptionistInstrumentation.init } else { diff --git a/Sources/DistributedActors/Instrumentation/os_signpost/ReceptionistInstrumentation+os_signpost.swift b/Sources/DistributedActors/Instrumentation/os_signpost/ReceptionistInstrumentation+os_signpost.swift index f2f2f251f..10937a304 100644 --- a/Sources/DistributedActors/Instrumentation/os_signpost/ReceptionistInstrumentation+os_signpost.swift +++ b/Sources/DistributedActors/Instrumentation/os_signpost/ReceptionistInstrumentation+os_signpost.swift @@ -21,7 +21,7 @@ import os.signpost @available(iOS 12.0, *) @available(tvOS 12.0, *) @available(watchOS 3.0, *) -public struct OSSignpostReceptionistInstrumentation: ReceptionistInstrumentation { +internal struct OSSignpostReceptionistInstrumentation: _ReceptionistInstrumentation { static let subsystem: StaticString = "com.apple.actors" static let category: StaticString = "Receptionist" @@ -33,7 +33,7 @@ public struct OSSignpostReceptionistInstrumentation: ReceptionistInstrumentation static let log = OSLog(subsystem: "\(Self.subsystem)", category: "\(Self.category)") - public init() {} + init() {} } // ==== ---------------------------------------------------------------------------------------------------------------- @@ -51,7 +51,7 @@ extension OSSignpostReceptionistInstrumentation { type:%{public}s """ - public func actorSubscribed(key: AnyReceptionKey, id: ActorID) { + func actorSubscribed(key: AnyReceptionKey, id: ActorID) { guard Self.log.signpostsEnabled else { return } @@ -74,7 +74,7 @@ extension OSSignpostReceptionistInstrumentation { type:%{public}s """ - public func actorRegistered(key: AnyReceptionKey, id: ActorID) { + func actorRegistered(key: AnyReceptionKey, id: ActorID) { guard Self.log.signpostsEnabled else { return } @@ -97,7 +97,7 @@ extension OSSignpostReceptionistInstrumentation { type:%{public}s """ - public func actorRemoved(key: AnyReceptionKey, id: ActorID) { + func actorRemoved(key: AnyReceptionKey, id: ActorID) { guard Self.log.signpostsEnabled else { return } @@ -122,7 +122,7 @@ extension OSSignpostReceptionistInstrumentation { regs:%{public}ld """ - public func listingPublished(key: AnyReceptionKey, subscribers: Int, registrations: Int) { + func listingPublished(key: AnyReceptionKey, subscribers: Int, registrations: Int) { guard Self.log.signpostsEnabled else { return } diff --git a/Sources/DistributedActors/InternalActorTransport.swift b/Sources/DistributedActors/InternalActorTransport.swift index 6f8f950c4..b665ec389 100644 --- a/Sources/DistributedActors/InternalActorTransport.swift +++ b/Sources/DistributedActors/InternalActorTransport.swift @@ -46,7 +46,7 @@ open class _InternalActorTransport { } /// May return `nil` if this transport is NOT able to resolve this ref. - open func _resolveUntyped(context: ResolveContext) -> AddressableActorRef? { + open func _resolveUntyped(context: ResolveContext) -> _AddressableActorRef? { fatalError("Not implemented: \(#function) in \(self) transport! Attempted to resolve: \(context)") } diff --git a/Sources/DistributedActors/LifecycleMonitoring/LifecycleWatch.swift b/Sources/DistributedActors/LifecycleMonitoring/LifecycleWatch.swift index 8b573bf34..60eb2460e 100644 --- a/Sources/DistributedActors/LifecycleMonitoring/LifecycleWatch.swift +++ b/Sources/DistributedActors/LifecycleMonitoring/LifecycleWatch.swift @@ -142,7 +142,7 @@ public final class LifecycleWatchContainer { typealias OnTerminatedFn = @Sendable (ClusterSystem.ActorID) async -> Void private var watching: [ClusterSystem.ActorID: OnTerminatedFn] = [:] - private var watchedBy: [ClusterSystem.ActorID: AddressableActorRef] = [:] + private var watchedBy: [ClusterSystem.ActorID: _AddressableActorRef] = [:] init(_ watcher: Act) where Act: DistributedActor, Act.ActorSystem == ClusterSystem { traceLog_DeathWatch("Make LifecycleWatchContainer owned by \(watcher.id)") @@ -249,7 +249,7 @@ extension LifecycleWatchContainer { // MARK: react to watch or unwatch signals public func becomeWatchedBy( - watcher: AddressableActorRef + watcher: _AddressableActorRef ) { guard watcher.id != self.watcherID else { traceLog_DeathWatch("Attempted to watch 'myself' [\(self.watcherID)], which is a no-op, since such watch's terminated can never be observed. " + @@ -261,7 +261,7 @@ extension LifecycleWatchContainer { self.watchedBy[watcher.id] = watcher } - internal func removeWatchedBy(watcher: AddressableActorRef) { + internal func removeWatchedBy(watcher: _AddressableActorRef) { traceLog_DeathWatch("Remove watched by: \(watcher.id) inside: \(self.watcherID)") self.watchedBy.removeValue(forKey: watcher.id) } diff --git a/Sources/DistributedActors/LifecycleMonitoring/_BehaviorDeathWatch.swift b/Sources/DistributedActors/LifecycleMonitoring/_BehaviorDeathWatch.swift index ecf33a27a..4ede1acb9 100644 --- a/Sources/DistributedActors/LifecycleMonitoring/_BehaviorDeathWatch.swift +++ b/Sources/DistributedActors/LifecycleMonitoring/_BehaviorDeathWatch.swift @@ -21,7 +21,7 @@ import NIO /// Marks an entity to be "watchable", meaning it may participate in Death Watch and form Death Pacts. /// -/// Watchable entities are `_ActorRef<>`, `Actor<>` but also actors of unknown type such as `AddressableActorRef`. +/// Watchable entities are `_ActorRef<>`, `Actor<>` but also actors of unknown type such as `_AddressableActorRef`. /// /// - SeeAlso: `_ActorContext.watch` /// - SeeAlso: `DeathWatch` @@ -30,7 +30,7 @@ public protocol _DeathWatchable: AddressableActor {} extension _ActorContext: _DeathWatchProtocol {} public protocol _DeathWatchProtocol { - associatedtype Message: ActorMessage + associatedtype Message: Codable /// Watches the given actor for termination, which means that this actor will receive a `.terminated` signal /// when the watched actor fails ("dies"), be it via throwing a Swift Error or performing some other kind of fault. @@ -117,9 +117,9 @@ public protocol _DeathWatchProtocol { // Implementation notes: // Care was taken to keep this implementation separate from the ActorCell however not require more storage space. @usableFromInline -internal struct DeathWatchImpl { - private var watching: [AddressableActorRef: OnTerminationMessage] = [:] - private var watchedBy: Set = [] +internal struct DeathWatchImpl { + private var watching: [_AddressableActorRef: OnTerminationMessage] = [:] + private var watchedBy: Set<_AddressableActorRef> = [] private var nodeDeathWatcher: NodeDeathWatcherShell.Ref @@ -151,7 +151,7 @@ internal struct DeathWatchImpl { file: String, line: UInt ) where Watchee: _DeathWatchable { traceLog_DeathWatch("issue watch: \(watchee) (from \(watcher) (myself))") - let addressableWatchee: AddressableActorRef = watchee.asAddressable + let addressableWatchee: _AddressableActorRef = watchee.asAddressable // watching ourselves is a no-op, since we would never be able to observe the Terminated message anyway: guard addressableWatchee.id != watcher.id else { @@ -194,7 +194,7 @@ internal struct DeathWatchImpl { let addressableWatchee = watchee.asAddressable // we could short circuit "if watchee == myself return" but it's not really worth checking since no-op anyway if self.watching.removeValue(forKey: addressableWatchee) != nil { - addressableWatchee._sendSystemMessage(.unwatch(watchee: addressableWatchee, watcher: AddressableActorRef(watcher)), file: file, line: line) + addressableWatchee._sendSystemMessage(.unwatch(watchee: addressableWatchee, watcher: _AddressableActorRef(watcher)), file: file, line: line) } } @@ -208,7 +208,7 @@ internal struct DeathWatchImpl { // ==== ------------------------------------------------------------------------------------------------------------ // MARK: react to watch or unwatch signals - public mutating func becomeWatchedBy(watcher: AddressableActorRef, myself: _ActorRef, parent: AddressableActorRef) { + public mutating func becomeWatchedBy(watcher: _AddressableActorRef, myself: _ActorRef, parent: _AddressableActorRef) { guard watcher.id != myself.id else { traceLog_DeathWatch("Attempted to watch 'myself' [\(myself)], which is a no-op, since such watch's terminated can never be observed. " + "Likely a programming error where the wrong actor ref was passed to watch(), please check your code.") @@ -228,7 +228,7 @@ internal struct DeathWatchImpl { self.watchedBy.insert(watcher) } - public mutating func removeWatchedBy(watcher: AddressableActorRef, myself: _ActorRef) { + public mutating func removeWatchedBy(watcher: _AddressableActorRef, myself: _ActorRef) { traceLog_DeathWatch("Remove watched by: \(watcher.id) inside: \(myself)") self.watchedBy.remove(watcher) } @@ -274,7 +274,7 @@ internal struct DeathWatchImpl { /// Does NOT immediately handle these `Terminated` signals, they are treated as any other normal signal would, /// such that the user can have a chance to handle and react to them. public mutating func receiveNodeTerminated(_ terminatedNode: UniqueNode, myself: _ReceivesSystemMessages) { - for watched: AddressableActorRef in self.watching.keys where watched.id.uniqueNode == terminatedNode { + for watched: _AddressableActorRef in self.watching.keys where watched.id.uniqueNode == terminatedNode { // we KNOW an actor existed if it is local and not resolved as /dead; otherwise it may have existed // for a remote ref we don't know for sure if it existed let existenceConfirmed = watched.refType.isLocal && !watched.id.path.starts(with: ._dead) @@ -291,7 +291,7 @@ internal struct DeathWatchImpl { for watcher in self.watchedBy { traceLog_DeathWatch("[\(myself)] Notify \(watcher) that we died") - watcher._sendSystemMessage(.terminated(ref: AddressableActorRef(myself), existenceConfirmed: true), file: #file, line: #line) + watcher._sendSystemMessage(.terminated(ref: _AddressableActorRef(myself), existenceConfirmed: true), file: #file, line: #line) } } @@ -302,14 +302,17 @@ internal struct DeathWatchImpl { guard watchedID._isRemote else { return } - self.nodeDeathWatcher.tell(.remoteActorWatched(watcher: AddressableActorRef(myself), remoteNode: watchedID.uniqueNode), file: file, line: line) + self.nodeDeathWatcher.tell(.remoteActorWatched(watcher: _AddressableActorRef(myself), remoteNode: watchedID.uniqueNode), file: file, line: line) } } // ==== ---------------------------------------------------------------------------------------------------------------- // MARK: Errors -public enum DeathPactError: Error { - case unhandledDeathPact(ActorID, myself: AddressableActorRef, message: String) +/// Death pact error is thrown when a watched behavior actor terminates, +/// and we do not handle the termination, triggering the "death pact" +/// which is "when the watched actor terminates, I terminate". +internal enum DeathPactError: Error { + case unhandledDeathPact(ActorID, myself: _AddressableActorRef, message: String) case unhandledDeathPactError(ClusterSystem.ActorID, myself: ClusterSystem.ActorID, message: String) } diff --git a/Sources/DistributedActors/Plugins/ClusterSystemSettings+Plugins.swift b/Sources/DistributedActors/Plugins/ClusterSystemSettings+Plugins.swift index 98bcbcfdc..f9697553d 100644 --- a/Sources/DistributedActors/Plugins/ClusterSystemSettings+Plugins.swift +++ b/Sources/DistributedActors/Plugins/ClusterSystemSettings+Plugins.swift @@ -59,18 +59,6 @@ public struct PluginsSettings { plugin.stop(system) } } - -// /// Stops all plugins in the *reversed* order as they were added. -// internal func stopAll(_ transport: ActorClusterTransport) async { -// // Shut down in reversed order so plugins with the fewest dependencies are stopped first! -// for plugin in self.plugins.reversed() { -// do { -// try await plugin.stop(transport) -// } catch { -// fatalError("Failed to stop plugin \(plugin.key)! Error: \(error)") -// } -// } -// } } extension PluginsSettings { diff --git a/Sources/DistributedActors/Protobuf/WireProtocol+Serialization.swift b/Sources/DistributedActors/Protobuf/WireProtocol+Serialization.swift index 227671e36..fc5122035 100644 --- a/Sources/DistributedActors/Protobuf/WireProtocol+Serialization.swift +++ b/Sources/DistributedActors/Protobuf/WireProtocol+Serialization.swift @@ -46,7 +46,7 @@ extension Wire.Envelope: _ProtobufRepresentable { throw WireEnvelopeError.missingManifest } - self.recipient = try ActorAddress(fromProto: proto.recipient, context: context) + self.recipient = try ActorID(fromProto: proto.recipient, context: context) self.payload = .data(proto.payload) self.manifest = .init(fromProto: proto.manifest) } diff --git a/Sources/DistributedActors/Receptionist/ActorContext+Receptionist.swift b/Sources/DistributedActors/Receptionist/ActorContext+Receptionist.swift index f11c79276..2ebd277c1 100644 --- a/Sources/DistributedActors/Receptionist/ActorContext+Receptionist.swift +++ b/Sources/DistributedActors/Receptionist/ActorContext+Receptionist.swift @@ -24,7 +24,7 @@ extension _ActorContext { extension _ActorContext { /// The receptionist enables type-safe and dynamic (subscription based) actor discovery. /// - /// Actors may register themselves when they start with an `Reception.Key` + /// Actors may register themselves when they start with an `_Reception.Key` /// /// - SeeAlso: `DistributedActors.Receptionist` for the `_ActorRef` version of this API. internal struct Receptionist: _MyselfReceptionistOperations { @@ -53,10 +53,10 @@ extension _ActorContext { /// thread-safe to mutate any of the actors state from this callback. @inlinable public func subscribeMyself( - to key: Reception.Key, - subReceive: @escaping (Reception.Listing) -> Void + to key: _Reception.Key, + subReceive: @escaping (_Reception.Listing) -> Void ) where Guest: _ReceptionistGuest { - let ref = self._underlyingContext.subReceive(Reception.Listing.self, subReceive) + let ref = self._underlyingContext.subReceive(_Reception.Listing.self, subReceive) self._system._receptionist.subscribe(ref, to: key) } } diff --git a/Sources/DistributedActors/Receptionist/DistributedReception.swift b/Sources/DistributedActors/Receptionist/DistributedReception.swift index efb5ac25b..51897d808 100644 --- a/Sources/DistributedActors/Receptionist/DistributedReception.swift +++ b/Sources/DistributedActors/Receptionist/DistributedReception.swift @@ -47,7 +47,7 @@ extension DistributedReception { self.id = value } - internal func resolve(system: ClusterSystem, id: ActorID) -> AddressableActorRef { + internal func resolve(system: ClusterSystem, id: ActorID) -> _AddressableActorRef { let ref: _ActorRef = system._resolve(context: ResolveContext(id: id, system: system)) return ref.asAddressable } @@ -76,7 +76,7 @@ struct AnyDistributedReceptionKey: Sendable, Codable, Hashable, CustomStringConv self.guestType = Guest.self } - func resolve(system: ClusterSystem, id: ActorID) -> AddressableActorRef { + func resolve(system: ClusterSystem, id: ActorID) -> _AddressableActorRef { // Since we don't have the type information here, we can't properly resolve // and the only safe thing to do is to return `deadLetters`. system.personalDeadLetters(type: Never.self, recipient: id).asAddressable @@ -141,10 +141,10 @@ struct AnyDistributedReceptionKey: Sendable, Codable, Hashable, CustomStringConv // /// Response to `Lookup` and `Subscribe` requests. // /// A listing MAY be empty. // public struct Listing: Equatable, CustomStringConvertible { -// let underlying: Set +// let underlying: Set<_AddressableActorRef> // let key: DistributedReception.Key // -// init(refs: Set, key: DistributedReception.Key) { +// init(refs: Set<_AddressableActorRef>, key: DistributedReception.Key) { // // TODO: assert the refs match type? // self.underlying = refs // self.key = key @@ -174,7 +174,7 @@ struct AnyDistributedReceptionKey: Sendable, Codable, Hashable, CustomStringConv // /// Retrieve all listed actor references, mapping them to their appropriate type. // /// Note that this operation is lazy and has to iterate over all the actors when performing the // /// iteration. -// public var refs: LazyMapSequence, _ActorRef> { +// public var refs: LazyMapSequence, _ActorRef> { // self.underlying.lazy.map { self.key._unsafeAsActorRef($0) } // } // @@ -212,7 +212,7 @@ struct AnyDistributedReceptionKey: Sendable, Codable, Hashable, CustomStringConv // } // } // -// protocol AnyReceptionistListing: ActorMessage { +// protocol AnyReceptionistListing: Codable { // // For comparing if two listings are equal // var refsAsAnyHashable: AnyHashable { get } // } @@ -227,7 +227,7 @@ struct AnyDistributedReceptionKey: Sendable, Codable, Hashable, CustomStringConv // } // // protocol ReceptionistListing: AnyReceptionistListing, Equatable { -// associatedtype Message: ActorMessage +// associatedtype Message: Codable // // var refs: Set<_ActorRef> { get } // } @@ -243,7 +243,7 @@ struct AnyDistributedReceptionKey: Sendable, Codable, Hashable, CustomStringConv // // extension DistributedReception { // /// Response to a `Register` message -// public final class Registered: NonTransportableActorMessage, CustomStringConvertible { +// public final class Registered: _NotActuallyCodableMessage, CustomStringConvertible { // internal let _guest: Guest // public let key: DistributedReception.Key // diff --git a/Sources/DistributedActors/Receptionist/Reception.swift b/Sources/DistributedActors/Receptionist/Reception.swift index c5c199edb..7f46a3a8f 100644 --- a/Sources/DistributedActors/Receptionist/Reception.swift +++ b/Sources/DistributedActors/Receptionist/Reception.swift @@ -18,12 +18,12 @@ import Distributed // MARK: Reception /// Namespace for public messages related to the Receptionist. -public enum Reception {} +public enum _Reception {} // ==== ---------------------------------------------------------------------------------------------------------------- -// MARK: Reception Key +// MARK: _ReceptionKey -extension Reception { +extension _Reception { /// Used to register and lookup actors in the receptionist. /// The key is a combination the Guest's type and an identifier to identify sub-groups of actors of that type. /// @@ -46,7 +46,7 @@ extension Reception { self.id = value } - internal func _unsafeAsActorRef(_ addressable: AddressableActorRef) -> _ActorRef { + internal func _unsafeAsActorRef(_ addressable: _AddressableActorRef) -> _ActorRef { if addressable.isRemote() { let remotePersonality: _RemoteClusterActorPersonality = addressable.ref._unsafeGetRemotePersonality(Guest.Message.self) return _ActorRef(.remote(remotePersonality)) @@ -59,7 +59,7 @@ extension Reception { } } - internal func resolve(system: ClusterSystem, id: ActorID) -> AddressableActorRef { + internal func resolve(system: ClusterSystem, id: ActorID) -> _AddressableActorRef { let ref: _ActorRef = system._resolve(context: ResolveContext(id: id, system: system)) return ref.asAddressable } @@ -69,22 +69,22 @@ extension Reception { } public var description: String { - "Reception.Key<\(Guest.self)>(id: \(self.id))" + "_Reception.Key<\(Guest.self)>(id: \(self.id))" } } } // ==== ---------------------------------------------------------------------------------------------------------------- -// MARK: Reception Listing +// MARK: _ReceptionListing -extension Reception { +extension _Reception { /// Response to `Lookup` and `Subscribe` requests. /// A listing MAY be empty. public struct Listing: Equatable, CustomStringConvertible { - let underlying: Set - let key: Reception.Key + let underlying: Set<_AddressableActorRef> + let key: _Reception.Key - init(refs: Set, key: Reception.Key) { + init(refs: Set<_AddressableActorRef>, key: _Reception.Key) { // TODO: assert the refs match type? self.underlying = refs self.key = key @@ -101,7 +101,7 @@ extension Reception { } public var description: String { - "Reception.Listing<\(Guest.self)>(\(self.underlying.map(\.id)))" + "_Reception.Listing<\(Guest.self)>(\(self.underlying.map(\.id)))" } public static func == (lhs: Listing, rhs: Listing) -> Bool { @@ -110,11 +110,11 @@ extension Reception { } } -extension Reception.Listing where Guest: _ReceivesMessages { +extension _Reception.Listing where Guest: _ReceivesMessages { /// Retrieve all listed actor references, mapping them to their appropriate type. /// Note that this operation is lazy and has to iterate over all the actors when performing the /// iteration. - public var refs: LazyMapSequence, _ActorRef> { + public var refs: LazyMapSequence, _ActorRef> { self.underlying.lazy.map { self.key._unsafeAsActorRef($0) } } @@ -146,7 +146,7 @@ extension Reception.Listing where Guest: _ReceivesMessages { } } -protocol AnyReceptionistListing: ActorMessage { +protocol AnyReceptionistListing: Codable { // For comparing if two listings are equal var refsAsAnyHashable: AnyHashable { get } } @@ -161,7 +161,7 @@ extension AnyReceptionistListing { } protocol ReceptionistListing: AnyReceptionistListing, Equatable { - associatedtype Message: ActorMessage + associatedtype Message: Codable var refs: Set<_ActorRef> { get } } @@ -173,32 +173,32 @@ extension ReceptionistListing { } // ==== ---------------------------------------------------------------------------------------------------------------- -// MARK: Reception Registered +// MARK: _ReceptionRegistered -extension Reception { +extension _Reception { /// Response to a `Register` message - public final class Registered: NonTransportableActorMessage, CustomStringConvertible { + public final class Registered: _NotActuallyCodableMessage, CustomStringConvertible { internal let _guest: Guest - public let key: Reception.Key + public let key: _Reception.Key - public init(_ guest: Guest, key: Reception.Key) { + public init(_ guest: Guest, key: _Reception.Key) { self._guest = guest self.key = key } public var description: String { - "Reception.Registered(guest: \(self._guest), key: \(self.key))" + "_Reception.Registered(guest: \(self._guest), key: \(self.key))" } } } -extension Reception.Registered where Guest: _ReceivesMessages { +extension _Reception.Registered where Guest: _ReceivesMessages { var ref: _ActorRef { self._guest._ref } } -extension Reception.Registered where Guest: DistributedActor, Guest.ActorSystem == ClusterSystem { +extension _Reception.Registered where Guest: DistributedActor, Guest.ActorSystem == ClusterSystem { public var actor: Guest { let system = self._guest.actorSystem diff --git a/Sources/DistributedActors/Receptionist/Receptionist+Serialization.swift b/Sources/DistributedActors/Receptionist/Receptionist+Serialization.swift index aeebe47f6..a3ac224d1 100644 --- a/Sources/DistributedActors/Receptionist/Receptionist+Serialization.swift +++ b/Sources/DistributedActors/Receptionist/Receptionist+Serialization.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -extension Reception.Listing: ActorMessage { +extension _Reception.Listing: Codable { enum CodingKeys: CodingKey { case listing case key @@ -21,9 +21,9 @@ extension Reception.Listing: ActorMessage { public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - self.key = try container.decode(Reception.Key.self, forKey: .key) + self.key = try container.decode(_Reception.Key.self, forKey: .key) let listingDecoder = try container.superDecoder(forKey: .listing) - self.underlying = try Set(from: listingDecoder) + self.underlying = try Set<_AddressableActorRef>(from: listingDecoder) } public func encode(to encoder: Encoder) throws { @@ -36,7 +36,7 @@ extension Reception.Listing: ActorMessage { } } -extension Reception.Key { +extension _Reception.Key { internal enum CodingKeys: CodingKey { case manifest case id @@ -44,7 +44,7 @@ extension Reception.Key { public init(from decoder: Decoder) throws { guard let context = decoder.actorSerializationContext else { - throw SerializationError.missingSerializationContext(decoder, Reception.Listing.self) + throw SerializationError.missingSerializationContext(decoder, _Reception.Listing.self) } let container = try decoder.container(keyedBy: CodingKeys.self) @@ -62,7 +62,7 @@ extension Reception.Key { public func encode(to encoder: Encoder) throws { guard let context: Serialization.Context = encoder.actorSerializationContext else { - throw SerializationError.missingSerializationContext(encoder, Reception.Listing.self) + throw SerializationError.missingSerializationContext(encoder, _Reception.Listing.self) } var container = encoder.container(keyedBy: CodingKeys.self) diff --git a/Sources/DistributedActors/Receptionist/Receptionist.swift b/Sources/DistributedActors/Receptionist/Receptionist.swift index f2d6d6844..79085e494 100644 --- a/Sources/DistributedActors/Receptionist/Receptionist.swift +++ b/Sources/DistributedActors/Receptionist/Receptionist.swift @@ -36,13 +36,13 @@ public struct Receptionist { internal static let naming: _ActorNaming = .unique("receptionist-ref") /// INTERNAL API - /// When sent to receptionist will register the specified `_ActorRef` under the given `Reception.Key` + /// When sent to receptionist will register the specified `_ActorRef` under the given `_Reception.Key` public class Register: _AnyRegister { public let guest: Guest - public let key: Reception.Key - public let replyTo: _ActorRef>? + public let key: _Reception.Key + public let replyTo: _ActorRef<_Reception.Registered>? - public init(_ ref: Guest, key: Reception.Key, replyTo: _ActorRef>? = nil) { + public init(_ ref: Guest, key: _Reception.Key, replyTo: _ActorRef<_Reception.Registered>? = nil) { self.guest = ref self.key = key self.replyTo = replyTo @@ -53,8 +53,8 @@ public struct Receptionist { throw SerializationError.nonTransportableMessage(type: "") } - override internal var _addressableActorRef: AddressableActorRef { - AddressableActorRef(self.guest._ref) + override internal var _addressableActorRef: _AddressableActorRef { + _AddressableActorRef(self.guest._ref) } override internal var _key: AnyReceptionKey { @@ -62,7 +62,7 @@ public struct Receptionist { } override internal func replyRegistered() { - self.replyTo?.tell(Reception.Registered(self.guest, key: self.key)) + self.replyTo?.tell(_Reception.Registered(self.guest, key: self.key)) } override public var description: String { @@ -71,12 +71,12 @@ public struct Receptionist { } /// INTERNAL API - /// Used to lookup `_ActorRef`s for the given `Reception.Key` + /// Used to lookup `_ActorRef`s for the given `_Reception.Key` public class Lookup: _Lookup, ListingRequest, CustomStringConvertible { - public let key: Reception.Key - public let subscriber: _ActorRef> + public let key: _Reception.Key + public let subscriber: _ActorRef<_Reception.Listing> - public init(key: Reception.Key, replyTo: _ActorRef>) { + public init(key: _Reception.Key, replyTo: _ActorRef<_Reception.Listing>) { self.key = key self.subscriber = replyTo super.init(_key: key.asAnyKey) @@ -86,8 +86,8 @@ public struct Receptionist { throw SerializationError.nonTransportableMessage(type: "\(Self.self)") } - override func replyWith(_ refs: Set) { - self.subscriber.tell(Reception.Listing(refs: refs, key: self.key)) + override func replyWith(_ refs: Set<_AddressableActorRef>) { + self.subscriber.tell(_Reception.Listing(refs: refs, key: self.key)) } public var description: String { @@ -98,10 +98,10 @@ public struct Receptionist { /// INTERNAL API /// Subscribe to periodic updates of the specified key public class Subscribe: _Subscribe, ListingRequest, CustomStringConvertible { - public let key: Reception.Key - public let subscriber: _ActorRef> + public let key: _Reception.Key + public let subscriber: _ActorRef<_Reception.Listing> - public init(key: Reception.Key, subscriber: _ActorRef>) { + public init(key: _Reception.Key, subscriber: _ActorRef<_Reception.Listing>) { self.key = key self.subscriber = subscriber super.init() @@ -119,12 +119,12 @@ public struct Receptionist { AnySubscribe(subscribe: self) } - override internal var _addressableActorRef: AddressableActorRef { + override internal var _addressableActorRef: _AddressableActorRef { self.subscriber.asAddressable } - func replyWith(_ refs: Set) { - self.subscriber.tell(Reception.Listing(refs: refs, key: self.key)) + func replyWith(_ refs: Set<_AddressableActorRef>) { + self.subscriber.tell(_Reception.Listing(refs: refs, key: self.key)) } public var description: String { @@ -134,7 +134,7 @@ public struct Receptionist { /// Storage container for a receptionist's registrations and subscriptions internal final class Storage { - internal var _registrations: [AnyReceptionKey: Set] = [:] + internal var _registrations: [AnyReceptionKey: Set<_AddressableActorRef>] = [:] internal var _subscriptions: [AnyReceptionKey: Set] = [:] /// Per (receptionist) node mapping of which keys are presently known to this receptionist on the given node. @@ -149,19 +149,19 @@ public struct Receptionist { // MARK: Registrations /// - returns: `true` if the value was a newly inserted value, `false` otherwise - func addRegistration(key: AnyReceptionKey, ref: AddressableActorRef) -> Bool { + func addRegistration(key: AnyReceptionKey, ref: _AddressableActorRef) -> Bool { self.addRefKeyMapping(id: ref.id, key: key) self.storeRegistrationNodeRelation(key: key, node: ref.id.uniqueNode) return self.addTo(dict: &self._registrations, key: key, value: ref) } - func removeRegistration(key: AnyReceptionKey, ref: AddressableActorRef) -> Set? { + func removeRegistration(key: AnyReceptionKey, ref: _AddressableActorRef) -> Set<_AddressableActorRef>? { _ = self.removeFromKeyMappings(ref) self.removeSingleRegistrationNodeRelation(key: key, node: ref.id.uniqueNode) return self.removeFrom(dict: &self._registrations, key: key, value: ref) } - func registrations(forKey key: AnyReceptionKey) -> Set? { + func registrations(forKey key: AnyReceptionKey) -> Set<_AddressableActorRef>? { self._registrations[key] } @@ -195,7 +195,7 @@ public struct Receptionist { self._subscriptions[key] } - // FIXME: improve this to always pass around AddressableActorRef rather than just address (in receptionist Subscribe message), remove this trick then + // FIXME: improve this to always pass around _AddressableActorRef rather than just address (in receptionist Subscribe message), remove this trick then /// - Returns: set of keys that this actor was REGISTERED under, and thus listings associated with it should be updated func removeFromKeyMappings(id: ActorID) -> RefMappingRemovalResult { let equalityHackRef = _ActorRef(.deadLetters(.init(Logger(label: ""), id: id, system: nil))) @@ -203,7 +203,7 @@ public struct Receptionist { } /// - Returns: set of keys that this actor was REGISTERED under, and thus listings associated with it should be updated - func removeFromKeyMappings(_ ref: AddressableActorRef) -> RefMappingRemovalResult { + func removeFromKeyMappings(_ ref: _AddressableActorRef) -> RefMappingRemovalResult { guard let associatedKeys = self._idToKeys.removeValue(forKey: ref.id) else { return RefMappingRemovalResult(registeredUnderKeys: []) } @@ -245,7 +245,7 @@ public struct Receptionist { // for every key that was related to the now terminated node for key in keys { // 1) we remove any registrations that it hosted - let registrations: Set = self._registrations.removeValue(forKey: key) ?? [] + let registrations: Set<_AddressableActorRef> = self._registrations.removeValue(forKey: key) ?? [] let remainingRegistrations = registrations.filter { $0.id.uniqueNode != node } if !remainingRegistrations.isEmpty { self._registrations[key] = remainingRegistrations @@ -366,8 +366,8 @@ public class _ReceptionistMessage: Codable, @unchecked Sendable {} internal typealias FullyQualifiedTypeName = String /// INTERNAL API -public class _AnyRegister: _ReceptionistMessage, NonTransportableActorMessage, CustomStringConvertible { - var _addressableActorRef: AddressableActorRef { _undefined() } +public class _AnyRegister: _ReceptionistMessage, _NotActuallyCodableMessage, CustomStringConvertible { + var _addressableActorRef: _AddressableActorRef { _undefined() } var _key: AnyReceptionKey { _undefined() } func replyRegistered() { @@ -379,7 +379,7 @@ public class _AnyRegister: _ReceptionistMessage, NonTransportableActorMessage, C } } -public class _Lookup: _ReceptionistMessage, NonTransportableActorMessage { +public class _Lookup: _ReceptionistMessage, _NotActuallyCodableMessage { let _key: AnyReceptionKey init(_key: AnyReceptionKey) { @@ -391,11 +391,11 @@ public class _Lookup: _ReceptionistMessage, NonTransportableActorMessage { throw SerializationError.nonTransportableMessage(type: "\(Self.self)") } - func replyWith(_ refs: Set) { + func replyWith(_ refs: Set<_AddressableActorRef>) { _undefined() } - func replyWith(_ refs: [AddressableActorRef]) { + func replyWith(_ refs: [_AddressableActorRef]) { _undefined() } } @@ -408,8 +408,8 @@ protocol ReceptionKeyProtocol { var asAnyKey: AnyReceptionKey { get } // `resolve` has to be here, because the key is the only thing that knows which - // type is requested. See implementation in `Reception.Key` - func resolve(system: ClusterSystem, id: ActorID) -> AddressableActorRef + // type is requested. See implementation in `_Reception.Key` + func resolve(system: ClusterSystem, id: ActorID) -> _AddressableActorRef } // :nodoc: @@ -422,12 +422,12 @@ public struct AnyReceptionKey: ReceptionKeyProtocol, Sendable, Codable, Hashable let id: String let guestType: Any.Type - init(_ key: Reception.Key) { + init(_ key: _Reception.Key) { self.id = key.id self.guestType = Guest.self } - func resolve(system: ClusterSystem, id: ActorID) -> AddressableActorRef { + func resolve(system: ClusterSystem, id: ActorID) -> _AddressableActorRef { // Since we don't have the type information here, we can't properly resolve // and the only safe thing to do is to return `deadLetters`. system.personalDeadLetters(type: Never.self, recipient: id).asAddressable @@ -485,7 +485,7 @@ public struct AnyReceptionKey: ReceptionKeyProtocol, Sendable, Codable, Hashable } } -public class _Subscribe: _ReceptionistMessage, NonTransportableActorMessage { +public class _Subscribe: _ReceptionistMessage, _NotActuallyCodableMessage { var _key: AnyReceptionKey { fatalErrorBacktrace("failed \(#function)") } @@ -494,7 +494,7 @@ public class _Subscribe: _ReceptionistMessage, NonTransportableActorMessage { fatalErrorBacktrace("failed \(#function)") } - var _addressableActorRef: AddressableActorRef { + var _addressableActorRef: _AddressableActorRef { fatalErrorBacktrace("failed \(#function)") } @@ -509,7 +509,7 @@ public class _Subscribe: _ReceptionistMessage, NonTransportableActorMessage { internal struct AnySubscribe: Hashable { let id: ActorID - let _replyWith: (Set) -> Void + let _replyWith: (Set<_AddressableActorRef>) -> Void init(subscribe: Receptionist.Subscribe) where Guest: _ReceptionistGuest { self.id = subscribe.subscriber.id @@ -521,7 +521,7 @@ internal struct AnySubscribe: Hashable { self._replyWith = { _ in () } } - func replyWith(_ refs: Set) { + func replyWith(_ refs: Set<_AddressableActorRef>) { self._replyWith(refs) } @@ -537,15 +537,15 @@ internal struct AnySubscribe: Hashable { internal protocol ListingRequest { associatedtype Guest: _ReceptionistGuest - var key: Reception.Key { get } + var key: _Reception.Key { get } var _key: AnyReceptionKey { get } - var subscriber: _ActorRef> { get } + var subscriber: _ActorRef<_Reception.Listing> { get } - func replyWith(_ refs: Set) + func replyWith(_ refs: Set<_AddressableActorRef>) } -internal final class _ReceptionistDelayedListingFlushTick: _ReceptionistMessage, NonTransportableActorMessage { +internal final class _ReceptionistDelayedListingFlushTick: _ReceptionistMessage, _NotActuallyCodableMessage { let key: AnyReceptionKey init(key: AnyReceptionKey) { diff --git a/Sources/DistributedActors/Receptionist/ReceptionistOperations.swift b/Sources/DistributedActors/Receptionist/ReceptionistOperations.swift index 8263f763f..62b73f9a1 100644 --- a/Sources/DistributedActors/Receptionist/ReceptionistOperations.swift +++ b/Sources/DistributedActors/Receptionist/ReceptionistOperations.swift @@ -29,8 +29,8 @@ internal protocol _BaseReceptionistOperations { func register( _ guest: Guest, as id: String, - replyTo: _ActorRef>? - ) -> Reception.Key where Guest: _ReceptionistGuest + replyTo: _ActorRef<_Reception.Registered>? + ) -> _Reception.Key where Guest: _ReceptionistGuest /// Registers passed in `actor` in the systems receptionist with given id. /// @@ -39,16 +39,16 @@ internal protocol _BaseReceptionistOperations { @discardableResult func register( _ guest: Guest, - with key: Reception.Key, - replyTo: _ActorRef>? - ) -> Reception.Key where Guest: _ReceptionistGuest + with key: _Reception.Key, + replyTo: _ActorRef<_Reception.Registered>? + ) -> _Reception.Key where Guest: _ReceptionistGuest /// Subscribe to changes in checked-in actors under given `key`. /// - /// The `subscriber` actor will be notified with `Reception.Listing` messages when new actors register, leave or die, under the passed in key. + /// The `subscriber` actor will be notified with `_Reception.Listing` messages when new actors register, leave or die, under the passed in key. func subscribe( - _ subscriber: _ActorRef>, - to key: Reception.Key + _ subscriber: _ActorRef<_Reception.Listing>, + to key: _Reception.Key ) where Guest: _ReceptionistGuest /// Perform a *single* lookup for an actor identified by the passed in `key`. @@ -56,8 +56,8 @@ internal protocol _BaseReceptionistOperations { /// - Parameters: /// - key: selects which actors we are interested in. func lookup( - _ key: Reception.Key, - replyTo: _ActorRef>, + _ key: _Reception.Key, + replyTo: _ActorRef<_Reception.Listing>, timeout: TimeAmount ) where Guest: _ReceptionistGuest } @@ -72,8 +72,8 @@ extension _ReceptionistOperations { internal func register( _ guest: Guest, as id: String, - replyTo: _ActorRef>? = nil - ) -> Reception.Key where Guest: _ReceptionistGuest { + replyTo: _ActorRef<_Reception.Registered>? = nil + ) -> _Reception.Key where Guest: _ReceptionistGuest { self.register(guest, with: .init(Guest.self, id: id), replyTo: replyTo) } @@ -81,24 +81,24 @@ extension _ReceptionistOperations { @discardableResult internal func register( _ guest: Guest, - with key: Reception.Key, - replyTo: _ActorRef>? = nil - ) -> Reception.Key where Guest: _ReceptionistGuest { + with key: _Reception.Key, + replyTo: _ActorRef<_Reception.Registered>? = nil + ) -> _Reception.Key where Guest: _ReceptionistGuest { self._system._receptionist.register(guest, with: key, replyTo: replyTo) } @inlinable internal func subscribe( - _ subscriber: _ActorRef>, - to key: Reception.Key + _ subscriber: _ActorRef<_Reception.Listing>, + to key: _Reception.Key ) where Guest: _ReceptionistGuest { self._system._receptionist.subscribe(subscriber, to: key) } @inlinable internal func lookup( - _ key: Reception.Key, - replyTo: _ActorRef>, + _ key: _Reception.Key, + replyTo: _ActorRef<_Reception.Listing>, timeout: TimeAmount = .effectivelyInfinite ) where Guest: _ReceptionistGuest { self._system._receptionist.lookup(key, replyTo: replyTo, timeout: timeout) @@ -120,13 +120,13 @@ internal protocol _MyselfReceptionistOperations: _ReceptionistOperations { /// Registers `myself` in the systems receptionist with given key. @discardableResult func registerMyself( - with key: Reception.Key, - replyTo: _ActorRef>? - ) -> Reception.Key + with key: _Reception.Key, + replyTo: _ActorRef<_Reception.Registered>? + ) -> _Reception.Key /// Subscribe to actors registering under given `key`. /// - /// A new `Reception.Listing` is emitted whenever new actors join (or leave) the reception, and the `onListingChange` is then + /// A new `_Reception.Listing` is emitted whenever new actors join (or leave) the reception, and the `onListingChange` is then /// invoked on the actors context. /// /// Current Limitation: Only ONE (the most recently set using this API) `onListingChange` for a given `key` is going to be executed. @@ -137,40 +137,40 @@ internal protocol _MyselfReceptionistOperations: _ReceptionistOperations { /// - callback: invoked whenever actors join/leave the reception or when they terminate. /// The invocation is made on the owning actor's context, meaning that it is safe to mutate actor state from the callback. func subscribeMyself( - to key: Reception.Key, - subReceive callback: @escaping (Reception.Listing) -> Void + to key: _Reception.Key, + subReceive callback: @escaping (_Reception.Listing) -> Void ) where Guest: _ReceptionistGuest /// Subscribe this actor to actors registering under given `key`. /// - /// A new `Reception.Listing` is emitted whenever new actors join (or leave) the reception. + /// A new `_Reception.Listing` is emitted whenever new actors join (or leave) the reception. /// /// - SeeAlso: `subscribeMyself(to:subReceive:)` func subscribeMyself( - to key: Reception.Key - ) where Guest: _ReceptionistGuest, Myself.Message == Reception.Listing + to key: _Reception.Key + ) where Guest: _ReceptionistGuest, Myself.Message == _Reception.Listing } extension _MyselfReceptionistOperations { @inlinable @discardableResult internal func registerMyself( - with key: Reception.Key, - replyTo: _ActorRef>? = nil - ) -> Reception.Key { + with key: _Reception.Key, + replyTo: _ActorRef<_Reception.Registered>? = nil + ) -> _Reception.Key { self.register(self._myself, with: key, replyTo: replyTo) return key } @inlinable internal func subscribeMyself( - to key: Reception.Key, - subReceive callback: @escaping (Reception.Listing) -> Void + to key: _Reception.Key, + subReceive callback: @escaping (_Reception.Listing) -> Void ) where Guest: _ReceptionistGuest { let subReceiveStringID = "subscribe-\(Guest.self)" - let id = _SubReceiveId>(id: subReceiveStringID) + let id = _SubReceiveId<_Reception.Listing>(id: subReceiveStringID) let subRef = self._underlyingContext - .subReceive(id, Reception.Listing.self) { listing in + .subReceive(id, _Reception.Listing.self) { listing in callback(listing) } @@ -179,8 +179,8 @@ extension _MyselfReceptionistOperations { @inlinable internal func subscribeMyself( - to key: Reception.Key - ) where Guest: _ReceptionistGuest, Myself.Message == Reception.Listing { + to key: _Reception.Key + ) where Guest: _ReceptionistGuest, Myself.Message == _Reception.Listing { self.subscribe(self._myself._ref, to: key) } } diff --git a/Sources/DistributedActors/Receptionist/SystemReceptionist.swift b/Sources/DistributedActors/Receptionist/SystemReceptionist.swift index c6926919b..735bf23ff 100644 --- a/Sources/DistributedActors/Receptionist/SystemReceptionist.swift +++ b/Sources/DistributedActors/Receptionist/SystemReceptionist.swift @@ -30,9 +30,9 @@ internal struct SystemReceptionist: _BaseReceptionistOperations { public func register( _ guest: Guest, as id: String, - replyTo: _ActorRef>? = nil - ) -> Reception.Key where Guest: _ReceptionistGuest { - let key: Reception.Key = Reception.Key(Guest.self, id: id) + replyTo: _ActorRef<_Reception.Registered>? = nil + ) -> _Reception.Key where Guest: _ReceptionistGuest { + let key: _Reception.Key = _Reception.Key(Guest.self, id: id) self.register(guest, with: key, replyTo: replyTo) return key } @@ -40,24 +40,24 @@ internal struct SystemReceptionist: _BaseReceptionistOperations { @discardableResult public func register( _ guest: Guest, - with key: Reception.Key, - replyTo: _ActorRef>? = nil - ) -> Reception.Key where Guest: _ReceptionistGuest { + with key: _Reception.Key, + replyTo: _ActorRef<_Reception.Registered>? = nil + ) -> _Reception.Key where Guest: _ReceptionistGuest { self.ref.tell(Receptionist.Register(guest, key: key, replyTo: replyTo)) return key } public func lookup( - _ key: Reception.Key, - replyTo: _ActorRef>, + _ key: _Reception.Key, + replyTo: _ActorRef<_Reception.Listing>, timeout: TimeAmount = .effectivelyInfinite ) where Guest: _ReceptionistGuest { self.ref.tell(Receptionist.Lookup(key: key, replyTo: replyTo)) } public func subscribe( - _ subscriber: _ActorRef>, - to key: Reception.Key + _ subscriber: _ActorRef<_Reception.Listing>, + to key: _Reception.Key ) where Guest: _ReceptionistGuest { self.ref.tell(Receptionist.Subscribe(key: key, subscriber: subscriber)) } diff --git a/Sources/DistributedActors/Refs+any.swift b/Sources/DistributedActors/Refs+any.swift index 69cd7b9d3..10a948677 100644 --- a/Sources/DistributedActors/Refs+any.swift +++ b/Sources/DistributedActors/Refs+any.swift @@ -21,13 +21,13 @@ import protocol NIO.EventLoop /// Represents an actor which we know existed at some point in time and this represents its type-erased reference. /// -/// An `AddressableActorRef` can be used as type-eraser for specific actor references (be it `Actor` or `_ActorRef` based), -/// as they may still be compared with the `AddressableActorRef` by comparing their respective addressable. +/// An `_AddressableActorRef` can be used as type-eraser for specific actor references (be it `Actor` or `_ActorRef` based), +/// as they may still be compared with the `_AddressableActorRef` by comparing their respective addressable. /// -/// This enables an `AddressableActorRef` to be useful for watching, storing and comparing actor references of various types with another. -/// Note that unlike a plain `ActorID` an `AddressableActorRef` still DOES hold an actual reference to the pointed to actor, +/// This enables an `_AddressableActorRef` to be useful for watching, storing and comparing actor references of various types with another. +/// Note that unlike a plain `ActorID` an `_AddressableActorRef` still DOES hold an actual reference to the pointed to actor, /// even though it is not able to send messages to it (due to the lack of type-safety when doing so). -public struct AddressableActorRef: _DeathWatchable, Hashable { +public struct _AddressableActorRef: _DeathWatchable, Hashable { @usableFromInline enum RefType { case remote @@ -65,7 +65,7 @@ public struct AddressableActorRef: _DeathWatchable, Hashable { self.ref.id } - public var asAddressable: AddressableActorRef { + public var asAddressable: _AddressableActorRef { self } @@ -85,18 +85,18 @@ public struct AddressableActorRef: _DeathWatchable, Hashable { } } -extension AddressableActorRef: CustomStringConvertible { +extension _AddressableActorRef: CustomStringConvertible { public var description: String { - "AddressableActorRef(\(self.ref.id))" + "_AddressableActorRef(\(self.ref.id))" } } -extension AddressableActorRef { +extension _AddressableActorRef { public func hash(into hasher: inout Hasher) { self.id.hash(into: &hasher) } - public static func == (lhs: AddressableActorRef, rhs: AddressableActorRef) -> Bool { + public static func == (lhs: _AddressableActorRef, rhs: _AddressableActorRef) -> Bool { lhs.id == rhs.id } } @@ -104,7 +104,7 @@ extension AddressableActorRef { // ==== ---------------------------------------------------------------------------------------------------------------- // MARK: Internal or unsafe methods -extension AddressableActorRef: _ReceivesSystemMessages { +extension _AddressableActorRef: _ReceivesSystemMessages { public func _tellOrDeadLetter(_ message: Any, file: String = #file, line: UInt = #line) { self.ref._tellOrDeadLetter(message, file: file, line: line) } @@ -121,7 +121,7 @@ extension AddressableActorRef: _ReceivesSystemMessages { self.ref._deserializeDeliver(messageBytes, using: manifest, on: pool, file: file, line: line) } - public func _unsafeGetRemotePersonality(_ type: M.Type = M.self) -> _RemoteClusterActorPersonality { + public func _unsafeGetRemotePersonality(_ type: M.Type = M.self) -> _RemoteClusterActorPersonality { self.ref._unsafeGetRemotePersonality(M.self) } } diff --git a/Sources/DistributedActors/Refs.swift b/Sources/DistributedActors/Refs.swift index 83dcc9e30..9a5922859 100644 --- a/Sources/DistributedActors/Refs.swift +++ b/Sources/DistributedActors/Refs.swift @@ -23,7 +23,7 @@ import struct NIO.ByteBuffer /// Represents a reference to an actor. /// All communication between actors is handled _through_ actor refs, which guarantee their isolation remains intact. -public struct _ActorRef: @unchecked Sendable, _ReceivesMessages, _DeathWatchable, _ReceivesSystemMessages { +public struct _ActorRef: @unchecked Sendable, _ReceivesMessages, _DeathWatchable, _ReceivesSystemMessages { /// INTERNAL API: May change without further notice. /// The actor ref is "aware" whether it represents a local, remote or otherwise special actor. /// @@ -85,20 +85,20 @@ public struct _ActorRef: @unchecked Sendable, _ReceivesMe } } -/// Any actor which is able to erase itself into an untyped `AddressableActorRef`. +/// Any actor which is able to erase itself into an untyped `_AddressableActorRef`. public protocol AddressableActor { - var asAddressable: AddressableActorRef { get } + var asAddressable: _AddressableActorRef { get } } extension _ActorRef { - /// Exposes given the current actor reference as limited capability representation of itself; an `AddressableActorRef`. + /// Exposes given the current actor reference as limited capability representation of itself; an `_AddressableActorRef`. /// - /// An `AddressableActorRef` can be used to uniquely identify an actor, however it is not possible to directly send + /// An `_AddressableActorRef` can be used to uniquely identify an actor, however it is not possible to directly send /// messages to such identified actor via this reference type. /// - /// - SeeAlso: `AddressableActorRef` for a detailed discussion of its typical use-cases. - public var asAddressable: AddressableActorRef { - AddressableActorRef(self) + /// - SeeAlso: `_AddressableActorRef` for a detailed discussion of its typical use-cases. + public var asAddressable: _AddressableActorRef { + _AddressableActorRef(self) } } @@ -147,7 +147,7 @@ extension _ActorRef.Personality { // MARK: Internal top generic "capability" abstractions; we'll need those for other "refs" public protocol _ReceivesMessages: Sendable, Codable { - associatedtype Message: ActorMessage + associatedtype Message: Codable /// Send message to actor referred to by this `_ActorRef`. /// /// The symbolic version of "tell" is `!` and should also be pronounced as "tell". @@ -186,7 +186,7 @@ public protocol _ReceivesSystemMessages: Codable { ) /// INTERNAL API - func _unsafeGetRemotePersonality(_ type: M.Type) -> _RemoteClusterActorPersonality + func _unsafeGetRemotePersonality(_ type: M.Type) -> _RemoteClusterActorPersonality } extension _ReceivesSystemMessages { @@ -301,7 +301,7 @@ extension _ActorRef { ) } - public func _unsafeGetRemotePersonality(_ type: M.Type = M.self) -> _RemoteClusterActorPersonality { + public func _unsafeGetRemotePersonality(_ type: M.Type = M.self) -> _RemoteClusterActorPersonality { switch self.personality { case .remote(let personality): return personality._unsafeAssumeCast(to: type) @@ -341,7 +341,7 @@ extension _ActorRef { /// and are such that a stopped actor can be released as soon as possible (shell), yet the cell remains /// active while anyone still holds references to it. The mailbox class on the other hand, is kept alive by /// by the cell, as it may result in message sends to dead letters which the mailbox handles -public final class _ActorCell { +public final class _ActorCell { let mailbox: _Mailbox weak var actor: _ActorShell? @@ -430,7 +430,7 @@ extension _ActorRef where Message == DeadLetter { /// Similar to an `ActorCell` but for some delegated actual "entity". /// This can be used to implement actor-like beings, which are backed by non-actor entities. // TODO: we could use this to make TestProbes more "real" rather than wrappers -open class _CellDelegate { +open class _CellDelegate { public init() { // nothing } @@ -515,7 +515,7 @@ internal struct TheOneWhoHasNoParent: _ReceivesSystemMessages { // FIXME: fix th } @usableFromInline - internal func _unsafeGetRemotePersonality(_ type: M.Type = M.self) -> _RemoteClusterActorPersonality { + internal func _unsafeGetRemotePersonality(_ type: M.Type = M.self) -> _RemoteClusterActorPersonality { CDistributedActorsMailbox.sact_dump_backtrace() fatalError("The \(self.id) actor MUST NOT be interacted with directly!") } @@ -654,7 +654,7 @@ public class _Guardian { } } - func stopChild(_ childRef: AddressableActorRef) throws { + func stopChild(_ childRef: _AddressableActorRef) throws { try self._childrenLock.synchronized { guard self._children.contains(identifiedBy: childRef.id) else { throw _ActorContextError.attemptedStoppingNonChildActor(ref: childRef) @@ -698,7 +698,7 @@ public class _Guardian { } extension _Guardian: _ActorTreeTraversable { - public func _traverse(context: _TraversalContext, _ visit: (_TraversalContext, AddressableActorRef) -> _TraversalDirective) -> _TraversalResult { + public func _traverse(context: _TraversalContext, _ visit: (_TraversalContext, _AddressableActorRef) -> _TraversalDirective) -> _TraversalResult { let children: _Children = self.children var c = context.deeper @@ -728,7 +728,7 @@ extension _Guardian: _ActorTreeTraversable { } } - public func _resolveUntyped(context: ResolveContext) -> AddressableActorRef { + public func _resolveUntyped(context: ResolveContext) -> _AddressableActorRef { guard let selector = context.selectorSegments.first else { fatalError("Expected selector in guardian._resolve()!") } diff --git a/Sources/DistributedActors/Serialization/Serialization+Context.swift b/Sources/DistributedActors/Serialization/Serialization+Context.swift index 001c88f15..57024ef5e 100644 --- a/Sources/DistributedActors/Serialization/Serialization+Context.swift +++ b/Sources/DistributedActors/Serialization/Serialization+Context.swift @@ -60,13 +60,13 @@ extension Serialization { public func _resolveActorRef( _ messageType: Message.Type = Message.self, identifiedBy id: ActorID, userInfo: [CodingUserInfoKey: Any] = [:] - ) -> _ActorRef where Message: ActorMessage { + ) -> _ActorRef where Message: Codable { let context = ResolveContext(id: id, system: self.system, userInfo: userInfo) return self.system._resolve(context: context) } - /// Similar to `_resolveActorRef` but for an untyped `AddressableActorRef`. - public func _resolveAddressableActorRef(identifiedBy id: ActorID, userInfo: [CodingUserInfoKey: Any] = [:]) -> AddressableActorRef { + /// Similar to `_resolveActorRef` but for an untyped `_AddressableActorRef`. + public func _resolveAddressableActorRef(identifiedBy id: ActorID, userInfo: [CodingUserInfoKey: Any] = [:]) -> _AddressableActorRef { let context = ResolveContext(id: id, system: self.system, userInfo: userInfo) return self.system._resolveUntyped(context: context) } diff --git a/Sources/DistributedActors/Serialization/Serialization+Manifest.swift b/Sources/DistributedActors/Serialization/Serialization+Manifest.swift index 530fa0c25..f96dd8a6a 100644 --- a/Sources/DistributedActors/Serialization/Serialization+Manifest.swift +++ b/Sources/DistributedActors/Serialization/Serialization+Manifest.swift @@ -115,7 +115,7 @@ extension Serialization { manifest = Manifest(serializerID: ._ProtobufRepresentable, hint: hint) } else if messageType is Codable.Type { manifest = Manifest(serializerID: self.settings.defaultSerializerID, hint: hint) - } else if messageType is NonTransportableActorMessage.Type { + } else if messageType is _NotActuallyCodableMessage.Type { manifest = Manifest(serializerID: .doNotSerialize, hint: nil) } else { manifest = nil diff --git a/Sources/DistributedActors/Serialization/Serialization+SerializerID.swift b/Sources/DistributedActors/Serialization/Serialization+SerializerID.swift index da9be85c7..873e7641f 100644 --- a/Sources/DistributedActors/Serialization/Serialization+SerializerID.swift +++ b/Sources/DistributedActors/Serialization/Serialization+SerializerID.swift @@ -113,7 +113,5 @@ extension Serialization { internal static let ErrorEnvelope: SerializerID = .foundationJSON internal static let BestEffortStringError: SerializerID = .foundationJSON - - internal static let WallTimeClock: SerializerID = .foundationJSON } } diff --git a/Sources/DistributedActors/Serialization/Serialization+Serializers.swift b/Sources/DistributedActors/Serialization/Serialization+Serializers.swift index 6e1be0579..70316e613 100644 --- a/Sources/DistributedActors/Serialization/Serialization+Serializers.swift +++ b/Sources/DistributedActors/Serialization/Serialization+Serializers.swift @@ -96,8 +96,6 @@ internal class NonTransportableSerializer: Serializer { /// Abstracts over different Encoder/Decoder and other serialization mechanisms. /// /// Serializers may directly work on `NIO.ByteBuffer` or on `Foundation.Data`. -/// -/// - Warning: This type may be replaced if we managed to pull Combine's "TopLevelEncoder" types into stdlib. public protocol AnySerializer { func trySerialize(_ message: Any) throws -> Serialization.Buffer diff --git a/Sources/DistributedActors/Serialization/Serialization+Settings.swift b/Sources/DistributedActors/Serialization/Serialization+Settings.swift index a6b43f60d..ac0748da2 100644 --- a/Sources/DistributedActors/Serialization/Serialization+Settings.swift +++ b/Sources/DistributedActors/Serialization/Serialization+Settings.swift @@ -101,7 +101,7 @@ extension Serialization.Settings { /// This can be used to "force" a specific serializer be used for a message type, /// regardless if it is codable or not. @discardableResult - public mutating func register( + public mutating func register( _ type: Message.Type, hint hintOverride: String? = nil, serializerID overrideSerializerID: SerializerID? = nil, alsoRegisterActorRef: Bool = true @@ -146,7 +146,7 @@ extension Serialization.Settings { /// /// This manifest will NOT be used when _sending_ messages of the `Message` type. @discardableResult - public mutating func registerInbound( + public mutating func registerInbound( _ type: Message.Type, hint hintOverride: String? = nil, serializerID overrideSerializerID: SerializerID? = nil ) -> Manifest { @@ -183,7 +183,7 @@ extension Serialization.Settings { } } - public mutating func getSpecializedOrRegisterManifest( + public mutating func getSpecializedOrRegisterManifest( _ type: Message.Type, serializerID: Serialization.SerializerID ) -> Serialization.Manifest { diff --git a/Sources/DistributedActors/Serialization/Serialization.swift b/Sources/DistributedActors/Serialization/Serialization.swift index ecc4f556c..8161f1a1a 100644 --- a/Sources/DistributedActors/Serialization/Serialization.swift +++ b/Sources/DistributedActors/Serialization/Serialization.swift @@ -591,9 +591,9 @@ extension Serialization { /// Validates serialization round-trip is possible for given message. /// /// Messages marked with `SkipSerializationVerification` are except from this verification. - public func verifySerializable(message: Message) throws { + public func verifySerializable(message: Message) throws { switch message { - case is NonTransportableActorMessage: + case is _NotActuallyCodableMessage: return // skip default: let serialized = try self.serialize(message) @@ -698,7 +698,7 @@ struct SerializerTypeKey: Hashable, CustomStringConvertible { } @usableFromInline - init(_ type: Message.Type) { + init(_ type: Message.Type) { self.type = type self._ensure = { serialization in try serialization._ensureSerializer(type) diff --git a/Sources/DistributedActors/StashBuffer.swift b/Sources/DistributedActors/StashBuffer.swift index 40ce6175f..b503260aa 100644 --- a/Sources/DistributedActors/StashBuffer.swift +++ b/Sources/DistributedActors/StashBuffer.swift @@ -20,7 +20,7 @@ /// of actors, they can't be prevented from receiving messages while waiting for /// the external source to respond. In this case messages should be stashed and /// then unstashed once the external source has responded. -public final class _StashBuffer { +public final class _StashBuffer { @usableFromInline let buffer: RingBuffer diff --git a/Sources/DistributedActors/Supervision.swift b/Sources/DistributedActors/Supervision.swift index 2c45cc3bf..8cf836420 100644 --- a/Sources/DistributedActors/Supervision.swift +++ b/Sources/DistributedActors/Supervision.swift @@ -381,7 +381,7 @@ internal enum ProcessingType { } @usableFromInline -internal enum ProcessingAction { +internal enum ProcessingAction { case start case message(Message) case signal(_Signal) @@ -408,7 +408,7 @@ extension ProcessingAction { /// /// Currently not for user extension. @usableFromInline -internal class Supervisor { +internal class Supervisor { @usableFromInline typealias Directive = SupervisionDirective @@ -583,7 +583,7 @@ internal class Supervisor { /// Supervisor equivalent to not having supervision enabled, since stopping is the default behavior of failing actors. /// At the same time, it may be useful to sometimes explicitly specify that for some type of error we want to stop /// (e.g. when used with composite supervisors, which restart for all failures, yet should not do so for some specific type of error). -final class StoppingSupervisor: Supervisor { +final class StoppingSupervisor: Supervisor { internal let failureType: Error.Type internal init(failureType: Error.Type) { @@ -617,7 +617,7 @@ final class StoppingSupervisor: Supervisor { } /// Escalates failure to parent, while failing the current actor. -final class EscalatingSupervisor: Supervisor { +final class EscalatingSupervisor: Supervisor { internal let failureType: Error.Type internal init(failureType: Error.Type) { @@ -654,7 +654,7 @@ final class EscalatingSupervisor: Supervisor { // // The scan also makes implementing the "catch" all types `_Supervision.AllFailures` etc simpler rather than having to search // the underlying map for the catch all handlers as well as the specific error. -final class CompositeSupervisor: Supervisor { +final class CompositeSupervisor: Supervisor { private let supervisors: [Supervisor] init(supervisors: [Supervisor]) { @@ -683,7 +683,7 @@ final class CompositeSupervisor: Supervisor { /// /// - SeeAlso: `Supervisor.handleFailure` @usableFromInline -internal enum SupervisionDirective { +internal enum SupervisionDirective { /// Directs mailbox to directly stop processing. case stop /// Directs mailbox to prepare AND complete a restart immediately. @@ -779,7 +779,7 @@ internal struct RestartDecisionLogic { } } -final class RestartingSupervisor: Supervisor { +final class RestartingSupervisor: Supervisor { internal let failureType: Error.Type internal let initialBehavior: _Behavior @@ -842,7 +842,7 @@ final class RestartingSupervisor: Supervisor { /// Behavior used to suspend after a `restartPrepare` has been issued by an `restartDelayed`. @usableFromInline -internal enum SupervisionRestartDelayedBehavior { +internal enum SupervisionRestartDelayedBehavior { @usableFromInline internal struct WakeUp {} diff --git a/Sources/DistributedActors/SystemMessages.swift b/Sources/DistributedActors/SystemMessages.swift index 60dd7fbaf..3b1eaf9ec 100644 --- a/Sources/DistributedActors/SystemMessages.swift +++ b/Sources/DistributedActors/SystemMessages.swift @@ -46,9 +46,9 @@ public enum _SystemMessage: Equatable { case start /// Notifies an actor that it is being watched by the `from` actor - case watch(watchee: AddressableActorRef, watcher: AddressableActorRef) + case watch(watchee: _AddressableActorRef, watcher: _AddressableActorRef) /// Notifies an actor that it is no longer being watched by the `from` actor - case unwatch(watchee: AddressableActorRef, watcher: AddressableActorRef) + case unwatch(watchee: _AddressableActorRef, watcher: _AddressableActorRef) /// Received after `watch` was issued to an actor ref /// - Parameters: @@ -56,7 +56,7 @@ public enum _SystemMessage: Equatable { /// - existenceConfirmed: true if the `terminated` message is sent as response to a watched actor terminating, /// and `false` if the existence of the actor could not be proven (e.g. message ended up being routed to deadLetters, /// or the node hosting the actor has been downed, thus we assumed the actor has died as well, but we cannot prove it did). - case terminated(ref: AddressableActorRef, existenceConfirmed: Bool, idTerminated: Bool) // TODO: more additional info? // TODO: send terminated PATH, not ref, sending to it does not make sense after all + case terminated(ref: _AddressableActorRef, existenceConfirmed: Bool, idTerminated: Bool) // TODO: more additional info? // TODO: send terminated PATH, not ref, sending to it does not make sense after all /// Extension point for transports or other plugins which may need to send custom signals to actors. /// The carried signal will be delivered as-is to the recipient actor. @@ -64,7 +64,7 @@ public enum _SystemMessage: Equatable { /// Child actor has terminated. This system message by itself does not necessarily cause a DeathPact and termination of the parent. /// If the message carries an `escalated` failure, the failure should apply to the parent as well, potentially tearing it down as well. - case childTerminated(ref: AddressableActorRef, TerminationCircumstances) + case childTerminated(ref: _AddressableActorRef, _TerminationCircumstances) /// Node has terminated, and all actors of this node shall be considered as terminated. /// This system message does _not_ have a direct counter part as `Signal`, and instead results in the sending of multiple @@ -92,8 +92,9 @@ public enum _SystemMessage: Equatable { case tombstone } +// TODO: Child actors are being removed /// The circumstances under which a child actor has terminated. -public enum TerminationCircumstances { +public enum _TerminationCircumstances { /// The actor stopped naturally, by becoming `.stop` case stopped /// The actor has failed during message processing. @@ -108,17 +109,17 @@ public enum TerminationCircumstances { extension _SystemMessage { @inlinable - internal static func terminated(ref: AddressableActorRef) -> _SystemMessage { + internal static func terminated(ref: _AddressableActorRef) -> _SystemMessage { .terminated(ref: ref, existenceConfirmed: false, idTerminated: false) } @inlinable - internal static func terminated(ref: AddressableActorRef, existenceConfirmed: Bool) -> _SystemMessage { + internal static func terminated(ref: _AddressableActorRef, existenceConfirmed: Bool) -> _SystemMessage { .terminated(ref: ref, existenceConfirmed: existenceConfirmed, idTerminated: false) } @inlinable - internal static func terminated(ref: AddressableActorRef, idTerminated: Bool) -> _SystemMessage { + internal static func terminated(ref: _AddressableActorRef, idTerminated: Bool) -> _SystemMessage { .terminated(ref: ref, existenceConfirmed: false, idTerminated: idTerminated) } } diff --git a/Sources/DistributedActors/Time.swift b/Sources/DistributedActors/Time.swift index a10674004..78bb95103 100644 --- a/Sources/DistributedActors/Time.swift +++ b/Sources/DistributedActors/Time.swift @@ -20,8 +20,6 @@ import struct NIO.TimeAmount // ==== ---------------------------------------------------------------------------------------------------------------- // MARK: TimeAmount -// TODO: We have discussed and wanted to "do your own" rather than import the NIO ones, but not entirely sold on the usefulness of replicating them -- ktoso - /// Represents a time _interval_. /// /// - note: `TimeAmount` should not be used to represent a point in time. @@ -31,7 +29,7 @@ public struct TimeAmount: Codable, Sendable { /// The nanoseconds representation of the `TimeAmount`. public let nanoseconds: Value - fileprivate init(_ nanoseconds: Value) { // FIXME: Needed the copy since this constructor + fileprivate init(_ nanoseconds: Value) { self.nanoseconds = nanoseconds } @@ -418,36 +416,6 @@ extension Deadline { } } -// ==== ---------------------------------------------------------------------------------------------------------------- -// MARK: Clock - -/// A `Clock` implementation using `Date`. -public struct WallTimeClock: Codable, Sendable, Comparable, CustomStringConvertible { - internal let timestamp: Date - - public static let zero = WallTimeClock(timestamp: Date.distantPast) - - public init() { - self.init(timestamp: Date()) - } - - public init(timestamp: Date) { - self.timestamp = timestamp - } - - public static func < (lhs: WallTimeClock, rhs: WallTimeClock) -> Bool { - lhs.timestamp < rhs.timestamp - } - - public static func == (lhs: WallTimeClock, rhs: WallTimeClock) -> Bool { - lhs.timestamp == rhs.timestamp - } - - public var description: String { - "\(self.timestamp.description)" - } -} - extension DispatchTimeInterval: CustomPrettyStringConvertible { /// Total amount of nanoseconds represented by this interval. /// diff --git a/Sources/DistributedActors/Timers+Distributed.swift b/Sources/DistributedActors/Timers+Distributed.swift index 19244490d..88a87577b 100644 --- a/Sources/DistributedActors/Timers+Distributed.swift +++ b/Sources/DistributedActors/Timers+Distributed.swift @@ -14,7 +14,6 @@ import Dispatch import Distributed -import Logging import struct NIO.TimeAmount @usableFromInline @@ -48,8 +47,8 @@ public final class ActorTimers where Act.ActorSystem == C @usableFromInline internal var installedTimers: [TimerKey: DistributedActorTimer] = [:] - @usableFromInline - internal var log: Logger + // TODO: this is a workaround, we're removing ActorTimers since they can't participate in structured cancellation + weak var actorSystem: Act.ActorSystem? /// Create a timers instance owned by the passed in actor. /// @@ -57,15 +56,10 @@ public final class ActorTimers where Act.ActorSystem == C /// /// - Parameter myself: public init(_ myself: Act) { - self.log = Logger(label: "\(myself)") // FIXME(distributed): pick up the actor logger (!!!) - log[metadataKey: "actor/id"] = "\(myself.id.detailedDescription)" self.ownerID = myself.id } deinit { - if installedTimers.count > 0 { - log.debug("\(Self.self) deinit, cancelling [\(installedTimers.count)] timers") // TODO: include actor address - } self._cancelAll(includeSystemTimers: true) } @@ -76,7 +70,6 @@ public final class ActorTimers where Act.ActorSystem == C internal func _cancelAll(includeSystemTimers: Bool) { for key in self.installedTimers.keys where includeSystemTimers || !key.isSystemTimer { - // TODO: represent with "system timer key" type? // TODO: the reason the `_` keys are not cancelled is because we want to cancel timers in _restartPrepare but we need "our restart timer" to remain self.cancel(for: key) } @@ -88,9 +81,6 @@ public final class ActorTimers where Act.ActorSystem == C @inlinable public func cancel(for key: TimerKey) { if let timer = self.installedTimers.removeValue(forKey: key) { -// if system.settings.logging.verboseTimers { -// self.log.trace("Cancel timer [\(key)] with generation [\(timer.generation)]", metadata: self.metadata) -// } timer.handle.cancel() } } @@ -105,8 +95,6 @@ public final class ActorTimers where Act.ActorSystem == C /// Starts a timer that will invoke the provided `call` closure on the actor's context after the specified delay. /// - /// Timer keys are used for logging purposes and should descriptively explain the purpose of this timer. - /// /// - Parameters: /// - key: the key associated with the timer /// - call: the call that will be made after the `delay` amount of time elapses @@ -122,8 +110,6 @@ public final class ActorTimers where Act.ActorSystem == C /// Starts a timer that will periodically invoke the passed in `call` closure on the actor's context. /// - /// Timer keys are used for logging purposes and should descriptively explain the purpose of this timer. - /// /// - Parameters: /// - key: the key associated with the timer /// - call: the call that will be executed after the `delay` amount of time elapses @@ -146,22 +132,37 @@ public final class ActorTimers where Act.ActorSystem == C ) { self.cancel(for: key) -// let generation = self.nextTimerGen() // TODO(distributed): we're not using generations since we don't have restarts - // let event = DistributedActorTimerEvent(key: key, generation: generation, owner: self.ownerID) let handle: Cancelable if repeated { handle = self.dispatchQueue.scheduleAsync(initialDelay: interval, interval: interval) { + // We take the lock to prevent the system from shutting down + // while we're in the middle of potentially issuing remote calls + // Which may cause: Cannot schedule tasks on an EventLoop that has already shut down. + // The actual solution is outstanding work tracking potentially. + let system = self.actorSystem + system?.shutdownLock.lock() + defer { + system?.shutdownLock.unlock() + } + await call() } } else { handle = self.dispatchQueue.scheduleOnceAsync(delay: interval) { + // We take the lock to prevent the system from shutting down + // while we're in the middle of potentially issuing remote calls + // Which may cause: Cannot schedule tasks on an EventLoop that has already shut down. + // The actual solution is outstanding work tracking potentially. + let system = self.actorSystem + system?.shutdownLock.lock() + defer { + system?.shutdownLock.unlock() + } + await call() } } -// if system.settings.logging.verboseTimers { -// self.log.trace("Started timer [\(key)] with generation [\(generation)]", metadata: self.metadata) -// } self.installedTimers[key] = DistributedActorTimer(key: key, repeated: repeated, handle: handle) } } diff --git a/Sources/DistributedActors/Version.swift b/Sources/DistributedActors/Version.swift index 6fe325622..51ce2946c 100644 --- a/Sources/DistributedActors/Version.swift +++ b/Sources/DistributedActors/Version.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Distributed Actors open source project // -// Copyright (c) 2018-2019 Apple Inc. and the Swift Distributed Actors project authors +// Copyright (c) 2018-2022 Apple Inc. and the Swift Distributed Actors project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -12,27 +12,30 @@ // //===----------------------------------------------------------------------===// -/// Version struct used to determine the version of an actor system, -/// including its wire compatibility with another system. -public struct Version: Equatable, CustomStringConvertible { - // TODO: Exact semantics remain to be defined. Reserved likely to be used for flags "connection modes" etc "don't connect me, I just send 1 message" etc? - public var reserved: UInt8 - public var major: UInt8 - public var minor: UInt8 - public var patch: UInt8 +extension ClusterSystem { + /// Version advertised to other nodes while joining the cluster. + /// + /// Can be used to determine wire of feature compatibility of nodes joining a cluster. + public struct Version: Equatable, CustomStringConvertible { + /// Exact semantics of the reserved field remain to be defined. + public var reserved: UInt8 + public var major: UInt8 + public var minor: UInt8 + public var patch: UInt8 - init(reserved: UInt8, major: UInt8, minor: UInt8, patch: UInt8) { - self.reserved = reserved - self.major = major - self.minor = minor - self.patch = patch - } + init(reserved: UInt8, major: UInt8, minor: UInt8, patch: UInt8) { + self.reserved = reserved + self.major = major + self.minor = minor + self.patch = patch + } - public var description: String { - "Version(\(self.major).\(self.minor).\(self.patch), reserved:\(self.reserved))" - } + public var description: String { + "Version(\(self.major).\(self.minor).\(self.patch), reserved:\(self.reserved))" + } - public var versionString: String { - "\(self.major).\(self.minor).\(self.patch)" + public var versionString: String { + "\(self.major).\(self.minor).\(self.patch)" + } } } diff --git a/Sources/DistributedActors/_ActorShell.swift b/Sources/DistributedActors/_ActorShell.swift index b3c0a2cfd..303ba4ad6 100644 --- a/Sources/DistributedActors/_ActorShell.swift +++ b/Sources/DistributedActors/_ActorShell.swift @@ -28,14 +28,14 @@ import NIO /// /// The shell is mutable, and full of dangerous and carefully threaded/ordered code, be extra cautious. // TODO: remove this and replace by the infrastructure which is now Swift's `actor` -public final class _ActorShell: _ActorContext, AbstractShellProtocol { +public final class _ActorShell: _ActorContext, AbstractShellProtocol { // The phrase that "actor change their behavior" can be understood quite literally; // On each message interpretation the actor may return a new behavior that will be handling the next message. @usableFromInline var behavior: _Behavior @usableFromInline - let _parent: AddressableActorRef + let _parent: _AddressableActorRef @usableFromInline let _id: ActorID @@ -47,9 +47,6 @@ public final class _ActorShell: _ActorContext, A // ==== ---------------------------------------------------------------------------------------------------------------- // MARK: Instrumentation - @usableFromInline - var instrumentation: ActorInstrumentation! - @usableFromInline let metrics: ActiveActorMetrics @@ -91,7 +88,7 @@ public final class _ActorShell: _ActorContext, A } @usableFromInline - var asAddressable: AddressableActorRef { + var asAddressable: _AddressableActorRef { self.myself.asAddressable } @@ -145,7 +142,7 @@ public final class _ActorShell: _ActorContext, A // MARK: _ActorShell implementation internal init( - system: ClusterSystem, parent: AddressableActorRef, + system: ClusterSystem, parent: _AddressableActorRef, behavior: _Behavior, id: ActorID, props: _Props, dispatcher: MessageDispatcher ) { @@ -177,8 +174,6 @@ public final class _ActorShell: _ActorContext, A super.init() - self.instrumentation = system.settings.instrumentation.makeActorInstrumentation(self, id) - self.instrumentation.actorSpawned() system.metrics.recordActorStart(self) } @@ -189,7 +184,6 @@ public final class _ActorShell: _ActorContext, A self.system.userCellInitCounter.loadThenWrappingDecrement(ordering: .relaxed) } #endif - self.instrumentation.actorStopped() self.system.metrics.recordActorStop(self) self._system = nil @@ -281,8 +275,6 @@ public final class _ActorShell: _ActorContext, A /// Returns: `true` if the actor remains alive, and `false` if it now is becoming `.stop` @inlinable func interpretMessage(message: Message) throws -> ActorRunResult { - self.instrumentation.actorReceivedStart(message: message, from: nil) - do { let next: _Behavior = try self.supervisor.interpretSupervised(target: self.behavior, context: self, message: message) @@ -291,10 +283,8 @@ public final class _ActorShell: _ActorContext, A #endif // TODO: make the \next printout nice TODO dont log messages (could leak pass etc) let runResult = try self.finishInterpretAnyMessage(next) - self.instrumentation.actorReceivedEnd(error: nil) return runResult } catch { - self.instrumentation.actorReceivedEnd(error: error) throw error } } @@ -610,7 +600,7 @@ public final class _ActorShell: _ActorContext, A } func notifyParentOfTermination() { - let parent: AddressableActorRef = self._parent + let parent: _AddressableActorRef = self._parent traceLog_DeathWatch("NOTIFY PARENT WE ARE DEAD, myself: [\(self.id)], parent [\(parent.id)]") guard case .failed(_, let failure) = self.behavior.underlying else { @@ -637,7 +627,7 @@ public final class _ActorShell: _ActorContext, A file: String = #file, line: UInt = #line, _ behavior: _Behavior ) throws -> _ActorRef - where M: ActorMessage + where M: Codable { try self.system.serialization._ensureSerializer(type, file: file, line: line) return try self._spawn(naming, props: props, behavior) @@ -651,12 +641,12 @@ public final class _ActorShell: _ActorContext, A file: String = #file, line: UInt = #line, _ behavior: _Behavior ) throws -> _ActorRef - where Message: ActorMessage + where Message: Codable { self.watch(try self._spawn(naming, props: props, behavior)) } - override public func stop(child ref: _ActorRef) throws { + override public func stop(child ref: _ActorRef) throws { try self._stop(child: ref) } @@ -693,7 +683,7 @@ public final class _ActorShell: _ActorContext, A } override public func subReceive(_ id: _SubReceiveId, _ subType: SubMessage.Type, _ closure: @escaping (SubMessage) throws -> Void) -> _ActorRef - where SubMessage: ActorMessage + where SubMessage: Codable { do { let wrappedClosure: (SubMessageCarry) throws -> _Behavior = { carry in @@ -746,7 +736,7 @@ public final class _ActorShell: _ActorContext, A } override public func messageAdapter(from fromType: From.Type, adapt: @escaping (From) -> Message?) -> _ActorRef - where From: ActorMessage + where From: Codable { do { let metaType = MetaType(fromType) @@ -789,9 +779,8 @@ public final class _ActorShell: _ActorContext, A // MARK: Internal system message / signal handling functions extension _ActorShell { - @inlinable func interpretSystemWatch(watcher: AddressableActorRef) { + @inlinable func interpretSystemWatch(watcher: _AddressableActorRef) { if self.behavior.isStillAlive { - self.instrumentation.actorWatchReceived(watchee: self.id, watcher: watcher.id) self.deathWatch.becomeWatchedBy(watcher: watcher, myself: self.myself, parent: self._parent) } else { // so we are in the middle of terminating already anyway @@ -799,8 +788,7 @@ extension _ActorShell { } } - @inlinable func interpretSystemUnwatch(watcher: AddressableActorRef) { - self.instrumentation.actorUnwatchReceived(watchee: self.id, watcher: watcher.id) + @inlinable func interpretSystemUnwatch(watcher: _AddressableActorRef) { self.deathWatch.removeWatchedBy(watcher: watcher, myself: self.myself) } @@ -886,7 +874,7 @@ extension _ActorShell { } @inlinable - func interpretChildTerminatedSignal(who terminatedRef: AddressableActorRef, terminated: _Signals._ChildTerminated) throws { + func interpretChildTerminatedSignal(who terminatedRef: _AddressableActorRef, terminated: _Signals._ChildTerminated) throws { #if SACT_TRACE_ACTOR_SHELL self.log.info("Received \(terminated)") #endif @@ -957,7 +945,7 @@ extension _ActorShell: CustomStringConvertible { internal protocol AbstractShellProtocol: _ActorTreeTraversable { var _myselfReceivesSystemMessages: _ReceivesSystemMessages { get } var children: _Children { get set } // lock-protected - var asAddressable: AddressableActorRef { get } + var asAddressable: _AddressableActorRef { get } var metrics: ActiveActorMetrics { get } } @@ -967,7 +955,7 @@ extension AbstractShellProtocol { self._myselfReceivesSystemMessages } - public func _traverse(context: _TraversalContext, _ visit: (_TraversalContext, AddressableActorRef) -> _TraversalDirective) -> _TraversalResult { + public func _traverse(context: _TraversalContext, _ visit: (_TraversalContext, _AddressableActorRef) -> _TraversalDirective) -> _TraversalResult { var c = context.deeper switch visit(context, self.asAddressable) { case .continue: @@ -985,7 +973,7 @@ extension AbstractShellProtocol { } public func _resolve(context: ResolveContext) -> _ActorRef - where Message: ActorMessage + where Message: Codable { let myself: _ReceivesSystemMessages = self._myselfReceivesSystemMessages @@ -1014,7 +1002,7 @@ extension AbstractShellProtocol { return self.children._resolve(context: context) } - public func _resolveUntyped(context: ResolveContext) -> AddressableActorRef { + public func _resolveUntyped(context: ResolveContext) -> _AddressableActorRef { guard context.selectorSegments.first != nil else { // no remaining selectors == we are the "selected" ref, apply uid check if self._myselfReceivesSystemMessages.id.incarnation == context.id.incarnation { diff --git a/Sources/DistributedActors/_BehaviorTimers.swift b/Sources/DistributedActors/_BehaviorTimers.swift index d7f978d45..978c18c49 100644 --- a/Sources/DistributedActors/_BehaviorTimers.swift +++ b/Sources/DistributedActors/_BehaviorTimers.swift @@ -79,7 +79,7 @@ extension TimerKey: ExpressibleByStringLiteral, ExpressibleByStringInterpolation } } -public final class _BehaviorTimers { +public final class _BehaviorTimers { @usableFromInline internal var timerGen: Int = 0 diff --git a/Sources/DistributedActors/_Mailbox.swift b/Sources/DistributedActors/_Mailbox.swift index 0243412b6..a12f310c7 100644 --- a/Sources/DistributedActors/_Mailbox.swift +++ b/Sources/DistributedActors/_Mailbox.swift @@ -54,7 +54,7 @@ internal enum MailboxBitMasks { // = 0b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0001_1101 } -internal final class _Mailbox { +internal final class _Mailbox { weak var shell: _ActorShell? let _status: ManagedAtomic = .init(0) let userMessages: MPSCLinkedQueue @@ -669,7 +669,7 @@ internal final class _Mailbox { // that it added some runtime overhead when retrieving the messages from the // queue, because additional metatype information was retrieved, therefore // we removed it -internal enum WrappedMessage: NonTransportableActorMessage { +internal enum WrappedMessage: _NotActuallyCodableMessage { case message(Any) case closure(ActorClosureCarry) case adaptedMessage(AdaptedMessageCarry) diff --git a/Sources/DistributedActors/_Signals.swift b/Sources/DistributedActors/_Signals.swift index 10537f653..970564690 100644 --- a/Sources/DistributedActors/_Signals.swift +++ b/Sources/DistributedActors/_Signals.swift @@ -23,7 +23,7 @@ import Distributed /// - Warning: Users MUST NOT implement new signals. /// Instances of them are reserved to only be created and managed by the actor system itself. /// - SeeAlso: `Signals`, for a complete listing of pre-defined signals. -public protocol _Signal: NonTransportableActorMessage, Sendable {} // FIXME: we could allow them as Codable, we never send them over the wire, but people might manually if they wanted to I suppose +public protocol _Signal: _NotActuallyCodableMessage, Sendable {} // FIXME: we could allow them as Codable, we never send them over the wire, but people might manually if they wanted to I suppose /// Namespace for all pre-defined `Signal` types. /// diff --git a/Sources/DistributedActorsBenchmarks/ActorPingPongBenchmarks.swift b/Sources/DistributedActorsBenchmarks/ActorPingPongBenchmarks.swift index 5b6254087..fda4412b4 100644 --- a/Sources/DistributedActorsBenchmarks/ActorPingPongBenchmarks.swift +++ b/Sources/DistributedActorsBenchmarks/ActorPingPongBenchmarks.swift @@ -98,7 +98,7 @@ private func tearDown() { } // === ----------------------------------------------------------------------------------------------------------------- -private enum PingPongCommand: NonTransportableActorMessage { +private enum PingPongCommand: _NotActuallyCodableMessage { case startPingPong( messagesPerPair: Int, numActors: Int, @@ -186,7 +186,7 @@ private func startPingPongActorPairs( return actors } -private struct EchoMessage: ActorMessage, CustomStringConvertible { +private struct EchoMessage: Codable, CustomStringConvertible { var seqNr: Int let replyTo: _ActorRef diff --git a/Sources/DistributedActorsBenchmarks/ActorRemotePingPongBenchmarks.swift b/Sources/DistributedActorsBenchmarks/ActorRemotePingPongBenchmarks.swift index 8d1af2a7d..34912c2e1 100644 --- a/Sources/DistributedActorsBenchmarks/ActorRemotePingPongBenchmarks.swift +++ b/Sources/DistributedActorsBenchmarks/ActorRemotePingPongBenchmarks.swift @@ -85,7 +85,7 @@ private func tearDown() { } // === ----------------------------------------------------------------------------------------------------------------- -private enum PingPongCommand: NonTransportableActorMessage { +private enum PingPongCommand: _NotActuallyCodableMessage { case startPingPong( messagesPerPair: Int, numActors: Int, @@ -173,7 +173,7 @@ private func startRemotePingPongActorPairs( return actors } -private struct EchoMessage: ActorMessage, CustomStringConvertible { +private struct EchoMessage: Codable, CustomStringConvertible { var seqNr: Int let replyTo: _ActorRef diff --git a/Sources/DistributedActorsBenchmarks/SerializationCodableBenchmarks.swift b/Sources/DistributedActorsBenchmarks/SerializationCodableBenchmarks.swift index 3ca6ca4df..1bbcd7789 100644 --- a/Sources/DistributedActorsBenchmarks/SerializationCodableBenchmarks.swift +++ b/Sources/DistributedActorsBenchmarks/SerializationCodableBenchmarks.swift @@ -53,7 +53,7 @@ private func tearDown() { // ------- -struct SmallMessage: ActorMessage { +struct SmallMessage: Codable { let number: Int let name: String } @@ -67,7 +67,7 @@ func bench_codable_roundTrip_message_small(n: Int) { // ------- -struct MessageWithRef: ActorMessage { +struct MessageWithRef: Codable { let number: Int let name: String let reference: _ActorRef @@ -87,7 +87,7 @@ func bench_codable_roundTrip_message_withRef(n: Int) { // ------- -struct MediumMessage: ActorMessage { +struct MediumMessage: Codable { struct NestedMessage: Codable { let field1: String let field2: Int32 diff --git a/Sources/DistributedActorsTestKit/ActorTestKit.swift b/Sources/DistributedActorsTestKit/ActorTestKit.swift index 7a12e46c0..c59c6fae3 100644 --- a/Sources/DistributedActorsTestKit/ActorTestKit.swift +++ b/Sources/DistributedActorsTestKit/ActorTestKit.swift @@ -65,7 +65,7 @@ public struct ActorTestKitSettings { extension ActorTestKit { /// Spawn an `ActorTestProbe` which offers various assertion methods for actor messaging interactions. - public func makeTestProbe( + public func makeTestProbe( _ naming: _ActorNaming? = nil, expecting type: Message.Type = Message.self, file: StaticString = #file, line: UInt = #line @@ -99,7 +99,7 @@ extension ActorTestKit { /// Spawns an `ActorTestProbe` and immediately subscribes it to the passed in event stream. /// /// - Hint: Use `fishForMessages` and `fishFor` to filter expectations for specific events. - public func spawnEventStreamTestProbe( + public func spawnEventStreamTestProbe( _ naming: _ActorNaming? = nil, subscribedTo eventStream: EventStream, file: String = #file, line: UInt = #line, column: UInt = #column @@ -248,7 +248,7 @@ extension ActorTestKit { precondition(!path.contains("#"), "assertion path MUST NOT contain # id section of an unique path.") let callSiteInfo = CallSiteInfo(file: file, line: line, column: column, function: #function) - let res: _TraversalResult = self.system._traverseAll { _, ref in + let res: _TraversalResult<_AddressableActorRef> = self.system._traverseAll { _, ref in if ref.id.path.description == path { return .accumulateSingle(ref) } else { @@ -289,13 +289,13 @@ extension ActorTestKit { extension ActorTestKit { /// Creates a _fake_ `_ActorContext` which can be used to pass around to fulfil type argument requirements, /// however it DOES NOT have the ability to perform any of the typical actor context actions (such as spawning etc). - public func makeFakeContext(of: M.Type = M.self) -> _ActorContext { + public func makeFakeContext(of: M.Type = M.self) -> _ActorContext { Mock_ActorContext(self.system) } /// Creates a _fake_ `_ActorContext` which can be used to pass around to fulfil type argument requirements, /// however it DOES NOT have the ability to perform any of the typical actor context actions (such as spawning etc). - public func makeFakeContext(for: _Behavior) -> _ActorContext { + public func makeFakeContext(for: _Behavior) -> _ActorContext { self.makeFakeContext(of: M.self) } } @@ -309,7 +309,7 @@ struct MockActorContextError: Error, CustomStringConvertible { } } -public final class Mock_ActorContext: _ActorContext { +public final class Mock_ActorContext: _ActorContext { private let _system: ClusterSystem public init(_ system: ClusterSystem) { @@ -369,7 +369,7 @@ public final class Mock_ActorContext: _ActorContext ) throws -> _ActorRef - where M: ActorMessage + where M: Codable { fatalError("Failed: \(MockActorContextError())") } @@ -380,7 +380,7 @@ public final class Mock_ActorContext: _ActorContext ) throws -> _ActorRef - where M: ActorMessage + where M: Codable { fatalError("Failed: \(MockActorContextError())") } @@ -485,23 +485,23 @@ extension ActorTestKit { /// If `expectedRefs` is specified, also compares it to the listing for `key` and requires an exact match. @available(*, deprecated, message: "Will be removed and replaced by API based on DistributedActor. Issue #824") public func ensureRegistered( - key: Reception.Key<_ActorRef>, + key: _Reception.Key<_ActorRef>, expectedCount: Int = 1, expectedRefs: Set<_ActorRef>? = nil, within: TimeAmount = .seconds(3) ) throws { - let lookupProbe = self.makeTestProbe(expecting: Reception.Listing<_ActorRef>.self) + let lookupProbe = self.makeTestProbe(expecting: _Reception.Listing<_ActorRef>.self) try self.eventually(within: within) { self.system._receptionist.lookup(key, replyTo: lookupProbe.ref) let listing = try lookupProbe.expectMessage() guard listing.refs.count == expectedCount else { - throw self.error("Expected Reception.Listing for key [\(key)] to have count [\(expectedCount)], but got [\(listing.refs.count)]") + throw self.error("Expected _Reception.Listing for key [\(key)] to have count [\(expectedCount)], but got [\(listing.refs.count)]") } if let expectedRefs = expectedRefs { guard Set(listing.refs) == expectedRefs else { - throw self.error("Expected Reception.Listing for key [\(key)] to have refs \(expectedRefs), but got \(listing.refs)") + throw self.error("Expected _Reception.Listing for key [\(key)] to have refs \(expectedRefs), but got \(listing.refs)") } } } diff --git a/Sources/DistributedActorsTestKit/TestProbes+Receptionist.swift b/Sources/DistributedActorsTestKit/TestProbes+Receptionist.swift index e270264c4..ec6fc3990 100644 --- a/Sources/DistributedActorsTestKit/TestProbes+Receptionist.swift +++ b/Sources/DistributedActorsTestKit/TestProbes+Receptionist.swift @@ -18,7 +18,7 @@ import XCTest // ==== ---------------------------------------------------------------------------------------------------------------- // MARK: ActorTestProbe: Receptionist expectations -extension ActorTestProbe where Message == Reception.Listing<_ActorRef> { +extension ActorTestProbe where Message == _Reception.Listing<_ActorRef> { /// Expect a listing eventually to contain only the `expected` references. /// /// Lack of listing emitted during the `within` period also yields a test-case failing error. diff --git a/Sources/DistributedActorsTestKit/TestProbes.swift b/Sources/DistributedActorsTestKit/TestProbes.swift index 8e9867fbf..609e01b80 100644 --- a/Sources/DistributedActorsTestKit/TestProbes.swift +++ b/Sources/DistributedActorsTestKit/TestProbes.swift @@ -18,21 +18,21 @@ import Foundation import XCTest internal enum ActorTestProbeCommand { - case watchCommand(who: AddressableActorRef, file: String, line: UInt) - case unwatchCommand(who: AddressableActorRef) + case watchCommand(who: _AddressableActorRef, file: String, line: UInt) + case unwatchCommand(who: _AddressableActorRef) case forwardCommand(send: () -> Void) case stopCommand case realMessage(message: M) } -extension ActorTestProbeCommand: NonTransportableActorMessage {} +extension ActorTestProbeCommand: _NotActuallyCodableMessage {} /// A special actor that can be used in place of real actors, yet in addition exposes useful assertion methods /// which make testing asynchronous actor interactions simpler. /// /// - SeeAlso: `ActorableTestProbe` which is the equivalent API for `Actorable`s. -public final class ActorTestProbe: @unchecked Sendable { +public final class ActorTestProbe: @unchecked Sendable { /// Name of the test probe (and underlying actor). public let name: String @@ -500,7 +500,7 @@ extension ActorTestProbe { } /// Allows intercepting messages -public final class ProbeInterceptor: _Interceptor { +public final class ProbeInterceptor: _Interceptor { let probe: ActorTestProbe public init(probe: ActorTestProbe) { @@ -715,7 +715,7 @@ extension ActorTestProbe { /// - ***Warning**: Remember to first `watch` the actors you are expecting termination for, /// otherwise the termination signal will never be received. /// - SeeAlso: `DeathWatch` - public func expectTerminatedInAnyOrder(_ refs: [AddressableActorRef], file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws { + public func expectTerminatedInAnyOrder(_ refs: [_AddressableActorRef], file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws { let callSite = CallSiteInfo(file: file, line: line, column: column, function: #function) var pathSet: Set = Set(refs.map(\.id)) diff --git a/Sources/ExecApp/boot.swift b/Sources/ExecApp/boot.swift index 06a1ba65d..6a5656e6c 100644 --- a/Sources/ExecApp/boot.swift +++ b/Sources/ExecApp/boot.swift @@ -23,19 +23,18 @@ enum Main { let system = await ClusterSystem("FirstSystem") { settings in settings.node.host = "127.0.0.1" settings.node.port = 7337 - settings.logging.useBuiltInFormatter = true } let second = await ClusterSystem("SecondSystem") { settings in settings.node.host = "127.0.0.1" settings.node.port = 8228 - settings.logging.useBuiltInFormatter = true } system.cluster.join(node: second.cluster.uniqueNode) print("LOCAL:") let greeter = Greeter(actorSystem: system) - try await greeter.hi(name: "Caplin") + let localGreeting = try await greeter.hi(name: "Caplin") + print("Got local greeting: \(localGreeting)") print("RESOLVE:") let resolved = try Greeter.resolve(id: greeter.id, using: system) diff --git a/Tests/DistributedActorsDocumentationTests/ActorDocExamples.swift b/Tests/DistributedActorsDocumentationTests/ActorDocExamples.swift index 7306a4f58..5d1840b45 100644 --- a/Tests/DistributedActorsDocumentationTests/ActorDocExamples.swift +++ b/Tests/DistributedActorsDocumentationTests/ActorDocExamples.swift @@ -23,7 +23,7 @@ import XCTest class ActorDocExamples: XCTestCase { // tag::message_greetings[] - enum Greetings: NonTransportableActorMessage { + enum Greetings: _NotActuallyCodableMessage { case greet(name: String) case greeting(String) } @@ -156,7 +156,7 @@ class ActorDocExamples: XCTestCase { func example_receptionist_register() { // tag::receptionist_register[] - let key = Reception.Key(_ActorRef.self, id: "my-actor") // <1> + let key = _Reception.Key(_ActorRef.self, id: "my-actor") // <1> let behavior: _Behavior = .setup { context in context.receptionist.registerMyself(with: key) // <2> @@ -172,7 +172,7 @@ class ActorDocExamples: XCTestCase { } func example_receptionist_lookup() { - let key = Reception.Key(_ActorRef.self, id: "my-actor") + let key = _Reception.Key(_ActorRef.self, id: "my-actor") let system = ClusterSystem("LookupExample") // tag::receptionist_lookup[] let response = system._receptionist.lookup(key, timeout: .seconds(1)) // <1> @@ -189,9 +189,9 @@ class ActorDocExamples: XCTestCase { } func example_receptionist_subscribe() { - let key = Reception.Key(_ActorRef.self, id: "my-actor") + let key = _Reception.Key(_ActorRef.self, id: "my-actor") // tag::receptionist_subscribe[] - let behavior: _Behavior>> = .setup { context in + let behavior: _Behavior<_Reception.Listing<_ActorRef>> = .setup { context in context.system._receptionist.subscribe(context.myself, to: key) // <1> return .receiveMessage { @@ -207,9 +207,9 @@ class ActorDocExamples: XCTestCase { } func example_context_receptionist_subscribe() { - let key = Reception.Key(_ActorRef.self, id: "my-actor") + let key = _Reception.Key(_ActorRef.self, id: "my-actor") // tag::context_receptionist_subscribe[] - let behavior: _Behavior>> = .setup { context in + let behavior: _Behavior<_Reception.Listing<_ActorRef>> = .setup { context in context.system._receptionist.subscribe(context.myself, to: key) // <1> return .receiveMessage { diff --git a/Tests/DistributedActorsDocumentationTests/InteropDocExamples.swift b/Tests/DistributedActorsDocumentationTests/InteropDocExamples.swift index 0e744a953..ac8c80e5d 100644 --- a/Tests/DistributedActorsDocumentationTests/InteropDocExamples.swift +++ b/Tests/DistributedActorsDocumentationTests/InteropDocExamples.swift @@ -21,7 +21,7 @@ import XCTest class InteropDocExamples: XCTestCase { func example_asyncOp_sendResult_dispatch() throws { // tag::message_greetings[] - enum Messages: NonTransportableActorMessage { + enum Messages: _NotActuallyCodableMessage { case string(String) } // end::message_greetings[] @@ -51,7 +51,7 @@ class InteropDocExamples: XCTestCase { func example_asyncOp_sendResult_insideActor() throws { // tag::asyncOp_sendResult_insideActor_enum_Messages[] - enum Messages: NonTransportableActorMessage { + enum Messages: _NotActuallyCodableMessage { case fetchData case result(String) } @@ -104,11 +104,11 @@ class InteropDocExamples: XCTestCase { } // tag::asyncOp_onResultAsync_enum_Messages[] - enum Messages: NonTransportableActorMessage { + enum Messages: _NotActuallyCodableMessage { case lookupUser(name: String, recipient: _ActorRef) } - enum LookupResponse: NonTransportableActorMessage { + enum LookupResponse: _NotActuallyCodableMessage { case user(User) case unknownUser(name: String) case lookupFailed(Error) @@ -161,7 +161,7 @@ class InteropDocExamples: XCTestCase { func example_asyncOp_awaitResult() throws { // tag::asyncOp_awaitResult_enum_Messages[] - enum Message: NonTransportableActorMessage { + enum Message: _NotActuallyCodableMessage { case addPrefix(string: String, recipient: _ActorRef) } // end::asyncOp_awaitResult_enum_Messages[] @@ -201,7 +201,7 @@ class InteropDocExamples: XCTestCase { } func example_asyncOp_awaitResultThrowing() throws { - enum Message: NonTransportableActorMessage { + enum Message: _NotActuallyCodableMessage { case addPrefix(string: String, recipient: _ActorRef) } diff --git a/Tests/DistributedActorsDocumentationTests/SerializationDocExamples.swift b/Tests/DistributedActorsDocumentationTests/SerializationDocExamples.swift index 002b429aa..d3730fb80 100644 --- a/Tests/DistributedActorsDocumentationTests/SerializationDocExamples.swift +++ b/Tests/DistributedActorsDocumentationTests/SerializationDocExamples.swift @@ -128,7 +128,7 @@ extension ParkingGarageStatus { // MARK: Serialization example - custom messages // tag::serialization_custom_messages[] -enum CustomlyEncodedMessage: Codable, NonTransportableActorMessage { +enum CustomlyEncodedMessage: Codable, _NotActuallyCodableMessage { case available case taken } diff --git a/Tests/DistributedActorsTestKitTests/ActorTestKitTests.swift b/Tests/DistributedActorsTestKitTests/ActorTestKitTests.swift index 85a0f133f..5eeb378b5 100644 --- a/Tests/DistributedActorsTestKitTests/ActorTestKitTests.swift +++ b/Tests/DistributedActorsTestKitTests/ActorTestKitTests.swift @@ -141,7 +141,7 @@ final class ActorTestKitTests: XCTestCase { func test_ensureRegistered() throws { let p = self.testKit.makeTestProbe(expecting: String.self) - let receptionKey: Reception.Key<_ActorRef> = "*" + let receptionKey: _Reception.Key<_ActorRef> = "*" self.system._receptionist.register(p.ref, as: "*") diff --git a/Tests/DistributedActorsTests/ActorAskTests.swift b/Tests/DistributedActorsTests/ActorAskTests.swift index 695577a30..98aa2ed98 100644 --- a/Tests/DistributedActorsTests/ActorAskTests.swift +++ b/Tests/DistributedActorsTests/ActorAskTests.swift @@ -19,7 +19,7 @@ import Foundation import XCTest final class ActorAskTests: ClusterSystemXCTestCase { - struct TestMessage: ActorMessage { + struct TestMessage: Codable { let replyTo: _ActorRef } @@ -87,7 +87,7 @@ final class ActorAskTests: ClusterSystemXCTestCase { result.shouldEqual("received:1") } - struct AnswerMePlease: ActorMessage { + struct AnswerMePlease: Codable { let replyTo: _ActorRef } diff --git a/Tests/DistributedActorsTests/ActorIsolationFailureHandlingTests.swift b/Tests/DistributedActorsTests/ActorIsolationFailureHandlingTests.swift index 33c095bbd..bc14ee533 100644 --- a/Tests/DistributedActorsTests/ActorIsolationFailureHandlingTests.swift +++ b/Tests/DistributedActorsTests/ActorIsolationFailureHandlingTests.swift @@ -22,12 +22,12 @@ final class ActorIsolationFailureHandlingTests: ClusterSystemXCTestCase { case simpleError(reason: String) } - enum SimpleProbeMessage: Equatable, NonTransportableActorMessage { + enum SimpleProbeMessage: Equatable, _NotActuallyCodableMessage { case spawned(child: _ActorRef) case echoing(message: String) } - enum FaultyWorkerMessage: NonTransportableActorMessage { + enum FaultyWorkerMessage: _NotActuallyCodableMessage { case work(n: Int, divideBy: Int) case throwError(error: Error) } diff --git a/Tests/DistributedActorsTests/ActorLoggingTests.swift b/Tests/DistributedActorsTests/ActorLoggingTests.swift index e6895ac3c..705194946 100644 --- a/Tests/DistributedActorsTests/ActorLoggingTests.swift +++ b/Tests/DistributedActorsTests/ActorLoggingTests.swift @@ -117,6 +117,6 @@ final class ActorLoggingTests: ClusterSystemXCTestCase { } } -private enum Rendered: String, ActorMessage { +private enum Rendered: String, Codable { case instance } diff --git a/Tests/DistributedActorsTests/ActorRefAdapterTests.swift b/Tests/DistributedActorsTests/ActorRefAdapterTests.swift index 454704b19..a96c499a1 100644 --- a/Tests/DistributedActorsTests/ActorRefAdapterTests.swift +++ b/Tests/DistributedActorsTests/ActorRefAdapterTests.swift @@ -140,7 +140,7 @@ class _ActorRefAdapterTests: ClusterSystemXCTestCase { try pAdapted.expectNoMessage(for: .milliseconds(10)) } - enum LifecycleTestMessage: NonTransportableActorMessage { + enum LifecycleTestMessage: _NotActuallyCodableMessage { case createAdapter(replyTo: _ActorRef<_ActorRef>) case crash case stop @@ -233,7 +233,7 @@ class _ActorRefAdapterTests: ClusterSystemXCTestCase { } func test_adaptedRef_useSpecificEnoughAdapterMostRecentlySet() throws { - class TopExample: NonTransportableActorMessage {} + class TopExample: _NotActuallyCodableMessage {} class BottomExample: TopExample {} let probe = self.testKit.makeTestProbe(expecting: String.self) diff --git a/Tests/DistributedActorsTests/ActorSubReceiveTests.swift b/Tests/DistributedActorsTests/ActorSubReceiveTests.swift index aea152799..d2d7305ea 100644 --- a/Tests/DistributedActorsTests/ActorSubReceiveTests.swift +++ b/Tests/DistributedActorsTests/ActorSubReceiveTests.swift @@ -89,11 +89,11 @@ final class ActorSubReceiveTests: ClusterSystemXCTestCase { let p = self.testKit.makeTestProbe(expecting: Int.self) let refProbe = self.testKit.makeTestProbe(expecting: _ActorRef.self) - struct GetState: ActorMessage { + struct GetState: Codable { let replyTo: _ActorRef } - struct IncrementAndGet: ActorMessage { + struct IncrementAndGet: Codable { let replyTo: _ActorRef } @@ -205,7 +205,7 @@ final class ActorSubReceiveTests: ClusterSystemXCTestCase { } func test_subReceive_shouldBeReplacedIfRegisteredAgainUnderSameKey() throws { - struct TestMessage: ActorMessage { + struct TestMessage: Codable { let replyTo: _ActorRef let message: String } diff --git a/Tests/DistributedActorsTests/BehaviorTests.swift b/Tests/DistributedActorsTests/BehaviorTests.swift index 26ceaccee..ed81a86f4 100644 --- a/Tests/DistributedActorsTests/BehaviorTests.swift +++ b/Tests/DistributedActorsTests/BehaviorTests.swift @@ -20,7 +20,7 @@ import NIO import XCTest final class BehaviorTests: ClusterSystemXCTestCase { - public struct TestMessage: ActorMessage { + public struct TestMessage: Codable { let message: String let replyTo: _ActorRef } @@ -144,7 +144,7 @@ final class BehaviorTests: ClusterSystemXCTestCase { // receiveSignalType was invoked successfully } - enum OrElseMessage: String, ActorMessage { + enum OrElseMessage: String, Codable { case first case second case other @@ -376,7 +376,7 @@ final class BehaviorTests: ClusterSystemXCTestCase { try capture.shouldContain(message: "*BehaviorTests.swift:\(mockLine)*") } - enum ContextClosureMessage: NonTransportableActorMessage { + enum ContextClosureMessage: _NotActuallyCodableMessage { case context(() -> _ActorRef) } diff --git a/Tests/DistributedActorsTests/Clocks/LamportClockTests.swift b/Tests/DistributedActorsTests/Clocks/LamportClockTests.swift deleted file mode 100644 index 5ba97128e..000000000 --- a/Tests/DistributedActorsTests/Clocks/LamportClockTests.swift +++ /dev/null @@ -1,36 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Distributed Actors open source project -// -// Copyright (c) 2018-2019 Apple Inc. and the Swift Distributed Actors project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.md for the list of Swift Distributed Actors project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import DistributedActors -import DistributedActorsTestKit -import XCTest - -final class LamportClockTests: XCTestCase { - func test_lamportClock_movesOnlyForward() { - var lClock = LamportClock() - lClock.time.shouldEqual(0) - - lClock.increment() - lClock.time.shouldEqual(1) - - lClock.witness(42) - lClock.time.shouldEqual(42) - - lClock.witness(13) - lClock.time.shouldEqual(42) - - lClock.increment() - lClock.time.shouldEqual(43) - } -} diff --git a/Tests/DistributedActorsTests/Cluster/DowningStrategy/TimeoutBasedDowningInstanceTests.swift b/Tests/DistributedActorsTests/Cluster/DowningStrategy/TimeoutBasedDowningInstanceTests.swift index f66eeb81d..c6245a93c 100644 --- a/Tests/DistributedActorsTests/Cluster/DowningStrategy/TimeoutBasedDowningInstanceTests.swift +++ b/Tests/DistributedActorsTests/Cluster/DowningStrategy/TimeoutBasedDowningInstanceTests.swift @@ -42,7 +42,7 @@ final class TimeoutBasedDowningInstanceTests: XCTestCase { self.instance.isLeader.shouldBeFalse() let directive = try self.instance.onLeaderChange(to: self.selfMember) // when no nodes are pending to be downed, the directive should be `.none` - guard case .none = directive else { + guard case .none = directive.underlying else { throw TestError("Expected directive to be .none") } self.instance.isLeader.shouldBeTrue() @@ -55,7 +55,7 @@ final class TimeoutBasedDowningInstanceTests: XCTestCase { _ = self.instance.membership.join(self.otherNode) let directive = try self.instance.onLeaderChange(to: self.otherMember) // we the node does not become the leader, the directive should be `.none` - guard case .none = directive else { + guard case .none = directive.underlying else { throw TestError("Expected directive to be .none") } self.instance.isLeader.shouldBeFalse() @@ -69,7 +69,7 @@ final class TimeoutBasedDowningInstanceTests: XCTestCase { self.instance.isLeader.shouldBeTrue() let directive = try self.instance.onLeaderChange(to: self.otherMember) // when losing leadership, the directive should be `.none` - guard case .none = directive else { + guard case .none = directive.underlying else { throw TestError("Expected directive to be .none") } self.instance.isLeader.shouldBeFalse() @@ -99,7 +99,7 @@ final class TimeoutBasedDowningInstanceTests: XCTestCase { let member = Cluster.Member(node: self.otherNode, status: .up) self.instance._markAsDown.insert(member) let directive = try self.instance.onLeaderChange(to: self.selfMember) - guard case .markAsDown(let addresses) = directive else { + guard case .markAsDown(let addresses) = directive.underlying else { throw Boom() } addresses.count.shouldEqual(1) @@ -116,7 +116,7 @@ final class TimeoutBasedDowningInstanceTests: XCTestCase { self.instance._unreachable.insert(member) let directive = self.instance.onTimeout(member) - guard case .none = directive else { + guard case .none = directive.underlying else { throw TestError("Expected directive to be .none") } @@ -129,7 +129,7 @@ final class TimeoutBasedDowningInstanceTests: XCTestCase { self.instance._unreachable.insert(member) let directive = self.instance.onTimeout(member) - guard case .markAsDown(let node) = directive else { + guard case .markAsDown(let node) = directive.underlying else { throw TestError("Expected directive to be .markAsDown") } @@ -147,12 +147,12 @@ final class TimeoutBasedDowningInstanceTests: XCTestCase { let unreachableMember = self.otherMember.asUnreachable let directive = self.instance.onMemberUnreachable(.init(member: unreachableMember)) - guard case .startTimer = directive else { + guard case .startTimer = directive.underlying else { throw TestError("Expected .startTimer, but got \(directive)") } let downDecision = self.instance.onTimeout(unreachableMember) - guard case .markAsDown(let nodesToDown) = downDecision else { + guard case .markAsDown(let nodesToDown) = downDecision.underlying else { throw TestError("Expected .markAsDown, but got \(directive)") } @@ -172,7 +172,7 @@ final class TimeoutBasedDowningInstanceTests: XCTestCase { self.instance._unreachable.insert(member) let directive = self.instance.onMemberRemoved(member) - guard case .cancelTimer = directive else { + guard case .cancelTimer = directive.underlying else { throw TestError("Expected directive to be .cancelTimer") } } @@ -182,7 +182,7 @@ final class TimeoutBasedDowningInstanceTests: XCTestCase { self.instance._markAsDown.insert(member) let directive = self.instance.onMemberRemoved(member) - guard case .none = directive else { + guard case .none = directive.underlying else { throw TestError("Expected directive to be .none") } } @@ -191,7 +191,7 @@ final class TimeoutBasedDowningInstanceTests: XCTestCase { let member = Cluster.Member(node: self.otherNode, status: .up) let directive = self.instance.onMemberRemoved(member) - guard case .none = directive else { + guard case .none = directive.underlying else { throw TestError("Expected directive to be .none") } } @@ -204,7 +204,7 @@ final class TimeoutBasedDowningInstanceTests: XCTestCase { self.instance._unreachable.insert(member) let directive = self.instance.onMemberReachable(.init(member: member.asUnreachable)) - guard case .cancelTimer = directive else { + guard case .cancelTimer = directive.underlying else { throw TestError("Expected directive to be .cancelTimer") } } @@ -214,7 +214,7 @@ final class TimeoutBasedDowningInstanceTests: XCTestCase { self.instance._markAsDown.insert(member) let directive = self.instance.onMemberReachable(.init(member: member.asUnreachable)) - guard case .none = directive else { + guard case .none = directive.underlying else { throw TestError("Expected directive to be .none") } } @@ -223,7 +223,7 @@ final class TimeoutBasedDowningInstanceTests: XCTestCase { let member = Cluster.Member(node: self.otherNode, status: .up) let directive = self.instance.onMemberReachable(.init(member: member.asUnreachable)) - guard case .none = directive else { + guard case .none = directive.underlying else { throw TestError("Expected directive to be .none") } } @@ -233,7 +233,7 @@ final class TimeoutBasedDowningInstanceTests: XCTestCase { func test_onMemberUnreachable_shouldAddAddressOfMemberToUnreachableSet() throws { let member = Cluster.Member(node: self.otherNode, status: .up) - guard case .startTimer = self.instance.onMemberUnreachable(.init(member: member.asUnreachable)) else { + guard case .startTimer = self.instance.onMemberUnreachable(.init(member: member.asUnreachable)).underlying else { throw TestError("Expected directive to be .startTimer") } self.instance._unreachable.shouldContain(member.asUnreachable) diff --git a/Tests/DistributedActorsTests/Cluster/MembershipGossipLogicSimulationTests.swift b/Tests/DistributedActorsTests/Cluster/MembershipGossipLogicSimulationTests.swift index d1bd1795b..50e3f1399 100644 --- a/Tests/DistributedActorsTests/Cluster/MembershipGossipLogicSimulationTests.swift +++ b/Tests/DistributedActorsTests/Cluster/MembershipGossipLogicSimulationTests.swift @@ -41,7 +41,7 @@ final class MembershipGossipLogicSimulationTests: ClusteredActorSystemsXCTestCas self._nodes.map(\.cluster.uniqueNode) } - var mockPeers: [AddressableActorRef] = [] + var mockPeers: [_AddressableActorRef] = [] var testKit: ActorTestKit { self.testKit(self.systems.first!) @@ -347,7 +347,7 @@ final class MembershipGossipLogicSimulationTests: ClusteredActorSystemsXCTestCas // information. let participatingGossips = self.logics.shuffled() for logic in participatingGossips { - let selectedPeers: [AddressableActorRef] = logic.selectPeers(self.peers(of: logic)) + let selectedPeers: [_AddressableActorRef] = logic.selectPeers(self.peers(of: logic)) log.notice("[\(logic.nodeName)] selected peers: \(selectedPeers.map(\.id.uniqueNode.node.systemName))") for targetPeer in selectedPeers { @@ -418,15 +418,15 @@ final class MembershipGossipLogicSimulationTests: ClusteredActorSystemsXCTestCas // ==== ---------------------------------------------------------------------------------------------------------------- // MARK: Support functions - func peers(of logic: MembershipGossipLogic) -> [AddressableActorRef] { + func peers(of logic: MembershipGossipLogic) -> [_AddressableActorRef] { Array(self.mockPeers.filter { $0.id.uniqueNode != logic.localNode }) } - func selectLogic(_ peer: AddressableActorRef) -> MembershipGossipLogic { + func selectLogic(_ peer: _AddressableActorRef) -> MembershipGossipLogic { (self.logics.first { $0.localNode == peer.id.uniqueNode })! } - func peer(_ logic: MembershipGossipLogic) -> AddressableActorRef { + func peer(_ logic: MembershipGossipLogic) -> _AddressableActorRef { let nodeName = logic.localNode.node.systemName if let peer = (self.mockPeers.first { $0.id.uniqueNode.node.systemName == nodeName }) { return peer diff --git a/Tests/DistributedActorsTests/Cluster/Reception/OpLogDistributedReceptionistClusteredTests.swift b/Tests/DistributedActorsTests/Cluster/Reception/OpLogDistributedReceptionistClusteredTests.swift index 4a2ea72fb..2918cb408 100644 --- a/Tests/DistributedActorsTests/Cluster/Reception/OpLogDistributedReceptionistClusteredTests.swift +++ b/Tests/DistributedActorsTests/Cluster/Reception/OpLogDistributedReceptionistClusteredTests.swift @@ -170,9 +170,9 @@ final class OpLogDistributedReceptionistClusteredTests: ClusteredActorSystemsXCT // $0.receptionist.ackPullReplicationIntervalSlow = .seconds(1) // } // -// let registeredProbe = self.testKit(local).makeTestProbe("registeredProbe", expecting: Reception.Registered<_ActorRef>.self) -// let localLookupProbe = self.testKit(local).makeTestProbe("localLookupProbe", expecting: Reception.Listing<_ActorRef>.self) -// let remoteLookupProbe = self.testKit(remote).makeTestProbe("remoteLookupProbe", expecting: Reception.Listing<_ActorRef>.self) +// let registeredProbe = self.testKit(local).makeTestProbe("registeredProbe", expecting: _Reception.Registered<_ActorRef>.self) +// let localLookupProbe = self.testKit(local).makeTestProbe("localLookupProbe", expecting: _Reception.Listing<_ActorRef>.self) +// let remoteLookupProbe = self.testKit(remote).makeTestProbe("remoteLookupProbe", expecting: _Reception.Listing<_ActorRef>.self) // // let behavior: _Behavior = .receiveMessage { _ in // .same @@ -183,7 +183,7 @@ final class OpLogDistributedReceptionistClusteredTests: ClusteredActorSystemsXCT // let refC: _ActorRef = try remote._spawn("refC", behavior) // let refD: _ActorRef = try remote._spawn("refD", behavior) // -// let key = Reception.Key(_ActorRef.self, id: "test") +// let key = _Reception.Key(_ActorRef.self, id: "test") // // local._receptionist.register(refA, with: key, replyTo: registeredProbe.ref) // _ = try registeredProbe.expectMessage() @@ -226,13 +226,13 @@ final class OpLogDistributedReceptionistClusteredTests: ClusteredActorSystemsXCT // $0.receptionist.ackPullReplicationIntervalSlow = .seconds(1) // } // -// let registeredProbe = self.testKit(first).makeTestProbe(expecting: Reception.Registered<_ActorRef>.self) -// let remoteLookupProbe = self.testKit(second).makeTestProbe(expecting: Reception.Listing<_ActorRef>.self) +// let registeredProbe = self.testKit(first).makeTestProbe(expecting: _Reception.Registered<_ActorRef>.self) +// let remoteLookupProbe = self.testKit(second).makeTestProbe(expecting: _Reception.Listing<_ActorRef>.self) // // let refA: _ActorRef = try first._spawn(.anonymous, self.stopOnMessage) // let refB: _ActorRef = try first._spawn(.anonymous, self.stopOnMessage) // -// let key = Reception.Key(_ActorRef.self, id: "test") +// let key = _Reception.Key(_ActorRef.self, id: "test") // // first._receptionist.register(refA, with: key, replyTo: registeredProbe.ref) // _ = try registeredProbe.expectMessage() @@ -274,17 +274,17 @@ final class OpLogDistributedReceptionistClusteredTests: ClusteredActorSystemsXCT // first.cluster.join(node: second.cluster.uniqueNode.node) // try assertAssociated(first, withExactly: second.settings.uniqueBindNode) // -// let firstKey = Reception.Key(_ActorRef.self, id: "first") -// let extraKey = Reception.Key(_ActorRef.self, id: "extra") +// let firstKey = _Reception.Key(_ActorRef.self, id: "first") +// let extraKey = _Reception.Key(_ActorRef.self, id: "extra") // // let ref = try first._spawn("hi", self.stopOnMessage) // first._receptionist.register(ref, with: firstKey) // first._receptionist.register(ref, with: extraKey) // -// let p1f = self.testKit(first).makeTestProbe("p1f", expecting: Reception.Listing<_ActorRef>.self) -// let p1e = self.testKit(first).makeTestProbe("p1e", expecting: Reception.Listing<_ActorRef>.self) -// let p2f = self.testKit(second).makeTestProbe("p2f", expecting: Reception.Listing<_ActorRef>.self) -// let p2e = self.testKit(second).makeTestProbe("p2e", expecting: Reception.Listing<_ActorRef>.self) +// let p1f = self.testKit(first).makeTestProbe("p1f", expecting: _Reception.Listing<_ActorRef>.self) +// let p1e = self.testKit(first).makeTestProbe("p1e", expecting: _Reception.Listing<_ActorRef>.self) +// let p2f = self.testKit(second).makeTestProbe("p2f", expecting: _Reception.Listing<_ActorRef>.self) +// let p2e = self.testKit(second).makeTestProbe("p2e", expecting: _Reception.Listing<_ActorRef>.self) // // // ensure the ref is registered and known under both keys to both nodes // first._receptionist.subscribe(p1f.ref, to: firstKey) @@ -317,7 +317,7 @@ final class OpLogDistributedReceptionistClusteredTests: ClusteredActorSystemsXCT // first.cluster.join(node: second.cluster.uniqueNode.node) // try assertAssociated(first, withExactly: second.settings.uniqueBindNode) // -// let key = Reception.Key(_ActorRef.self, id: "key") +// let key = _Reception.Key(_ActorRef.self, id: "key") // // let firstRef = try first._spawn("onFirst", self.stopOnMessage) // first._receptionist.register(firstRef, with: key) @@ -325,8 +325,8 @@ final class OpLogDistributedReceptionistClusteredTests: ClusteredActorSystemsXCT // let secondRef = try second._spawn("onSecond", self.stopOnMessage) // second._receptionist.register(secondRef, with: key) // -// let p1 = self.testKit(first).makeTestProbe("p1", expecting: Reception.Listing<_ActorRef>.self) -// let p2 = self.testKit(second).makeTestProbe("p2", expecting: Reception.Listing<_ActorRef>.self) +// let p1 = self.testKit(first).makeTestProbe("p1", expecting: _Reception.Listing<_ActorRef>.self) +// let p2 = self.testKit(second).makeTestProbe("p2", expecting: _Reception.Listing<_ActorRef>.self) // // // ensure the ref is registered and known under both keys to both nodes // first._receptionist.subscribe(p1.ref, to: key) @@ -349,7 +349,7 @@ final class OpLogDistributedReceptionistClusteredTests: ClusteredActorSystemsXCT // first.cluster.join(node: second.cluster.uniqueNode.node) // try assertAssociated(first, withExactly: second.settings.uniqueBindNode) // -// let key = Reception.Key(_ActorRef.self, id: "key") +// let key = _Reception.Key(_ActorRef.self, id: "key") // // let firstRef = try first._spawn("onFirst", self.stopOnMessage) // first._receptionist.register(firstRef, with: key) @@ -360,8 +360,8 @@ final class OpLogDistributedReceptionistClusteredTests: ClusteredActorSystemsXCT // return ref // } // -// let p1 = self.testKit(first).makeTestProbe("p1", expecting: Reception.Listing<_ActorRef>.self) -// let p2 = self.testKit(second).makeTestProbe("p2", expecting: Reception.Listing<_ActorRef>.self) +// let p1 = self.testKit(first).makeTestProbe("p1", expecting: _Reception.Listing<_ActorRef>.self) +// let p2 = self.testKit(second).makeTestProbe("p2", expecting: _Reception.Listing<_ActorRef>.self) // // // ensure the ref is registered and known under both keys to both nodes // first._receptionist.subscribe(p1.ref, to: key) @@ -389,7 +389,7 @@ final class OpLogDistributedReceptionistClusteredTests: ClusteredActorSystemsXCT // first.cluster.join(node: second.cluster.uniqueNode.node) // try assertAssociated(first, withExactly: second.settings.uniqueBindNode) // -// let key = Reception.Key(_ActorRef.self, id: "first") +// let key = _Reception.Key(_ActorRef.self, id: "first") // // var allRefs: Set<_ActorRef> = [] // for i in 1 ... (first.settings.receptionist.syncBatchSize * 10) { @@ -398,8 +398,8 @@ final class OpLogDistributedReceptionistClusteredTests: ClusteredActorSystemsXCT // _ = allRefs.insert(ref) // } // -// let p1 = self.testKit(first).makeTestProbe("p1", expecting: Reception.Listing<_ActorRef>.self) -// let p2 = self.testKit(second).makeTestProbe("p2", expecting: Reception.Listing<_ActorRef>.self) +// let p1 = self.testKit(first).makeTestProbe("p1", expecting: _Reception.Listing<_ActorRef>.self) +// let p2 = self.testKit(second).makeTestProbe("p2", expecting: _Reception.Listing<_ActorRef>.self) // // // ensure the ref is registered and known under both keys to both nodes // first._receptionist.subscribe(p1.ref, to: key) @@ -420,13 +420,13 @@ final class OpLogDistributedReceptionistClusteredTests: ClusteredActorSystemsXCT // try self.joinNodes(node: first, with: third) // try self.joinNodes(node: fourth, with: second) // -// let key = Reception.Key(_ActorRef.self, id: "key") +// let key = _Reception.Key(_ActorRef.self, id: "key") // // let ref = try first._spawn("hi", self.stopOnMessage) // first._receptionist.register(ref, with: key) // // func expectListingContainsRef(on system: ClusterSystem) throws { -// let p = self.testKit(system).makeTestProbe("p", expecting: Reception.Listing<_ActorRef>.self) +// let p = self.testKit(system).makeTestProbe("p", expecting: _Reception.Listing<_ActorRef>.self) // system._receptionist.subscribe(p.ref, to: key) // // try p.eventuallyExpectListing(expected: [ref], within: .seconds(3)) diff --git a/Tests/DistributedActorsTests/Cluster/Reception/_OpLogClusterReceptionistClusteredTests.swift b/Tests/DistributedActorsTests/Cluster/Reception/_OpLogClusterReceptionistClusteredTests.swift index 80fc256c6..53c9fe06b 100644 --- a/Tests/DistributedActorsTests/Cluster/Reception/_OpLogClusterReceptionistClusteredTests.swift +++ b/Tests/DistributedActorsTests/Cluster/Reception/_OpLogClusterReceptionistClusteredTests.swift @@ -49,7 +49,7 @@ final class _OpLogClusterReceptionistClusteredTests: ClusteredActorSystemsXCTest try self.joinNodes(node: local, with: remote) let probe = testKit.makeTestProbe(expecting: String.self) - let registeredProbe = testKit.makeTestProbe("registered", expecting: Reception.Registered<_ActorRef>.self) + let registeredProbe = testKit.makeTestProbe("registered", expecting: _Reception.Registered<_ActorRef>.self) let ref: _ActorRef = try local._spawn( .anonymous, @@ -59,10 +59,10 @@ final class _OpLogClusterReceptionistClusteredTests: ClusteredActorSystemsXCTest } ) - let key = Reception.Key(_ActorRef.self, id: "test") + let key = _Reception.Key(_ActorRef.self, id: "test") // subscribe on `remote` - let subscriberProbe = testKit.makeTestProbe(expecting: Reception.Listing<_ActorRef>.self) + let subscriberProbe = testKit.makeTestProbe(expecting: _Reception.Listing<_ActorRef>.self) remote._receptionist.subscribe(subscriberProbe.ref, to: key) _ = try subscriberProbe.expectMessage() @@ -86,8 +86,8 @@ final class _OpLogClusterReceptionistClusteredTests: ClusteredActorSystemsXCTest } let probe = self.testKit(local).makeTestProbe(expecting: String.self) - let registeredProbe = self.testKit(local).makeTestProbe(expecting: Reception.Registered<_ActorRef>.self) - let lookupProbe = self.testKit(local).makeTestProbe(expecting: Reception.Listing<_ActorRef>.self) + let registeredProbe = self.testKit(local).makeTestProbe(expecting: _Reception.Registered<_ActorRef>.self) + let lookupProbe = self.testKit(local).makeTestProbe(expecting: _Reception.Listing<_ActorRef>.self) let ref: _ActorRef = try local._spawn( .anonymous, @@ -97,7 +97,7 @@ final class _OpLogClusterReceptionistClusteredTests: ClusteredActorSystemsXCTest } ) - let key = Reception.Key(_ActorRef.self, id: "test") + let key = _Reception.Key(_ActorRef.self, id: "test") remote._receptionist.subscribe(lookupProbe.ref, to: key) @@ -124,9 +124,9 @@ final class _OpLogClusterReceptionistClusteredTests: ClusteredActorSystemsXCTest $0.receptionist.ackPullReplicationIntervalSlow = .seconds(1) } - let registeredProbe = self.testKit(local).makeTestProbe("registeredProbe", expecting: Reception.Registered<_ActorRef>.self) - let localLookupProbe = self.testKit(local).makeTestProbe("localLookupProbe", expecting: Reception.Listing<_ActorRef>.self) - let remoteLookupProbe = self.testKit(remote).makeTestProbe("remoteLookupProbe", expecting: Reception.Listing<_ActorRef>.self) + let registeredProbe = self.testKit(local).makeTestProbe("registeredProbe", expecting: _Reception.Registered<_ActorRef>.self) + let localLookupProbe = self.testKit(local).makeTestProbe("localLookupProbe", expecting: _Reception.Listing<_ActorRef>.self) + let remoteLookupProbe = self.testKit(remote).makeTestProbe("remoteLookupProbe", expecting: _Reception.Listing<_ActorRef>.self) let behavior: _Behavior = .receiveMessage { _ in .same @@ -137,7 +137,7 @@ final class _OpLogClusterReceptionistClusteredTests: ClusteredActorSystemsXCTest let refC: _ActorRef = try remote._spawn("refC", behavior) let refD: _ActorRef = try remote._spawn("refD", behavior) - let key = Reception.Key(_ActorRef.self, id: "test") + let key = _Reception.Key(_ActorRef.self, id: "test") local._receptionist.register(refA, with: key, replyTo: registeredProbe.ref) _ = try registeredProbe.expectMessage() @@ -180,13 +180,13 @@ final class _OpLogClusterReceptionistClusteredTests: ClusteredActorSystemsXCTest $0.receptionist.ackPullReplicationIntervalSlow = .seconds(1) } - let registeredProbe = self.testKit(first).makeTestProbe(expecting: Reception.Registered<_ActorRef>.self) - let remoteLookupProbe = self.testKit(second).makeTestProbe(expecting: Reception.Listing<_ActorRef>.self) + let registeredProbe = self.testKit(first).makeTestProbe(expecting: _Reception.Registered<_ActorRef>.self) + let remoteLookupProbe = self.testKit(second).makeTestProbe(expecting: _Reception.Listing<_ActorRef>.self) let refA: _ActorRef = try first._spawn(.anonymous, self.stopOnMessage) let refB: _ActorRef = try first._spawn(.anonymous, self.stopOnMessage) - let key = Reception.Key(_ActorRef.self, id: "test") + let key = _Reception.Key(_ActorRef.self, id: "test") first._receptionist.register(refA, with: key, replyTo: registeredProbe.ref) _ = try registeredProbe.expectMessage() @@ -228,17 +228,17 @@ final class _OpLogClusterReceptionistClusteredTests: ClusteredActorSystemsXCTest first.cluster.join(node: second.cluster.uniqueNode.node) try assertAssociated(first, withExactly: second.settings.uniqueBindNode) - let firstKey = Reception.Key(_ActorRef.self, id: "first") - let extraKey = Reception.Key(_ActorRef.self, id: "extra") + let firstKey = _Reception.Key(_ActorRef.self, id: "first") + let extraKey = _Reception.Key(_ActorRef.self, id: "extra") let ref = try first._spawn("hi", self.stopOnMessage) first._receptionist.register(ref, with: firstKey) first._receptionist.register(ref, with: extraKey) - let p1f = self.testKit(first).makeTestProbe("p1f", expecting: Reception.Listing<_ActorRef>.self) - let p1e = self.testKit(first).makeTestProbe("p1e", expecting: Reception.Listing<_ActorRef>.self) - let p2f = self.testKit(second).makeTestProbe("p2f", expecting: Reception.Listing<_ActorRef>.self) - let p2e = self.testKit(second).makeTestProbe("p2e", expecting: Reception.Listing<_ActorRef>.self) + let p1f = self.testKit(first).makeTestProbe("p1f", expecting: _Reception.Listing<_ActorRef>.self) + let p1e = self.testKit(first).makeTestProbe("p1e", expecting: _Reception.Listing<_ActorRef>.self) + let p2f = self.testKit(second).makeTestProbe("p2f", expecting: _Reception.Listing<_ActorRef>.self) + let p2e = self.testKit(second).makeTestProbe("p2e", expecting: _Reception.Listing<_ActorRef>.self) // ensure the ref is registered and known under both keys to both nodes first._receptionist.subscribe(p1f.ref, to: firstKey) @@ -271,7 +271,7 @@ final class _OpLogClusterReceptionistClusteredTests: ClusteredActorSystemsXCTest first.cluster.join(node: second.cluster.uniqueNode.node) try assertAssociated(first, withExactly: second.settings.uniqueBindNode) - let key = Reception.Key(_ActorRef.self, id: "key") + let key = _Reception.Key(_ActorRef.self, id: "key") let firstRef = try first._spawn("onFirst", self.stopOnMessage) first._receptionist.register(firstRef, with: key) @@ -279,8 +279,8 @@ final class _OpLogClusterReceptionistClusteredTests: ClusteredActorSystemsXCTest let secondRef = try second._spawn("onSecond", self.stopOnMessage) second._receptionist.register(secondRef, with: key) - let p1 = self.testKit(first).makeTestProbe("p1", expecting: Reception.Listing<_ActorRef>.self) - let p2 = self.testKit(second).makeTestProbe("p2", expecting: Reception.Listing<_ActorRef>.self) + let p1 = self.testKit(first).makeTestProbe("p1", expecting: _Reception.Listing<_ActorRef>.self) + let p2 = self.testKit(second).makeTestProbe("p2", expecting: _Reception.Listing<_ActorRef>.self) // ensure the ref is registered and known under both keys to both nodes first._receptionist.subscribe(p1.ref, to: key) @@ -303,7 +303,7 @@ final class _OpLogClusterReceptionistClusteredTests: ClusteredActorSystemsXCTest first.cluster.join(node: second.cluster.uniqueNode.node) try assertAssociated(first, withExactly: second.settings.uniqueBindNode) - let key = Reception.Key(_ActorRef.self, id: "key") + let key = _Reception.Key(_ActorRef.self, id: "key") let firstRef = try first._spawn("onFirst", self.stopOnMessage) first._receptionist.register(firstRef, with: key) @@ -314,8 +314,8 @@ final class _OpLogClusterReceptionistClusteredTests: ClusteredActorSystemsXCTest return ref } - let p1 = self.testKit(first).makeTestProbe("p1", expecting: Reception.Listing<_ActorRef>.self) - let p2 = self.testKit(second).makeTestProbe("p2", expecting: Reception.Listing<_ActorRef>.self) + let p1 = self.testKit(first).makeTestProbe("p1", expecting: _Reception.Listing<_ActorRef>.self) + let p2 = self.testKit(second).makeTestProbe("p2", expecting: _Reception.Listing<_ActorRef>.self) // ensure the ref is registered and known under both keys to both nodes first._receptionist.subscribe(p1.ref, to: key) @@ -343,7 +343,7 @@ final class _OpLogClusterReceptionistClusteredTests: ClusteredActorSystemsXCTest first.cluster.join(node: second.cluster.uniqueNode.node) try assertAssociated(first, withExactly: second.settings.uniqueBindNode) - let key = Reception.Key(_ActorRef.self, id: "first") + let key = _Reception.Key(_ActorRef.self, id: "first") var allRefs: Set<_ActorRef> = [] for i in 1 ... (first.settings.receptionist.syncBatchSize * 10) { @@ -352,8 +352,8 @@ final class _OpLogClusterReceptionistClusteredTests: ClusteredActorSystemsXCTest _ = allRefs.insert(ref) } - let p1 = self.testKit(first).makeTestProbe("p1", expecting: Reception.Listing<_ActorRef>.self) - let p2 = self.testKit(second).makeTestProbe("p2", expecting: Reception.Listing<_ActorRef>.self) + let p1 = self.testKit(first).makeTestProbe("p1", expecting: _Reception.Listing<_ActorRef>.self) + let p2 = self.testKit(second).makeTestProbe("p2", expecting: _Reception.Listing<_ActorRef>.self) // ensure the ref is registered and known under both keys to both nodes first._receptionist.subscribe(p1.ref, to: key) @@ -374,13 +374,13 @@ final class _OpLogClusterReceptionistClusteredTests: ClusteredActorSystemsXCTest try self.joinNodes(node: first, with: third) try self.joinNodes(node: fourth, with: second) - let key = Reception.Key(_ActorRef.self, id: "key") + let key = _Reception.Key(_ActorRef.self, id: "key") let ref = try first._spawn("hi", self.stopOnMessage) first._receptionist.register(ref, with: key) func expectListingContainsRef(on system: ClusterSystem) throws { - let p = self.testKit(system).makeTestProbe("p", expecting: Reception.Listing<_ActorRef>.self) + let p = self.testKit(system).makeTestProbe("p", expecting: _Reception.Listing<_ActorRef>.self) system._receptionist.subscribe(p.ref, to: key) try p.eventuallyExpectListing(expected: [ref], within: .seconds(3)) diff --git a/Tests/DistributedActorsTests/Cluster/RemoteMessagingClusteredTests.swift b/Tests/DistributedActorsTests/Cluster/RemoteMessagingClusteredTests.swift index 86fb3a400..7e97ae294 100644 --- a/Tests/DistributedActorsTests/Cluster/RemoteMessagingClusteredTests.swift +++ b/Tests/DistributedActorsTests/Cluster/RemoteMessagingClusteredTests.swift @@ -308,17 +308,17 @@ final class RemoteMessagingClusteredTests: ClusteredActorSystemsXCTestCase { } } -struct WrappedString: ActorMessage { +struct WrappedString: Codable { let string: String } -private enum SerializationBehavior: String, ActorMessage { +private enum SerializationBehavior: String, Codable { case succeed case failEncoding case failDecoding } -private struct SerializationTestMessage: ActorMessage { +private struct SerializationTestMessage: Codable { let serializationBehavior: SerializationBehavior } @@ -345,7 +345,7 @@ extension SerializationTestMessage { } } -private struct EchoTestMessage: ActorMessage { +private struct EchoTestMessage: Codable { let string: String let respondTo: _ActorRef } diff --git a/Tests/DistributedActorsTests/Cluster/RemotingHandshakeStateMachineTests.swift b/Tests/DistributedActorsTests/Cluster/RemotingHandshakeStateMachineTests.swift index c9fd5bb38..d7b96e377 100644 --- a/Tests/DistributedActorsTests/Cluster/RemotingHandshakeStateMachineTests.swift +++ b/Tests/DistributedActorsTests/Cluster/RemotingHandshakeStateMachineTests.swift @@ -115,7 +115,7 @@ final class RemoteHandshakeStateMachineTests: XCTestCase { error = rejected.error } - "\(error)".shouldEqual("incompatibleProtocolVersion(local: Version(0.0.1, reserved:0), remote: Version(1.0.1, reserved:0))") + "\(error)".shouldEqual("incompatibleProtocolVersion(local: Version(1.0.0, reserved:0), remote: Version(2.0.0, reserved:0))") } // ==== ------------------------------------------------------------------------------------------------------------ diff --git a/Tests/DistributedActorsTests/Cluster/SWIM/SWIMActorShellClusteredTests.swift b/Tests/DistributedActorsTests/Cluster/SWIM/SWIMActorShellClusteredTests.swift index 1a3bf5442..e84e5d4f7 100644 --- a/Tests/DistributedActorsTests/Cluster/SWIM/SWIMActorShellClusteredTests.swift +++ b/Tests/DistributedActorsTests/Cluster/SWIM/SWIMActorShellClusteredTests.swift @@ -340,7 +340,7 @@ final class SWIMShellClusteredTests: ClusteredActorSystemsXCTestCase { // ==== ------------------------------------------------------------------------------------------------------------ // MARK: utility functions - struct ForwardedSWIMMessage: ActorMessage { + struct ForwardedSWIMMessage: Codable { let message: SWIM.Message let recipient: _ActorRef } diff --git a/Tests/DistributedActorsTests/DistributedReceptionistTests.swift b/Tests/DistributedActorsTests/DistributedReceptionistTests.swift index d997e39d7..0fd186fe0 100644 --- a/Tests/DistributedActorsTests/DistributedReceptionistTests.swift +++ b/Tests/DistributedActorsTests/DistributedReceptionistTests.swift @@ -125,11 +125,11 @@ final class DistributedReceptionistTests: ClusterSystemXCTestCase { // func test_receptionist_shouldNotRegisterTheSameRefTwice() throws { // let receptionist = SystemReceptionist(ref: try system._spawn("receptionist", self.receptionistBehavior)) -// let lookupProbe: ActorTestProbe>> = self.testKit.makeTestProbe() +// let lookupProbe: ActorTestProbe<_Reception.Listing<_ActorRef>> = self.testKit.makeTestProbe() // // let ref: _ActorRef = try system._spawn(.anonymous, .receiveMessage { _ in .same }) // -// let key = Reception.Key(_ActorRef.self, id: "test") +// let key = _Reception.Key(_ActorRef.self, id: "test") // // receptionist.checkIn(ref, with: key) // receptionist.checkIn(ref, with: key) @@ -143,7 +143,7 @@ final class DistributedReceptionistTests: ClusterSystemXCTestCase { // // func test_receptionist_shouldRemoveAndAddNewSingletonRef() throws { // let receptionist = SystemReceptionist(ref: try system._spawn("receptionist", self.receptionistBehavior)) -// let lookupProbe: ActorTestProbe>> = self.testKit.makeTestProbe() +// let lookupProbe: ActorTestProbe<_Reception.Listing<_ActorRef>> = self.testKit.makeTestProbe() // // let old: _ActorRef = try system._spawn( // .anonymous, @@ -159,7 +159,7 @@ final class DistributedReceptionistTests: ClusterSystemXCTestCase { // } // ) // -// let key = Reception.Key(_ActorRef.self, id: "shouldBeOne") +// let key = _Reception.Key(_ActorRef.self, id: "shouldBeOne") // // receptionist.checkIn(old, with: key) // old.tell("stop") @@ -177,7 +177,7 @@ final class DistributedReceptionistTests: ClusterSystemXCTestCase { // // func test_receptionist_shouldReplyWithRegistered() throws { // let receptionist = SystemReceptionist(ref: try system._spawn("receptionist", self.receptionistBehavior)) -// let probe: ActorTestProbe>> = self.testKit.makeTestProbe() +// let probe: ActorTestProbe<_Reception.Registered<_ActorRef>> = self.testKit.makeTestProbe() // // let key = DistributedReception.Key(_ActorRef.self, id: "test") // @@ -191,7 +191,7 @@ final class DistributedReceptionistTests: ClusterSystemXCTestCase { // // func test_receptionist_shouldCheckOutTerminatedRefs() throws { // let receptionist = SystemReceptionist(ref: try system._spawn("receptionist", self.receptionistBehavior)) -// let lookupProbe: ActorTestProbe>> = self.testKit.makeTestProbe() +// let lookupProbe: ActorTestProbe<_Reception.Listing<_ActorRef>> = self.testKit.makeTestProbe() // // let ref: _ActorRef = try system._spawn( // .anonymous, @@ -200,7 +200,7 @@ final class DistributedReceptionistTests: ClusterSystemXCTestCase { // } // ) // -// let key = Reception.Key(_ActorRef.self, id: "test") +// let key = _Reception.Key(_ActorRef.self, id: "test") // // receptionist.checkIn(ref, with: key) // @@ -219,7 +219,7 @@ final class DistributedReceptionistTests: ClusterSystemXCTestCase { // // func test_receptionist_shouldContinuouslySendUpdatesForSubscriptions() throws { // let receptionist = SystemReceptionist(ref: try system._spawn("receptionist", self.receptionistBehavior)) -// let lookupProbe: ActorTestProbe>> = self.testKit.makeTestProbe() +// let lookupProbe: ActorTestProbe<_Reception.Listing<_ActorRef>> = self.testKit.makeTestProbe() // // let refA: _ActorRef = try system._spawn( // .anonymous, @@ -235,19 +235,19 @@ final class DistributedReceptionistTests: ClusterSystemXCTestCase { // } // ) // -// let key = Reception.Key(_ActorRef.self, id: "test") +// let key = _Reception.Key(_ActorRef.self, id: "test") // // receptionist.listing(lookupProbe.ref, to: key) -// try lookupProbe.expectMessage(Reception.Listing(refs: [], key: key)) +// try lookupProbe.expectMessage(_Reception.Listing(refs: [], key: key)) // // receptionist.checkIn(refA, with: key) -// try lookupProbe.expectMessage(Reception.Listing(refs: [refA.asAddressable], key: key)) +// try lookupProbe.expectMessage(_Reception.Listing(refs: [refA.asAddressable], key: key)) // // receptionist.checkIn(refB, with: key) -// try lookupProbe.expectMessage(Reception.Listing(refs: [refA.asAddressable, refB.asAddressable], key: key)) +// try lookupProbe.expectMessage(_Reception.Listing(refs: [refA.asAddressable, refB.asAddressable], key: key)) // // refB.tell("stop") -// try lookupProbe.expectMessage(Reception.Listing(refs: [refA.asAddressable], key: key)) +// try lookupProbe.expectMessage(_Reception.Listing(refs: [refA.asAddressable], key: key)) // } // // // ==== ------------------------------------------------------------------------------------------------------------ @@ -255,9 +255,9 @@ final class DistributedReceptionistTests: ClusterSystemXCTestCase { // // func test_delayedFlush_shouldEmitEvenWhenAllPeersRemoved() throws { // let receptionist = SystemReceptionist(ref: try system._spawn("receptionist", self.receptionistBehavior)) -// let lookupProbe: ActorTestProbe>> = self.testKit.makeTestProbe() +// let lookupProbe: ActorTestProbe<_Reception.Listing<_ActorRef>> = self.testKit.makeTestProbe() // -// let key = Reception.Key(_ActorRef.self, id: "test") +// let key = _Reception.Key(_ActorRef.self, id: "test") // // receptionist.listing(lookupProbe.ref, to: key) // _ = try lookupProbe.expectMessage() diff --git a/Tests/DistributedActorsTests/EventStreamTests.swift b/Tests/DistributedActorsTests/EventStreamTests.swift index 8e2026a01..52001331f 100644 --- a/Tests/DistributedActorsTests/EventStreamTests.swift +++ b/Tests/DistributedActorsTests/EventStreamTests.swift @@ -116,7 +116,7 @@ final class EventStreamTests: ClusterSystemXCTestCase { } } -private final class EventStreamConsumer: @unchecked Sendable { +private final class EventStreamConsumer: @unchecked Sendable { let running: UnsafeAtomic = .create(false) let counter: UnsafeAtomic = .create(0) diff --git a/Tests/DistributedActorsTests/Gossip/GossiperShellTests.swift b/Tests/DistributedActorsTests/Gossip/GossiperShellTests.swift index caad31f59..f4c7bafc3 100644 --- a/Tests/DistributedActorsTests/Gossip/GossiperShellTests.swift +++ b/Tests/DistributedActorsTests/Gossip/GossiperShellTests.swift @@ -29,7 +29,7 @@ final class GossiperShellTests: ClusterSystemXCTestCase { // MARK: test_down_beGossipedToOtherNodes func test_down_beGossipedToOtherNodes() throws { - let p = self.testKit.makeTestProbe(expecting: [AddressableActorRef].self) + let p = self.testKit.makeTestProbe(expecting: [_AddressableActorRef].self) let control = try Gossiper._spawn( self.system, @@ -71,23 +71,23 @@ final class GossiperShellTests: ClusterSystemXCTestCase { typealias Acknowledgement = String - let offeredPeersProbe: _ActorRef<[AddressableActorRef]> - init(offeredPeersProbe: _ActorRef<[AddressableActorRef]>) { + let offeredPeersProbe: _ActorRef<[_AddressableActorRef]> + init(offeredPeersProbe: _ActorRef<[_AddressableActorRef]>) { self.offeredPeersProbe = offeredPeersProbe } - func selectPeers(_ peers: [AddressableActorRef]) -> [AddressableActorRef] { + func selectPeers(_ peers: [_AddressableActorRef]) -> [_AddressableActorRef] { self.offeredPeersProbe.tell(peers) return [] } - func makePayload(target: AddressableActorRef) -> Gossip? { + func makePayload(target: _AddressableActorRef) -> Gossip? { nil } - func receiveAcknowledgement(_ acknowledgement: Acknowledgement, from peer: AddressableActorRef, confirming gossip: Gossip) {} + func receiveAcknowledgement(_ acknowledgement: Acknowledgement, from peer: _AddressableActorRef, confirming gossip: Gossip) {} - func receiveGossip(_ gossip: Gossip, from peer: AddressableActorRef) -> Acknowledgement? { + func receiveGossip(_ gossip: Gossip, from peer: _AddressableActorRef) -> Acknowledgement? { nil } @@ -144,19 +144,19 @@ final class GossiperShellTests: ClusterSystemXCTestCase { typealias Acknowledgement = String - func selectPeers(_ peers: [AddressableActorRef]) -> [AddressableActorRef] { + func selectPeers(_ peers: [_AddressableActorRef]) -> [_AddressableActorRef] { peers } - func makePayload(target: AddressableActorRef) -> Gossip? { + func makePayload(target: _AddressableActorRef) -> Gossip? { .init("Hello") // legal but will produce a warning } - func receiveAcknowledgement(_ acknowledgement: Acknowledgement, from peer: AddressableActorRef, confirming gossip: Gossip) { + func receiveAcknowledgement(_ acknowledgement: Acknowledgement, from peer: _AddressableActorRef, confirming gossip: Gossip) { self.probe.tell("un-expected acknowledgement: \(acknowledgement) from \(peer) confirming \(gossip)") } - func receiveGossip(_ gossip: Gossip, from peer: AddressableActorRef) -> Acknowledgement? { + func receiveGossip(_ gossip: Gossip, from peer: _AddressableActorRef) -> Acknowledgement? { nil } diff --git a/Tests/DistributedActorsTests/InterceptorTests.swift b/Tests/DistributedActorsTests/InterceptorTests.swift index 00b5b1bf1..6dd669563 100644 --- a/Tests/DistributedActorsTests/InterceptorTests.swift +++ b/Tests/DistributedActorsTests/InterceptorTests.swift @@ -34,7 +34,7 @@ final class ShoutingInterceptor: _Interceptor { } } -final class TerminatedInterceptor: _Interceptor { +final class TerminatedInterceptor: _Interceptor { let probe: ActorTestProbe<_Signals.Terminated> init(probe: ActorTestProbe<_Signals.Terminated>) { @@ -155,7 +155,7 @@ final class InterceptorTests: ClusterSystemXCTestCase { try p.expectNoMessage(for: .milliseconds(500)) } - class SignalToStringInterceptor: _Interceptor { + class SignalToStringInterceptor: _Interceptor { let probe: ActorTestProbe init(_ probe: ActorTestProbe) { diff --git a/Tests/DistributedActorsTests/Metrics/ActorMemoryTests.swift b/Tests/DistributedActorsTests/Metrics/ActorMemoryTests.swift index 300088da4..829e0a4cd 100644 --- a/Tests/DistributedActorsTests/Metrics/ActorMemoryTests.swift +++ b/Tests/DistributedActorsTests/Metrics/ActorMemoryTests.swift @@ -24,8 +24,8 @@ final class ActorMemoryTests: XCTestCase { func test_osx_actorShell_instanceSize() { #if os(OSX) - class_getInstanceSize(_ActorShell.self).shouldEqual(560) - class_getInstanceSize(_ActorShell.self).shouldEqual(560) + class_getInstanceSize(_ActorShell.self).shouldEqual(520) + class_getInstanceSize(_ActorShell.self).shouldEqual(520) #else print("Skipping test_osx_actorShell_instanceSize as requires Objective-C runtime") #endif diff --git a/Tests/DistributedActorsTests/Metrics/ActorMetricsTests.swift b/Tests/DistributedActorsTests/Metrics/ActorMetricsTests.swift index 846677206..b1ddf5dd8 100644 --- a/Tests/DistributedActorsTests/Metrics/ActorMetricsTests.swift +++ b/Tests/DistributedActorsTests/Metrics/ActorMetricsTests.swift @@ -72,7 +72,6 @@ final class ActorMetricsTests: ClusteredActorSystemsXCTestCase { sleep(5) let gauge = try self.metrics.expectGauge("first.measuredActorGroup.mailbox.count") - pprint("Mailbox run visualized via \(gauge): \(gauge.values)") // we can't really reliably test that we get to some "maximum" since the actor starts processing messages as they come in gauge.lastValue.shouldEqual(0) // after processing we must always go back to zero } diff --git a/Tests/DistributedActorsTests/ParentChildActorTests.swift b/Tests/DistributedActorsTests/ParentChildActorTests.swift index 5e0e7da02..a642e0fc6 100644 --- a/Tests/DistributedActorsTests/ParentChildActorTests.swift +++ b/Tests/DistributedActorsTests/ParentChildActorTests.swift @@ -19,7 +19,7 @@ import XCTest final class ParentChildActorTests: ClusterSystemXCTestCase { typealias ParentRef = _ActorRef - enum ParentProtocol: NonTransportableActorMessage { + enum ParentProtocol: _NotActuallyCodableMessage { case spawnChild(name: String, behavior: _Behavior) case spawnAnonymousChild(behavior: _Behavior) @@ -29,7 +29,7 @@ final class ParentChildActorTests: ClusterSystemXCTestCase { } typealias ChildRef = _ActorRef - enum ChildProtocol: NonTransportableActorMessage { + enum ChildProtocol: _NotActuallyCodableMessage { case howAreYou(replyTo: _ActorRef) case fail case throwWhoops @@ -37,7 +37,7 @@ final class ParentChildActorTests: ClusterSystemXCTestCase { } typealias ParentChildProbeRef = _ActorRef - enum ParentChildProbeProtocol: Equatable, NonTransportableActorMessage { + enum ParentChildProbeProtocol: Equatable, _NotActuallyCodableMessage { case spawned(child: ChildRef) case spawnFailed(path: ActorPath) @@ -244,7 +244,7 @@ final class ParentChildActorTests: ClusterSystemXCTestCase { parent.tell("stop") - try p.expectMessage().shouldStartWith(prefix: "Errored:attemptedStoppingNonChildActor(ref: AddressableActorRef(/user/$testProbe") + try p.expectMessage().shouldStartWith(prefix: "Errored:attemptedStoppingNonChildActor(ref: _AddressableActorRef(/user/$testProbe") try p.expectTerminated(parent) } @@ -268,7 +268,7 @@ final class ParentChildActorTests: ClusterSystemXCTestCase { parent.tell("stop") - try p.expectMessage().shouldStartWith(prefix: "Errored:attemptedStoppingMyselfUsingContext(ref: AddressableActorRef(/user/parent-5") + try p.expectMessage().shouldStartWith(prefix: "Errored:attemptedStoppingMyselfUsingContext(ref: _AddressableActorRef(/user/parent-5") try p.expectTerminated(parent) } diff --git a/Tests/DistributedActorsTests/SerializationTests.swift b/Tests/DistributedActorsTests/SerializationTests.swift index 1dec82b49..801a3f72e 100644 --- a/Tests/DistributedActorsTests/SerializationTests.swift +++ b/Tests/DistributedActorsTests/SerializationTests.swift @@ -103,7 +103,7 @@ class SerializationTests: ClusterSystemXCTestCase { let encoded = try encoder.encode(id) pinfo("Serialized actor path: \(encoded.copyToNewByteBuffer().stringDebugDescription())") - let decodedID = try decoder.decode(ActorAddress.self, from: encoded) + let decodedID = try decoder.decode(ActorID.self, from: encoded) pinfo("Deserialized again: \(String(reflecting: decodedID))") "\(decodedID)".shouldEqual("sact://one@127.0.0.1:1234/user/hello") @@ -415,17 +415,17 @@ private class Mid: Top, Hashable { } } -private struct HasStringRef: ActorMessage, Equatable { +private struct HasStringRef: Codable, Equatable { let containedRef: _ActorRef } -private struct HasIntRef: ActorMessage, Equatable { +private struct HasIntRef: Codable, Equatable { let containedRef: _ActorRef } -private struct InterestingMessage: ActorMessage, Equatable {} +private struct InterestingMessage: Codable, Equatable {} -private struct HasInterestingMessageRef: ActorMessage, Equatable { +private struct HasInterestingMessageRef: Codable, Equatable { let containedInterestingRef: _ActorRef } diff --git a/Tests/DistributedActorsTests/SupervisionTests.swift b/Tests/DistributedActorsTests/SupervisionTests.swift index 6c16d6b5f..780dda54c 100644 --- a/Tests/DistributedActorsTests/SupervisionTests.swift +++ b/Tests/DistributedActorsTests/SupervisionTests.swift @@ -25,27 +25,27 @@ import Glibc #endif final class SupervisionTests: ClusterSystemXCTestCase { - enum FaultyError: Error, NonTransportableActorMessage { + enum FaultyError: Error, _NotActuallyCodableMessage { case boom(message: String) } - enum FaultyMessage: NonTransportableActorMessage { + enum FaultyMessage: _NotActuallyCodableMessage { case pleaseThrow(error: Error) case echo(message: String, replyTo: _ActorRef) case pleaseFailAwaiting(message: String) } - enum SimpleProbeMessages: Equatable, NonTransportableActorMessage { + enum SimpleProbeMessages: Equatable, _NotActuallyCodableMessage { case spawned(child: _ActorRef) case echoing(message: String) } - enum WorkerMessages: Equatable, NonTransportableActorMessage { + enum WorkerMessages: Equatable, _NotActuallyCodableMessage { case setupRunning(ref: _ActorRef) case echo(message: String) } - enum FailureMode: NonTransportableActorMessage { + enum FailureMode: _NotActuallyCodableMessage { case throwing // case faulting // Not implemented @@ -1078,7 +1078,7 @@ final class SupervisionTests: ClusterSystemXCTestCase { try p.expectMessage("starting") } - private struct PleaseReplyError: Error, ActorMessage, Equatable {} + private struct PleaseReplyError: Error, Codable, Equatable {} private struct EasilyCatchableError: Error, Equatable {} private struct CantTouchThisError: Error, Equatable {} private struct CatchMeError: Error, Equatable {} diff --git a/Tests/DistributedActorsTests/TraversalTests.swift b/Tests/DistributedActorsTests/TraversalTests.swift index b5717149e..bd6edf8ad 100644 --- a/Tests/DistributedActorsTests/TraversalTests.swift +++ b/Tests/DistributedActorsTests/TraversalTests.swift @@ -21,7 +21,7 @@ import NIOFoundationCompat import XCTest final class TraversalTests: ClusterSystemXCTestCase { - struct ActorReady: ActorMessage { + struct ActorReady: Codable { let name: String } diff --git a/Tests/DistributedActorsTests/_ActorRefReceptionistTests.swift b/Tests/DistributedActorsTests/_ActorRefReceptionistTests.swift index e1fd03aad..c8340dcac 100644 --- a/Tests/DistributedActorsTests/_ActorRefReceptionistTests.swift +++ b/Tests/DistributedActorsTests/_ActorRefReceptionistTests.swift @@ -24,7 +24,7 @@ final class _ActorRefReceptionistTests: ClusterSystemXCTestCase { func test_receptionist_shouldRespondWithRegisteredRefsForKey() throws { let receptionist = SystemReceptionist(ref: try system._spawn("receptionist", self.receptionistBehavior)) let probe: ActorTestProbe = self.testKit.makeTestProbe() - let lookupProbe: ActorTestProbe>> = self.testKit.makeTestProbe() + let lookupProbe: ActorTestProbe<_Reception.Listing<_ActorRef>> = self.testKit.makeTestProbe() let refA: _ActorRef = try system._spawn( .anonymous, @@ -42,7 +42,7 @@ final class _ActorRefReceptionistTests: ClusterSystemXCTestCase { } ) - let key = Reception.Key(_ActorRef.self, id: "test") + let key = _Reception.Key(_ActorRef.self, id: "test") receptionist.register(refA, with: key) receptionist.register(refB, with: key) @@ -60,7 +60,7 @@ final class _ActorRefReceptionistTests: ClusterSystemXCTestCase { func test_receptionist_shouldRespondWithEmptyRefForUnknownKey() throws { let receptionist = SystemReceptionist(ref: try system._spawn("receptionist", self.receptionistBehavior)) - let lookupProbe: ActorTestProbe>> = self.testKit.makeTestProbe() + let lookupProbe: ActorTestProbe<_Reception.Listing<_ActorRef>> = self.testKit.makeTestProbe() let ref: _ActorRef = try system._spawn( .anonymous, @@ -69,11 +69,11 @@ final class _ActorRefReceptionistTests: ClusterSystemXCTestCase { } ) - let key = Reception.Key(_ActorRef.self, id: "test") + let key = _Reception.Key(_ActorRef.self, id: "test") receptionist.register(ref, with: key) - let unknownKey = Reception.Key(_ActorRef.self, id: "unknown") + let unknownKey = _Reception.Key(_ActorRef.self, id: "unknown") receptionist.lookup(unknownKey, replyTo: lookupProbe.ref) let listing = try lookupProbe.expectMessage() @@ -83,11 +83,11 @@ final class _ActorRefReceptionistTests: ClusterSystemXCTestCase { func test_receptionist_shouldNotRegisterTheSameRefTwice() throws { let receptionist = SystemReceptionist(ref: try system._spawn("receptionist", self.receptionistBehavior)) - let lookupProbe: ActorTestProbe>> = self.testKit.makeTestProbe() + let lookupProbe: ActorTestProbe<_Reception.Listing<_ActorRef>> = self.testKit.makeTestProbe() let ref: _ActorRef = try system._spawn(.anonymous, .receiveMessage { _ in .same }) - let key = Reception.Key(_ActorRef.self, id: "test") + let key = _Reception.Key(_ActorRef.self, id: "test") receptionist.register(ref, with: key) receptionist.register(ref, with: key) @@ -101,7 +101,7 @@ final class _ActorRefReceptionistTests: ClusterSystemXCTestCase { func test_receptionist_shouldRemoveAndAddNewSingletonRef() throws { let receptionist = SystemReceptionist(ref: try system._spawn("receptionist", self.receptionistBehavior)) - let lookupProbe: ActorTestProbe>> = self.testKit.makeTestProbe() + let lookupProbe: ActorTestProbe<_Reception.Listing<_ActorRef>> = self.testKit.makeTestProbe() let old: _ActorRef = try system._spawn( .anonymous, @@ -117,7 +117,7 @@ final class _ActorRefReceptionistTests: ClusterSystemXCTestCase { } ) - let key = Reception.Key(_ActorRef.self, id: "shouldBeOne") + let key = _Reception.Key(_ActorRef.self, id: "shouldBeOne") receptionist.register(old, with: key) old.tell("stop") @@ -135,7 +135,7 @@ final class _ActorRefReceptionistTests: ClusterSystemXCTestCase { func test_receptionist_shouldReplyWithRegistered() throws { let receptionist = SystemReceptionist(ref: try system._spawn("receptionist", self.receptionistBehavior)) - let probe: ActorTestProbe>> = self.testKit.makeTestProbe() + let probe: ActorTestProbe<_Reception.Registered<_ActorRef>> = self.testKit.makeTestProbe() let ref: _ActorRef = try system._spawn( .anonymous, @@ -144,7 +144,7 @@ final class _ActorRefReceptionistTests: ClusterSystemXCTestCase { } ) - let key = Reception.Key(_ActorRef.self, id: "test") + let key = _Reception.Key(_ActorRef.self, id: "test") receptionist.register(ref, with: key, replyTo: probe.ref) @@ -156,7 +156,7 @@ final class _ActorRefReceptionistTests: ClusterSystemXCTestCase { func test_receptionist_shouldUnregisterTerminatedRefs() throws { let receptionist = SystemReceptionist(ref: try system._spawn("receptionist", self.receptionistBehavior)) - let lookupProbe: ActorTestProbe>> = self.testKit.makeTestProbe() + let lookupProbe: ActorTestProbe<_Reception.Listing<_ActorRef>> = self.testKit.makeTestProbe() let ref: _ActorRef = try system._spawn( .anonymous, @@ -165,7 +165,7 @@ final class _ActorRefReceptionistTests: ClusterSystemXCTestCase { } ) - let key = Reception.Key(_ActorRef.self, id: "test") + let key = _Reception.Key(_ActorRef.self, id: "test") receptionist.register(ref, with: key) @@ -184,7 +184,7 @@ final class _ActorRefReceptionistTests: ClusterSystemXCTestCase { func test_receptionist_shouldContinuouslySendUpdatesForSubscriptions() throws { let receptionist = SystemReceptionist(ref: try system._spawn("receptionist", self.receptionistBehavior)) - let lookupProbe: ActorTestProbe>> = self.testKit.makeTestProbe() + let lookupProbe: ActorTestProbe<_Reception.Listing<_ActorRef>> = self.testKit.makeTestProbe() let refA: _ActorRef = try system._spawn( .anonymous, @@ -200,19 +200,19 @@ final class _ActorRefReceptionistTests: ClusterSystemXCTestCase { } ) - let key = Reception.Key(_ActorRef.self, id: "test") + let key = _Reception.Key(_ActorRef.self, id: "test") receptionist.subscribe(lookupProbe.ref, to: key) - try lookupProbe.expectMessage(Reception.Listing(refs: [], key: key)) + try lookupProbe.expectMessage(_Reception.Listing(refs: [], key: key)) receptionist.register(refA, with: key) - try lookupProbe.expectMessage(Reception.Listing(refs: [refA.asAddressable], key: key)) + try lookupProbe.expectMessage(_Reception.Listing(refs: [refA.asAddressable], key: key)) receptionist.register(refB, with: key) - try lookupProbe.expectMessage(Reception.Listing(refs: [refA.asAddressable, refB.asAddressable], key: key)) + try lookupProbe.expectMessage(_Reception.Listing(refs: [refA.asAddressable, refB.asAddressable], key: key)) refB.tell("stop") - try lookupProbe.expectMessage(Reception.Listing(refs: [refA.asAddressable], key: key)) + try lookupProbe.expectMessage(_Reception.Listing(refs: [refA.asAddressable], key: key)) } // ==== ------------------------------------------------------------------------------------------------------------ @@ -220,9 +220,9 @@ final class _ActorRefReceptionistTests: ClusterSystemXCTestCase { func test_delayedFlush_shouldEmitEvenWhenAllPeersRemoved() throws { let receptionist = SystemReceptionist(ref: try system._spawn("receptionist", self.receptionistBehavior)) - let lookupProbe: ActorTestProbe>> = self.testKit.makeTestProbe() + let lookupProbe: ActorTestProbe<_Reception.Listing<_ActorRef>> = self.testKit.makeTestProbe() - let key = Reception.Key(_ActorRef.self, id: "test") + let key = _Reception.Key(_ActorRef.self, id: "test") receptionist.subscribe(lookupProbe.ref, to: key) _ = try lookupProbe.expectMessage() diff --git a/Tests/DistributedActorsTests/_DeathWatchTests.swift b/Tests/DistributedActorsTests/_DeathWatchTests.swift index 7e9760e10..b7b49c098 100644 --- a/Tests/DistributedActorsTests/_DeathWatchTests.swift +++ b/Tests/DistributedActorsTests/_DeathWatchTests.swift @@ -458,11 +458,11 @@ final class DeathWatchTests: ClusterSystemXCTestCase { } } -private enum Done: String, ActorMessage { +private enum Done: String, Codable { case done } -private enum RomeoMessage: ActorMessage { +private enum RomeoMessage: Codable { case pleaseWatch(juliet: _ActorRef, probe: _ActorRef) } @@ -498,10 +498,10 @@ extension RomeoMessage { } } -private enum JulietMessage: String, ActorMessage { +private enum JulietMessage: String, Codable { case takePoison } -private enum StoppableRefMessage: String, ActorMessage { +private enum StoppableRefMessage: String, Codable { case stop }