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

Class loading fails on an interrupted thread causing com.mongodb.event.ServerClosedEvent to fail to load when Mongo detects a cluster change #38611

Closed
manjian14 opened this issue Nov 30, 2023 · 29 comments
Assignees
Labels
type: regression A regression from a previous release
Milestone

Comments

@manjian14
Copy link

We've encountered a connectivity problem after migrating from Spring Boot 3.1.5/3.1.6 to 3.2.0. The error log points to a missing class:

java.lang.ClassNotFoundException: com.mongodb.event.ServerClosedEvent

This is affecting our connection to MongoDB "AWS DocumentDB." Any guidance or resolution on this matter would be greatly appreciated.

Opera Snapshot_2023-11-30_121747_us-east-1 console aws amazon com
Opera Snapshot_2023-11-30_121933_us-east-1 console aws amazon com

@manjian14 manjian14 changed the title Connectivity Issue with MongoDB "AWS DocumentDB" After Upgrading to Spring Boot 3.2.0 from 3.1.5 | 3.16 Connectivity Issue with MongoDB "AWS DocumentDB" After Upgrading to Spring Boot 3.2.0 from 3.1.5 | 3.1.6 Nov 30, 2023
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Nov 30, 2023
@mhalbritter
Copy link
Contributor

Hello!

I can't see any code from Boot in the stacktrace, besides the JAR loader. I'm not sure if this has something to do with the Uber JAR rewrite. Can you please try with the classic loader implementation and report back if that fixes the issue?

See here for details: https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.2-Release-Notes#nested-jar-support

@mhalbritter mhalbritter added the status: waiting-for-feedback We need additional information before we can continue label Nov 30, 2023
@manjian14
Copy link
Author

manjian14 commented Nov 30, 2023

The connectivity issue with MongoDB "AWS DocumentDB" appears resolved after implementing the classic loader in Spring Boot 3.2.0:

bootJar {
    archiveFileName = "${rootProject.name}.${archiveExtension.get()}"
    loaderImplementation = LoaderImplementation.CLASSIC
}

Any additional insights or recommendations moving forward would be appreciated.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Nov 30, 2023
@mhalbritter
Copy link
Contributor

cc @philwebb

@wilkinsona
Copy link
Member

wilkinsona commented Dec 5, 2023

@manjian14 The stack trace would suggest that the problem occurs when there's a change in the topology of the cluster but I haven't been able to reproduce it. Can you please provide a minimal sample and the steps to follow to cause the failure?

@wilkinsona wilkinsona added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Dec 5, 2023
@manjian14
Copy link
Author

manjian14 commented Dec 5, 2023

I've configured my application to connect to an AWS DocDB cluster using the standard driver configuration. Below is a snippet from my MongoDbConfiguration class:

@Slf4j
@Configuration
@EnableMongoRepositories(value = "com.????")
@ConditionalOnProperty(prefix = "mongo.connection", value = "string")
public class MongoDbConfiguration extends AbstractMongoClientConfiguration {

    @Value("${mongo.connection.string}")
    private String connectionString;

    @Override
    @Primary
    @Bean
    public @NonNull MongoClient mongoClient() {
        return MongoClients.create(connectionString);
    }

    @Override
    protected @NonNull String getDatabaseName() {
        return "???";
    }
}

In this setup, I've embedded the username and password within the cluster URL. The cluster has two replica sets, one for reading and one for writing. Additionally, the database is not accessible from outside; access is restricted either by whitelisted IP addresses or by project roles within the ECS cluster.

The AWS DocDB version is 5.0.0, the latest version as per AWS recommendations. The project is using Gradle version 8.5 and Java version 17. The base image is amazoncorretto:17-al2023-headful, and during the image build process, I've imported the SSL certificate using the keytool tool.

If you have any specific questions or if there's anything else you'd like to know, feel free to ask!

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Dec 5, 2023
@philwebb
Copy link
Member

philwebb commented Dec 5, 2023

@manjian14 Is there any chance you could switch back to the DEFAULT loader implementation and try 3.2.1-SNAPSHOT? We've fixed a few bugs with the nested loader. I'm doubtful that we've caught this one, but I'd like to check since it seems hard for us to replicate the problem locally.

