syntax = "proto3";

// Cross-node shuffle transport. Mirrors the in-process `ShuffleMessage`
// enum (`crates/laminar-core/src/shuffle/message.rs`) so the Tonic
// migration is a direct frame-for-frame translation of the existing
// hand-rolled TCP codec.
//
// Code is generated by tonic-build (see this crate's build.rs).
package laminar.shuffle.v1;

// One sender pushes a forward-only stream of frames to one downstream
// peer. Client-streaming fits the existing fire-and-forget model: the
// outbound side never acts on per-frame replies, so the server returns a
// single summary once the client half-closes. Backpressure is the
// gRPC/HTTP2 flow-control window plus the consumer's bounded queue.
service ShuffleTransport {
  rpc Shuffle(stream ShuffleFrame) returns (ShuffleSummary);
}

// One frame on a shuffle stream. The oneof variants are 1:1 with the
// `ShuffleMessage` enum. `Hello` is expected first so the receiver can
// attribute every subsequent frame to its sending peer.
message ShuffleFrame {
  oneof kind {
    Hello hello = 1;
    VnodeData vnode_data = 2;
    Barrier barrier = 3;
    Close close = 4;
  }
}

// Connection handshake: the sender identifying itself.
message Hello {
  uint64 node_id = 1;
}

// A batch of rows the sender has already classified as belonging to a
// single `vnode`, tagged with the logical `stage` (operator / MV name)
// so a receiver shared by several sharded operators can demux frames to
// the right one without re-hashing.
message VnodeData {
  string stage = 1;
  uint32 vnode = 2;
  // Arrow IPC stream encoding of one RecordBatch (schema + batch),
  // produced by `serialization::serialize_batch_stream`. Kept as an
  // opaque IPC blob — not re-modelled in protobuf — so a schema roll on
  // one frame cannot poison the stream.
  bytes arrow_ipc = 3;
}

// A Chandy-Lamport checkpoint barrier flowing in-band with the data,
// mirroring `CheckpointBarrier`.
message Barrier {
  uint64 checkpoint_id = 1;
  uint64 epoch = 2;
  uint64 flags = 3;
}

// Graceful-close signal with a short human-readable reason.
message Close {
  string reason = 1;
}

// Returned once when the client half-closes the stream.
message ShuffleSummary {
  uint64 frames_received = 1;
}
