Skip to content

Conversation

@yim-lee
Copy link
Member

@yim-lee yim-lee commented Apr 8, 2020

Motivation:
ErrorEnvelope's handling of Codable errors not yet implemented.

Modifications:
Finish implementing ErrorEnvelope. Frequently ran into the error below and had to take a different approach.

value of protocol type 'Codable' (aka 'Decodable & Encodable') cannot conform to 'Encodable'; only struct/enum/class types can conform to protocols

Results:
Resolves #512

Motivation:
`ErrorEnvelope`'s handling of `Codable` errors not yet implemented.

Modifications:
Finish implementing `ErrorEnvelope`. Frequently ran into the error below and had to take a different approach.

```
value of protocol type 'Codable' (aka 'Decodable & Encodable') cannot conform to 'Encodable'; only struct/enum/class types can conform to protocols
```

Results:
Resolves #512
@yim-lee yim-lee requested a review from ktoso April 8, 2020 19:46
internal static let AckOps: SerializerID = .foundationJSON

internal static let ErrorEnvelope: SerializerID = .foundationJSON
internal static let BestEffortStringError: SerializerID = .foundationJSON
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't seem like this change is required but adding them here to be consistent

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right yeah because we default to JSON so it works fine by using it.

Thanks for adding here 👍

/// By doing this before system startup you can ensure a specific serializer is used for those messages.
/// Make sure tha other nodes in the system are configured the same way though.
public mutating func registerCodable<Message: ActorMessage>(
public mutating func registerCodable<Message: Codable>(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated to the PR.

Even though ActorMessage is a type alias of Codable, using Codable explicitly here to better match method name (similar to registerProtobufRepresentable)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right ok, thankfully I'm able to remove both those specialized functions soon :)

public typealias CodableError = Error & Codable

public init<Failure: Error>(_ error: Failure) {
private let _boxed: BoxedCodableError
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is cleaner and so we can avoid doing type checks everywhere, plus BestEffortStringError is Codable.

// FIXME: implement being able to encode and carry Codable errors (!)
// if let codableError = self.error as? Codable {
// try container.encode(context.outboundManifest(type(of: self.error as Any)), forKey: .manifest)
// try container.encode(codableError, forKey: .error)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't get this approach to work because Codable is a protocol:

value of protocol type 'Codable' (aka 'Decodable & Encodable') cannot conform to 'Encodable'; only struct/enum/class types can conform to protocols

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, what you dod though is absolutely right 👍

throw SerializationError.unableToDeserialize(hint: "Error type \(errorType) is not Codable")
}

let errorDecoder = try container.superDecoder(forKey: .error)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found this by serendipity. Seems to work (according to the tests).

@yim-lee
Copy link
Member Author

yim-lee commented Apr 8, 2020

Test failure: #550

@swift-server-bot test this please

}

let container = try decoder.container(keyedBy: CodingKeys.self)
private struct BoxedCodableError: Codable {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually don't need separate type. Removed.

// FIXME: Needs better impl: https://github.com/apple/swift-distributed-actors/issues/512
public struct ErrorEnvelope: Error, ActorMessage {
public let error: Error
public typealias CodableError = Error & Codable
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

}

let errorDecoder = try container.superDecoder(forKey: .error)
self.codableError = try codableErrorType.init(from: errorDecoder)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh interesting

Copy link
Member

@ktoso ktoso left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, thanks a lot :)

// FIXME: implement being able to encode and carry Codable errors (!)
// if let codableError = self.error as? Codable {
// try container.encode(context.outboundManifest(type(of: self.error as Any)), forKey: .manifest)
// try container.encode(codableError, forKey: .error)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, what you dod though is absolutely right 👍

internal static let AckOps: SerializerID = .foundationJSON

internal static let ErrorEnvelope: SerializerID = .foundationJSON
internal static let BestEffortStringError: SerializerID = .foundationJSON
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right yeah because we default to JSON so it works fine by using it.

Thanks for adding here 👍

/// By doing this before system startup you can ensure a specific serializer is used for those messages.
/// Make sure tha other nodes in the system are configured the same way though.
public mutating func registerCodable<Message: ActorMessage>(
public mutating func registerCodable<Message: Codable>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right ok, thankfully I'm able to remove both those specialized functions soon :)


// errors
settings.registerCodable(ErrorEnvelope.self) // TODO: can be removed once https://github.com/apple/swift/pull/30318 lands
settings.registerCodable(BestEffortStringError.self) // TODO: can be removed once https://github.com/apple/swift/pull/30318 lands
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@ktoso ktoso merged commit 79ebc89 into apple:master Apr 9, 2020
@ktoso ktoso deleted the error-envelope-1 branch April 9, 2020 02:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ErrorEnvelope: Codable, make it possible to encode and carry a Codable error

2 participants