Reputation: 1903
I use test container for integration testing. However, when used in ReplicaSet 3 nodes, the tests fail with an error
DEBUG 18112 --- [ Test worker] org.mongodb.driver.connection : Connection pool created for mongod3:27019 using options maxIdleTimeMS=0, minPoolSize=5, maxPoolSize=100, maxConnecting=2, waitQueueTimeoutMS=120000 2024-10-27T19:50:25.790+03:00 DEBUG 18112 --- [ Test worker] org.mongodb.driver.cluster : Updating cluster description to {type=REPLICA_SET, servers=[{address=mongod1:27017, type=UNKNOWN, state=CONNECTING}, {address=mongod2:27018, type=UNKNOWN, state=CONNECTING}, {address=mongod3:27019, type=UNKNOWN, state=CONNECTING}]
: ..... created with settings MongoClientSettings{readPreference=ReadPreference{name=primaryPreferred, hedgeOptions=null}, writeConcern=WriteConcern{w=majority, wTimeout=null ms, journal=null}, retryWrites=true, retryReads=true, readConcern=ReadConcern{level=null}, credential=MongoCredential{mechanism=null, userName='root', source='admin', password=, mechanismProperties=}, transportSettings=null, commandListeners=[], codecRegistry=ProvidersCodecRegistry{codecProviders=[ValueCodecProvider{}, BsonValueCodecProvider{}, DBRefCodecProvider{}, DBObjectCodecProvider{}, DocumentCodecProvider{}, CollectionCodecProvider{}, IterableCodecProvider{}, MapCodecProvider{}, GeoJsonCodecProvider{}, GridFSFileCodecProvider{}, Jsr310CodecProvider{}, JsonObjectCodecProvider{}, BsonCodecProvider{}, EnumCodecProvider{}, com.mongodb.client.model.mql.ExpressionCodecProvider@4bfe83d, com.mongodb.Jep395RecordCodecProvider@5906ebfb, com.mongodb.KotlinCodecProvider@10fc1a22]}, loggerSettings=LoggerSettings{maxDocumentLength=1000}, clusterSettings={hosts=[mongod1:27017, mongod2:27018, mongod3:27019], srvServiceName=mongodb, mode=MULTIPLE, requiredClusterType=REPLICA_SET, requiredReplicaSetName='rs0', serverSelector='null', clusterListeners='[]', serverSelectionTimeout='30000 ms', localThreshold='15 ms'}, socketSettings=SocketSettings{connectTimeoutMS=10000, readTimeoutMS=0, receiveBufferSize=0, proxySettings=ProxySettings{host=null, port=null, username=null, password=null}}, heartbeatSocketSettings=SocketSettings{connectTimeoutMS=10000, readTimeoutMS=10000, receiveBufferSize=0, proxySettings=ProxySettings{host=null, port=null, username=null, password=null}}, connectionPoolSettings=ConnectionPoolSettings{maxSize=100, minSize=5, maxWaitTimeMS=120000, maxConnectionLifeTimeMS=0, maxConnectionIdleTimeMS=0, maintenanceInitialDelayMS=0, maintenanceFrequencyMS=60000, connectionPoolListeners=[], maxConnecting=2}, serverSettings=ServerSettings{heartbeatFrequencyMS=10000, minHeartbeatFrequencyMS=500, serverListeners='[]', serverMonitorListeners='[]'}, sslSettings=SslSettings{enabled=false, invalidHostNameAllowed=false, context=null}, applicationName='null', compressorList=[], uuidRepresentation=UNSPECIFIED, serverApi=null, autoEncryptionSettings=null, dnsClient=null, inetAddressResolver=null, contextProvider=null}
: Exception in monitor thread while connecting to server mongod3:27019
com.mongodb.MongoSocketOpenException: Exception opening socket
I have run an integration test and it seems that there is an attempt to initialize the connection data specified in application.yml and application-test.yml is ignored.
Testcontainers creates a mongo in which there is only one node, but this does not work.
….
@Transactional(readOnly = true)
public Flux<AccountDto> findAll(int page, int size) {
Pageable pageable = PageRequest.of(page, size);
return accountRepository.findAllByIdNotNullOrderByIdAsc(pageable)
.map(this::mapToDto);
}
….
@ActiveProfiles("test")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = DemoApplication.class)
@Testcontainers
@AutoConfigureWebTestClient
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class AccountControllerIntegrationTest {
@Autowired
private WebTestClient webTestClient;
@Autowired
private AccountRepository accountRepository;
@Container
static MongoDBContainer mongoDBContainer = new MongoDBContainer("mongo:4.4.2")
.withCommand("--replSet", "rs0")
.withExposedPorts(27017);
static{
mongoDBContainer.start();
}
@DynamicPropertySource
static void setProperties(DynamicPropertyRegistry registry) {
String replicaSetUrl = mongoDBContainer.getReplicaSetUrl();
System.out.println("MongoDB URL: " + replicaSetUrl);
String modifiedUrl = replicaSetUrl + "&readPreference=primary";
registry.add("spring.data.mongodb.uri", () -> modifiedUrl);
}
@BeforeEach
void setUp() {
accountRepository.deleteAll().block();
}
…..
```java
@Primary
@Validated
@Data
@ConfigurationProperties(prefix = "mongodb.uri", ignoreUnknownFields = false)
public class MongoCustomProperties {
private SetMongoProperties set = new SetMongoProperties();
@Data
public static class SetMongoProperties {
String prefix = "mongodb://";
private String hostFirst = "localhost";
private String hostSecond = "localhost";
private String hostThird = "localhost";
private int portFirstInstance = 27017;
private int portSecondInstance = 27018;
private int portThirdInstance = 27019;
private String database = "accounts";
private String username = "root";
private String password = "root";
private String authenticationDatabase = "admin";
private String replicaSetName = "replicaSet";
private int maxPoolSize = 100;
private int minPoolSize = 5;
private boolean retryWrites = true;
private String writeAcknowledgement = "majority"; private String readPreference = "primaryPreferred";
@Configuration
@RequiredArgsConstructor
@EnableConfigurationProperties(MongoCustomProperties.class)
@EnableReactiveMongoRepositories(basePackages = "com.example.demo.repository")
public class MongoConfig extends AbstractReactiveMongoConfiguration {
private final MongoCustomProperties mongoProperties;
@Override
protected String getDatabaseName() {
return mongoProperties.getSet().getDatabase();
}
@Override
public com.mongodb.reactivestreams.client.MongoClient reactiveMongoClient() {
String mongoUri = mongoProperties.getSet().buildUri();
return com.mongodb.reactivestreams.client.MongoClients.create(mongoUri);
}
@Bean
public ReactiveMongoTemplate reactiveMongoTemplate() {
return new ReactiveMongoTemplate(reactiveMongoClient(), getDatabaseName());
}
@Bean
public ReactiveMongoTransactionManager transactionManager(ReactiveMongoDatabaseFactory factory) {
return new ReactiveMongoTransactionManager(factory);
}
}
application.yml
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration
server:
port: 9080
mongodb:
uri:
set:
prefix: 'mongodb://'
host-first: mongod1
host-second: mongod2
host-third: mongod3
port-first-instance: 27017
port-second-instance: 27018
port-third-instance: 27019
database: accounts
username: root
password: root
authentication-database: admin
replicaSet-name: rs0
maxPoolSize: 100
minPoolSize: 5
retryWrites: true
writeAcknowledgement: majority
readPreference: primaryPreferred
application-test.yml
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration
- org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration
- org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration
- org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration
- org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration
I also used this approach
@Container
static MongoDBContainer mongoDBContainer1 = new MongoDBContainer(DockerImageName.parse("mongo:latest"))
.withCommand("--replSet", "rs0")
.withExposedPorts(27017);
@Container
static MongoDBContainer mongoDBContainer2 = new MongoDBContainer(DockerImageName.parse("mongo:latest"))
.withCommand("--replSet", "rs0")
.withExposedPorts(27018);
@Container
static MongoDBContainer mongoDBContainer3 = new MongoDBContainer(DockerImageName.parse("mongo:latest"))
.withCommand("--replSet", "rs0")
.withExposedPorts(27019);
static {
mongoDBContainer1.start();
mongoDBContainer2.start();
mongoDBContainer3.start();
try {
Thread.sleep(10000);
String initCommand = String.format("mongo --host %s --port %d --eval \"rs.initiate({_id: 'rs0', members: [{_id: 0, host: '%s:%d'}, {_id: 1, host: '%s:%d'}, {_id: 2, host: '%s:%d'}]})\"",
mongoDBContainer1.getHost(), mongoDBContainer1.getMappedPort(27017),
mongoDBContainer2.getHost(), mongoDBContainer2.getMappedPort(27018),
mongoDBContainer3.getHost(), mongoDBContainer3.getMappedPort(27019));
Runtime.getRuntime().exec(initCommand);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
2024-10-27T20:24:52.649+03:00 INFO 9636 - - - [- local host:2624] org.mongodb.driver.cluster:Server local host: 2629 is no longer a member of the replica set. Removing a cluster from the client view. 2024-10-27T20:24:52.651+03:00 DEBUGGING 9636 - - - [- local host:2624] org.mongodb.driver.connection : connection pool closed for local access:2629 2024-10-27T20:24:52.653+03:00 INFO 9636 - - - [- local host:2624] org.mongodb.driver.cluster:Server local host: 2624 is no longer a member of the replica set. Removing a cluster from the client view. 2024-10-27T20:24:52.654+03:00 INFO 9636 - - - [4961e6dbc:27017] org.mongodb.driver.cluster : Exception in the monitoring stream when connecting to the server f914961e6dbc:27017
com.mongodb.MongoSocketException: Not known yet (f914961e6dbc)
Who has any idea why this is happening?
Maybe someone has an understanding of how it works and what needs to be fixed here?
Upvotes: 0
Views: 34