75 lines
3.0 KiB
Go
75 lines
3.0 KiB
Go
/*
|
||
Copyright 2020 The Qmgo Authors.
|
||
Licensed under the Apache License, Version 2.0 (the "License");
|
||
you may not use this file except in compliance with the License.
|
||
You may obtain a copy of the License at
|
||
http://www.apache.org/licenses/LICENSE-2.0
|
||
Unless required by applicable law or agreed to in writing, software
|
||
distributed under the License is distributed on an "AS IS" BASIS,
|
||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
See the License for the specific language governing permissions and
|
||
limitations under the License.
|
||
*/
|
||
|
||
package qmgo
|
||
|
||
import (
|
||
"context"
|
||
|
||
opts "github.com/qiniu/qmgo/options"
|
||
"go.mongodb.org/mongo-driver/mongo"
|
||
"go.mongodb.org/mongo-driver/mongo/options"
|
||
"go.mongodb.org/mongo-driver/x/mongo/driver"
|
||
)
|
||
|
||
// Session is an struct that represents a MongoDB logical session
|
||
type Session struct {
|
||
session mongo.Session
|
||
}
|
||
|
||
// StartTransaction starts transaction
|
||
//precondition:
|
||
//- version of mongoDB server >= v4.0
|
||
//- Topology of mongoDB server is not Single
|
||
//At the same time, please pay attention to the following
|
||
//- make sure all operations in callback use the sessCtx as context parameter
|
||
//- Dont forget to call EndSession if session is not used anymore
|
||
//- if operations in callback takes more than(include equal) 120s, the operations will not take effect,
|
||
//- if operation in callback return qmgo.ErrTransactionRetry,
|
||
// the whole transaction will retry, so this transaction must be idempotent
|
||
//- if operations in callback return qmgo.ErrTransactionNotSupported,
|
||
//- If the ctx parameter already has a Session attached to it, it will be replaced by this session.
|
||
func (s *Session) StartTransaction(ctx context.Context, cb func(sessCtx context.Context) (interface{}, error), opts ...*opts.TransactionOptions) (interface{}, error) {
|
||
transactionOpts := options.Transaction()
|
||
if len(opts) > 0 && opts[0].TransactionOptions != nil {
|
||
transactionOpts = opts[0].TransactionOptions
|
||
}
|
||
result, err := s.session.WithTransaction(ctx, wrapperCustomCb(cb), transactionOpts)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// EndSession will abort any existing transactions and close the session.
|
||
func (s *Session) EndSession(ctx context.Context) {
|
||
s.session.EndSession(ctx)
|
||
}
|
||
|
||
// AbortTransaction aborts the active transaction for this session. This method will return an error if there is no
|
||
// active transaction for this session or the transaction has been committed or aborted.
|
||
func (s *Session) AbortTransaction(ctx context.Context) error {
|
||
return s.session.AbortTransaction(ctx)
|
||
}
|
||
|
||
// wrapperCustomF wrapper caller's callback function to mongo dirver's
|
||
func wrapperCustomCb(cb func(ctx context.Context) (interface{}, error)) func(sessCtx mongo.SessionContext) (interface{}, error) {
|
||
return func(sessCtx mongo.SessionContext) (interface{}, error) {
|
||
result, err := cb(sessCtx)
|
||
if err == ErrTransactionRetry {
|
||
return nil, mongo.CommandError{Labels: []string{driver.TransientTransactionError}}
|
||
}
|
||
return result, err
|
||
}
|
||
}
|