github.com/wfusion/gofusion@v1.1.14/common/infra/drivers/mongo/mongo.go (about)

     1  package mongo
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  	"time"
     8  
     9  	"go.mongodb.org/mongo-driver/mongo"
    10  	"go.mongodb.org/mongo-driver/mongo/options"
    11  
    12  	"github.com/wfusion/gofusion/common/constant"
    13  	"github.com/wfusion/gofusion/common/utils"
    14  )
    15  
    16  // Option
    17  //nolint: revive // mongo options comments too long issue
    18  type Option struct {
    19  	DB        string   `yaml:"db" json:"db" toml:"db"`
    20  	AuthDB    string   `yaml:"auth_db" json:"auth_db" toml:"auth_db" default:"admin"`
    21  	User      string   `yaml:"user" json:"user" toml:"user"`
    22  	Password  string   `yaml:"password" json:"password" toml:"password" encrypted:""`
    23  	Endpoints []string `yaml:"endpoints" json:"endpoints" toml:"endpoints"`
    24  
    25  	// Timeout specifies the amount of time that a single operation run on this Client can execute before returning an error.
    26  	// The deadline of any operation run through the Client will be honored above any Timeout set on the Client; Timeout will only
    27  	// be honored if there is no deadline on the operation Context. Timeout can also be set through the "timeoutMS" URI option
    28  	// (e.g. "timeoutMS=1000"). The default value is nil, meaning operations do not inherit a timeout from the Client.
    29  	//
    30  	// If any Timeout is set (even 0) on the Client, the values of MaxTime on operation options, TransactionOptions.MaxCommitTime and
    31  	// SessionOptions.DefaultMaxCommitTime will be ignored. Setting Timeout and SocketTimeout or writeConcern.wTimeout will result
    32  	// in undefined behavior.
    33  	//
    34  	// NOTE(benjirewis): SetTimeout represents unstable, provisional API. The behavior of the driver when a Timeout is specified is
    35  	// subject to change.
    36  	Timeout string `yaml:"timeout" json:"timeout" toml:"timeout" default:"5s"`
    37  	// ConnTimeout specifies a timeout that is used for creating connections to the server. If a custom Dialer is
    38  	// specified through SetDialer, this option must not be used. This can be set through ApplyURI with the
    39  	// "connectTimeoutMS" (e.g "connectTimeoutMS=30") option. If set to 0, no timeout will be used. The default is 30
    40  	// seconds.
    41  	ConnTimeout string `yaml:"conn_timeout" json:"conn_timeout" toml:"conn_timeout" default:"30s"`
    42  	// SocketTimeout specifies the timeout to be used for the Client's socket reads and writes.
    43  	//
    44  	// NOTE(benjirewis): SocketTimeout will be deprecated in a future release. The more general Timeout option
    45  	// may be used in its place to control the amount of time that a single operation can run before returning
    46  	// an error. Setting SocketTimeout and Timeout on a single client will result in undefined behavior.
    47  	SocketTimeout string `yaml:"socket_timeout" json:"socket_timeout" toml:"socket_timeout" default:"5s"`
    48  	// HeartbeatInterval specifies the amount of time to wait between periodic background server checks. This can also be
    49  	// set through the "heartbeatIntervalMS" URI option (e.g. "heartbeatIntervalMS=10000"). The default is 10 seconds.
    50  	HeartbeatInterval string `yaml:"heartbeat_interval" json:"heartbeat_interval" toml:"heartbeat_interval" default:"10s"`
    51  
    52  	// MaxConnecting specifies the maximum number of connections a connection pool may establish simultaneously. This can
    53  	// also be set through the "maxConnecting" URI option (e.g. "maxConnecting=2"). If this is 0, the default is used. The
    54  	// default is 2. Values greater than 100 are not recommended.
    55  	MaxConnecting uint64 `yaml:"max_connecting" json:"max_connecting" toml:"max_connecting" default:"2"`
    56  	// MinPoolSize specifies the minimum number of connections allowed in the driver's connection pool to each server. If
    57  	// this is non-zero, each server's pool will be maintained in the background to ensure that the size does not fall below
    58  	// the minimum. This can also be set through the "minPoolSize" URI option (e.g. "minPoolSize=100"). The default is 0.
    59  	MinPoolSize uint64 `yaml:"min_pool_size" json:"min_pool_size" toml:"min_pool_size"`
    60  	// MaxPoolSize specifies that maximum number of connections allowed in the driver's connection pool to each server.
    61  	// Requests to a server will block if this maximum is reached. This can also be set through the "maxPoolSize" URI option
    62  	// (e.g. "maxPoolSize=100"). If this is 0, maximum connection pool size is not limited. The default is 100.
    63  	MaxPoolSize uint64 `yaml:"max_pool_size" json:"max_pool_size" toml:"max_pool_size" default:"100"`
    64  	// MaxConnIdleTime specifies the maximum amount of time that a connection will remain idle in a connection pool
    65  	// before it is removed from the pool and closed. This can also be set through the "maxIdleTimeMS" URI option (e.g.
    66  	// "maxIdleTimeMS=10000"). The default is 0, meaning a connection can remain unused indefinitely.
    67  	MaxConnIdleTime string `yaml:"max_conn_idle_time" json:"max_conn_idle_time" toml:"max_conn_idle_time" default:"10s"`
    68  
    69  	// RetryWrites specifies whether supported write operations should be retried once on certain errors, such as network
    70  	// errors.
    71  	//
    72  	// Supported operations are InsertOne, UpdateOne, ReplaceOne, DeleteOne, FindOneAndDelete, FindOneAndReplace,
    73  	// FindOneAndDelete, InsertMany, and BulkWrite. Note that BulkWrite requests must not include UpdateManyModel or
    74  	// DeleteManyModel instances to be considered retryable. Unacknowledged writes will not be retried, even if this option
    75  	// is set to true.
    76  	//
    77  	// This option requires server version >= 3.6 and a replica set or sharded cluster and will be ignored for any other
    78  	// cluster type. This can also be set through the "retryWrites" URI option (e.g. "retryWrites=true"). The default is
    79  	// true.
    80  	RetryWrites bool `yaml:"retry_writes" json:"retry_writes" toml:"retry_writes" default:"true"`
    81  
    82  	// SetRetryReads specifies whether supported read operations should be retried once on certain errors, such as network
    83  	// errors.
    84  	//
    85  	// Supported operations are Find, FindOne, Aggregate without a $out stage, Distinct, CountDocuments,
    86  	// EstimatedDocumentCount, Watch (for Client, Database, and Collection), ListCollections, and ListDatabases. Note that
    87  	// operations run through RunCommand are not retried.
    88  	//
    89  	// This option requires server version >= 3.6 and driver version >= 1.1.0. The default is true.
    90  	RetryReads bool `yaml:"retry_reads" json:"retry_reads" toml:"retry_reads" default:"true"`
    91  }
    92  
    93  var Default Dialect = new(defaultDialect)
    94  
    95  type defaultDialect struct{}
    96  
    97  func (d *defaultDialect) New(ctx context.Context, option Option, opts ...utils.OptionExtender) (cli *Mongo, err error) {
    98  	opt := options.Client().ApplyURI(d.parseOption(option))
    99  	opt.SetRetryReads(option.RetryReads)
   100  	opt.SetRetryWrites(option.RetryWrites)
   101  	d.wrapDurationSetter(option.Timeout, func(du time.Duration) { opt.SetTimeout(du) })
   102  	d.wrapDurationSetter(option.ConnTimeout, func(du time.Duration) { opt.SetConnectTimeout(du) })
   103  	d.wrapDurationSetter(option.SocketTimeout, func(du time.Duration) { opt.SetSocketTimeout(du) })
   104  	d.wrapDurationSetter(option.MaxConnIdleTime, func(du time.Duration) { opt.SetMaxConnIdleTime(du) })
   105  	d.wrapDurationSetter(option.HeartbeatInterval, func(du time.Duration) { opt.SetHeartbeatInterval(du) })
   106  	d.wrapNumberSetter(option.MaxConnecting, func(nu uint64) { opt.SetMaxConnecting(option.MaxConnecting) })
   107  	d.wrapNumberSetter(option.MinPoolSize, func(nu uint64) { opt.SetMinPoolSize(option.MinPoolSize) })
   108  	d.wrapNumberSetter(option.MaxPoolSize, func(nu uint64) { opt.SetMaxPoolSize(option.MaxPoolSize) })
   109  
   110  	newOpt := utils.ApplyOptions[newOption](opts...)
   111  	if newOpt.monitor != nil {
   112  		opt = opt.SetMonitor(newOpt.monitor)
   113  	}
   114  	if newOpt.poolMonitor != nil {
   115  		opt = opt.SetPoolMonitor(newOpt.poolMonitor)
   116  	}
   117  
   118  	mgoCli, err := mongo.Connect(ctx, opt)
   119  	if err != nil {
   120  		return
   121  	}
   122  
   123  	// authentication check
   124  	if err = mgoCli.Ping(ctx, nil); err != nil {
   125  		return
   126  	}
   127  
   128  	cli = &Mongo{Client: mgoCli}
   129  	return
   130  }
   131  
   132  func (d *defaultDialect) wrapDurationSetter(s string, setter func(du time.Duration)) {
   133  	if utils.IsStrBlank(s) {
   134  		return
   135  	}
   136  	duration, err := time.ParseDuration(s)
   137  	if err != nil {
   138  		panic(err)
   139  	}
   140  	setter(duration)
   141  }
   142  
   143  func (d *defaultDialect) wrapNumberSetter(n uint64, setter func(nu uint64)) {
   144  	if n > 0 {
   145  		setter(n)
   146  	}
   147  }
   148  
   149  func (d *defaultDialect) parseOption(option Option) (dsn string) {
   150  	return fmt.Sprintf("mongodb://%s:%s@%s/%s?authSource=%s",
   151  		option.User, option.Password, strings.Join(option.Endpoints, constant.Comma), option.DB, option.AuthDB)
   152  }