@philwebb philwebb added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Dec 5, 2023
@manjian14
Copy link
Author

I tried using 3.2.1-SNAPSHOT, but ran into a problem—' springboot binaries are incompatible with your platform'

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Dec 6, 2023
@wilkinsona
Copy link
Member

What platform are you using? It's rather unfortunate that it apparently won't allow you to test snapshot artifacts.

@wilkinsona wilkinsona added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Dec 6, 2023
@msievers
Copy link

msievers commented Dec 6, 2023

Seeing the same error with our own MongoDB cluster(s) deployed to our own Kubernetes starting with Spring Boot 3.2.0.

java.lang.NoClassDefFoundError: com/mongodb/event/ServerClosedEvent

I'll give it a try with 3.2.1-SNAPSHOT.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Dec 6, 2023
@msievers
Copy link

msievers commented Dec 6, 2023

Same thing with 3.2.1-SNAPSHOT

java.lang.NoClassDefFoundError: com/mongodb/event/ServerClosedEvent
	at com.mongodb.internal.connection.DefaultServer.close(DefaultServer.java:172)
	at com.mongodb.internal.connection.AbstractMultiServerCluster.removeExtraHosts(AbstractMultiServerCluster.java:436)
	at com.mongodb.internal.connection.AbstractMultiServerCluster.ensureServers(AbstractMultiServerCluster.java:407)
	at com.mongodb.internal.connection.AbstractMultiServerCluster.handleReplicaSetMemberChanged(AbstractMultiServerCluster.java:248)
	at com.mongodb.internal.connection.AbstractMultiServerCluster.lambda$onChange$3(AbstractMultiServerCluster.java:197)
	at com.mongodb.internal.Locks.lambda$withInterruptibleLock$1(Locks.java:53)
	at com.mongodb.internal.Locks.checkedWithInterruptibleLock(Locks.java:66)
	at com.mongodb.internal.Locks.withInterruptibleLock(Locks.java:59)
	at com.mongodb.internal.Locks.withInterruptibleLock(Locks.java:52)
	at com.mongodb.internal.connection.BaseCluster.withLock(BaseCluster.java:226)
	at com.mongodb.internal.connection.AbstractMultiServerCluster.withLock(AbstractMultiServerCluster.java:54)
	at com.mongodb.internal.connection.AbstractMultiServerCluster.onChange(AbstractMultiServerCluster.java:165)
	at com.mongodb.internal.connection.DefaultSdamServerDescriptionManager.updateDescription(DefaultSdamServerDescriptionManager.java:113)
	at com.mongodb.internal.connection.DefaultSdamServerDescriptionManager.lambda$update$0(DefaultSdamServerDescriptionManager.java:75)
	at com.mongodb.internal.Locks.lambda$withInterruptibleLock$1(Locks.java:53)
	at com.mongodb.internal.Locks.checkedWithInterruptibleLock(Locks.java:66)
	at com.mongodb.internal.Locks.withInterruptibleLock(Locks.java:59)
	at com.mongodb.internal.Locks.withInterruptibleLock(Locks.java:52)
	at com.mongodb.internal.connection.BaseCluster.withLock(BaseCluster.java:226)
	at com.mongodb.internal.connection.AbstractMultiServerCluster.withLock(AbstractMultiServerCluster.java:54)
	at com.mongodb.internal.connection.DefaultSdamServerDescriptionManager.update(DefaultSdamServerDescriptionManager.java:60)
	at com.mongodb.internal.connection.DefaultServerMonitor$ServerMonitorRunnable.run(DefaultServerMonitor.java:169)
	at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.lang.ClassNotFoundException: com.mongodb.event.ServerClosedEvent
	at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:445)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:593)
	at org.springframework.boot.loader.net.protocol.jar.JarUrlClassLoader.loadClass(JarUrlClassLoader.java:104)
	at org.springframework.boot.loader.launch.LaunchedClassLoader.loadClass(LaunchedClassLoader.java:91)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
	... 23 more 

@msievers
Copy link

msievers commented Dec 6, 2023

The errors are gone when using <loaderImplementation>CLASSIC</loaderImplementation> with Spring Boot 3.2.0

@philwebb
Copy link
Member

