Skip to content

Cleanup all resources in RedisHealthIndicator #36304

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

Closed
jxblum opened this issue Jul 10, 2023 · 4 comments
Closed

Cleanup all resources in RedisHealthIndicator #36304

jxblum opened this issue Jul 10, 2023 · 4 comments
Labels
status: waiting-for-internal-feedback An issue that needs input from a member or another Spring Team

Comments

@jxblum
Copy link
Contributor

jxblum commented Jul 10, 2023

This block of code in the Spring Boot Actuator, RedisHealthIndicator should be restructured as:

try (RedisConnection connection = RedisConnectionUtils.getConnection(this.redisConnectionFactory)) {
    doHealthCheck(builder, connection);
}
finally {
    RedisConnectionUtils.releaseConnection(connection, this.redisConnectionFactory);
}

Alternatively, this could be structured as:

RedisConnection connection = null;
try {
    connection = RedisConnectionUtils.getConnection(this.redisConnectionFactory);
    doHealthCheck(builder, connection);
}
finally {
    RedisConnectionUtils.releaseConnection(connection, this.redisConnectionFactory);
}

If any kind of RuntimeException is thrown from the RedisConnectionFactory.getConnection() method (indirectly via the Spring Data Redis RedisConnectionUtils.getConnection(:RedisConnectionFactory) method, here then here), then there is a possibility that system resources may not get properly cleaned up (for instance, a Socket is still allocated, even connected, but fails during a TLS handshake, or perhaps during username/password auth).

A reference to a connection could be returned on line 49, but won't be released properly if the call throws an exception as it exists outside the try-finally block.

All RedisConnection(s) are AutoCloseable (Javadoc).

This was uncovered during the analysis of Spring Data Redis Issue #2619.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jul 10, 2023
@jxblum
Copy link
Contributor Author

jxblum commented Jul 10, 2023

To clarify, I don't thing Spring Boot (Actuator, and the RedisHealthIndicator in particular) is causing the user's perceived TCP connection leak in this case, nor is Spring Data Redis. But, after reviewing the code in both Spring Data Redis and Spring Boot, I do think this is an opportunity for a small improvement in RedisHealthIndicator.

@jxblum jxblum changed the title Cleanup all resources in Redis HealthIndicator Cleanup all resources in RedisHealthIndicator Jul 10, 2023
@wilkinsona
Copy link
Member

Thanks, John, but I'm not sure I understand the problem. If an exception is thrown from RedisConnectionUtils#getConnection there will be no RedisConnection available for us to close. I think the second option you described above shows this most clearly:

RedisConnection connection = null;
try {
    connection = RedisConnectionUtils.getConnection(this.redisConnectionFactory);
    doHealthCheck(builder, connection);
}
finally {
    RedisConnectionUtils.releaseConnection(connection, this.redisConnectionFactory);
}

If RedisConnectionUtils.getConnection(this.redisConnectionFactory) throws an exception, connection will remain null. The finally block with then call RedisConnectionUtils.releaseConnection(null, this.redisConnectionFactory);. That won't fail but it also won't do anything as releaseConnection returns immediately when conn is null:

public static void releaseConnection(@Nullable RedisConnection conn, RedisConnectionFactory factory) {
    if (conn == null) {
        return;
    }

Have I overlooked something? Can you please clarify if I have.

@wilkinsona wilkinsona added the status: waiting-for-internal-feedback An issue that needs input from a member or another Spring Team label Jul 11, 2023
@jxblum
Copy link
Contributor Author

jxblum commented Jul 12, 2023

Hi Andy-

Sorry. On second thought, I think you are correct.

I have been bouncing back and forth between the user's problem (TCP connection leak) in the SD Redis ticket I referenced and what Spring Boot Actuator was doing inside the RedisHealthIndicator (in combination w/ SD Redis) that might possibly be causing this to happen. I think I am beginning to overthink it now.

When I saw the connection acquisition outside the try-finally block I thought, uh-oh! But, there is actually no way for the resource to be leaked by the framework in this case unless the driver itself is not properly releasing system resources, when some Exception is thrown.

This could be the case during a failed TLS handshake, or even simple username/password auth, since the TCP Socket would still be opened and established to do the handshake, or auth. Even in this case, a certain amount of responsibility still falls on users to manage the system resources properly for their context and UC/Reqs (such as, configuring SO_LINGER, SO_REUSEADDR, SO_TIMEOUT, etc).

For clarification (and quick sanity check), there is no repeated process (e.g. Thread) inside Boot to run any of the HealthIndicators in an automatic fashion is there? There isn't AFAIA, not unless the user coded up some automatic check themselves, or are maybe using a monitoring application/tool. Otherwise, it is all upon request by hitting the URL endpoint, correct?

So, outside of some syntactic sugar (and minimizing the scope of the connection variable), RedisHealthIndicator is probably fine as is.

NOTE: FYI, beyond opening and closing connections, RedisConnectionUtils additionally synchronizes with any configured transaction, which might add a bit of unnecessary overhead for such a simple health assessment (check), given, and I assume, there isn't a need for a transaction in this case.

@wilkinsona
Copy link
Member

Otherwise, it is all upon request by hitting the URL endpoint, correct?

Correct.

@wilkinsona wilkinsona closed this as not planned Won't fix, can't repro, duplicate, stale Jul 12, 2023
@wilkinsona wilkinsona removed the status: waiting-for-triage An issue we've not yet triaged label Aug 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: waiting-for-internal-feedback An issue that needs input from a member or another Spring Team
Projects
None yet
Development

No branches or pull requests

3 participants