From e0544da684be9db82f160659f4317460d501a251 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 8 Jan 2020 10:32:20 +1300 Subject: [PATCH] Updates to gRPC versioning doc (#16428) * Minor updates to gRPC versioning doc * Update versioning.md * Update versioning.md * Update versioning.md Feedback from https://github.com/aspnet/AspNetCore.Docs/pull/16404#discussion_r363765102 * Update versioning.md * Update versioning.md * Edit pass on gRPC versioning PR (#16447) Co-authored-by: Scott Addie <10702007+scottaddie@users.noreply.github.com> --- aspnetcore/grpc/versioning.md | 56 ++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/aspnetcore/grpc/versioning.md b/aspnetcore/grpc/versioning.md index cc8bea16c0..2917a89c21 100644 --- a/aspnetcore/grpc/versioning.md +++ b/aspnetcore/grpc/versioning.md @@ -18,52 +18,54 @@ New features added to an app can require gRPC services provided to clients to ch ## Backwards compatibility -The gRPC protocol is designed to support services that change over time. Generally additions to gRPC services and methods are non-breaking. Non-breaking means existing clients continue to work. Changing or deleting gRPC services are breaking changes. Breaking changes mean existing clients fail. +The gRPC protocol is designed to support services that change over time. Generally, additions to gRPC services and methods are non-breaking. Non-breaking changes allow existing clients to continue working without changes. Changing or deleting gRPC services are breaking changes. When gRPC services have breaking changes, clients using that service have to be updated and redeployed. Making non-breaking changes to a service has a number of benefits: -- Existing clients continue to run. -- Avoids work involved with notifying clients of breaking changes, and updating them. -- Only one version of the service needs to be documented and maintained. +* Existing clients continue to run. +* Avoids work involved with notifying clients of breaking changes, and updating them. +* Only one version of the service needs to be documented and maintained. -This content focuses on whether changes are **breaking at a gRPC protocol and .NET binary compatibility level**. When making changes, consider whether older clients can logically continue working. For example, adding a new field to a request message: +This content focuses on whether changes are **breaking at a gRPC protocol and .NET binary compatibility level**. When making protocol and binary-compatible changes, you must also consider whether older clients can continue working with the new server behavior. For example, adding a new field to a request message: -* Is not a protocol breaking change. -* Returning an error status on the server if the new field is not set makes it a breaking change for old clients. +* Isn't a protocol breaking change. +* Returning an error status on the server if the new field isn't set makes it a breaking change for old clients. + +Behavior compatibility is determined by your app-specific code. ### Non-breaking changes -These changes are non-breaking at a gRPC protocol level, and .NET binary level. +These changes are non-breaking at a gRPC protocol level and .NET binary level. -- **Adding a new service** -- **Adding a new method to a service** -- **Adding a field to a request message** - Fields added to a request message are deserialized with the [default value](https://developers.google.com/protocol-buffers/docs/proto3#default) on the server when not set. To be non-breaking the service will need to succeed when it is not set by older clients. -- **Adding a field to a response message** - Fields added to a response message are deserialized into the message's [unknown fields](https://developers.google.com/protocol-buffers/docs/proto3#unknowns) collection on the client. -- **Adding a value to an enum** - Enums are serialized as a numeric value. New enum values are deserialized on the client to the enum value without an enum name. To be non-breaking older clients will need to run correctly when they receive and unexcepted value. +* **Adding a new service** +* **Adding a new method to a service** +* **Adding a field to a request message** - Fields added to a request message are deserialized with the [default value](https://developers.google.com/protocol-buffers/docs/proto3#default) on the server when not set. To be a non-breaking change, the service must succeed when the new field isn't set by older clients. +* **Adding a field to a response message** - Fields added to a response message are deserialized into the message's [unknown fields](https://developers.google.com/protocol-buffers/docs/proto3#unknowns) collection on the client. +* **Adding a value to an enum** - Enums are serialized as a numeric value. New enum values are deserialized on the client to the enum value without an enum name. To be a non-breaking change, older clients must run correctly when receiving the new enum value. ### Binary breaking changes The following changes are non-breaking at a gRPC protocol level, but the client needs to be updated if it upgrades to the latest *.proto* contract or client .NET assembly. Binary compatibility is important if you plan to publish a gRPC library to NuGet. -- **Removing a field** - Values from a removed field are deserialized to a message's [unknown fields](https://developers.google.com/protocol-buffers/docs/proto3#unknowns). This isn't a gRPC protocol breaking change, but the client needs to be updated if it upgrades to the latest contract. It is important that a removed field number isn't accidentally reused in the future. One way to make sure this doesn't happen is to specify deleted field numbers and names on the message using Protobuf's [`reserved`](https://developers.google.com/protocol-buffers/docs/proto3#reserved) keyword. -- **Renaming a field** - Field names are only used in generated code. The field number is used to identify fields on the network. The client will need to be updated if it upgrades to the latest contract. -- **Renaming a message** - Message names are not sent on the network so this isn't a gRPC protocol breaking change, but the client will need to be updated if it upgrades to the latest contract. -- **Changing csharp_namespace** - Changing `csharp_namespace` will change the namespace of generated .NET types. This isn't a gRPC protocol breaking change, but the client needs to be updated if it upgrades to the latest contract. +* **Removing a field** - Values from a removed field are deserialized to a message's [unknown fields](https://developers.google.com/protocol-buffers/docs/proto3#unknowns). This isn't a gRPC protocol breaking change, but the client needs to be updated if it upgrades to the latest contract. It's important that a removed field number isn't accidentally reused in the future. To ensure this doesn't happen, specify deleted field numbers and names on the message using Protobuf's [reserved](https://developers.google.com/protocol-buffers/docs/proto3#reserved) keyword. +* **Renaming a message** - Message names aren't typically sent on the network, so this isn't a gRPC protocol breaking change. The client will need to be updated if it upgrades to the latest contract. One situation where message names **are** sent on the network is with [Any](https://developers.google.com/protocol-buffers/docs/proto3#any) fields, when the message name is used to identify the message type. +* **Changing csharp_namespace** - Changing `csharp_namespace` will change the namespace of generated .NET types. This isn't a gRPC protocol breaking change, but the client needs to be updated if it upgrades to the latest contract. ### Breaking changes -These are protocol and binary breaking changes. +The following items are protocol and binary breaking changes: -- **Changing a field data type** - Changing a field's data type to an [incompatible type](https://developers.google.com/protocol-buffers/docs/proto3#updating) will cause errors when deserializing the message. Even if the new data type is compatible, it is likely the client will need to be updated to support the new type if it upgrades to the latest contract. -- **Changing a field number** - The field number is used to identify fields on the network. -- **Renaming a package, service or method** - gRPC uses the package name, service name and method name to build the URL. The client gets an *UNIMPLEMENTED* status from the server. -- **Removing a service or method** - The client gets an *UNIMPLEMENTED* status from the server when calling the removed method. +* **Renaming a field** - With Protobuf content, the field names are only used in generated code. The field number is used to identify fields on the network. Renaming a field isn't a protocol breaking change for Protobuf. However, if a server is using JSON content then renaming a field is a breaking change. +* **Changing a field data type** - Changing a field's data type to an [incompatible type](https://developers.google.com/protocol-buffers/docs/proto3#updating) will cause errors when deserializing the message. Even if the new data type is compatible, it's likely the client needs to be updated to support the new type if it upgrades to the latest contract. +* **Changing a field number** - With Protobuf payloads, the field number is used to identify fields on the network. +* **Renaming a package, service or method** - gRPC uses the package name, service name, and method name to build the URL. The client gets an *UNIMPLEMENTED* status from the server. +* **Removing a service or method** - The client gets an *UNIMPLEMENTED* status from the server when calling the removed method. ## Version number services Services should strive to remain backwards compatible with old clients. Eventually changes to your app may require breaking changes. Breaking old clients and forcing them to be updated along with your service isn't a good user experience. A way to maintain backwards compatibility while making breaking changes is to publish multiple versions of a service. -gRPC supports an optional [`package`](https://developers.google.com/protocol-buffers/docs/proto3#packages) specifier, which functions much like a .NET namespace. In fact the `package` will be used as the .NET namespace for generated .NET types if `option csharp_namespace` is not set in the *.proto* file. The package can be used to specify a version number for your service and its messages: +gRPC supports an optional [package](https://developers.google.com/protocol-buffers/docs/proto3#packages) specifier, which functions much like a .NET namespace. In fact, the `package` will be used as the .NET namespace for generated .NET types if `option csharp_namespace` is not set in the *.proto* file. The package can be used to specify a version number for your service and its messages: [!code-protobuf[](versioning/sample/greet.v1.proto?highlight=3)] @@ -85,11 +87,11 @@ app.UseEndpoints(endpoints => }); ``` -Including a version number in the package name gives you the opportunity to publish a *v2* version of your service with breaking changes, while continuing to support older clients who call the *v1* version. Once clients have updated to use the *v2* service you can choose to remove the old version. When planning to publish multiple versions of a service: +Including a version number in the package name gives you the opportunity to publish a *v2* version of your service with breaking changes, while continuing to support older clients who call the *v1* version. Once clients have updated to use the *v2* service, you can choose to remove the old version. When planning to publish multiple versions of a service: -- Avoid breaking changes if reasonable. -- Don't update the version number unless making breaking changes. -- Do update the version number when you make breaking changes. +* Avoid breaking changes if reasonable. +* Don't update the version number unless making breaking changes. +* Do update the version number when you make breaking changes. Publishing multiple versions of a service duplicates it. To reduce duplication, consider moving business logic from the service implementations to a centralized location that can be reused by the old and new implementations: