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

无法回滚事务 #77

Open
frank0234 opened this issue Oct 16, 2023 · 8 comments
Open

无法回滚事务 #77

frank0234 opened this issue Oct 16, 2023 · 8 comments

Comments

@frank0234
Copy link

使用OrmContext提供的MongoClient无法回滚事务,自行创建的client可以回滚

@godotg
Copy link
Contributor

godotg commented Oct 16, 2023

你指的是操作单条文档的事务,还是操作多条文档的事务。

@godotg
Copy link
Contributor

godotg commented Oct 16, 2023

系统环境、相关软件版本;
描述你遇到的问题,简洁有效的说明
如何重现问题,描述遇到的问题的发生步骤,最好给出代码

@godotg
Copy link
Contributor

godotg commented Oct 16, 2023

在MongoDB中,对单个文档的操作是原子的。由于您可以使用嵌入式文档和数组来捕获单个文档结构中数据之间的关系,而不是跨多个文档和集合进行规范化,因此这种单文档原子性消除了许多实际用例对多文档事务的需求。

@godotg
Copy link
Contributor

godotg commented Oct 16, 2023

https://www.mongodb.com/docs/v7.0/core/transactions-production-consideration/

必须要用复制集或者分片集群才能使用事务,否则可能导致意料之外的结果

@frank0234
Copy link
Author

frank0234 commented Oct 17, 2023

系统环境、相关软件版本; 描述你遇到的问题,简洁有效的说明 如何重现问题,描述遇到的问题的发生步骤,最好给出代码

单机测试环境:我写了一个测试用例。

package com.zfoo.orm.transaction;

import com.mongodb.client.*;
import com.zfoo.orm.OrmContext;
import com.zfoo.orm.entity.UserEntity;
import org.bson.Document;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TransactionTest {

    @Test
    public void test() {
        var user = new UserEntity();
        user.setId(1000L);
        user.setC(1);
        user.setE("User");

        MongoClient myClient = MongoClients.create("mongodb://127.0.0.1:27017/admin");
        MongoDatabase database = myClient.getDatabase("test");
        String dbInfo = database.runCommand(new Document("buildInfo", 1)).getString("version");
        System.out.println("数据库版本:" + dbInfo);
        MongoCollection<UserEntity> myCollection = database.getCollection("user", UserEntity.class);
        database.drop();
        assert 0 == myCollection.countDocuments();
        ClientSession mySession = myClient.startSession();
        try {
            myClient.startSession().withTransaction(() -> {
                myCollection.insertOne(user);
                //ID重复失败
                myCollection.insertOne(user);
                return true;
            });
        } catch (Exception e) {
            System.out.println("-----My rollback-----");
            mySession.close();
            assert 0 == myCollection.countDocuments();
            System.out.println("My rollback success");
        }

        new ClassPathXmlApplicationContext("application.xml");
        MongoClient yourClient = OrmContext.getOrmManager().mongoClient();
        ClientSession yourSession = yourClient.startSession();
        try {
            yourClient.startSession().withTransaction(() -> {
                OrmContext.getAccessor().insert(user);
                OrmContext.getAccessor().insert(user);
                return true;
            });
        } catch (Exception e) {
            System.out.println("-----your rollback-----");
            yourSession.close();
            UserEntity userEntity = OrmContext.getAccessor().load(1000, UserEntity.class);
            System.out.println("userEntity:" + userEntity);
            assert null == userEntity;
            System.out.println("your rollback success");
        }
    }

}

输出的log如下:

2023-10-17 15:51:42 [ INFO] [main] org.mongodb.driver.client.info(SLF4JLogger.java:71) - MongoClient with metadata {"driver": {"name": "mongo-java-driver|sync", "version": "4.10.2"}, "os": {"type": "Windows", "name": "Windows Server 2022", "architecture": "amd64", "version": "10.0"}, "platform": "Java/Oracle Corporation/21+35-2513"} created with settings MongoClientSettings{readPreference=primary, writeConcern=WriteConcern{w=null, wTimeout=null ms, journal=null}, retryWrites=true, retryReads=true, readConcern=ReadConcern{level=null}, credential=null, streamFactoryFactory=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@626abbd0, com.mongodb.Jep395RecordCodecProvider@169bb4dd, com.mongodb.KotlinCodecProvider@1f9e9475]}, loggerSettings=LoggerSettings{maxDocumentLength=1000}, clusterSettings={hosts=[127.0.0.1:27017], srvServiceName=mongodb, mode=SINGLE, requiredClusterType=UNKNOWN, requiredReplicaSetName='null', serverSelector='null', clusterListeners='[]', serverSelectionTimeout='30000 ms', localThreshold='30000 ms'}, socketSettings=SocketSettings{connectTimeoutMS=10000, readTimeoutMS=0, receiveBufferSize=0, sendBufferSize=0}, heartbeatSocketSettings=SocketSettings{connectTimeoutMS=10000, readTimeoutMS=10000, receiveBufferSize=0, sendBufferSize=0}, connectionPoolSettings=ConnectionPoolSettings{maxSize=100, minSize=0, 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}
2023-10-17 15:51:42 [ INFO] [cluster-ClusterId{value='652e3d0e04a66463a1467d34', description='null'}-127.0.0.1:27017] org.mongodb.driver.cluster.info(SLF4JLogger.java:71) - Monitor thread successfully connected to server with description ServerDescription{address=127.0.0.1:27017, type=STANDALONE, state=CONNECTED, ok=true, minWireVersion=0, maxWireVersion=17, maxDocumentSize=16777216, logicalSessionTimeoutMinutes=30, roundTripTimeNanos=31770400}

数据库版本:6.0.11-rc0

-----My rollback-----

My rollback success

2023-10-17 15:51:43 [ INFO] [main] org.mongodb.driver.client.info(SLF4JLogger.java:71) - MongoClient with metadata {"driver": {"name": "mongo-java-driver|sync", "version": "4.10.2"}, "os": {"type": "Windows", "name": "Windows Server 2022", "architecture": "amd64", "version": "10.0"}, "platform": "Java/Oracle Corporation/21+35-2513"} created with settings MongoClientSettings{readPreference=primary, writeConcern=WriteConcern{w=null, wTimeout=null ms, journal=null}, retryWrites=true, retryReads=true, readConcern=ReadConcern{level=null}, credential=null, streamFactoryFactory=null, commandListeners=[], codecRegistry=ProvidersCodecRegistry{codecProviders=[ProvidersCodecRegistry{codecProviders=[ValueCodecProvider{}, BsonValueCodecProvider{}, DBRefCodecProvider{}, DBObjectCodecProvider{}, DocumentCodecProvider{}, CollectionCodecProvider{}, IterableCodecProvider{}, MapCodecProvider{}, GeoJsonCodecProvider{}, GridFSFileCodecProvider{}, Jsr310CodecProvider{}, JsonObjectCodecProvider{}, BsonCodecProvider{}, EnumCodecProvider{}, com.mongodb.client.model.mql.ExpressionCodecProvider@626abbd0, com.mongodb.Jep395RecordCodecProvider@169bb4dd, com.mongodb.KotlinCodecProvider@1f9e9475]}, ProvidersCodecRegistry{codecProviders=[org.bson.codecs.pojo.PojoCodecProvider@460b6d54]}]}, loggerSettings=LoggerSettings{maxDocumentLength=1000}, clusterSettings={hosts=[127.0.0.1:27017], srvServiceName=mongodb, mode=SINGLE, requiredClusterType=UNKNOWN, requiredReplicaSetName='null', serverSelector='null', clusterListeners='[]', serverSelectionTimeout='30000 ms', localThreshold='30000 ms'}, socketSettings=SocketSettings{connectTimeoutMS=10000, readTimeoutMS=0, receiveBufferSize=0, sendBufferSize=0}, heartbeatSocketSettings=SocketSettings{connectTimeoutMS=10000, readTimeoutMS=10000, receiveBufferSize=0, sendBufferSize=0}, connectionPoolSettings=ConnectionPoolSettings{maxSize=33, minSize=1, 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}
2023-10-17 15:51:43 [ INFO] [cluster-ClusterId{value='652e3d0f04a66463a1467d35', description='null'}-127.0.0.1:27017] org.mongodb.driver.cluster.info(SLF4JLogger.java:71) - Monitor thread successfully connected to server with description ServerDescription{address=127.0.0.1:27017, type=STANDALONE, state=CONNECTED, ok=true, minWireVersion=0, maxWireVersion=17, maxDocumentSize=16777216, logicalSessionTimeoutMinutes=30, roundTripTimeNanos=4795900}
2023-10-17 15:51:43 [ INFO] [main] org.mongodb.driver.cluster.info(SLF4JLogger.java:71) - Cluster description not yet available. Waiting for 30000 ms before timing out
2023-10-17 15:51:43 [ INFO] [main] com.zfoo.orm.OrmContext.onApplicationEvent(OrmContext.java:93) - Orm started successfully and cost [0.19] seconds

-----your rollback-----

userEntity:UserEntity{id=1000, a=0, b=0, c=1, d=false, e='User', f='null', l=null}

java.lang.AssertionError
at com.zfoo.orm.transaction.TransactionTest.test(TransactionTest.java:56)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)

@godotg
Copy link
Contributor

godotg commented Oct 17, 2023

    MongoClient myClient = MongoClients.create("mongodb://127.0.0.1:27017/admin");
    MongoDatabase database = myClient.getDatabase("test");
    String dbInfo = database.runCommand(new Document("buildInfo", 1)).getString("version");
    System.out.println("数据库版本:" + dbInfo);
    MongoCollection<UserEntity> myCollection = database.getCollection("user", UserEntity.class);
    myCollection.insertOne(user);
    
    这种写法是有问题的,没有指定对象的bson的编解码无法使用对象

参考下面的标准写法,只能用Document
https://github.com/zfoo-project/zfoo/blob/main/orm/src/test/java/com/zfoo/orm/client/SingleMongoTest.java

@frank0234
Copy link
Author

    MongoClient myClient = MongoClients.create("mongodb://127.0.0.1:27017/admin");
    MongoDatabase database = myClient.getDatabase("test");
    String dbInfo = database.runCommand(new Document("buildInfo", 1)).getString("version");
    System.out.println("数据库版本:" + dbInfo);
    MongoCollection<UserEntity> myCollection = database.getCollection("user", UserEntity.class);
    myCollection.insertOne(user);
    
    这种写法是有问题的,没有指定对象的bson的编解码无法使用对象

参考下面的标准写法,只能用Document https://github.com/zfoo-project/zfoo/blob/main/orm/src/test/java/com/zfoo/orm/client/SingleMongoTest.java

那不就没用ORM了,这种写法确实取不到数据,至少能回滚。是不是初始化client的参数有问题才导致无法回滚?

@godotg
Copy link
Contributor

godotg commented Oct 17, 2023

弄个副本集再看看事务,单机看不出来

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants