github.com/lingyao2333/mo-zero@v1.4.1/core/stores/mon/collection.go (about)

     1  package mon
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"time"
     7  
     8  	"github.com/lingyao2333/mo-zero/core/breaker"
     9  	"github.com/lingyao2333/mo-zero/core/logx"
    10  	"github.com/lingyao2333/mo-zero/core/timex"
    11  	"go.mongodb.org/mongo-driver/mongo"
    12  	mopt "go.mongodb.org/mongo-driver/mongo/options"
    13  	"go.mongodb.org/mongo-driver/x/mongo/driver/session"
    14  )
    15  
    16  const (
    17  	defaultSlowThreshold = time.Millisecond * 500
    18  	// spanName is the span name of the mongo calls.
    19  	spanName = "mongo"
    20  
    21  	// mongodb method names
    22  	aggregate              = "Aggregate"
    23  	bulkWrite              = "BulkWrite"
    24  	countDocuments         = "CountDocuments"
    25  	deleteMany             = "DeleteMany"
    26  	deleteOne              = "DeleteOne"
    27  	distinct               = "Distinct"
    28  	estimatedDocumentCount = "EstimatedDocumentCount"
    29  	find                   = "Find"
    30  	findOne                = "FindOne"
    31  	findOneAndDelete       = "FindOneAndDelete"
    32  	findOneAndReplace      = "FindOneAndReplace"
    33  	findOneAndUpdate       = "FindOneAndUpdate"
    34  	insertMany             = "InsertMany"
    35  	insertOne              = "InsertOne"
    36  	replaceOne             = "ReplaceOne"
    37  	updateByID             = "UpdateByID"
    38  	updateMany             = "UpdateMany"
    39  	updateOne              = "UpdateOne"
    40  )
    41  
    42  // ErrNotFound is an alias of mongo.ErrNoDocuments
    43  var ErrNotFound = mongo.ErrNoDocuments
    44  
    45  type (
    46  	// Collection defines a MongoDB collection.
    47  	Collection interface {
    48  		// Aggregate executes an aggregation pipeline.
    49  		Aggregate(ctx context.Context, pipeline interface{}, opts ...*mopt.AggregateOptions) (
    50  			*mongo.Cursor, error)
    51  		// BulkWrite performs a bulk write operation.
    52  		BulkWrite(ctx context.Context, models []mongo.WriteModel, opts ...*mopt.BulkWriteOptions) (
    53  			*mongo.BulkWriteResult, error)
    54  		// Clone creates a copy of this collection with the same settings.
    55  		Clone(opts ...*mopt.CollectionOptions) (*mongo.Collection, error)
    56  		// CountDocuments returns the number of documents in the collection that match the filter.
    57  		CountDocuments(ctx context.Context, filter interface{}, opts ...*mopt.CountOptions) (int64, error)
    58  		// Database returns the database that this collection is a part of.
    59  		Database() *mongo.Database
    60  		// DeleteMany deletes documents from the collection that match the filter.
    61  		DeleteMany(ctx context.Context, filter interface{}, opts ...*mopt.DeleteOptions) (
    62  			*mongo.DeleteResult, error)
    63  		// DeleteOne deletes at most one document from the collection that matches the filter.
    64  		DeleteOne(ctx context.Context, filter interface{}, opts ...*mopt.DeleteOptions) (
    65  			*mongo.DeleteResult, error)
    66  		// Distinct returns a list of distinct values for the given key across the collection.
    67  		Distinct(ctx context.Context, fieldName string, filter interface{},
    68  			opts ...*mopt.DistinctOptions) ([]interface{}, error)
    69  		// Drop drops this collection from database.
    70  		Drop(ctx context.Context) error
    71  		// EstimatedDocumentCount returns an estimate of the count of documents in a collection
    72  		// using collection metadata.
    73  		EstimatedDocumentCount(ctx context.Context, opts ...*mopt.EstimatedDocumentCountOptions) (int64, error)
    74  		// Find finds the documents matching the provided filter.
    75  		Find(ctx context.Context, filter interface{}, opts ...*mopt.FindOptions) (*mongo.Cursor, error)
    76  		// FindOne returns up to one document that matches the provided filter.
    77  		FindOne(ctx context.Context, filter interface{}, opts ...*mopt.FindOneOptions) (
    78  			*mongo.SingleResult, error)
    79  		// FindOneAndDelete returns at most one document that matches the filter. If the filter
    80  		// matches multiple documents, only the first document is deleted.
    81  		FindOneAndDelete(ctx context.Context, filter interface{}, opts ...*mopt.FindOneAndDeleteOptions) (
    82  			*mongo.SingleResult, error)
    83  		// FindOneAndReplace returns at most one document that matches the filter. If the filter
    84  		// matches multiple documents, FindOneAndReplace returns the first document in the
    85  		// collection that matches the filter.
    86  		FindOneAndReplace(ctx context.Context, filter, replacement interface{},
    87  			opts ...*mopt.FindOneAndReplaceOptions) (*mongo.SingleResult, error)
    88  		// FindOneAndUpdate returns at most one document that matches the filter. If the filter
    89  		// matches multiple documents, FindOneAndUpdate returns the first document in the
    90  		// collection that matches the filter.
    91  		FindOneAndUpdate(ctx context.Context, filter, update interface{},
    92  			opts ...*mopt.FindOneAndUpdateOptions) (*mongo.SingleResult, error)
    93  		// Indexes returns the index view for this collection.
    94  		Indexes() mongo.IndexView
    95  		// InsertMany inserts the provided documents.
    96  		InsertMany(ctx context.Context, documents []interface{}, opts ...*mopt.InsertManyOptions) (
    97  			*mongo.InsertManyResult, error)
    98  		// InsertOne inserts the provided document.
    99  		InsertOne(ctx context.Context, document interface{}, opts ...*mopt.InsertOneOptions) (
   100  			*mongo.InsertOneResult, error)
   101  		// ReplaceOne replaces at most one document that matches the filter.
   102  		ReplaceOne(ctx context.Context, filter, replacement interface{},
   103  			opts ...*mopt.ReplaceOptions) (*mongo.UpdateResult, error)
   104  		// UpdateByID updates a single document matching the provided filter.
   105  		UpdateByID(ctx context.Context, id, update interface{},
   106  			opts ...*mopt.UpdateOptions) (*mongo.UpdateResult, error)
   107  		// UpdateMany updates the provided documents.
   108  		UpdateMany(ctx context.Context, filter, update interface{},
   109  			opts ...*mopt.UpdateOptions) (*mongo.UpdateResult, error)
   110  		// UpdateOne updates a single document matching the provided filter.
   111  		UpdateOne(ctx context.Context, filter, update interface{},
   112  			opts ...*mopt.UpdateOptions) (*mongo.UpdateResult, error)
   113  		// Watch returns a change stream cursor used to receive notifications of changes to the collection.
   114  		Watch(ctx context.Context, pipeline interface{}, opts ...*mopt.ChangeStreamOptions) (
   115  			*mongo.ChangeStream, error)
   116  	}
   117  
   118  	decoratedCollection struct {
   119  		*mongo.Collection
   120  		name string
   121  		brk  breaker.Breaker
   122  	}
   123  
   124  	keepablePromise struct {
   125  		promise breaker.Promise
   126  		log     func(error)
   127  	}
   128  )
   129  
   130  func newCollection(collection *mongo.Collection, brk breaker.Breaker) Collection {
   131  	return &decoratedCollection{
   132  		Collection: collection,
   133  		name:       collection.Name(),
   134  		brk:        brk,
   135  	}
   136  }
   137  
   138  func (c *decoratedCollection) Aggregate(ctx context.Context, pipeline interface{},
   139  	opts ...*mopt.AggregateOptions) (cur *mongo.Cursor, err error) {
   140  	ctx, span := startSpan(ctx, aggregate)
   141  	defer func() {
   142  		endSpan(span, err)
   143  	}()
   144  
   145  	err = c.brk.DoWithAcceptable(func() error {
   146  		starTime := timex.Now()
   147  		defer func() {
   148  			c.logDurationSimple(ctx, aggregate, starTime, err)
   149  		}()
   150  
   151  		cur, err = c.Collection.Aggregate(ctx, pipeline, opts...)
   152  		return err
   153  	}, acceptable)
   154  
   155  	return
   156  }
   157  
   158  func (c *decoratedCollection) BulkWrite(ctx context.Context, models []mongo.WriteModel,
   159  	opts ...*mopt.BulkWriteOptions) (res *mongo.BulkWriteResult, err error) {
   160  	ctx, span := startSpan(ctx, bulkWrite)
   161  	defer func() {
   162  		endSpan(span, err)
   163  	}()
   164  
   165  	err = c.brk.DoWithAcceptable(func() error {
   166  		startTime := timex.Now()
   167  		defer func() {
   168  			c.logDurationSimple(ctx, bulkWrite, startTime, err)
   169  		}()
   170  
   171  		res, err = c.Collection.BulkWrite(ctx, models, opts...)
   172  		return err
   173  	}, acceptable)
   174  
   175  	return
   176  }
   177  
   178  func (c *decoratedCollection) CountDocuments(ctx context.Context, filter interface{},
   179  	opts ...*mopt.CountOptions) (count int64, err error) {
   180  	ctx, span := startSpan(ctx, countDocuments)
   181  	defer func() {
   182  		endSpan(span, err)
   183  	}()
   184  
   185  	err = c.brk.DoWithAcceptable(func() error {
   186  		startTime := timex.Now()
   187  		defer func() {
   188  			c.logDurationSimple(ctx, countDocuments, startTime, err)
   189  		}()
   190  
   191  		count, err = c.Collection.CountDocuments(ctx, filter, opts...)
   192  		return err
   193  	}, acceptable)
   194  
   195  	return
   196  }
   197  
   198  func (c *decoratedCollection) DeleteMany(ctx context.Context, filter interface{},
   199  	opts ...*mopt.DeleteOptions) (res *mongo.DeleteResult, err error) {
   200  	ctx, span := startSpan(ctx, deleteMany)
   201  	defer func() {
   202  		endSpan(span, err)
   203  	}()
   204  
   205  	err = c.brk.DoWithAcceptable(func() error {
   206  		startTime := timex.Now()
   207  		defer func() {
   208  			c.logDurationSimple(ctx, deleteMany, startTime, err)
   209  		}()
   210  
   211  		res, err = c.Collection.DeleteMany(ctx, filter, opts...)
   212  		return err
   213  	}, acceptable)
   214  
   215  	return
   216  }
   217  
   218  func (c *decoratedCollection) DeleteOne(ctx context.Context, filter interface{},
   219  	opts ...*mopt.DeleteOptions) (res *mongo.DeleteResult, err error) {
   220  	ctx, span := startSpan(ctx, deleteOne)
   221  	defer func() {
   222  		endSpan(span, err)
   223  	}()
   224  
   225  	err = c.brk.DoWithAcceptable(func() error {
   226  		startTime := timex.Now()
   227  		defer func() {
   228  			c.logDuration(ctx, deleteOne, startTime, err, filter)
   229  		}()
   230  
   231  		res, err = c.Collection.DeleteOne(ctx, filter, opts...)
   232  		return err
   233  	}, acceptable)
   234  
   235  	return
   236  }
   237  
   238  func (c *decoratedCollection) Distinct(ctx context.Context, fieldName string, filter interface{},
   239  	opts ...*mopt.DistinctOptions) (val []interface{}, err error) {
   240  	ctx, span := startSpan(ctx, distinct)
   241  	defer func() {
   242  		endSpan(span, err)
   243  	}()
   244  
   245  	err = c.brk.DoWithAcceptable(func() error {
   246  		startTime := timex.Now()
   247  		defer func() {
   248  			c.logDurationSimple(ctx, distinct, startTime, err)
   249  		}()
   250  
   251  		val, err = c.Collection.Distinct(ctx, fieldName, filter, opts...)
   252  		return err
   253  	}, acceptable)
   254  
   255  	return
   256  }
   257  
   258  func (c *decoratedCollection) EstimatedDocumentCount(ctx context.Context,
   259  	opts ...*mopt.EstimatedDocumentCountOptions) (val int64, err error) {
   260  	ctx, span := startSpan(ctx, estimatedDocumentCount)
   261  	defer func() {
   262  		endSpan(span, err)
   263  	}()
   264  
   265  	err = c.brk.DoWithAcceptable(func() error {
   266  		startTime := timex.Now()
   267  		defer func() {
   268  			c.logDurationSimple(ctx, estimatedDocumentCount, startTime, err)
   269  		}()
   270  
   271  		val, err = c.Collection.EstimatedDocumentCount(ctx, opts...)
   272  		return err
   273  	}, acceptable)
   274  
   275  	return
   276  }
   277  
   278  func (c *decoratedCollection) Find(ctx context.Context, filter interface{},
   279  	opts ...*mopt.FindOptions) (cur *mongo.Cursor, err error) {
   280  	ctx, span := startSpan(ctx, find)
   281  	defer func() {
   282  		endSpan(span, err)
   283  	}()
   284  
   285  	err = c.brk.DoWithAcceptable(func() error {
   286  		startTime := timex.Now()
   287  		defer func() {
   288  			c.logDuration(ctx, find, startTime, err, filter)
   289  		}()
   290  
   291  		cur, err = c.Collection.Find(ctx, filter, opts...)
   292  		return err
   293  	}, acceptable)
   294  
   295  	return
   296  }
   297  
   298  func (c *decoratedCollection) FindOne(ctx context.Context, filter interface{},
   299  	opts ...*mopt.FindOneOptions) (res *mongo.SingleResult, err error) {
   300  	ctx, span := startSpan(ctx, findOne)
   301  	defer func() {
   302  		endSpan(span, err)
   303  	}()
   304  
   305  	err = c.brk.DoWithAcceptable(func() error {
   306  		startTime := timex.Now()
   307  		defer func() {
   308  			c.logDuration(ctx, findOne, startTime, err, filter)
   309  		}()
   310  
   311  		res = c.Collection.FindOne(ctx, filter, opts...)
   312  		err = res.Err()
   313  		return err
   314  	}, acceptable)
   315  
   316  	return
   317  }
   318  
   319  func (c *decoratedCollection) FindOneAndDelete(ctx context.Context, filter interface{},
   320  	opts ...*mopt.FindOneAndDeleteOptions) (res *mongo.SingleResult, err error) {
   321  	ctx, span := startSpan(ctx, findOneAndDelete)
   322  	defer func() {
   323  		endSpan(span, err)
   324  	}()
   325  
   326  	err = c.brk.DoWithAcceptable(func() error {
   327  		startTime := timex.Now()
   328  		defer func() {
   329  			c.logDuration(ctx, findOneAndDelete, startTime, err, filter)
   330  		}()
   331  
   332  		res = c.Collection.FindOneAndDelete(ctx, filter, opts...)
   333  		err = res.Err()
   334  		return err
   335  	}, acceptable)
   336  
   337  	return
   338  }
   339  
   340  func (c *decoratedCollection) FindOneAndReplace(ctx context.Context, filter interface{},
   341  	replacement interface{}, opts ...*mopt.FindOneAndReplaceOptions) (
   342  	res *mongo.SingleResult, err error) {
   343  	ctx, span := startSpan(ctx, findOneAndReplace)
   344  	defer func() {
   345  		endSpan(span, err)
   346  	}()
   347  
   348  	err = c.brk.DoWithAcceptable(func() error {
   349  		startTime := timex.Now()
   350  		defer func() {
   351  			c.logDuration(ctx, findOneAndReplace, startTime, err, filter, replacement)
   352  		}()
   353  
   354  		res = c.Collection.FindOneAndReplace(ctx, filter, replacement, opts...)
   355  		err = res.Err()
   356  		return err
   357  	}, acceptable)
   358  
   359  	return
   360  }
   361  
   362  func (c *decoratedCollection) FindOneAndUpdate(ctx context.Context, filter, update interface{},
   363  	opts ...*mopt.FindOneAndUpdateOptions) (res *mongo.SingleResult, err error) {
   364  	ctx, span := startSpan(ctx, findOneAndUpdate)
   365  	defer func() {
   366  		endSpan(span, err)
   367  	}()
   368  
   369  	err = c.brk.DoWithAcceptable(func() error {
   370  		startTime := timex.Now()
   371  		defer func() {
   372  			c.logDuration(ctx, findOneAndUpdate, startTime, err, filter, update)
   373  		}()
   374  
   375  		res = c.Collection.FindOneAndUpdate(ctx, filter, update, opts...)
   376  		err = res.Err()
   377  		return err
   378  	}, acceptable)
   379  
   380  	return
   381  }
   382  
   383  func (c *decoratedCollection) InsertMany(ctx context.Context, documents []interface{},
   384  	opts ...*mopt.InsertManyOptions) (res *mongo.InsertManyResult, err error) {
   385  	ctx, span := startSpan(ctx, insertMany)
   386  	defer func() {
   387  		endSpan(span, err)
   388  	}()
   389  
   390  	err = c.brk.DoWithAcceptable(func() error {
   391  		startTime := timex.Now()
   392  		defer func() {
   393  			c.logDurationSimple(ctx, insertMany, startTime, err)
   394  		}()
   395  
   396  		res, err = c.Collection.InsertMany(ctx, documents, opts...)
   397  		return err
   398  	}, acceptable)
   399  
   400  	return
   401  }
   402  
   403  func (c *decoratedCollection) InsertOne(ctx context.Context, document interface{},
   404  	opts ...*mopt.InsertOneOptions) (res *mongo.InsertOneResult, err error) {
   405  	ctx, span := startSpan(ctx, insertOne)
   406  	defer func() {
   407  		endSpan(span, err)
   408  	}()
   409  
   410  	err = c.brk.DoWithAcceptable(func() error {
   411  		startTime := timex.Now()
   412  		defer func() {
   413  			c.logDuration(ctx, insertOne, startTime, err, document)
   414  		}()
   415  
   416  		res, err = c.Collection.InsertOne(ctx, document, opts...)
   417  		return err
   418  	}, acceptable)
   419  
   420  	return
   421  }
   422  
   423  func (c *decoratedCollection) ReplaceOne(ctx context.Context, filter, replacement interface{},
   424  	opts ...*mopt.ReplaceOptions) (res *mongo.UpdateResult, err error) {
   425  	ctx, span := startSpan(ctx, replaceOne)
   426  	defer func() {
   427  		endSpan(span, err)
   428  	}()
   429  
   430  	err = c.brk.DoWithAcceptable(func() error {
   431  		startTime := timex.Now()
   432  		defer func() {
   433  			c.logDuration(ctx, replaceOne, startTime, err, filter, replacement)
   434  		}()
   435  
   436  		res, err = c.Collection.ReplaceOne(ctx, filter, replacement, opts...)
   437  		return err
   438  	}, acceptable)
   439  
   440  	return
   441  }
   442  
   443  func (c *decoratedCollection) UpdateByID(ctx context.Context, id, update interface{},
   444  	opts ...*mopt.UpdateOptions) (res *mongo.UpdateResult, err error) {
   445  	ctx, span := startSpan(ctx, updateByID)
   446  	defer func() {
   447  		endSpan(span, err)
   448  	}()
   449  
   450  	err = c.brk.DoWithAcceptable(func() error {
   451  		startTime := timex.Now()
   452  		defer func() {
   453  			c.logDuration(ctx, updateByID, startTime, err, id, update)
   454  		}()
   455  
   456  		res, err = c.Collection.UpdateByID(ctx, id, update, opts...)
   457  		return err
   458  	}, acceptable)
   459  
   460  	return
   461  }
   462  
   463  func (c *decoratedCollection) UpdateMany(ctx context.Context, filter, update interface{},
   464  	opts ...*mopt.UpdateOptions) (res *mongo.UpdateResult, err error) {
   465  	ctx, span := startSpan(ctx, updateMany)
   466  	defer func() {
   467  		endSpan(span, err)
   468  	}()
   469  
   470  	err = c.brk.DoWithAcceptable(func() error {
   471  		startTime := timex.Now()
   472  		defer func() {
   473  			c.logDurationSimple(ctx, updateMany, startTime, err)
   474  		}()
   475  
   476  		res, err = c.Collection.UpdateMany(ctx, filter, update, opts...)
   477  		return err
   478  	}, acceptable)
   479  
   480  	return
   481  }
   482  
   483  func (c *decoratedCollection) UpdateOne(ctx context.Context, filter, update interface{},
   484  	opts ...*mopt.UpdateOptions) (res *mongo.UpdateResult, err error) {
   485  	ctx, span := startSpan(ctx, updateOne)
   486  	defer func() {
   487  		endSpan(span, err)
   488  	}()
   489  
   490  	err = c.brk.DoWithAcceptable(func() error {
   491  		startTime := timex.Now()
   492  		defer func() {
   493  			c.logDuration(ctx, updateOne, startTime, err, filter, update)
   494  		}()
   495  
   496  		res, err = c.Collection.UpdateOne(ctx, filter, update, opts...)
   497  		return err
   498  	}, acceptable)
   499  
   500  	return
   501  }
   502  
   503  func (c *decoratedCollection) logDuration(ctx context.Context, method string,
   504  	startTime time.Duration, err error, docs ...interface{}) {
   505  	duration := timex.Since(startTime)
   506  	logger := logx.WithContext(ctx).WithDuration(duration)
   507  
   508  	content, jerr := json.Marshal(docs)
   509  	// jerr should not be non-nil, but we don't care much on this,
   510  	// if non-nil, we just log without docs.
   511  	if jerr != nil {
   512  		if err != nil {
   513  			if duration > slowThreshold.Load() {
   514  				logger.Slowf("[MONGO] mongo(%s) - slowcall - %s - fail(%s)", c.name, method, err.Error())
   515  			} else {
   516  				logger.Infof("mongo(%s) - %s - fail(%s)", c.name, method, err.Error())
   517  			}
   518  		} else {
   519  			if duration > slowThreshold.Load() {
   520  				logger.Slowf("[MONGO] mongo(%s) - slowcall - %s - ok", c.name, method)
   521  			} else {
   522  				logger.Infof("mongo(%s) - %s - ok", c.name, method)
   523  			}
   524  		}
   525  	} else if err != nil {
   526  		if duration > slowThreshold.Load() {
   527  			logger.Slowf("[MONGO] mongo(%s) - slowcall - %s - fail(%s) - %s",
   528  				c.name, method, err.Error(), string(content))
   529  		} else {
   530  			logger.Infof("mongo(%s) - %s - fail(%s) - %s",
   531  				c.name, method, err.Error(), string(content))
   532  		}
   533  	} else {
   534  		if duration > slowThreshold.Load() {
   535  			logger.Slowf("[MONGO] mongo(%s) - slowcall - %s - ok - %s",
   536  				c.name, method, string(content))
   537  		} else {
   538  			logger.Infof("mongo(%s) - %s - ok - %s", c.name, method, string(content))
   539  		}
   540  	}
   541  }
   542  
   543  func (c *decoratedCollection) logDurationSimple(ctx context.Context, method string, startTime time.Duration, err error) {
   544  	logDuration(ctx, c.name, method, startTime, err)
   545  }
   546  
   547  func (p keepablePromise) accept(err error) error {
   548  	p.promise.Accept()
   549  	p.log(err)
   550  	return err
   551  }
   552  
   553  func (p keepablePromise) keep(err error) error {
   554  	if acceptable(err) {
   555  		p.promise.Accept()
   556  	} else {
   557  		p.promise.Reject(err.Error())
   558  	}
   559  
   560  	p.log(err)
   561  	return err
   562  }
   563  
   564  func acceptable(err error) bool {
   565  	return err == nil || err == mongo.ErrNoDocuments || err == mongo.ErrNilValue ||
   566  		err == mongo.ErrNilDocument || err == mongo.ErrNilCursor || err == mongo.ErrEmptySlice ||
   567  		// session errors
   568  		err == session.ErrSessionEnded || err == session.ErrNoTransactStarted ||
   569  		err == session.ErrTransactInProgress || err == session.ErrAbortAfterCommit ||
   570  		err == session.ErrAbortTwice || err == session.ErrCommitAfterAbort ||
   571  		err == session.ErrUnackWCUnsupported || err == session.ErrSnapshotTransaction
   572  }