2

First time

I am trying to develop a controller to save data in DocumentDB in AWS.

In the first time it saves, but in the second time, I am looking for this register saved in database, I got this and change some data, and save, but...

I am getting this error:

Caused by: com.mongodb.MongoCommandException: Command failed with error 301: 'Retryable writes are not supported' on server aws:27017. The full response is {"ok": 0.0, "code": 301, "errmsg": "Retryable writes are not supported", "operationTime": {"$timestamp": {"t": 1641469879, "i": 1}}}

This my java code

@Service
public class SaveStateHandler extends Handler<SaveStateCommand> {

    @Autowired
    private MongoRepository repository;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Override
    public String handle(Command command) {

        SaveStateCommand cmd = (SaveStateCommand) command;

        State state = buildState(cmd);

        repository.save(state);

        return state.getId();
    }

    private State buildState(SaveStateCommand cmd) {

        State state = State
                .builder()
                .activityId(cmd.getActivityId())
                .agent(cmd.getAgent())
                .stateId(cmd.getStateId())
                .data(cmd.getData())
                .dataAlteracao(LocalDateTime.now())
                .build();

        State stateFound = findState(cmd);

        if (stateFound != null) {
            state.setId(stateFound.getId());
        }

        return state;
    }

    private State findState(SaveStateCommand request) {

        Query query = new Query();

        selectField(query);
        where(request, query);

        return mongoTemplate.findOne(query, State.class);
    }

    private void selectField(Query query) {
        query.fields().include("id");
    }

    private void where(SaveStateCommand request, Query query) {
        query.addCriteria(new Criteria().andOperator(
                Criteria.where("activityId").is(request.getActivityId()),
                Criteria.where("agent").is(request.getAgent())));
    }
}

In AWS they suggest to use retryWrites=false but I don´t know how to do it in Spring Boot.

I use Spring Boot 2.2.1

I tryed to do this

    @Bean
    public MongoClientSettings mongoSettings() {
        return MongoClientSettings
                .builder()
                .retryWrites(Boolean.FALSE)
                .build();
    }

But not worked.

=================================================================================

Second Time

I connected to AWS DocumentDb with SSH Tunnel.

Started my application with these database configuration

@Configuration
@EnableConfigurationProperties({MongoProperties.class})
public class MongoAutoConfiguration {

    private final MongoClientFactory factory;
    private final MongoClientOptions options;
    private MongoClient mongo;

    public MongoAutoConfiguration(MongoProperties properties, ObjectProvider<MongoClientOptions> options, Environment environment) {

        this.options = options.getIfAvailable();

        if (StringUtils.isEmpty(properties.getUsername()) || StringUtils.isEmpty(properties.getPassword())) {
            properties.setUsername(null);
            properties.setPassword(null);
        }

        properties.setUri(createUri(properties));

        this.factory = new MongoClientFactory(properties, environment);
    }

    private String createUri(MongoProperties properties) {

        String uri = "mongodb://";

        if (StringUtils.hasText(properties.getUsername()) && !StringUtils.isEmpty(properties.getPassword())) {
            uri = uri + properties.getUsername() + ":" + new String(properties.getPassword()) + "@";
        }

        return uri + properties.getHost() + ":" + properties.getPort() + "/" + properties.getDatabase() + "?retryWrites=false";
    }

    @PreDestroy
    public void close() {
        if (this.mongo != null) {
            this.mongo.close();
        }
    }

    @Bean
    public MongoClient mongo() {
        this.mongo = this.factory.createMongoClient(this.options);
        return this.mongo;
    }
}

And localy it saves the data without error.

But, if I put my API update in AWS ECS, and try to save, got the same error.

=================================================================================

Dependencies

        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-mongodb</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>


        <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-mongodb</artifactId>
            <version>4.1.4</version>
        </dependency>

Diego Macario
  • 1,240
  • 2
  • 21
  • 33

1 Answers1

1

When you construct your connection string, you can include the parameters for disabling retryable writes, by adding this to your connection URI:

?replicaSet=rs0&readPreference=primaryPreferred&retryWrites=false&maxIdleTimeMS=30000

Then use this when creating the database factory and mongo template (this example uses the Reactive database factory, but the principle is the same for the SimpleMongoClientDatabaseFactory:

@Bean
fun reactiveMongoDatabaseFactory(
        @Value("\${spring.data.mongodb.uri}") uri: String,
        @Value("\${mongodb.database-name}") database: String
): ReactiveMongoDatabaseFactory {
    val parsedURI = URI(uri)
    return SimpleReactiveMongoDatabaseFactory(MongoClients.create(uri), database)
}
Erik Finnman
  • 1,459
  • 18
  • 25
  • If you look at my code, you will se that I put the parameters in uri. – Diego Macario Jan 13 '22 at 12:07
  • Are you sure the database name should be a part of the URI @DiegoMacario? We are not using that, and in the URI generated by AWS it's not a part of the connection string either: `mongodb://xxxx:yyy@clusterurl.eu-west-1.docdb.amazonaws.com:27017/?replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false` – Erik Finnman Jan 13 '22 at 13:46