IJPL-159270 IJent: don't send huge chunks over gRPC

* There's a hard limit for an *incoming* message in gRPC-Java. The new code doesn't send requests that lead to huge responses.
* Requests for huge data chunks lead to bigger memory consumption on the remote side.

GitOrigin-RevId: 376b2c20a1ea3a7ce65527ba4b7d979f9bc245af
This commit is contained in:
Vladimir Lagunov
2024-07-31 15:08:13 +02:00
committed by intellij-monorepo-bot
parent 9278a9eafa
commit d18bb6ac3a
3 changed files with 43 additions and 1 deletions

View File

@@ -28,6 +28,9 @@ sealed interface IjentTunnelsApi {
* The connection exists as a pair of channels [Connection.channelToServer] and [Connection.channelFromServer],
* which allow communicating to a remote server from the IDE side.
*
* Packets sent to the channel and received from the channel may be split and/or concatenated.
* The packets may be split only if their size exceeds [com.intellij.platform.ijent.spi.RECOMMENDED_MAX_PACKET_SIZE].
*
* If the connection gets closed from the server, then the channels also get closed in the sense of [SendChannel.close].
*
* If an exception happens during sending, then [Connection.channelFromServer] gets closed exceptionally with [RemoteNetworkException].
@@ -178,9 +181,12 @@ operator fun Connection.component2(): ReceiveChannel<ByteBuffer> = channelFromSe
interface IjentTunnelsPosixApi : IjentTunnelsApi {
/**
* Creates a remote UNIX socket forwarding, i.e. IJent listens waits for a connection on the remote machine, and when the connection
* Creates a remote UNIX socket forwarding. IJent listens for a connection on the remote machine, and when the connection
* is accepted, the IDE communicates to the remote client via a pair of Kotlin channels.
*
* Packets sent to the channel and received from the channel may be split and/or concatenated.
* The packets may be split only if their size exceeds [com.intellij.platform.ijent.spi.RECOMMENDED_MAX_PACKET_SIZE].
*
* The call accepts only one connection. If multiple connections should be accepted, the function is supposed to be called in a loop:
* ```kotlin
* val ijent: IjentApi = ijentApiFactory()

View File

@@ -268,6 +268,8 @@ sealed interface IjentOpenedFile {
* Note, that [ReadResult.Bytes] can be `0` if [buf] cannot accept new data.
*
* This operation modifies the file's cursor, i.e. [tell] may show different results before and after this function is invoked.
*
* It reads not more than [com.intellij.platform.ijent.spi.RECOMMENDED_MAX_PACKET_SIZE].
*/
suspend fun read(buf: ByteBuffer): IjentFsResult<ReadResult, ReadError>
@@ -275,6 +277,8 @@ sealed interface IjentOpenedFile {
* Reads data from the position [offset] of the file.
*
* This operation does not modify the file's cursor, i.e. [tell] will show the same result before and after this function is invoked.
*
* It reads not more than [com.intellij.platform.ijent.spi.RECOMMENDED_MAX_PACKET_SIZE].
*/
suspend fun read(buf: ByteBuffer, offset: Long) : IjentFsResult<ReadResult, ReadError>
@@ -293,10 +297,20 @@ sealed interface IjentOpenedFile {
}
interface Writer : IjentOpenedFile {
/**
* TODO Document
*
* It writes not more than [com.intellij.platform.ijent.spi.RECOMMENDED_MAX_PACKET_SIZE].
*/
suspend fun write(buf: ByteBuffer): IjentFsResult<
Int,
WriteError>
/**
* TODO Document
*
* It writes not more than [com.intellij.platform.ijent.spi.RECOMMENDED_MAX_PACKET_SIZE].
*/
suspend fun write(buf: ByteBuffer, pos: Long): IjentFsResult<
Int,
WriteError>

View File

@@ -0,0 +1,22 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.platform.ijent.spi
/**
* A soft limit for a single message size. It can be applied to tunnels, file reading and writing, etc.
*
* The API implementation should apply this limit itself, so API users may know nothing about this constant.
* Moreover, API users are not supposed to fragment packets with this constant, because the API implementation would do that again.
*
* It's a good practice to use this limit for sending data chunks to IJent,
* because the other side of communication may be a machine with little RAM,
* and creating hundreds of megabytes of packets can be undesirable.
*
* Also, it's known that the gRPC implementation has a hard limit for _incoming_ messages, so this constant should be used in
* requests to mitigate huge responses.
*
* It's acceptable if the message exceeds this limit for a few kilobytes.
*
* 131072 is 2^17 -- this number is chosen with no research, just a wild guess.
* It may be changed if necessary, but it must not be close to the timeout from `io.grpc.internal.MessageDeframer.processHeader`
*/
const val RECOMMENDED_MAX_PACKET_SIZE = 131_072