syntax = "proto3";
package heartbeatpb;

option go_package = "github.com/pingcap/ticdc/heartbeatpb";

message TableSpan {
    int64 TableID = 1;
    bytes StartKey = 2;
    bytes EndKey = 3;
    uint32 KeyspaceID = 4;
}

message HeartBeatRequest {
    ChangefeedID changefeedID = 1;
    Watermark watermark = 2;
    Watermark redoWatermark = 3;
    repeated TableSpanStatus statuses = 4;
    bool compeleteStatus = 5;  // Whether includes all table spans in the changefeed?
    RunningError err = 6;
}

message Watermark {
    uint64 checkpointTs = 1;  // min checkpointTs of all tables in the DispatcherManager
    uint64 resolvedTs = 2;    // min resolvedTs of all tables in the DispatcherManager
    uint64 seq = 3;           // sequence number, it's used to check whether the watermark is outdated
    uint64 lastSyncedTs = 4;  // last synced ts of all tables in the DispatcherManager, used in /:changefeed_id/synced API
}

enum Action {
    Write = 0;
    Pass = 1;
}

message DispatcherAction {
    Action action = 1;
    uint64 CommitTs = 2;   // DDLCommitTs
    bool IsSyncPoint = 3;  // sync point Event and ddl Event could have the same CommitTs, so we need to distinguish them.
}

message ACK {
    uint64 CommitTs = 1;   // DDLCommitTs
    bool IsSyncPoint = 2;  // sync point Event and ddl Event could have the same CommitTs, so we need to distinguish them.
}

message InfluencedDispatchers {
    InfluenceType InfluenceType = 1;
    // only exist when type is normal
    repeated DispatcherID DispatcherIDs = 2;
    // only exist when type is DB.
    int64 SchemaID = 3;
    // only exist when type is all or db, and in heartbeat response.
    DispatcherID excludeDispatcherId = 4;
}

message DispatcherStatus {
    InfluencedDispatchers influencedDispatchers = 1;
    DispatcherAction action = 2;
    ACK ack = 3;
}

message HeartBeatResponse {
    ChangefeedID changefeedID = 1;
    repeated DispatcherStatus dispatcherStatuses = 2;
    int64 mode = 3;
}

message CheckpointTsMessage {
    ChangefeedID changefeedID = 1;
    uint64 checkpointTs = 2;
}

// RedoMetaMessage used to send unflushed ts to update redo meta 
// from maintainer to dispatcher manager
message RedoMetaMessage {
    ChangefeedID changefeedID = 1;
    uint64 resolvedTs = 2;
    uint64 checkpointTs = 3;
}

// RedoResolvedTsProgressMessage used to send redo meta flushed resolvedTs 
// from dispatcher manager to maintainer
message RedoResolvedTsProgressMessage {
    ChangefeedID changefeedID = 1;
    uint64 resolvedTs = 2;
}

// RedoResolvedTsForwardMessage used to send redoGlobalTs (redo meta flushed resolvedTs) 
// from maintainer to dispatcher manager
message RedoResolvedTsForwardMessage {
    ChangefeedID changefeedID = 1;
    uint64 resolvedTs = 2;
}

enum ScheduleAction {
    Create = 0;
    Remove = 1;
}

enum OperatorType {
    O_Add = 0;
    O_Remove = 1;
    O_Move = 2;
    O_Split = 3;
    O_Merge = 4;
}

message DispatcherConfig {
    TableSpan span = 1;
    uint64 startTs = 2;
    DispatcherID dispatcherID = 3;
    int64 schemaID = 4;
    int64 mode = 5;
    // skipDMLAsStartTs indicates whether to skip DML events at (startTs+1).
    // It is mainly used when a dispatcher is recreated during an in-flight DDL barrier:
    // we need to replay the DDL at blockTs by starting from (blockTs-1), while avoiding
    // potential duplicate DML writes at blockTs.
    bool skipDMLAsStartTs = 6;
}

message ScheduleDispatcherRequest {
    ChangefeedID changefeedID = 1;
    DispatcherConfig config = 2;
    ScheduleAction scheduleAction = 3;
    OperatorType operatorType = 4;
}

message MergeDispatcherRequest {
    ChangefeedID changefeedID = 1;
    repeated DispatcherID dispatcherIDs = 2;  // dispatcherIDs from the dispatchers that need to be merged
    DispatcherID mergedDispatcherID = 3;      // the dispatcherID of the new dispatcher to be merged to.
    int64 mode = 4;
}

message MaintainerHeartbeat {
    repeated MaintainerStatus statuses = 1;
}

message MaintainerStatus {
    ChangefeedID changefeedID = 1;
    string feed_state = 2;
    ComponentState state = 3;
    uint64 checkpoint_ts = 4;
    repeated RunningError err = 5;
    bool bootstrap_done = 6;
    uint64 lastSyncedTs = 7;  // last synced ts of all tables in the changefeed, used in /:changefeed_id/synced API
}

message CoordinatorBootstrapRequest {
    int64 version = 1;
}

message CoordinatorBootstrapResponse {
    repeated MaintainerStatus statuses = 1;
}

message AddMaintainerRequest {
    ChangefeedID id = 1;
    bytes config = 2;
    uint64 checkpoint_ts = 3;
    bool is_new_changefeed = 4;  // only true when the changefeed is new created or resumed with overwriteCheckpointTs
    uint32 keyspace_id = 5;
}

message RemoveMaintainerRequest {
    ChangefeedID id = 1;
    bool cascade = 2;
    bool removed = 3;
    uint32 keyspace_id = 4;
}

message MaintainerBootstrapRequest {
    ChangefeedID changefeedID = 1;
    bytes config = 2;
    uint64 start_ts = 3;
    DispatcherID table_trigger_event_dispatcher_id = 4;
    bool is_new_changefeed = 5;                               // only true when the changefeed is new created or resumed with overwriteCheckpointTs
    DispatcherID table_trigger_redo_dispatcher_id = 6;  // only for redo
    uint32 keyspace_id = 7;
}

message MaintainerBootstrapResponse {
    ChangefeedID changefeedID = 1;
    repeated BootstrapTableSpan spans = 2;
    RunningError err = 3;
    // For nodes with a table trigger event dispatcher:
    // - Reports checkpoint_ts in bootstrap response
    // - This checkpoint_ts is used to fetch table lists from schema store during maintainer restart
    // to ensure maintainer's table list stays synchronized with the dispatcher's DDL progress
    // since the dispatcher handles all table creation/deletion DDLs
    //
    // For nodes without a table trigger event dispatcher:
    // - checkpoint_ts is set to 0
    uint64 checkpoint_ts = 4;
    // redo_checkpoint_ts is similar to checkpoint_ts, for nodes with a table trigger redo dispatcher.
    // It will be used when redo enable.
    uint64 redo_checkpoint_ts = 5;
    repeated ScheduleDispatcherRequest operators = 6;
}

message MaintainerPostBootstrapRequest {
    ChangefeedID changefeedID = 1;
    DispatcherID table_trigger_event_dispatcher_id = 2;
    repeated SchemaInfo schemas = 3;
    repeated SchemaInfo redo_schemas = 4;
}

message MaintainerPostBootstrapResponse {
    ChangefeedID changefeedID = 1;
    DispatcherID table_trigger_event_dispatcher_id = 2;
    RunningError err = 3;
}

message SchemaInfo {
    int64 SchemaID = 1;
    string SchemaName = 2;
    repeated TableInfo Tables = 3;
}

message TableInfo {
    int64 TableID = 1;
    string TableName = 2;
}

enum BlockStage {
    NONE = 0;
    WAITING = 1;
    WRITING = 2;
    DONE = 3;
}

message BootstrapTableSpan {
    DispatcherID ID = 1;
    int64 SchemaID = 2;
    TableSpan span = 3;
    ComponentState component_status = 4;
    uint64 checkpoint_ts = 5;
    State block_state = 6;
    int64 mode = 7;
}

message MaintainerCloseRequest {
    ChangefeedID changefeedID = 1;
    // true when remove changefeed, false when pause the changefeed.
    bool removed = 2;
}

