// Copyright 2019 Lightbend Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // gRPC interface for common messages and services for Entity user functions. syntax = "proto3"; package cloudstate; // Any is used so that domain events defined according to the functions business domain can be embedded inside // the protocol. import "google/protobuf/any.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/descriptor.proto"; option java_package = "io.cloudstate.protocol"; option go_package = "github.com/cloudstateio/go-support/cloudstate/protocol;protocol"; // Transport-specific metadata associated with a message. // // The semantics of the metadata are not defined in this protocol, but rather, depend on the transport on which a // particular instance of the metadata maps to. What keys or values are allowed or disallowed, whether duplicate values // for the same key are allowed and how they are handled, and whether key names are case sensitive or not, are all // undefined in the context of the Cloudstate protocol. // // If a metadata entry associated with a message can't be expressed in an underlying transport, for example, due to // invalid characters in a key or value, the behavior of the proxy is undefined. This is because metadata is transport // specific, so if the user function chooses to use metadata, it is choosing to be specific to a particular transport, // which is beyond the scope of the Cloudstate protocol, and it's therefore the user function's responsibility to adhere // to the semantics of that transport. The proxy MAY decide to drop metadata entries if it knows they are invalid or // unsupported. If a metadata entry is dropped, the proxy MAY inform the user function that the entry was dropped by // sending an error message to the EntityDiscovery.ReportError gRPC call. // // The metadata MAY also contain CloudEvent metadata. If a message comes from a Cloudstate event source, the Cloudstate // proxy MUST attach CloudEvent metadata to it if the event doesn't already have CloudEvent metadata attached to it. // This metadata SHALL be encoded according to the binary mode of the CloudEvent HTTP protocol binding, which can be // found here: // // https://github.com/cloudevents/spec/blob/master/http-protocol-binding.md // // The Cloudstate proxy MAY synthesize appropriate values for Cloudstate metadata if no equivalent metadata exists in // the event source, for example, if there is no type, the Cloudstate proxy MAY use the name of the gRPC message as the // CloudEvent type, and if there is no source, the Cloudstate proxy MAY use the name of the topic as the source. // // If an incoming message does have CloudEvent metadata attached to it, the Cloudstate proxy MUST transcode that // CloudEvent metadata to the HTTP protocol binding as described above. // // Messages sent from the user function to an event destination MAY include CloudEvent metadata. If they include any // CloudEvent metadata, they MUST include all required CloudEvent attributes, including id, source, specversion and // type. The behavior of the proxy is undefined if some of these attributes, but not others, are included - the proxy // MAY ignore them all, or MAY generate values itself, but SHOULD NOT fail sending the message. If the destination for // the message is an event destination, the Cloudstate proxy MUST transcode the supplied Cloudstate metadata to a // binding appropriate for the underlying transport for that event destination, it MUST NOT pass the CloudEvent // metadata as is unless the transport uses the same binding rules. message Metadata { // The metadata entries. repeated MetadataEntry entries = 1; } // A metadata entry. message MetadataEntry { // Key for the entry. Valid keys depend on the transport from or to which this metadata is sent. string key = 1; // The value. oneof value { // A string value. Valid values depend on the transport from or which this metadata is sent. // // If the transport does not support string values, the behavior of the Cloudstate proxy is undefined from the // point of view of this protocol. If there is a convention in the protocol for encoding string values as // UTF-8 bytes, then the Cloudstate proxy MAY do that. string string_value = 2; // A bytes value. Valid values depend on the transport from or which this metadata is sent. // // If the transport does not support bytes values, the behavior of the Cloudstate proxy is undefined from the // point of view of this protocol. If there is a convention in the protocol for encoding bytes values as // Base64 encoded strings, then the Cloudstate proxy MAY do that. bytes bytes_value = 3; } } // A reply to the sender. message Reply { // The reply payload google.protobuf.Any payload = 1; // Metadata for the reply // // Not all transports support per message metadata, for example, gRPC doesn't. The Cloudstate proxy MAY ignore the // metadata in this case, or it MAY lift the metadata into another place, for example, in gRPC, a unary call MAY // have its reply metadata placed in the headers of the HTTP response, or the first reply to a streamed call MAY // have its metadata placed in the headers of the HTTP response. // // If the metadata is ignored, the Cloudstate proxy MAY notify the user function by sending an error message to the // EntityDiscovery.ReportError gRPC call. cloudstate.Metadata metadata = 2; } // Forwards handling of this request to another entity. message Forward { // The name of the service to forward to. string service_name = 1; // The name of the command. string command_name = 2; // The payload. google.protobuf.Any payload = 3; // The metadata to include with the forward Metadata metadata = 4; } // An action for the client message ClientAction { oneof action { // Send a reply Reply reply = 1; // Forward to another entity Forward forward = 2; // Send a failure to the client Failure failure = 3; } } // A side effect to be done after this command is handled. message SideEffect { // The name of the service to perform the side effect on. string service_name = 1; // The name of the command. string command_name = 2; // The payload of the command. google.protobuf.Any payload = 3; // Whether this side effect should be performed synchronously, ie, before the reply is eventually // sent, or not. bool synchronous = 4; // The metadata to include with the side effect Metadata metadata = 5; } // A command. For each command received, a reply must be sent with a matching command id. message Command { // The ID of the entity. string entity_id = 1; // A command id. int64 id = 2; // Command name string name = 3; // The command payload. google.protobuf.Any payload = 4; // Whether the command is streamed or not. bool streamed = 5; // The command metadata. // // Not all transports support per message metadata, for example, gRPC doesn't. The Cloudstate proxy MAY include // metadata from other locations in this case, for example, in gRPC, a unary call MAY have the HTTP request headers // attached to the command, while a streamed call MAY have the HTTP request headers attached as the metadata for // either the first command, or every command. This specification leaves this behavior undefined. Metadata metadata = 6; } message StreamCancelled { // The ID of the entity string entity_id = 1; // The command id int64 id = 2; } // A failure reply. If this is returned, it will be translated into a gRPC unknown // error with the corresponding description if supplied. message Failure { // The id of the command being replied to. Must match the input command. int64 command_id = 1; // A description of the error. string description = 2; // Whether this failure should trigger an entity restart. bool restart = 3; } message EntitySpec { // This should be the Descriptors.FileDescriptorSet in proto serialized from as generated by: // protoc --include_imports \ // --proto_path= \ // --descriptor_set_out=user-function.desc \ // bytes proto = 1; // The entities being served. repeated Entity entities = 2; // Optional information about the service. ServiceInfo service_info = 3; } // Information about the service that proxy is proxying to. // All of the information in here is optional. It may be useful for debug purposes. message ServiceInfo { // The name of the service, eg, "shopping-cart". string service_name = 1; // The version of the service. string service_version = 2; // A description of the runtime for the service. Can be anything, but examples might be: // - node v10.15.2 // - OpenJDK Runtime Environment 1.8.0_192-b12 string service_runtime = 3; // If using a support library, the name of that library, eg "cloudstate" string support_library_name = 4; // The version of the support library being used. string support_library_version = 5; // Cloudstate protocol major version accepted by the support library. int32 protocol_major_version = 6; // Cloudstate protocol minor version accepted by the support library. int32 protocol_minor_version = 7; } message Entity { // The type of entity. By convention, this should be a fully qualified entity protocol grpc // service name, for example, cloudstate.eventsourced.EventSourced. string entity_type = 1; // The name of the service to load from the protobuf file. string service_name = 2; // The ID to namespace state by. How this is used depends on the type of entity, for example, // event sourced entities will prefix this to the persistence id. string persistence_id = 3; // The passivation strategy for the entity. EntityPassivationStrategy passivation_strategy = 4; } // // The semantics is to provide a flexible way for entity user functions to configure the passivation strategy. // This strategy is sent to the proxy at discovery time allowing the proxy to configure the corresponding entities. // The only passivation strategy supported is the timeout strategy and configuring this is optional for the entity. // If an entity user function does not configure the passivation strategy the proxy uses its fallback default value. // // The passivation strategy for the entity user function. message EntityPassivationStrategy { oneof strategy { // the timeout passivation strategy. TimeoutPassivationStrategy timeout = 1; } } // A passivation strategy based on a timeout. The idle timeout after which a user function's entity is passivated. message TimeoutPassivationStrategy { // The timeout in millis int64 timeout = 1; } message UserFunctionError { string message = 1; } message ProxyInfo { int32 protocol_major_version = 1; int32 protocol_minor_version = 2; string proxy_name = 3; string proxy_version = 4; repeated string supported_entity_types = 5; } // Entity discovery service. service EntityDiscovery { // Discover what entities the user function wishes to serve. rpc discover(ProxyInfo) returns (EntitySpec) {} // Report an error back to the user function. This will only be invoked to tell the user function // that it has done something wrong, eg, violated the protocol, tried to use an entity type that // isn't supported, or attempted to forward to an entity that doesn't exist, etc. These messages // should be logged clearly for debugging purposes. rpc reportError(UserFunctionError) returns (google.protobuf.Empty) {} }