Skip to content

Commit

Permalink
=ser serializing actor id metadata, incl. wellKnown apple#987
Browse files Browse the repository at this point in the history
  • Loading branch information
ktoso committed Jul 20, 2022
1 parent 173d22c commit 11b248a
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 12 deletions.
2 changes: 1 addition & 1 deletion Sources/ActorSingletonPlugin/ActorSingletonProxy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ internal distributed actor ActorSingletonProxy<Act: ClusterSingletonProtocol>: A

private func updateTargetNode(node: UniqueNode?) async throws {
guard self.targetNode != node else {
self.log.debug("Skip updating target node. New node is already the same as current targetNode.", metadata: self.metadata())
self.log.trace("Skip updating target node. New node is already the same as current targetNode.", metadata: self.metadata())
return
}

Expand Down
39 changes: 30 additions & 9 deletions Sources/DistributedActors/ActorID.swift
Original file line number Diff line number Diff line change
Expand Up @@ -952,15 +952,25 @@ extension ActorID: Codable {
var metadataContainer = container.nestedContainer(keyedBy: ActorCoding.MetadataKeys.self, forKey: ActorCoding.CodingKeys.metadata)

let keys = ActorMetadataKeys.__instance
if (metadataSettings == nil || metadataSettings!.propagateMetadata.contains(keys.path.id)),
let value = self.metadata.path
{
func shouldPropagate<V: Sendable & Codable>(_ key: ActorMetadataKey<V>, metadata: ActorMetadata) -> V? {
if metadataSettings == nil || metadataSettings!.propagateMetadata.contains(key.id) {
if let value = metadata[key.id] {
let value = value as! V // as!-safe, the keys guarantee we only store well typed values in metadata
return value
}
}
return nil
}

// Handle well known metadata types
if let value = shouldPropagate(keys.path, metadata: self.metadata) {
try metadataContainer.encode(value, forKey: ActorCoding.MetadataKeys.path)
}
if (metadataSettings == nil || metadataSettings!.propagateMetadata.contains(keys.type.id)),
let value = self.metadata.type
{
try metadataContainer.encode(value, forKey: ActorCoding.MetadataKeys.type)
if let value = shouldPropagate(keys.type, metadata: self.metadata) {
try metadataContainer.encode(value.mangledName, forKey: ActorCoding.MetadataKeys.type)
}
if let value = shouldPropagate(keys.wellKnown, metadata: self.metadata) {
try metadataContainer.encode(value, forKey: ActorCoding.MetadataKeys.wellKnown)
}

try encodeCustomMetadata(self.metadata, &metadataContainer)
Expand All @@ -979,8 +989,17 @@ extension ActorID: Codable {
if let metadataContainer = try? container.nestedContainer(keyedBy: ActorCoding.MetadataKeys.self, forKey: ActorCoding.CodingKeys.metadata) {
// tags container found, try to decode all known tags:

// FIXME: implement decoding tags/metadata in general

let metadata = ActorMetadata()
if let value = try? metadataContainer.decodeIfPresent(ActorPath.self, forKey: ActorCoding.MetadataKeys.path) {
metadata.path = value
}
if let value = try? metadataContainer.decodeIfPresent(String.self, forKey: ActorCoding.MetadataKeys.type) {
metadata.type = .init(mangledName: value)
}
if let value = try? metadataContainer.decodeIfPresent(String.self, forKey: ActorCoding.MetadataKeys.wellKnown) {
metadata.wellKnown = value
}

if let context = decoder.actorSerializationContext {
let decodeCustomMetadata = context.system.settings.actorMetadata.decodeCustomMetadata
try decodeCustomMetadata(metadataContainer, self.metadata)
Expand All @@ -994,6 +1013,8 @@ extension ActorID: Codable {
// _openExistential(key, do: store) // the `as` here is required, because: inferred result type 'any ActorTagKey.Type' requires explicit coercion due to loss of generic requirements
// }
}

self.context = .init(lifecycle: nil, remoteCallInterceptor: nil, metadata: metadata)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@ public enum ActorCoding {
public enum MetadataKeys: CodingKey {
case path
case type
case wellKnown
case custom(String)

public init?(stringValue: String) {
switch stringValue {
case "path": self = .path
case "type": self = .type
case "wellKnown": self = .wellKnown
default: self = .custom(stringValue)
}
}
Expand All @@ -46,7 +48,8 @@ public enum ActorCoding {
switch self {
case .path: return 0
case .type: return 1
case .custom: return 2
case .wellKnown: return 2
case .custom: return 64
}
}

Expand All @@ -58,6 +61,7 @@ public enum ActorCoding {
switch self {
case .path: return "path"
case .type: return "type"
case .wellKnown: return "wellKnown"
case .custom(let id): return id
}
}
Expand Down
18 changes: 17 additions & 1 deletion Tests/DistributedActorsTests/ActorIDMetadataTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,17 @@ public protocol ExampleClusterSingletonProtocol: DistributedActor {
distributed actor ThereCanBeOnlyOneClusterSingleton: ExampleClusterSingletonProtocol {
typealias ActorSystem = ClusterSystem

@ActorID.Metadata(\.wellKnown)
public var wellKnownName: String

@ActorID.Metadata(\.exampleClusterSingletonID)
public var singletonID: String
// TODO(swift): impossible to assign initial value here, as _enclosingInstance is not available yet "the-one"

init(actorSystem: ActorSystem) async {
self.actorSystem = actorSystem
self.singletonID = "the-boss"
self.wellKnownName = "boss-singleton"
}
}

Expand Down Expand Up @@ -79,11 +83,23 @@ final class ActorIDMetadataTests: ClusteredActorSystemsXCTestCase {
"\(example)".shouldContain("\"user-id\": \"user-1234\"")
try await example.assertThat(userID: userID)
}

func test_metadata_initializedInline() async throws {
let system = await setUpNode("first")
let singleton = await ThereCanBeOnlyOneClusterSingleton(actorSystem: system)

singleton.metadata.exampleClusterSingletonID.shouldEqual("the-boss")
}

func test_metadata_wellKnown_serialized() async throws {
let system = await setUpNode("first")
let singleton = await ThereCanBeOnlyOneClusterSingleton(actorSystem: system)

let encoded = try JSONEncoder().encode(singleton)
let encodedString = String(data: encoded, encoding: .utf8)!
encodedString.shouldContain("\"wellKnown\":\"boss-singleton\"")

let back = try! JSONDecoder().decode(ActorID.self, from: encoded)
back.metadata.wellKnown.shouldEqual("boss-singleton")
}
}

0 comments on commit 11b248a

Please sign in to comment.