Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more information about http2 connection #1276

Closed
polRk opened this issue Oct 15, 2024 · 14 comments
Closed

Add more information about http2 connection #1276

polRk opened this issue Oct 15, 2024 · 14 comments
Labels
enhancement New feature or request

Comments

@polRk
Copy link

polRk commented Oct 15, 2024

Is your feature request related to a problem? Please describe.

CONTEXT

Within the YDB SDK, client-side load balancing is used. We send a single request to any database node, and it returns an array of available endpoints. Our task is to maintain connections to these endpoints as needed: close connections that we no longer need to use, create new connections to those that are missing, balance requests across these connections, and have the ability to send a request to a specific connection.

PROBLEM

Currently, it's not very clear how to properly close a connection (graceful shutdown), how to obtain the status of a connection, and how to receive notifications when a connection has been closed.

ONLY FOR HTTP2

Describe the solution you'd like

Extends Transport with EventEmitter, emit information about transport status changes, add current status of connection.

Additional context
https://ydb.tech/docs/en/recipes/ydb-sdk/balancing
https://blog.ydb.tech/client-side-balancing-in-ydb-89677b34fd5b

@polRk polRk added the enhancement New feature or request label Oct 15, 2024
@srikrsna-buf
Copy link
Member

srikrsna-buf commented Nov 5, 2024

For H/2 you can use the nodeOptions transport option to override createConnection callback to get a handle on the connection and inspect the status. If you want greater control over the connections you can use the sessionManager option to provide a NodeHttp2ClientSessionManager type to manage the connection.

export type NodeHttp2TransportOptions = {
/**
* A manager for the HTTP/2 connection of the transport.
*
* Providing this option makes nodeOptions as well as the HTTP/2 session
* options (pingIntervalMs et cetera) ineffective.
*/
sessionManager?: NodeHttp2ClientSessionManager;
/**
* Options passed to the connect() call of the Node.js built-in
* http2 module.
*/
nodeOptions?: http2.ClientSessionOptions | http2.SecureClientSessionOptions;
} & Http2SessionOptions;

@polRk
Copy link
Author

polRk commented Nov 12, 2024

Is connect-es provides default NodeHttp2ClientSessionManager ?

@timostamm
Copy link
Member

Yes, see the export Http2SessionManager.

@polRk
Copy link
Author

polRk commented Nov 13, 2024

How to gracefully shutdown htt2 connection (for example, server send me information do not use this endpoint) ? Maybe will be good implement disposable in transport?

I need HTTP2 connection management tools to help me balance my clients. I open 20 connections at once and keep them updated if the server sends me information about new endpoints or if any of the endpoints become unavailable. I need to be able to safely close the connection and create new ones.

For now, I can only createTransport, but I don't know how to gracefully shutdown them.

@polRk
Copy link
Author

polRk commented Nov 13, 2024

It may be that the endpoint is available now, and after 5 minutes it is unavailable, and then it is available again. I need to effectively manage the current connections, understand in advance that the connection should not be used due to an error or loss of connection.

@srikrsna-buf
Copy link
Member

It may be that the endpoint is available now, and after 5 minutes it is unavailable, and then it is available again.

The default session manager manages the connections according to HTTP/2 semantics (like goaway) and has settings to tweak ping/idle timeouts. You can look at the source code here. If the existing options don't cover your use case you can tweak it to do as you need.

@polRk
Copy link
Author

polRk commented Nov 13, 2024

@srikrsna-buf I have a clear belief that Transport needs graceful shutdown (for example, Symbol.disposable), what do you think?

@srikrsna-buf
Copy link
Member

I have a clear belief that Transport needs graceful shutdown (for example, Symbol.disposable), what do you think?

Transport doesn't retain any connections. It is not even aware of connections. It just calls an abstract http function similar to fetch. Can you explain a bit more why you think this is needed?

@polRk
Copy link
Author

polRk commented Nov 21, 2024

Can you explain a bit more why you think this is needed?

I see problems with memory leaks and connections here if the connection is often opened and closed (it is necessary to close connections in order to reduce the load on the third service with which these connections are established)

@timostamm
Copy link
Member

We will not emit events from Transport, and we will not make it disposable. It's a purposefully simple interface, and these features don't translate well to different HTTP versions, and will complicate implementations for different network stacks.

The abstractions here are Transport and UniversalClientFn. The Node.js client uses a NodeHttp2ClientSessionManager for HTTP /2. This is where you can hook in to manage HTTP/2 connections.

If you want to close all connections,

  • create a Http2SessionManager, pass it to the transport, and hold a reference
  • call abort to close all connections.

If you want to do it gracefully,

  • wait for all streams in your application to finish.
  • poll Http2SessionManager's state until it's idle before calling abort.

If you found a memory leak doing this, we'd appreciate a reproducible example, and will investigate it.

Symbol.dispose / disposeAsync could implement the above, but it's just sugar (that we currently cannot support with our minimum compiler versions).

It looks like the task from your issue description can be implemented by

  • create a transport for every endpoint
  • set a low idleConnectionTimeoutMs
  • use round robin to balance requests across the transports in your application code.

If you need more control, for example to implement DNS-based load balancing or connection pooling, you can implement it as a NodeHttp2ClientSessionManager. It's not trivial because the Node.js http2 module isn't easy to work with.

@polRk
Copy link
Author

polRk commented Nov 22, 2024

Just simple error case with memory leak:

let app = express()

app.use("/", (req, res) => {
  let data = await createClient(new Transport(), ClientSchema).callRPCMethod()

  res.send(JSON.strigify(data))
})

@timostamm
Copy link
Member

The transport will maintain the idle connection (for 15 minutes by default), so this will grow the number of connections (and memory use) quickly.

Instead, create the transport once and re-use it:

let app = express()

const transport = new createGrpcTransport();

app.use("/", (req, res) => {
  let data = await createClient(transport, ClientSchema).callRPCMethod()

  res.send(JSON.strigify(data))
})

(You'd keep references to multiple transports here if you want to go with balancing as outlined in #1276 (comment))

We don't have documentation besides the JSDoc comments in the source yet. We're tracking this in connectrpc/connectrpc.com#41

@polRk
Copy link
Author

polRk commented Dec 2, 2024

@timostamm Implementing methods for graceful connection shutdown is important for several reasons:

Resource Leaks: If connections are not closed properly, it can lead to resource leaks such as memory and file descriptor leaks. This can cause performance and stability issues in the application.

Security: Open connections can be vulnerable to attacks such as data interception or unauthorized access. Closing connections helps minimize these risks.

System Stability: Improper connection management can lead to exhaustion of available resources, causing system or application crashes.

Data Consistency: When working with databases, proper connection closure ensures that all transactions are completed and data remains consistent.

Performance: Open connections can consume significant resources, slowing down the system. Closing unused connections frees up these resources for other tasks.

I really want this to be done at the connect level because each consumer will have to write a lot of code and deal with a very complex Http2SessionManager.

@chrispine
Copy link
Contributor

Thanks for raising these concerns! However, we have no plans to work on this in the near term.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants