要获取事务支持则需要安装对应的或高版本的mongodb
和驱动(pymongo(python)
,mongo-driver(go)
)
func (s *sessionImpl) WithTransaction(ctx context.Context, fn func(sessCtx SessionContext) (interface{}, error),
opts ...*options.TransactionOptions) (interface{}, error) {
// 超时时间为:120 * time.Second
timeout := time.NewTimer(withTransactionTimeout)
defer timeout.Stop()
var err error
for {
// 开启事务
err = s.StartTransaction(opts...)
if err != nil {
return nil, err
}
// 回调函数(会话上下文)
res, err := fn(NewSessionContext(ctx, s))
// 执行失败终止事务
if err != nil {
if s.clientSession.TransactionRunning() {
// 终止事务
_ = s.AbortTransaction(internal.NewBackgroundContext(ctx))
}
select {
case <-timeout.C:
return nil, err
default:
}
if errorHasLabel(err, driver.TransientTransactionError) {
continue
}
return res, err
}
// 判断在回调函数里面是否直接通过会话上下文终止事务了
err = s.clientSession.CheckAbortTransaction()
if err != nil {
return res, nil
}
if ctx.Err() != nil {
_ = s.AbortTransaction(internal.NewBackgroundContext(ctx))
return nil, ctx.Err()
}
// CommitLoop提交事务循环,还在上面那个for里面
CommitLoop:
for {
// 提交
err = s.CommitTransaction(ctx)
if err == nil {
// 返回成功结果 res, err := fn(NewSessionContext(ctx, s))
return res, nil
}
// 超时判断
select {
case <-timeout.C:
return res, err
default:
}
if cerr, ok := err.(CommandError); ok {
// UnknownTransactionCommitResult = "UnknownTransactionCommitResult"
if cerr.HasErrorLabel(driver.UnknownTransactionCommitResult) && !cerr.IsMaxTimeMSExpiredError() {
continue
}
// errorHasLabel:包含规定的错误信息,返回true
// TransientTransactionError = "TransientTransactionError"
if cerr.HasErrorLabel(driver.TransientTransactionError) {
break CommitLoop
}
}
return res, err
}
}
}
def with_transaction(
self,
callback: Callable[["ClientSession"], _T],
read_concern: Optional[ReadConcern] = None,
write_concern: Optional[WriteConcern] = None,
read_preference: Optional[_ServerMode] = None,
max_commit_time_ms: Optional[int] = None,
) -> _T:
start_time = time.monotonic()
while True:
self.start_transaction(read_concern, write_concern, read_preference, max_commit_time_ms)
try:
ret = callback(self)
except Exception as exc:
if self.in_transaction:
self.abort_transaction()
if (
isinstance(exc, PyMongoError)
and exc.has_error_label("TransientTransactionError")
and _within_time_limit(start_time)
):
# Retry the entire transaction.
continue
raise
if not self.in_transaction:
# Assume callback intentionally ended the transaction.
return ret
while True:
try:
self.commit_transaction()
except PyMongoError as exc:
if (
exc.has_error_label("UnknownTransactionCommitResult")
and _within_time_limit(start_time)
and not _max_time_expired_error(exc)
):
# Retry the commit.
continue
if exc.has_error_label("TransientTransactionError") and _within_time_limit(
start_time
):
# Retry the entire transaction.
break
raise
# Commit succeeded.
return ret
使用更加简便的with方法
func main() {
clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")
client, err := mongo.Connect(context.TODO(), clientOptions)
collection:=client.Database().Collection()
if err != nil {
log.Fatal(err)
}
wc := writeconcern.New(writeconcern.WMajority())
txnOptions := options.Transaction().SetWriteConcern(wc)
session,_:=client.StartSession()
result,_:=session.WithTransaction(context.TODO(), func(sessCtx mongo.SessionContext) (interface{}, error) {
result,err:=collection.Find(context.TODO(),bson.M{})
return result,err
},txnOptions)
cur,ok:=result.(*mongo.Cursor)
if ok{
fmt.Println("断言成功")
for cur.Next(context.TODO()){
m:=make(map[string]interface{})
cur.Decode(m)
fmt.Println(m)
}
}else{
fmt.Println("断言失败")
}
}
import pymongo
client = pymongo.MongoClient()
collection = client.get_database().get_collection()
def call_back(ctx: pymongo.ContextManager):
print(collection.find_one())
with client.start_session() as session:
session.with_transaction(call_back)
高版本的mongoApi废弃了count()方法,如果需要统计数量可以使用:
collection.estimated_document_count()
,collection.count_documents(filter)
collection.CountDocuments(ctx,filter)
,collection.EstimatedDocumentCount(ctx)