philwebb commented Dec 6, 2023

@msievers Are you able to provide a sample application that we can debug?

@msievers
Copy link

msievers commented Dec 6, 2023

@philwebb I'll give it a try and ping you once I got it. As I cannot take one of our company apps where I see this happen, I hope it can be reproduced with some simple playground app.

@wilkinsona
Copy link
Member

As an alternative to switching back to the classic loader implementation, another workaround is to load the class up front:

@SpringBootApplication
public class Application {

    public static void main(String[] args) throws Exception {
        Class.forName("com.mongodb.event.ServerClosedEvent");
        SpringApplication.run(Application.class, args);
    }

}

@wilkinsona
Copy link
Member

This is another variant of #38154, but this time allowing the interruption to continue by rethrowing the ClosedByInterruptException is insufficient. We really need to perform the read in an uninterruptible manner. Unfortunately, the JDK's support for that is an implementation detail of sun.nio.ch.FileChannelImpl so we'll need to implement our own.

@wilkinsona
Copy link
Member

@wilkinsona wilkinsona changed the title Connectivity Issue with MongoDB "AWS DocumentDB" After Upgrading to Spring Boot 3.2.0 from 3.1.5 | 3.1.6 Class loading fails on an interrupted thread causing com.mongodb.event.ServerClosedEvent to fail to load when Mongo detects a cluster change Dec 7, 2023
@wilkinsona wilkinsona added type: regression A regression from a previous release and removed status: waiting-for-triage An issue we've not yet triaged status: feedback-provided Feedback has been provided labels Dec 7, 2023
@wilkinsona wilkinsona added this to the 3.2.x milestone Dec 7, 2023
@philwebb philwebb modified the milestones: 3.2.x, 3.2.1 Dec 7, 2023
@philwebb
Copy link
Member

philwebb commented Dec 7, 2023

We've just pushed a fix for this. It would be much appreciated if someone could try the latest 3.2.1 SNAPSHOT with the default loader to see if we've fixed the problem.

@msievers
Copy link

msievers commented Dec 7, 2023

@philwebb I'll give it a try and report back.

@msievers
Copy link

msievers commented Dec 7, 2023

@philwebb It's still failing with 3.2.1-SNAPSHOT. Was the last CI build completed successfully? It's orange, maybe the changes were not published/pushed, see

https://ci.spring.io/teams/spring-boot/pipelines/spring-boot-3.2.x/jobs/build/builds/802

@philwebb
Copy link
Member

philwebb commented Dec 7, 2023

Gahh, those failures are a pain. It wasn't published but I also didn't get a notification. I've kicked it off again and I'll comment back here when it's done. Sorry about that.

@philwebb
Copy link
Member

philwebb commented Dec 8, 2023

Sorry @msievers, we're having some trouble with our CI right now and we haven't managed to publish jars yet.

@wilkinsona
Copy link
Member

@msievers Our CI's been fixed and a new snapshot 3.2.1-SNAPSHOT build has been published. Could you please give it a try if you have the time?

@msievers
Copy link

@philwebb @wilkinsona I can confirm that for my two use cases, one being our internal application and the other the application I created on GitHub to reproduce the error, the issue seems to have disappeared with the use of the current Spring Boot 3.2.1-SNAPSHOT version.

@wilkinsona
Copy link
Member

Thank you, @msievers. That's some greatly appreciated good news.

ryanmax added a commit to Lane-Library/eresources-database that referenced this issue Dec 20, 2023
philwebb added a commit that referenced this issue Apr 16, 2024
Refine the fix for gh-38611 so that `ClosedByInterruptException` no
longer retries in a loop.

Our previous fix was flawed due to the fact that another interrupt
could occur after we clear the first and whilst we are reading data.
If this happens 10 times in a row, we raise an exception and end up
causing NoClassDefFoundError errors.

Our new approach retains the use of `FileChannel` and a direct buffer
up to the point that a `ClosedByInterruptException` is raised or the
thread is detected as interrupted.  At that point, we temporarily
switch to using a `RandomAccessFile` to access the data. This will
block the thread until the data has been read.

Fixes gh-40096
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: regression A regression from a previous release
Projects
None yet
Development

No branches or pull requests

7 participants