-
Notifications
You must be signed in to change notification settings - Fork 1.2k
ClusterConnectionProvider might cause TCP connection leak #2619
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
Comments
Actuator is part of Spring Boot, not Spring Data Redis. |
but core code is in spring-data-redis, main context in LettuceConnectionFactory$SharedConnection.getConnection |
1 similar comment
but core code is in spring-data-redis, main context in LettuceConnectionFactory$SharedConnection.getConnection |
sorry, my presentation is not exactly |
Let me review and evaluate this further. |
Closed by mistake. |
After further analysis, I am not convinced that this is necessarily a problem with Spring Data Redis. To state this case slightly differently, what would you expect to happen in this case (regarding this statement):
And specifically, when an Exception is thrown from I agree that the Typically, a connection (e.g. You could also argue that 1) Since a The logic inside Spring Boot Actuator's try (RedisConnection connection = RedisConnectionUtils.getConnection(this.redisConnectionFactory)) {
doHealthCheck(builder, connection);
}
finally {
RedisConnectionUtils.releaseConnection(connection, this.redisConnectionFactory);
} By establishing a However, unlike JDBC, and even unlike Jedis (though maybe more similar to Reactive), is that opening Although, in this particular case/context ( You can see that Lettuce Given the Lettuce driver is returning a " Something like the following, possibly(??): interface LettuceConnectionProvider {
default <T extends StatefulConnection<?, ?>> T getConnection(Class<T> connectionType) {
BiFunction<T, Throwable, T> handleExceptionOnConnectionOpen = (connection cause) -> {
try {
if (cause instanceof <WhatTypeOfRuntimeExceptionHere>) {
RedisConnectionUtils.releaseConnection(connection);
throw cause;
}
return connection;
}
catch (Throwable ignore) {
}
} ;
return LettuceFutureUtils.join(getConnectionAsync(connectionType)
.handleAsync(handleExceptionOnConnectionOpen));
}
} |
Other factors to consider include, but are not limited to:
Typically, a connection "Pool" is responsible for acquiring and releasing connections along with their associated resources (TCP sockets, etc), especially for "failed" connections, even on "open" no less. I'd almost argue that your Spring Boot (Data Redis) application should be failing-fast, particularly if the authentication criteria has not been properly specified and configured. Why should the application even be allowed to start much less allow users to access the Spring Boot Actuator Spring Data Redis does offer and allow (disabled by default) validation of Finally, and arguably, this could be something the (Lettuce) Redis driver(s) should be handling specifically, especially as it pertains to low-level resource (like TCP sockets), given drivers might do different things depending on the underlying OS, no less. |
In any case, I want to get @mp911de or @christophstrobl's thoughts and opinions on this before we proceed with any changes to the framework. |
Do you have an example exception @970655147 that causes connection leaks for you? |
Hello, reoccur like that
import com.hx.boot.context.SpringContext; @EnableAsync
}
|
@970655147 - First of all, you "ignored" the very thing (i.e. resulting "Exception") we asked you to provide (in detail) in the first place: // get connection
} catch (Exception e) {
// ignore
} Regarding:
What Exception? By ignoring the Exception, you are implying any Exception, and it does not matter. But, it does! It does because it matters what happens during the "process" of opening the connection. An Exception resulting from a username/password auth error is different from an Exception occurring because an incorrect IP address or port to the server was configured/resolved is different from an Exception thrown because of a failed TLS handshake (verification) during SSL/TLS identification & authentication (as described by IBM, for instance) is different from other TCP errors, and so on. In the later case, as with username/password-based auth, a TCP connection is still established and therefore system resources will be allocated and used to complete the steps of the TLS handshake/verification (over SSL/TLS). After all, Secure Sockets, or a "secure" Socket, like This is also OS/system dependent. As such, this why (TCP) Sockets have configuration (such as SO_LINGER, among other Secondarily, this code: LettuceConnectionFactory factory = SpringContext.getBean(LettuceConnectionFactory.class);
for (int i = 0; i < 100; i++) {
try {
ReactiveRedisConnection connection = factory.getReactiveConnection();
...
..
. Does not necessarily constitute a connection leak simply because an Exception might be thrown during an attempt to acquire a connection. You are simply iterating and opening connections without proper connection "handling". And, if it were unbounded, your problem would only exasberate the "perceived" problem. Most file-based system resources (TCP Socket connections are no exception; see here), must be properly handled inside a block, such as the following (using your example): for (int i = 0; i < 100; i++) {
try (ReactiveRedisConnection connection = factory.getReactiveConnection()) {
}
catch (Exception cause) {
// handle the exceptional condition appropriately; such as releasing/closing related resources and
} Just because you attempt to establish a connection and 1) an Exception is thrown or 2) the block of code opening/using the connection falls out of scope, does not mean the framework, or any other facility for that matter, will automatically (or automagically) "close" the connection. And, even if "close" is called, it does not mean system resources will be immediately released (again, it depends on the Socket configuration using a factory when the Socket is created/opened). It is always the user's responsibility to properly handle the connection, whether you are opening a Redis connection (regardless of type, e.g. Reactive), a (SQL) database connection (e.g. with JDBC), or simply opening a file. For example: try (FileReader fileReader = new FileReader(file)) {
// do something with the "open" file
} If not properly handled, there is no guarantee that systems resources (even in the file case) will be properly released. Again, mileage varies based on OS/system, among other factors. Also see this SO post as further evidence to this case. |
1 last thing... This is also why it is important to use some connection management facility, such as "pooling", which Spring Data Redis supports for both the Jedis and Lettuce drivers (see here). In addition, the closer you get to system controlled resources, like TCP connections, the more it matters what type of driver you are using, some of which can be system dependent, even. It is also why certain drivers perform better or behave slightly differently on different platforms, especially given no 2 things (like OSes) are equal in this regard. This could also arguably be a driver responsibility and concern more than a framework-level concern. Frameworks like Spring Data Redis are not in the business of managing connections, rather, only acquiring and using them for some higher purpose, like the data access patterns for querying, etc. And, in this case, it is allowing your application code to acquire a connection "directly" (I might add) for some low-level purpose, and as such, your application code becomes responsible for that resource. It would be different if the framework were acquiring/opening a connection on your behalf, such as would be the case in |
following code based on spring-data-redis 2.0.5-RELEASE 1. What Exception?"Do you have an example exception", I have a wrong reading, I make sense of "example case"
2. with out exception handling on example casemy presentation is not imprecisely 3. my first idea to proposal to spring-data-redisthe call stack trace in this case is like following
if ex "Unable to connect to Redis" thrown from LettuceConnectionFactory$SharedConnection.getNativeConnection, so this.connection is always null
4. lettuce's RedisClusterClient.loadPartitions processingI was wondering following processing handle loadPartitions:787, RedisClusterClient (io.lettuce.core.cluster)
but I'm not sure, I'm not experienced, the "NioSocketChannel" is only increase without decrease
|
Regarding #3 above, and specifically, this statement:
"What" will connect to redis-server over and over again? There is nothing in the call stack to which your refer that repeatedly (i.e. loops, and) attempts to establish a connection until (implied) successful (e.g. non-null). Using Spring Data Redis
caused by: ... If you are using "pooling" (highly recommended in this case), then the only difference would be an additional call between 1383 and 53, on line 97 in the
In any case, this is very much dependent on the caller. In your specific case, that would be the Spring Boot Spring Data Redis cannot possibly know all the ways in which callers will attempt to acquire and use connections. Again, outside of You have 1 of 3 options, here:
I strongly encourage you to consider |
Regarding #4: I gather, the assessment of the Redis partitions distributed across the clutter is triggered by Spring Data Redis, here, and specifically. Again, this is very much Redis driver based (Lettuce, in this instance), and largely outside the control of Spring Data Redis. Also, the Lettuce driver code does not strike me as incorrect. It properly wraps the connection in a |
this is a project using spring-data-redis, spring-boot-actuator
there are 2 case, might reoccur that
config address that isn't redis server, follow try a mysql services,
then visit "http://localhost:8080/actuator/health", there leak 6 tcp connection each visit
spring.redis.cluster.nodes: 192.168.220.133:3306,192.168.220.133:3307,192.168.220.133:3308,192.168.220.133:3309,192.168.220.133:3310,192.168.220.133:3311
config redis server with password, but didn't config "spring.redis.password"
then visit "http://localhost:8080/actuator/health", there leak 6 tcp connection each visit
spring.redis.cluster.nodes: 192.168.220.133:7001,192.168.220.133:7002,192.168.220.133:7003,192.168.220.133:7004,192.168.220.133:7005,192.168.220.133:7006
use "ll /proc/$pid/fd | grep socket" to watch connections
The text was updated successfully, but these errors were encountered: