The primary condition for MongoDB to support transactions is that the MongoDB version must be 4.x or above, and it must be in replica set mode. Since many times using MongoDB does not require deploying multiple replicas, but transactions are desired, the 'single replica mode' can be used, which ensures that there is only one MongoDB instance while still being in replica set mode. This article uses MongoDB 5.0.8 as an example.
This article is just a brief note on issues encountered in daily use; if there are any errors, please feel free to point them out.
First, here is the docker-compose.yml#
version: '3.0'
services:
mongo:
image: mongo:5.0.8
restart: unless-stopped
container_name: mongodb
command: --replSet rs0 --bind_ip_all --keyFile /data/mongodb/keyFile
environment:
TZ: 'Asia/Shanghai'
# Username and password
MONGO_INITDB_ROOT_USERNAME: 'admin'
MONGO_INITDB_ROOT_PASSWORD: 'password'
ports:
- 27017:27017
volumes:
- ./mongodb/data:/data/db
- ./mongodb/keyFile:/data/mongodb/keyFile
Prepare the keyFile#
I roughly tested that the keyFile is not needed in version 4.x, but it is required in version 5.x; otherwise, it will report “BadValue: security.keyFile is required when authorization is enabled with replica sets.”
Generate the keyFile#
openssl rand -base64 128 > ./mongodb/keyFile
Here, ./mongodb/keyFile specifies the name of the generated file and the folder in which it is located.
Set permissions and ownership#
The permissions of the keyFile must be 600; if the permissions are too broad, it will report “error opening file: /data/mongodb/keyFile: bad file” on startup.
After changing the permissions to 600, the ownership of the keyFile must also be changed to mongodb; otherwise, it will report "permissions on /data/mongodb/keyFile are too open" on startup.
Since the container is started, the ownership of the keyFile must be changed to 999, so that the container will automatically change the ownership of the keyFile to mongodb.
sudo chmod 600 keyFile
sudo chown 999:999 keyFile
Outside the container
Inside the container
Once the keyFile is prepared, you can start the container according to the above docker-compose.yml.
Initialization#
After the container starts, MongoDB is still not usable; you need to enter the container to initialize the replica set.
Enter the container
docker exec -it mongodb /bin/bash
Log into MongoDB with the username and password you just set
mongo -u admin --authenticationDatabase admin
Execute the initialization
rs.initiate()
If it displays, it means it was successful.
At this point, MongoDB is considered to be fully started.
Spring Boot project configuration#
To implement transactions, configuration is also needed in the project.
Spring Boot version: 2.1.6.RELEASE
@Configuration
@Slf4j
public class MongoTransactionConfig {
@Bean
MongoTransactionManager transactionManager(MongoDbFactory factory){
log.warn("Enabling MongoDB transactions");
return new MongoTransactionManager(factory);
}
}
In higher versions of Spring Boot, MongoDbFactory has been deprecated and should be replaced with MongoDatabaseFactory.
Spring Boot version: 2.6.1
@Configuration
@Slf4j
public class MongoTransactionConfig {
@Bean
MongoTransactionManager transactionManager(MongoDatabaseFactory factory){
log.warn("Enabling MongoDB transactions");
return new MongoTransactionManager(factory);
}
}
After adding the configuration class, you only need to add the @Transactional annotation to the method to implement MongoDB transactions.
!!! New !!!
Many times we want to retain the mounted data and rebuild the container. Since Docker randomly assigns a hostname to the container every time a new container is built, but we still use the data mounted from the previous container, this causes the hostname in the MongoDB replica set configuration to still be that of the previous container, which leads to the newly started MongoDB being unusable. Therefore, we only need to change the hostname in the MongoDB replica set configuration to solve this problem.
When we start MongoDB and connect directly, this error will be reported.
First, we enter the container and check the current hostname of the container.
Using the method mentioned above, log into MongoDB with the admin account and check the config.
rs.config()
As mentioned above, the hostname in the config is different from the current container's hostname.
Method 1: Change the hostname#
- Get the replica set configuration
config=rs.conf()
- Based on the JSON format of the above configuration, you can identify the position of the hostname and reassign it (change host to the current container's host).
config.members[0].host="c3e9261f8a04:27017"
- Finally, update the config
rs.reconfig(config, {force : true})
After changing, MongoDB can start normally.
Method 2: Specify the hostname in the YAML#
version: '3.0'
services:
mongo:
hostname: mongo501
image: mongo:5.0.8
restart: unless-stopped
container_name: mongodb
command: --replSet rs0 --bind_ip_all --keyFile /data/mongodb/keyFile
environment:
TZ: 'Asia/Shanghai'
# Username and password
MONGO_INITDB_ROOT_USERNAME: 'admin'
MONGO_INITDB_ROOT_PASSWORD: 'password'
ports:
- 27017:27017
volumes:
- ./mongodb/data:/data/db
- ./mongodb/keyFile:/data/mongodb/keyFile
In this way, the hostname of each newly created container will be the same.