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 }