一文讀懂 MongoDB驅動程式 API

MongoDB中文社群發表於2022-03-11


回撥API和核心API




回撥 API

核心 API



回撥API




回撥 API 包含以下邏輯:

示例:

該示例使用新的回撥 API 來處理事務,它啟動事務、執行指定的操作並提交(或在出錯時中止)。新的回撥 API 包 含  "TransientTransactionError" "UnknownTransactionCommitResult"  提交錯誤的重試邏輯。

重要

  • 推薦。 使用針對 MongoDB 部署版本更新的 MongoDB 驅動程式。對於 MongoDB 4.2 部署(副本集和分片叢集)上的事務,客戶端 必須使用為 MongoDB 4.2 更新的 MongoDB 驅動程式。

  • 使用驅動程式時,事務中的每個操作 必須與會話相關聯(即將會話傳遞給每個操作)。

  • 事務中的操作使用  事務級別的讀關注 事務級別的寫關注 ,和  事務級別的讀偏好

  • 在 MongoDB 4.2 及更早版本中,你無法在事務中建立集合。如果在事務內部執行,導致文件插入的寫操作(例如  insert  或帶有  upsert: true  的更新操作)必須在  已有的 集合上執行。

  • 從 MongoDB 4.4 開始,你可以隱式或顯式地在事務中建立集合。但是,你比須使用針對 4.4 更新的 MongoDB 驅動程式。有關詳細資訊,請參閱  在事務中建立集合和索引

    
    // WithTransactionExample is an example of using the Session.WithTransaction function.
    
    func WithTransactionExample() {
    
      ctx := context.Background()
    
      // For a replica set, include the replica set name and a seedlist of the members in the URI string; e.g.
    
      // uri := "mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017/?replicaSet=myRepl"
    
      // For a sharded cluster, connect to the mongos instances; e.g.
    
      // uri := "mongodb://mongos0.example.com:27017,mongos1.example.com:27017/"
    
      var uri string
    
    
    
     clientOpts := options.Client().ApplyURI(uri)  client, err := mongo.Connect(ctx, clientOpts)  if err != nil {    panic(err)  }  defer func() { _ = client.Disconnect(ctx) }()
     // Prereq: Create collections.  wcMajority := writeconcern.New(writeconcern.WMajority(), writeconcern.WTimeout(1*time.Second))  wcMajorityCollectionOpts := options.Collection().SetWriteConcern(wcMajority)  fooColl := client.Database("mydb1").Collection("foo", wcMajorityCollectionOpts)  barColl := client.Database("mydb1").Collection("bar", wcMajorityCollectionOpts)
     // Step 1: Define the callback that specifies the sequence of operations to perform inside the transaction.  callback := func(sessCtx mongo.SessionContext) (interface{}, error) {    // Important: You must pass sessCtx as the Context parameter to the operations for them to be executed in the    // transaction.    if _, err := fooColl.InsertOne(sessCtx, bson.D{{"abc", 1}}); err != nil {      return nil, err    }    if _, err := barColl.InsertOne(sessCtx, bson.D{{"xyz", 999}}); err != nil {      return nil, err    }
       return nil, nil  }
     // Step 2: Start a session and run the callback using WithTransaction.  session, err := client.StartSession()  if err != nil {    panic(err)  }  defer session.EndSession(ctx)
     result, err := session.WithTransaction(ctx, callback)  if err != nil {    panic(err)  }  fmt.Printf("result: %v\n", result) }


    核心API




    核心事務 API 不包含標記錯誤的重試邏輯:

    示例:

    以下的示例包含了針對暫時性錯誤重試事務和針對未知提交錯誤重試提交的邏輯:
      
      runTransactionWithRetry := 
      
      func
      (sctx mongo.SessionContext, txnFn 
      func(mongo.SessionContext) 
      error) 
      error {
      
          
      for {
      
            err := txnFn(sctx) 
      // Performs transaction.
      
            
      if err == 
      nil {
      
              
      return 
      nil
      
            }
      
      
      
           log.Println( "Transaction aborted. Caught exception during transaction.")
            // If transient error, retry the whole transaction       if cmdErr, ok := err.(mongo.CommandError); ok && cmdErr.HasErrorLabel( "TransientTransactionError") {        log.Println( "TransientTransactionError, retrying transaction...")         continue      }       return err    }  }
       commitWithRetry := func (sctx mongo.SessionContext) error {     for {      err := sctx.CommitTransaction(sctx)       switch e := err.( type) {       case nil:        log.Println( "Transaction committed.")         return nil       case mongo.CommandError:         // Can retry commit         if e.HasErrorLabel( "UnknownTransactionCommitResult") {          log.Println( "UnknownTransactionCommitResult, retrying commit operation...")           continue        }        log.Println( "Error during commit...")         return e       default:        log.Println( "Error during commit...")         return e      }    }  }
        // Updates two collections in a transaction.  updateEmployeeInfo := func (sctx mongo.SessionContext) error {    employees := client.Database( "hr").Collection( "employees")    events := client.Database( "reporting").Collection( "events")
         err := sctx.StartTransaction(options.Transaction().      SetReadConcern(readconcern.Snapshot()).      SetWriteConcern(writeconcern.New(writeconcern.WMajority())),    )     if err != nil {       return err    }
         _, err = employees.UpdateOne(sctx, bson.D{{ "employee", 3}}, bson.D{{ "$set", bson.D{{ "status", "Inactive"}}}})     if err != nil {      sctx.AbortTransaction(sctx)      log.Println( "caught exception during transaction, aborting.")       return err    }    _, err = events.InsertOne(sctx, bson.D{{ "employee", 3}, { "status", bson.D{{ "new", "Inactive"}, { "old", "Active"}}}})     if err != nil {      sctx.AbortTransaction(sctx)      log.Println( "caught exception during transaction, aborting.")       return err    }
          return commitWithRetry(sctx)  }
        return client.UseSessionWithOptions(    ctx, options.Session().SetDefaultReadPreference(readpref.Primary()),     func (sctx mongo.SessionContext) error {       return runTransactionWithRetry(sctx, updateEmployeeInfo)    },  ) }


      驅動程式版本

      對於 MongoDB 4.2 部署(副本集和分片叢集)上的事務 ,客戶端 必須使用為 MongoDB 4.2 更新的 MongoDB 驅動程式:

      C 1.15.0 C# 2.9.0 Go 1.1 Java 3.11.0 Node 3.3.0 Perl 2.2.0 Python 3.9.0 Ruby 2.10.0 Scala 2.7.0



      對於 MongoDB 4.0 副本集上的事務,客戶端需要為 MongoDB 4.0 或更高版本更新 MongoDB 驅動程式。

      Java 3.8.0Python 3.7.0C 1.11.0 C# 2.7Node 3.1.0Ruby 2.6.0 Perl 2.0.0PHP (PHPC) 1.5.0Scala 2.4.0




      事務錯誤處理
      無論是哪種資料庫系統,無論是MongoDB還是關係型資料庫,應用程式都應該採取措施處理事務提交過程中的錯誤,幷包含事務的重試邏輯。

      "TransientTransactionError"

      無論  retryWrites 的值是多少,事務內部的單個寫操作都不可重試。如果操作遇到一個錯誤 與標籤相關   "TransientTransactionError" ,比如當主節點降級,事務會作為一個整體被重試。

      • 回撥 API 包含了  "TransientTransactionError"  的重試邏輯。

      • 核心事務 API 不包含  "TransientTransactionError"  的重試邏輯。為了處理  "TransientTransactionError" ,應用程式應該明確地包含錯誤的重試邏輯。

      "UnknownTransactionCommitResult"

      提交操作是 可重試的寫操作 。如果提交操作遇到錯誤,無論  retryWrites 的值是多少,MongoDB 驅動程式都會重試提交。

      如果提交操作遇到標記為  "UnknownTransactionCommitResult" 的錯誤,提交可以被重試。

      • 回撥 API 包含了  "UnknownTransactionCommitResult" 的重試邏輯。

      • 核心事務 API 不包含  "UnknownTransactionCommitResult" 的重試邏輯。為了處理  "UnknownTransactionCommitResult" ,應用程式應該明確地包含錯誤的重試邏輯。


      驅動程式版本錯誤

      在具有多個  mongos  例項的分片叢集上,使用為 MongoDB 4.0 更新的驅動程式執行事務 (而不是 MongoDB 4.2)將失敗並可能導致錯誤,包括:
      註釋
      你的驅動程式可能會返回不同的錯誤。有關詳細資訊,請參閱驅動程式的文件。

      Error Code Error Message
      251 cannot continue txnId -1 for session ... with txnId 1
      50940 cannot commit with no participants

      對於 MongoDB 4.2 部署(副本集和分片叢集)上的事務,使用為 MongoDB 4.2 更新的 MongoDB 驅動程式。



      附加資訊



      mongo   Shell 示例

      下面列出的  mongo  shell 方法可用於事務:

      註釋

      mongo  shell 示例為了簡單起見省略了重試邏輯和強大的錯誤處理。有關在應用程式中包含事務的更實際示例,請參閱  事務錯誤處理  。

        
        
        // Create collections:
        
        db.getSiblingDB(
        "mydb1").foo.insert( {
        abc: 
        0}, { 
        writeConcern: { 
        w: 
        "majority", 
        wtimeout: 
        2000 } } );
        
        db.getSiblingDB(
        "mydb2").bar.insert( {
        xyz: 
        0}, { 
        writeConcern: { 
        w: 
        "majority", 
        wtimeout: 
        2000 } } );
        
        
        
        // Start a session. session = db.getMongo().startSession( { readPreference: { mode: "primary" } } );
        coll1 = session.getDatabase( "mydb1").foo; coll2 = session.getDatabase( "mydb2").bar;
        // Start a transaction session.startTransaction( { readConcern: { level: "local" }, writeConcern: { w: "majority" } } );
        // Operations inside the transaction try {   coll1.insertOne( { abc: 1 } );   coll2.insertOne( { xyz: 999 } ); } catch (error) {   // Abort transaction on error   session.abortTransaction();   throw error; }
        // Commit the transaction using write concern set at transaction start session.commitTransaction();
        session.endSession();


        原文連結:


        譯者: 李正洋
        DBA一枚,擅長oracle/mongodb//tidb等多種資料庫。

        現階段對開源分散式資料庫、雲端計算等領域有很大興趣;平時喜歡打羽毛球、看電影等。


        來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69961190/viewspace-2869418/,如需轉載,請註明出處,否則將追究法律責任。

        相關文章