//The message types in this file are used for persisting metadata about write operations in a transaction log, so they
//can be replayed in other replicas if/when such a replica becomes available. The transaction log is currently only
//used for migration of repositories to Mesh, but will likely also be used as part of recovery/repair in phase 4b.
//
//These transaction messages are _currently_ not used in RPC operations, but this may change when recovery/repair is
//explored further.

syntax = "proto3";

package mesh.v1;

import "mesh/v1/error.proto";
import "mesh/v1/git_error.proto";
import "mesh/v1/git_model.proto";
import "mesh/v1/pull_request.proto";
import "mesh/v1/ref.proto";
import "mesh/v1/repository.proto";
import "mesh/v1/stream.proto";

option go_package = "atlassian.com/bitbucket/mesh/rpc/v1";
option java_multiple_files = true;
option java_outer_classname = "TransactionProtos";
option java_package = "com.atlassian.bitbucket.mesh.rpc.v1";

message RpcAbortStagingPack {
}

message RpcApplyTransactionsRequest {
  string repository = 1;
  RpcGitTimeouts timeouts = 2;

  repeated RpcTransaction transactions = 3;
}

message RpcApplyTransactionsResponse {
  //The number of successfully applied transactions
  uint32 success_count = 1;
  //If one of the transactions in the batch failed, the associated error of the (success_count + 1)th transaction
  //is returned as either an error or a git_error
  oneof error_oneof {
    RpcError error = 2;
    RpcGitError git_error = 3;
  }
}

/* Sent by the transaction coordinator to indicate that the transaction should be committed */
message RpcCommitTransactionRequest {
  //The version of the repository before the transaction commits
  uint64 version = 4;
}

/* The response for a commit request. If the commit fails, an error indicates what happened. Otherwise the new
   content hash is returned */
message RpcCommitTransactionResponse {
  oneof response_oneof {
    //If present, indicates the transaction committed successfully and provides details about the
    //content-hash and version the replica arrived at
    RpcReplicaVersion replica_version = 1;
    //If present, indicates the transaction was not committed
    RpcTransactionCommitFailed commit_failed = 2;
  }
}

message RpcPrepareTransactionRequest {
  //The write operation to prepare
  RpcTransaction transaction = 1;
  //The time in seconds that the caller is prepared to wait for this node to vote whether the transaction
  //should be applied or not. If the node does not respond within this timeout, the caller will assume a NO vote.
  int64 timeout = 2;
  //The leader node ID
  string leader = 3;
}

message RpcPrepareTransactionResponse {
  enum Vote {
    //The node sent a vote that isn't understood, or didn't send a vote at all. This will be treated the same as if
    //the node voted `NO`.
    UNKNOWN = 0;
    //The node has successfully prepared the transaction and is ready to commit if a quorum is formed.
    YES = 1;
    //The node could not prepare the transaction. If the transaction is committed, the node will become inconsistent
    //and require repair.
    NO = 2;
    //The node is in an inconsistent state such that its vote should not be counted. Generally a node that cannot
    //prepare a transaction should vote `NO`, but in some cases it's better for it to opt out instead. For example,
    //if a node has just been assigned a new partition replica and has not yet backfilled some repositories, it may
    //abstain from voting on transactions for those repositories to prevent blocking writes while the backfill is in
    //progress.
    ABSTAIN = 3;
  }

  //The node's vote.
  Vote vote = 1;
  //For nodes that vote `YES`, this field can also be set to indicate the node's write token was available and has
  //been granted to the current transaction. For any vote other than `YES` this field should always remain unset.
  RpcWriteTokenGranted write_token_granted = 2;
}

message RpcReplicaVersion {
  string content_hash = 1;
  uint64 version = 2;
}

/* Sent by the transaction coordinator to indicate that the transaction should be rolled back */
message RpcRollBackTransactionRequest {
}

/* The response for a rollback request. If the rollback fails (e.g. somehow the transaction is committed anyway),
   an error indicates what happened */
message RpcRollBackTransactionResponse {
  oneof response_oneof {
    //If present, indicates the rollback failed
    RpcTransactionRollbackFailed rollback_failed = 2;
  }
}

message RpcStagePackRequest {
  string repository = 1;
  string transaction_id = 2;
}

message RpcTransactFollowerFragment {
  oneof fragment_oneof {
    RpcPrepareTransactionResponse prepare_response = 1;
    RpcCommitTransactionResponse commit_response = 2;
    RpcRollBackTransactionResponse roll_back_response = 3;
    RpcYieldWriteTokenRequest yield_token_request = 4;
    RpcWriteTokenGranted token_granted = 5;
    RpcAbortStagingPack abort_staging_pack = 6;
  }
  //The follower's logical timestamp
  uint64 logical_timestamp = 10;
}

message RpcTransactLeaderFragment {
  oneof fragment_oneof {
    RpcPrepareTransactionRequest prepare_request = 1;
    RpcCommitTransactionRequest commit_request = 2;
    RpcStagePackRequest stage_pack_request = 6;
    RpcStreamChunk pack_chunk = 7;
    RpcRollBackTransactionRequest roll_back_request = 3;
    RpcTransactionSummary summary = 4;
    RpcWriteTokenYielded token_yielded = 5;
  }
  //The leader's logical timestamp
  uint64 logical_timestamp = 10;
}

message RpcTransaction {
  string id = 1;
  string repository = 2;
  RpcPerson person = 3;
  uint64 timestamp = 4;

  oneof details_oneof {
    RpcCreateRepositoryRequest create_repository = 5;
    RpcDeletePullRequestRequest delete_pull_request = 6;
    RpcDeleteRepositoryRequest delete_repository = 7;
    RpcSetDefaultBranchRequest set_default_branch = 8;
    RpcSetMetadataRequest set_metadata = 9;
    RpcSetTranscodeEnabledRequest set_transcode = 10;
    RpcUpdateRefsRequest update_refs = 11;
  }

  //For very large transactions (e.g. a ref-update that updates many 1000s of refs), the RpcTransaction may be
  //split into multiple fragments to not exceed the maximum gRPC message size. These subsequent messages will be
  //identical except for the details field. Only the last fragment should have more_details set to false.
  bool more_details = 12;
}

message RpcTransactionSummary {
  //Indicates that a quorum of replicas successfully committed the transaction.
  bool committed = 1;
  //The content hash that the repository should have after completion of the transaction.
  string content_hash = 2;
  //List of replica IDs that did not reach the provided content_hash and should be considered out-of-sync.
  repeated string inconsistent_replicas = 3;
  //The version of the repository after the transaction completed
  uint64 version = 4;
}

//@since 1.2
message RpcVerifyConsistencyFollowerFragment {
  oneof fragment_oneof {
    RpcWriteTokenGranted token_granted = 1;
    RpcYieldWriteTokenRequest yield_token_request = 2;
  }

  //The follower's logical timestamp
  uint64 logical_timestamp = 10;
}

//@since 1.2
message RpcVerifyConsistencyLeaderFragment {
  oneof fragment_oneof {
    RpcRepositoryConsistencySummary consistency_summary = 1;
    RpcWriteTokenRequest token_request = 2;
    RpcWriteTokenYielded token_yielded = 3;
  }

  //The leader's logical timestamp
  uint64 logical_timestamp = 10;
}

message RpcYieldWriteTokenRequest {
}

message RpcWriteTokenYielded {
}

message RpcWriteTokenGranted {
  RpcReplicaVersion replica_version = 1;
}

//@since 1.2
message RpcWriteTokenRequest {
  string repository = 1;
  string transaction_id = 2;
}