message MaintainerCloseResponse {
    ChangefeedID changefeedID = 1;
    bool success = 2;
}

enum InfluenceType {
    All = 0;
    DB = 1;
    Normal = 2;
}

message InfluencedTables {
    InfluenceType InfluenceType = 1;
    // only exist when type is normal
    repeated int64 TableIDs = 2;
    // only exist when type is DB.
    int64 SchemaID = 3;
}

message Table {
    int64 TableID = 1;
    int64 SchemaID = 2;
    bool Splitable = 3;
}

message SchemaIDChange {
    int64 TableID = 1;
    int64 OldSchemaID = 2;
    int64 NewSchemaID = 3;
}

message State {
    bool IsBlocked = 1;
    uint64 BlockTs = 2;
    InfluencedTables BlockTables = 3;
    InfluencedTables NeedDroppedTables = 4;
    repeated Table NeedAddedTables = 5;
    repeated SchemaIDChange UpdatedSchemas = 6;
    bool IsSyncPoint = 7;
    BlockStage stage = 8;  // means whether the block is waiting / writing / done
}

message TableSpanBlockStatus {
    DispatcherID ID = 1;  // for which dispatcher
    State state = 2;
    int64 mode = 3;
}

message TableSpanStatus {
    DispatcherID ID = 1;  // for which dispatcher
    ComponentState component_status = 2;
    uint64 checkpoint_ts = 3;
    float event_size_per_second = 4;
    int64 mode = 5;
}

message BlockStatusRequest {
    ChangefeedID changefeedID = 1;
    repeated TableSpanBlockStatus blockStatuses = 2;
    int64 mode = 3;
}

enum ComponentState {
    Working = 0;
    Stopped = 1;
    Removed = 2;
    Initializing = 3;
    Preparing = 4;     // Preparing for merge, not ready for receive data from event collector
    MergeReady = 5;    // Ready for merge(upstream is finishing prepare data), but not begin send data to dispatcher.
    WaitingMerge = 6;  // the old dispatcher is waiting merge ready and then be closed
}

message RunningError {
    string time = 1;
    string node = 2;
    string code = 3;
    string message = 4;
}

message DispatcherID {
    uint64 high = 1;
    uint64 low = 2;
}

message ChangefeedID {
    uint64 high = 1;
    uint64 low = 2;
    string name = 3;
    string keyspace = 4;
}

message LogCoordinatorResolvedTsRequest {
    ChangefeedID changefeedID = 1;
}

message LogCoordinatorResolvedTsResponse {
    ChangefeedID changefeedID = 1;
    uint64 resolvedTs = 2;
}

enum ChecksumState {
    // UNINITIALIZED is the default value when the field is absent in proto3.
    // It must block checkpoint advancement until the checksum is explicitly verified as OK.
    UNINITIALIZED = 0;
    // MISMATCH means the checksum comparison failed. We need to block forwarding checkpointTs.
    MISMATCH = 1;
    // MATCH means the checksum comparison is successful. We can forward checkpointTs.
    MATCH = 2;
}

message ChecksumMeta {
    ChecksumState state = 1;
    // checksumStateSeq is a monotonic version for checksumState, 
    // used to discard out-of-order meta info.
    uint64 checksumStateSeq = 2;
}

message DispatcherSetChecksum {
    uint64 count = 1;
    uint64 xorHigh = 2;
    uint64 xorLow = 3;
    uint64 sumHigh = 4;
    uint64 sumLow = 5;
}

// DispatcherSetChecksumAckResponse is sent from dispatcherManager to maintainer
// to ack the set checksum request.
message DispatcherSetChecksumAckResponse {
    ChangefeedID changefeedID = 1;
    uint64 epoch = 2;
    int64 mode = 3;
    uint64 seq = 4;
}

// DispatcherSetChecksumUpdateRequest is sent from maintainer to dispatcherManager
// to update the set checksum.
message DispatcherSetChecksumUpdateRequest {
    ChangefeedID changefeedID = 1;
    uint64 epoch = 2;
    int64 mode = 3;
    uint64 seq = 4;
    DispatcherSetChecksum checksum = 5;
}