如何为嵌入式 mongo (flapdoodle) 全局设置 WriteConcern 以修复间歇性测试失败
How to set WriteConcern globally for embedded mongo (flapdoodle) to fix intermittent test failures
问题:有没有办法更改 (Flapdoodle) 嵌入式 mongo 的配置,以便我可以保证写入发生在读取之前? 请注意问题是特别是关于 Flapdoodle 的实现,而不是一般的 MongoDB。我只想在测试和配置中应用它(如果可能的话),而不是通过我所有的 Mongo 操作并在任何地方设置写入问题。
详情
我在测试环境中使用 Spring + 嵌入式 mongo。 Maven 依赖项如下所示:
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<version>1.50.1</version>
<scope>test</scope>
</dependency>
使用测试配置 class:
@Configuration
@EnableAutoConfiguration(exclude = { EmbeddedMongoAutoConfiguration.class })
@EnableMongoRepositories(basePackages = "path.to.repos")
public class TestMongoConfig {
private static final String DESTROY_METHOD_CLOSE = "close";
private static final String DESTROY_METHOD_STOP = "stop";
private static final Logger LOGGER = LoggerFactory.getLogger(TestMongoConfig.class);
@Autowired
private MongoProperties mongoProperties;
@Autowired(required = false)
private MongoClientOptions mongoClientOptions;
@Autowired
private Environment environment;
@Bean(destroyMethod = DESTROY_METHOD_CLOSE)
public MongoClient mongo() throws IOException {
Net net = mongodProcess().getConfig().net();
mongoProperties.setHost(net.getServerAddress().getHostName());
mongoProperties.setPort(net.getPort());
return mongoProperties.createMongoClient(this.mongoClientOptions, environment);
}
@Bean(destroyMethod = DESTROY_METHOD_STOP)
public MongodProcess mongodProcess() throws IOException {
return mongodExecutable().start();
}
@Bean(destroyMethod = DESTROY_METHOD_STOP)
public MongodExecutable mongodExecutable() throws IOException {
return mongodStarter().prepare(mongodConfig());
}
@Bean
public IMongodConfig mongodConfig() throws IOException {
return new MongodConfigBuilder().version(Version.Main.PRODUCTION).build();
}
@Bean
public MongodStarter mongodStarter() {
Command command = Command.MongoD;
IRuntimeConfig runtimeConfig = new RuntimeConfigBuilder()
.defaultsWithLogger(command, LOGGER)
.artifactStore(new ExtractedArtifactStoreBuilder()
.defaults(command)
.download(new DownloadConfigBuilder()
.defaultsForCommand(command).build())
.executableNaming(new UserTempNaming()))
.build();
return MongodStarter.getInstance(runtimeConfig);
}
其中 class 非常标准,只是遵循 flapdoodle 的示例。
95% 的情况下效果很好,我的所有测试都通过了。间歇性地一些随机测试失败,例如当我做这样的事情时:
userRepository.customMethodPushToList(user.getId(), aString);
user = userRepository.findOne(user.getId());
assertEquals(2, user.getSomeList().size());
customMethodPushToList
所做的只是将字符串推送到数据库中 User
条目中的列表。这里没什么特别的。然而,有 5% 的时间测试失败。
我 认为 这是因为与写入问题有关...即我在更新之前检索了该对象。
提前致谢。
编辑
根据发帖者的回答,我手动创建了 MongoClientOperations
:
MongoClientOptions mongoClientOptions = MongoClientOptions.builder().writeConcern(WriteConcern.ACKNOWLEDGED).build();
目前一切顺利...
我觉得你拿到DB(用getDatabase())之后需要用到的是withWriteConcern():
http://api.mongodb.com/java/3.0/com/mongodb/async/client/MongoDatabase.html#withWriteConcern-com.mongodb.WriteConcern-
并使用确认:
http://api.mongodb.com/java/3.0/com/mongodb/WriteConcern.html#ACKNOWLEDGED
也可以使用 withReadPreference()
http://api.mongodb.com/java/3.0/com/mongodb/async/client/MongoDatabase.html#withReadPreference-com.mongodb.ReadPreference-
并使用 primary():
http://api.mongodb.com/java/3.0/com/mongodb/ReadPreference.html#primary--
这将使行为尽可能微不足道。
问题:有没有办法更改 (Flapdoodle) 嵌入式 mongo 的配置,以便我可以保证写入发生在读取之前? 请注意问题是特别是关于 Flapdoodle 的实现,而不是一般的 MongoDB。我只想在测试和配置中应用它(如果可能的话),而不是通过我所有的 Mongo 操作并在任何地方设置写入问题。
详情
我在测试环境中使用 Spring + 嵌入式 mongo。 Maven 依赖项如下所示:
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<version>1.50.1</version>
<scope>test</scope>
</dependency>
使用测试配置 class:
@Configuration
@EnableAutoConfiguration(exclude = { EmbeddedMongoAutoConfiguration.class })
@EnableMongoRepositories(basePackages = "path.to.repos")
public class TestMongoConfig {
private static final String DESTROY_METHOD_CLOSE = "close";
private static final String DESTROY_METHOD_STOP = "stop";
private static final Logger LOGGER = LoggerFactory.getLogger(TestMongoConfig.class);
@Autowired
private MongoProperties mongoProperties;
@Autowired(required = false)
private MongoClientOptions mongoClientOptions;
@Autowired
private Environment environment;
@Bean(destroyMethod = DESTROY_METHOD_CLOSE)
public MongoClient mongo() throws IOException {
Net net = mongodProcess().getConfig().net();
mongoProperties.setHost(net.getServerAddress().getHostName());
mongoProperties.setPort(net.getPort());
return mongoProperties.createMongoClient(this.mongoClientOptions, environment);
}
@Bean(destroyMethod = DESTROY_METHOD_STOP)
public MongodProcess mongodProcess() throws IOException {
return mongodExecutable().start();
}
@Bean(destroyMethod = DESTROY_METHOD_STOP)
public MongodExecutable mongodExecutable() throws IOException {
return mongodStarter().prepare(mongodConfig());
}
@Bean
public IMongodConfig mongodConfig() throws IOException {
return new MongodConfigBuilder().version(Version.Main.PRODUCTION).build();
}
@Bean
public MongodStarter mongodStarter() {
Command command = Command.MongoD;
IRuntimeConfig runtimeConfig = new RuntimeConfigBuilder()
.defaultsWithLogger(command, LOGGER)
.artifactStore(new ExtractedArtifactStoreBuilder()
.defaults(command)
.download(new DownloadConfigBuilder()
.defaultsForCommand(command).build())
.executableNaming(new UserTempNaming()))
.build();
return MongodStarter.getInstance(runtimeConfig);
}
其中 class 非常标准,只是遵循 flapdoodle 的示例。
95% 的情况下效果很好,我的所有测试都通过了。间歇性地一些随机测试失败,例如当我做这样的事情时:
userRepository.customMethodPushToList(user.getId(), aString);
user = userRepository.findOne(user.getId());
assertEquals(2, user.getSomeList().size());
customMethodPushToList
所做的只是将字符串推送到数据库中 User
条目中的列表。这里没什么特别的。然而,有 5% 的时间测试失败。
我 认为 这是因为与写入问题有关...即我在更新之前检索了该对象。
提前致谢。
编辑
根据发帖者的回答,我手动创建了 MongoClientOperations
:
MongoClientOptions mongoClientOptions = MongoClientOptions.builder().writeConcern(WriteConcern.ACKNOWLEDGED).build();
目前一切顺利...
我觉得你拿到DB(用getDatabase())之后需要用到的是withWriteConcern(): http://api.mongodb.com/java/3.0/com/mongodb/async/client/MongoDatabase.html#withWriteConcern-com.mongodb.WriteConcern-
并使用确认: http://api.mongodb.com/java/3.0/com/mongodb/WriteConcern.html#ACKNOWLEDGED
也可以使用 withReadPreference() http://api.mongodb.com/java/3.0/com/mongodb/async/client/MongoDatabase.html#withReadPreference-com.mongodb.ReadPreference-
并使用 primary(): http://api.mongodb.com/java/3.0/com/mongodb/ReadPreference.html#primary--
这将使行为尽可能微不足道。