// Copyright 2021 Lightbend Inc.

// gRPC interface for Replicated Entity user functions.

syntax = "proto3";

package akkaserverless.component.replicatedentity;

option go_package = "github.com/lightbend/akkaserverless-go-sdk/akkaserverless/entity;entity";
option java_package = "com.akkaserverless.protocol";

import "akkaserverless/component/component.proto";
import "akkaserverless/component/entity/entity.proto";
import "google/protobuf/any.proto";

// Replicated Entities Protocol
//
// Note that while this protocol provides support for replicated entities, the data
// types sent across the protocol are not replicated entities themselves. It is the
// responsibility of the Akka Serverless proxy to implement the replicated entities,
// merge functions, vector clocks etc, not the user function. The user function
// need only hold the current value in memory, and this protocol sends deltas to the
// user function to update its in memory value as necessary. These deltas have no
// way of dealing with conflicts, hence it is important that the Akka Serverless proxy
// always knows what the state of the user function's in memory value is before
// sending a delta. If the Akka Serverless proxy is not sure what the value is, eg
// because it has just sent an operation to the user function which may have updated its
// value as a result, the proxy should wait until it gets the result of the operation
// back, to ensure its in memory value is in sync with the user function so that
// it can calculate deltas that won't conflict.
//
// The user function is expected to update its value both as the result of
// receiving deltas from the proxy, as well as when it sends deltas. It must not
// update its value in any other circumstance, updating the value in response to
// any other stimuli risks the value becoming out of sync with the Akka
// Serverless proxy. The user function will not be sent back deltas as a result
// of its own changes.
//
// An invocation of handle is made for each entity being handled. It may be kept
// alive and used to handle multiple commands, and may subsequently be
// terminated if that entity becomes idle, or if the entity is deleted. Shutdown
// is typically done for efficiency reasons, unless the entity is explicitly
// deleted, and a terminated handle stream does not mean the proxy has stopped
// tracking the state of the entity in its memory.
//
// Special care must be taken when working with maps and sets. The keys/values
// are google.protobuf.Any, which encodes the value as binary protobuf, however,
// serialized protobufs are not stable, two semantically equal objects could
// encode to different bytes. It is the responsibility of the user function to
// ensure that stable encodings are used.
service ReplicatedEntities {
  // After invoking handle, the first message sent will always be a
  // ReplicatedEntityInit message, containing the entity ID, and, if it exists or
  // is available, a delta for the initial state of the entity. After that, one or
  // more commands may be sent, as well as deltas as they arrive.
  //
  // The user function must respond with one reply per command in. They do not
  // necessarily have to be sent in the same order that the commands were sent,
  // the command ID is used to correlate commands to replies.
  rpc Handle(stream ReplicatedEntityStreamIn) returns (stream ReplicatedEntityStreamOut);
}

// Message for the Replicated Entity handle stream in.
message ReplicatedEntityStreamIn {
  oneof message {
    // Always sent first, and only once.
    ReplicatedEntityInit init = 1;
    // A delta to be applied to the current state. May be sent at any time.
    ReplicatedEntityDelta delta = 2;
    // Delete the entity. May be sent at any time. The user function should
    // clear its state when it receives this. A proxy may decide to terminate
    // the stream after sending this.
    ReplicatedEntityDelete delete = 3;
    // A command, may be sent at any time.
    entity.Command command = 4;
  }
}

// Message for the Replicated Entity handle stream out.
message ReplicatedEntityStreamOut {
  oneof message {
    // A reply to an incoming command. Either one reply, or one failure, must be
    // sent in response to each command.
    ReplicatedEntityReply reply = 1;
    // A failure. Either sent in response to a command, or sent if some other
    // error occurs.
    Failure failure = 2;
  }
}

