Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions crates/engram-host-agent/src/grpc_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ use engram_protocol::grpc::{
SendHarnessToolResultRequest, SnapshotBeginResponse, SnapshotResponse, StartAgentRequest,
UnbindHarnessSessionRequest,
};
use engram_protocol::grpc_client::link_remote_parent;
use engram_protocol::grpc_client::{link_remote_parent, HOST_RPC_MAX_MESSAGE_BYTES};
use engram_protocol::wire::WireExecRequest;
use futures::Stream;
use std::pin::Pin;
Expand Down Expand Up @@ -191,7 +191,11 @@ pub async fn serve(
.initial_stream_window_size(Some(16 * 1024 * 1024))
.initial_connection_window_size(Some(32 * 1024 * 1024))
.http2_adaptive_window(Some(true))
.add_service(HostServiceServer::new(svc).max_encoding_message_size(32 * 1024 * 1024))
.add_service(
HostServiceServer::new(svc)
.max_encoding_message_size(HOST_RPC_MAX_MESSAGE_BYTES)
.max_decoding_message_size(HOST_RPC_MAX_MESSAGE_BYTES),
)
.serve_with_incoming(incoming)
.await
}
Expand Down
23 changes: 21 additions & 2 deletions crates/engram-host-agent/tests/grpc_pause_resume.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use parking_lot::Mutex;
#[derive(Default)]
struct FakeHost {
calls: Mutex<Vec<(&'static str, SandboxId)>>,
prompt_bytes: Mutex<Vec<usize>>,
}

#[async_trait]
Expand Down Expand Up @@ -100,10 +101,11 @@ impl HostClient for FakeHost {
&self,
_: SandboxId,
_: String,
_: String,
text: String,
_: Option<String>,
) -> Result<(), SandboxError> {
unreachable!()
self.prompt_bytes.lock().push(text.len());
Ok(())
}
async fn proxy_shell(&self, _: SandboxId) -> Result<ShellTunnel, SandboxError> {
unreachable!()
Expand Down Expand Up @@ -182,3 +184,20 @@ async fn pause_then_resume_round_trip_to_the_host_with_the_right_sandbox() {
"pause/resume did not round-trip in order with the right sandbox id",
);
}

/// A move's restore request carries the VM's memory manifest and is larger
/// than tonic's 4 MiB default decode limit. The host accepts it.
#[tokio::test]
async fn the_host_accepts_a_request_above_the_tonic_default_limit() {
let host = Arc::new(FakeHost::default());
let addr = boot_grpc_server(host.clone()).await;
let client = connect_grpc_client(addr).await;

let text = "x".repeat(6 * 1024 * 1024);
client
.send_prompt(SandboxId::new(), "p1".into(), text.clone(), None)
.await
.expect("a 6 MiB request reaches the host");

assert_eq!(*host.prompt_bytes.lock(), vec![text.len()]);
}
15 changes: 9 additions & 6 deletions crates/engram-protocol/src/grpc_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,13 @@ fn restore_rpc_timeout() -> Duration {
Duration::from_secs(secs)
}

/// The largest `HostService` message either side decodes or encodes.
/// tonic's 4 MiB default is below a move's restore request, which carries
/// the memory manifest of the VM (about 6 MB for 32 GiB of guest RAM),
/// and below a peer chunk frame. The cap is a limit check; it allocates
/// nothing.
pub const HOST_RPC_MAX_MESSAGE_BYTES: usize = 32 * 1024 * 1024;

/// Coord-side client wrapping a `tonic::transport::Channel` to one
/// host. Cheap to clone (tonic clients hold an `Arc<Channel>`
/// internally); one instance per host is the steady-state shape.
Expand All @@ -159,15 +166,11 @@ impl GrpcHostClient {
/// Every RPC carries the caller's `traceparent` via
/// [`TraceparentInjector`] (ADR 0019 distributed tracing).
///
/// The inbound decode cap is 32 MiB on every client (was an opt-in
/// for `ReapMaterializeDir`'s 16 MiB reply; ADR 0095's
/// `PeerChunkGet` frames — 4 MiB data + overhead — need past
/// tonic's 4 MiB default too, and the cap is just a limit check,
/// no allocation change).
/// The client decodes replies up to [`HOST_RPC_MAX_MESSAGE_BYTES`].
pub fn new(channel: Channel) -> Self {
Self {
inner: HostServiceClient::with_interceptor(channel, TraceparentInjector)
.max_decoding_message_size(32 * 1024 * 1024),
.max_decoding_message_size(HOST_RPC_MAX_MESSAGE_BYTES),
}
}

Expand Down
Loading