// A Replicated Entity delta
//
// Deltas only carry the change in value to be applied to the current (possibly
// empty) state.
message ReplicatedEntityDelta {
  oneof delta {
    ReplicatedCounterDelta counter = 1;
    ReplicatedSetDelta replicated_set = 2;
    ReplicatedRegisterDelta register = 3;
    ReplicatedMapDelta replicated_map = 4;
    ReplicatedCounterMapDelta replicated_counter_map = 5;
    ReplicatedRegisterMapDelta replicated_register_map = 6;
    ReplicatedMultiMapDelta replicated_multi_map = 7;
    VoteDelta vote = 8;
  }
}

message ReplicatedCounterDelta {
  sint64 change = 1;
}

message ReplicatedSetDelta {
  // If cleared is set, the set must be cleared before added is processed.
  bool cleared = 1;
  repeated google.protobuf.Any removed = 2;
  repeated google.protobuf.Any added = 3;
}

message ReplicatedRegisterDelta {
  google.protobuf.Any value = 1;
  ReplicatedEntityClock clock = 2;
  int64 custom_clock_value = 3;
}

message ReplicatedMapDelta {
  bool cleared = 1;
  repeated google.protobuf.Any removed = 2;
  repeated ReplicatedMapEntryDelta updated = 3;
  repeated ReplicatedMapEntryDelta added = 4;
}

message ReplicatedMapEntryDelta {
  // The entry key.
  google.protobuf.Any key = 1;
  ReplicatedEntityDelta delta = 2;
}

message ReplicatedCounterMapDelta {
  bool cleared = 1;
  repeated google.protobuf.Any removed = 2;
  repeated ReplicatedCounterMapEntryDelta updated = 3;
}

message ReplicatedCounterMapEntryDelta {
  google.protobuf.Any key = 1;
  ReplicatedCounterDelta delta = 2;
}

message ReplicatedRegisterMapDelta {
  bool cleared = 1;
  repeated google.protobuf.Any removed = 2;
  repeated ReplicatedRegisterMapEntryDelta updated = 3;
}

message ReplicatedRegisterMapEntryDelta {
  google.protobuf.Any key = 1;
  ReplicatedRegisterDelta delta = 2;
}

message ReplicatedMultiMapDelta {
  bool cleared = 1;
  repeated google.protobuf.Any removed = 2;
  repeated ReplicatedMultiMapEntryDelta updated = 3;
}

message ReplicatedMultiMapEntryDelta {
  google.protobuf.Any key = 1;
  ReplicatedSetDelta delta = 2;
}

message VoteDelta {
  // Set by the proxy initially or when the vote has changed.
  // Only set by the user function to change the node's current vote.
  bool self_vote = 1;
  // Only set by the proxy initially or when the vote has changed.
  // Ignored by the proxy when set by the user function.
  int32 votes_for = 2;
  // Only set by the proxy initially or when the vote has changed.
  // Ignored by the proxy when set by the user function.
  int32 total_voters = 3;
}

message ReplicatedEntityInit {
  string service_name = 1;
  string entity_id = 2;
  ReplicatedEntityDelta delta = 3;
}

message ReplicatedEntityDelete {}

message ReplicatedEntityReply {
  int64 command_id = 1;
  ClientAction client_action = 2;
  repeated SideEffect side_effects = 4;
  ReplicatedEntityStateAction state_action = 5;
}

message ReplicatedEntityStateAction {
  oneof action {
    ReplicatedEntityDelta update = 1;
    ReplicatedEntityDelete delete = 2;
  }
}

enum ReplicatedEntityClock {
  // Use the default clock for deciding the last write, which is the system
  // clock's milliseconds since epoch.
  REPLICATED_ENTITY_CLOCK_DEFAULT_UNSPECIFIED = 0;
  // Use the reverse semantics with the default clock, to enable first write
  // wins.
  REPLICATED_ENTITY_CLOCK_REVERSE = 1;
  // Use a custom clock value, set using custom_clock_value.
  REPLICATED_ENTITY_CLOCK_CUSTOM = 2;
  // Use a custom clock value, but automatically increment it by one if the
  // clock value from the current value is equal to the custom_clock_value.
  REPLICATED_ENTITY_CLOCK_CUSTOM_AUTO_INCREMENT = 3;
}
