github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/lorry/engines/mongodb/manager.go (about) 1 /* 2 Copyright (C) 2022-2023 ApeCloud Co., Ltd 3 4 This file is part of KubeBlocks project 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU Affero General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU Affero General Public License for more details. 15 16 You should have received a copy of the GNU Affero General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 package mongodb 21 22 import ( 23 "context" 24 "encoding/json" 25 "fmt" 26 "math/rand" 27 "strings" 28 "time" 29 30 "github.com/pkg/errors" 31 "go.mongodb.org/mongo-driver/bson" 32 "go.mongodb.org/mongo-driver/mongo" 33 "go.mongodb.org/mongo-driver/mongo/options" 34 "go.mongodb.org/mongo-driver/mongo/readpref" 35 "go.mongodb.org/mongo-driver/mongo/writeconcern" 36 ctrl "sigs.k8s.io/controller-runtime" 37 38 "github.com/1aal/kubeblocks/pkg/lorry/dcs" 39 "github.com/1aal/kubeblocks/pkg/lorry/engines" 40 ) 41 42 const ( 43 PrimaryPriority = 2 44 SecondaryPriority = 1 45 46 ServiceType = "mongodb" 47 ) 48 49 type Manager struct { 50 engines.DBManagerBase 51 Client *mongo.Client 52 Database *mongo.Database 53 } 54 55 var Mgr *Manager 56 var _ engines.DBManager = &Manager{} 57 58 func NewManager(properties engines.Properties) (engines.DBManager, error) { 59 ctx := context.Background() 60 logger := ctrl.Log.WithName("MongoDB") 61 config, err := NewConfig(properties) 62 if err != nil { 63 return nil, err 64 } 65 66 opts := options.Client(). 67 SetHosts(config.hosts). 68 SetReplicaSet(config.replSetName). 69 SetAuth(options.Credential{ 70 Password: config.password, 71 Username: config.username, 72 }). 73 SetWriteConcern(writeconcern.New(writeconcern.WMajority(), writeconcern.J(true))). 74 SetReadPreference(readpref.Primary()). 75 SetDirect(config.direct) 76 77 client, err := mongo.Connect(ctx, opts) 78 if err != nil { 79 return nil, errors.Wrap(err, "connect to mongodb") 80 } 81 82 defer func() { 83 if err != nil { 84 derr := client.Disconnect(ctx) 85 if derr != nil { 86 logger.Error(err, "failed to disconnect") 87 } 88 } 89 }() 90 91 managerBase, err := engines.NewDBManagerBase(logger) 92 if err != nil { 93 return nil, err 94 } 95 96 Mgr = &Manager{ 97 DBManagerBase: *managerBase, 98 Client: client, 99 Database: client.Database(config.databaseName), 100 } 101 102 return Mgr, nil 103 } 104 105 func (mgr *Manager) InitializeCluster(ctx context.Context, cluster *dcs.Cluster) error { 106 return mgr.InitiateReplSet(ctx, cluster) 107 } 108 109 // InitiateReplSet is a method to create MongoDB cluster 110 func (mgr *Manager) InitiateReplSet(ctx context.Context, cluster *dcs.Cluster) error { 111 configMembers := make([]ConfigMember, len(cluster.Members)) 112 113 for i, member := range cluster.Members { 114 configMembers[i].ID = i 115 configMembers[i].Host = cluster.GetMemberAddrWithPort(member) 116 if strings.HasPrefix(member.Name, mgr.CurrentMemberName) { 117 configMembers[i].Priority = PrimaryPriority 118 } else { 119 configMembers[i].Priority = SecondaryPriority 120 } 121 } 122 123 config := RSConfig{ 124 ID: mgr.ClusterCompName, 125 Members: configMembers, 126 } 127 client, err := NewLocalUnauthClient(ctx) 128 if err != nil { 129 mgr.Logger.Error(err, "Get local unauth client failed") 130 return err 131 } 132 defer client.Disconnect(context.TODO()) //nolint:errcheck 133 134 configJSON, _ := json.Marshal(config) 135 mgr.Logger.Info(fmt.Sprintf("Initial Replset Config: %s", string(configJSON))) 136 response := client.Database("admin").RunCommand(ctx, bson.M{"replSetInitiate": config}) 137 if response.Err() != nil { 138 return response.Err() 139 } 140 return nil 141 } 142 143 // IsClusterInitialized is a method to check if cluster is initailized or not 144 func (mgr *Manager) IsClusterInitialized(ctx context.Context, cluster *dcs.Cluster) (bool, error) { 145 client, err := mgr.GetReplSetClient(ctx, cluster) 146 if err != nil { 147 mgr.Logger.Info("Get leader client failed", "error", err) 148 return false, err 149 } 150 defer client.Disconnect(ctx) //nolint:errcheck 151 152 ctx1, cancel := context.WithTimeout(ctx, 1000*time.Millisecond) 153 defer cancel() 154 rsStatus, err := GetReplSetStatus(ctx1, client) 155 if rsStatus != nil { 156 return rsStatus.Set != "", nil 157 } 158 mgr.Logger.Info("Get replSet status failed", "error", err) 159 160 if !mgr.IsFirstMember() { 161 return false, nil 162 } 163 164 client, err = NewLocalUnauthClient(ctx) 165 if err != nil { 166 mgr.Logger.Info("Get local unauth client failed", "error", err) 167 return false, err 168 } 169 defer client.Disconnect(ctx) //nolint:errcheck 170 171 rsStatus, err = GetReplSetStatus(ctx, client) 172 if rsStatus != nil { 173 return rsStatus.Set != "", nil 174 } 175 176 err = errors.Cause(err) 177 if cmdErr, ok := err.(mongo.CommandError); ok && cmdErr.Name == "NotYetInitialized" { 178 return false, nil 179 } 180 mgr.Logger.Info("Get replSet status with local unauth client failed", "error", err) 181 182 rsStatus, err = mgr.GetReplSetStatus(ctx) 183 if rsStatus != nil { 184 return rsStatus.Set != "", nil 185 } 186 if err != nil { 187 mgr.Logger.Info("Get replSet status with local auth client failed", "error", err) 188 return false, err 189 } 190 191 mgr.Logger.Info("Get replSet status failed", "error", err) 192 return false, err 193 } 194 195 func (mgr *Manager) IsRootCreated(ctx context.Context) (bool, error) { 196 if !mgr.IsFirstMember() { 197 return true, nil 198 } 199 200 client, err := NewLocalUnauthClient(ctx) 201 if err != nil { 202 mgr.Logger.Info("Get local unauth client failed", "error", err) 203 return false, err 204 } 205 defer client.Disconnect(ctx) //nolint:errcheck 206 207 _, err = GetReplSetStatus(ctx, client) 208 if err == nil { 209 return false, nil 210 } 211 err = errors.Cause(err) 212 if cmdErr, ok := err.(mongo.CommandError); ok && cmdErr.Name == "Unauthorized" { 213 return true, nil 214 } 215 216 mgr.Logger.Info("Get replSet status with local unauth client failed", "error", err) 217 218 _, err = mgr.GetReplSetStatus(ctx) 219 if err == nil { 220 return true, nil 221 } 222 223 mgr.Logger.Info("Get replSet status with local auth client failed", "error", err) 224 return false, err 225 226 } 227 228 func (mgr *Manager) CreateRoot(ctx context.Context) error { 229 if !mgr.IsFirstMember() { 230 return nil 231 } 232 233 client, err := NewLocalUnauthClient(ctx) 234 if err != nil { 235 mgr.Logger.Info("Get local unauth client failed", "error", err) 236 return err 237 } 238 defer client.Disconnect(ctx) //nolint:errcheck 239 240 role := map[string]interface{}{ 241 "role": "root", 242 "db": "admin", 243 } 244 245 mgr.Logger.Info(fmt.Sprintf("Create user: %s, passwd: %s, roles: %v", config.username, config.password, role)) 246 err = CreateUser(ctx, client, config.username, config.password, role) 247 if err != nil { 248 mgr.Logger.Info("Create Root failed", "error", err) 249 return err 250 } 251 252 return nil 253 } 254 255 func (mgr *Manager) IsRunning() bool { 256 // ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) 257 // defer cancel() 258 259 // err := mgr.Client.Ping(ctx, readpref.Nearest()) 260 // if err != nil { 261 // mgr.Logger.Infof("DB is not ready: %v", err) 262 // return false 263 // } 264 return true 265 } 266 267 func (mgr *Manager) IsDBStartupReady() bool { 268 if mgr.DBStartupReady { 269 return true 270 } 271 ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) 272 defer cancel() 273 274 err := mgr.Client.Ping(ctx, readpref.Primary()) 275 if err != nil { 276 mgr.Logger.Info("DB is not ready", "error", err) 277 return false 278 } 279 mgr.DBStartupReady = true 280 mgr.Logger.Info("DB startup ready") 281 return true 282 } 283 284 func (mgr *Manager) GetMemberState(ctx context.Context) (string, error) { 285 status, err := mgr.GetReplSetStatus(ctx) 286 if err != nil { 287 mgr.Logger.Error(err, "rs.status() error") 288 return "", err 289 } 290 291 self := status.GetSelf() 292 if self == nil { 293 return "", nil 294 } 295 return strings.ToLower(self.StateStr), nil 296 } 297 298 func (mgr *Manager) GetReplSetStatus(ctx context.Context) (*ReplSetStatus, error) { 299 return GetReplSetStatus(ctx, mgr.Client) 300 } 301 302 func (mgr *Manager) IsLeaderMember(ctx context.Context, cluster *dcs.Cluster, dcsMember *dcs.Member) (bool, error) { 303 status, err := mgr.GetReplSetStatus(ctx) 304 if err != nil { 305 mgr.Logger.Error(err, "rs.status() error") 306 return false, err 307 } 308 for _, member := range status.Members { 309 if strings.HasPrefix(member.Name, dcsMember.Name) { 310 if member.StateStr == "PRIMARY" { 311 return true, nil 312 } 313 break 314 } 315 } 316 return false, nil 317 } 318 319 func (mgr *Manager) IsLeader(ctx context.Context, cluster *dcs.Cluster) (bool, error) { 320 cur := mgr.Client.Database("admin").RunCommand(ctx, bson.D{{Key: "isMaster", Value: 1}}) 321 if cur.Err() != nil { 322 return false, errors.Wrap(cur.Err(), "run isMaster") 323 } 324 325 resp := IsMasterResp{} 326 if err := cur.Decode(&resp); err != nil { 327 return false, errors.Wrap(err, "decode isMaster response") 328 } 329 330 if resp.OK != 1 { 331 return false, errors.Errorf("mongo says: %s", resp.Errmsg) 332 } 333 334 return resp.IsMaster, nil 335 } 336 337 func (mgr *Manager) GetReplSetConfig(ctx context.Context) (*RSConfig, error) { 338 return GetReplSetConfig(ctx, mgr.Client) 339 } 340 341 func (mgr *Manager) GetMemberAddrs(ctx context.Context, cluster *dcs.Cluster) []string { 342 client, err := mgr.GetReplSetClient(ctx, cluster) 343 if err != nil { 344 mgr.Logger.Error(err, "Get replSet client failed") 345 return nil 346 } 347 defer client.Disconnect(ctx) //nolint:errcheck 348 349 rsConfig, err := GetReplSetConfig(ctx, client) 350 if rsConfig == nil { 351 mgr.Logger.Error(err, "Get replSet config failed") 352 return nil 353 } 354 355 return mgr.GetMemberAddrsFromRSConfig(rsConfig) 356 } 357 358 func (mgr *Manager) GetMemberAddrsFromRSConfig(rsConfig *RSConfig) []string { 359 if rsConfig == nil { 360 return []string{} 361 } 362 363 hosts := make([]string, len(rsConfig.Members)) 364 for i, member := range rsConfig.Members { 365 hosts[i] = member.Host 366 } 367 return hosts 368 } 369 370 func (mgr *Manager) GetReplSetClient(ctx context.Context, cluster *dcs.Cluster) (*mongo.Client, error) { 371 hosts := cluster.GetMemberAddrs() 372 return NewReplSetClient(ctx, hosts) 373 } 374 375 func (mgr *Manager) GetLeaderClient(ctx context.Context, cluster *dcs.Cluster) (*mongo.Client, error) { 376 if cluster.Leader == nil || cluster.Leader.Name == "" { 377 return nil, fmt.Errorf("cluster has no leader") 378 } 379 380 leaderMember := cluster.GetMemberWithName(cluster.Leader.Name) 381 host := cluster.GetMemberAddrWithPort(*leaderMember) 382 return NewReplSetClient(context.TODO(), []string{host}) 383 } 384 385 func (mgr *Manager) GetReplSetClientWithHosts(ctx context.Context, hosts []string) (*mongo.Client, error) { 386 if len(hosts) == 0 { 387 err := errors.New("Get replset client whitout hosts") 388 mgr.Logger.Error(err, "Get replset client whitout hosts") 389 return nil, err 390 } 391 392 opts := options.Client(). 393 SetHosts(hosts). 394 SetReplicaSet(config.replSetName). 395 SetAuth(options.Credential{ 396 Password: config.password, 397 Username: config.username, 398 }). 399 SetWriteConcern(writeconcern.New(writeconcern.WMajority(), writeconcern.J(true))). 400 SetReadPreference(readpref.Primary()). 401 SetDirect(false) 402 403 client, err := mongo.Connect(ctx, opts) 404 if err != nil { 405 return nil, errors.Wrap(err, "connect to mongodb") 406 } 407 return client, err 408 } 409 410 func (mgr *Manager) IsCurrentMemberInCluster(ctx context.Context, cluster *dcs.Cluster) bool { 411 client, err := mgr.GetReplSetClient(ctx, cluster) 412 if err != nil { 413 mgr.Logger.Error(err, "Get replSet client failed") 414 return true 415 } 416 defer client.Disconnect(ctx) //nolint:errcheck 417 418 rsConfig, err := GetReplSetConfig(ctx, client) 419 if rsConfig == nil { 420 mgr.Logger.Error(err, "Get replSet config failed") 421 // 422 return true 423 } 424 425 for _, member := range rsConfig.Members { 426 if strings.HasPrefix(member.Host, mgr.GetCurrentMemberName()) { 427 return true 428 } 429 } 430 431 return false 432 } 433 434 func (mgr *Manager) IsCurrentMemberHealthy(ctx context.Context, cluster *dcs.Cluster) bool { 435 return mgr.IsMemberHealthy(ctx, cluster, nil) 436 } 437 438 func (mgr *Manager) IsMemberHealthy(ctx context.Context, cluster *dcs.Cluster, member *dcs.Member) bool { 439 var memberName string 440 if member != nil { 441 memberName = member.Name 442 } else { 443 memberName = mgr.CurrentMemberName 444 } 445 446 rsStatus, _ := mgr.GetReplSetStatus(ctx) 447 if rsStatus == nil { 448 return false 449 } 450 451 for _, member := range rsStatus.Members { 452 if strings.HasPrefix(member.Name, memberName) && member.Health == 1 { 453 return true 454 } 455 } 456 return false 457 } 458 459 func (mgr *Manager) Recover(context.Context) error { 460 return nil 461 } 462 463 func (mgr *Manager) JoinCurrentMemberToCluster(ctx context.Context, cluster *dcs.Cluster) error { 464 client, err := mgr.GetReplSetClient(ctx, cluster) 465 if err != nil { 466 return err 467 } 468 defer client.Disconnect(ctx) //nolint:errcheck 469 470 currentMember := cluster.GetMemberWithName(mgr.GetCurrentMemberName()) 471 currentHost := cluster.GetMemberAddrWithPort(*currentMember) 472 rsConfig, err := GetReplSetConfig(ctx, client) 473 if rsConfig == nil { 474 mgr.Logger.Error(err, "Get replSet config failed") 475 return err 476 } 477 478 var lastID int 479 var configMember ConfigMember 480 for _, configMember = range rsConfig.Members { 481 if configMember.ID > lastID { 482 lastID = configMember.ID 483 } 484 } 485 configMember.ID = lastID + 1 486 configMember.Host = currentHost 487 configMember.Priority = SecondaryPriority 488 rsConfig.Members = append(rsConfig.Members, configMember) 489 490 rsConfig.Version++ 491 return SetReplSetConfig(ctx, client, rsConfig) 492 } 493 494 func (mgr *Manager) LeaveMemberFromCluster(ctx context.Context, cluster *dcs.Cluster, memberName string) error { 495 client, err := mgr.GetReplSetClient(ctx, cluster) 496 if err != nil { 497 return err 498 } 499 defer client.Disconnect(ctx) //nolint:errcheck 500 501 rsConfig, err := GetReplSetConfig(ctx, client) 502 if rsConfig == nil { 503 mgr.Logger.Error(err, "Get replSet config failed") 504 return err 505 } 506 507 mgr.Logger.Info(fmt.Sprintf("Delete member: %s", memberName)) 508 configMembers := make([]ConfigMember, 0, len(rsConfig.Members)-1) 509 for _, configMember := range rsConfig.Members { 510 if strings.HasPrefix(configMember.Host, memberName) { 511 configMembers = append(configMembers, configMember) 512 } 513 } 514 515 rsConfig.Members = configMembers 516 rsConfig.Version++ 517 return SetReplSetConfig(ctx, client, rsConfig) 518 } 519 520 func (mgr *Manager) IsClusterHealthy(ctx context.Context, cluster *dcs.Cluster) bool { 521 client, err := mgr.GetReplSetClient(ctx, cluster) 522 if err != nil { 523 mgr.Logger.Error(err, "Get leader client failed") 524 return false 525 } 526 defer client.Disconnect(ctx) //nolint:errcheck 527 528 status, err := GetReplSetStatus(ctx, client) 529 if err != nil { 530 return false 531 } 532 mgr.Logger.Info(fmt.Sprintf("cluster status: %v", status)) 533 return status.OK != 0 534 } 535 536 func (mgr *Manager) IsPromoted(ctx context.Context) bool { 537 isLeader, err := mgr.IsLeader(ctx, nil) 538 if err != nil || !isLeader { 539 mgr.Logger.Error(err, "Is leader check failed") 540 return false 541 } 542 543 rsConfig, err := mgr.GetReplSetConfig(ctx) 544 if rsConfig == nil { 545 mgr.Logger.Error(err, "Get replSet config failed") 546 return false 547 } 548 for i := range rsConfig.Members { 549 if strings.HasPrefix(rsConfig.Members[i].Host, mgr.CurrentMemberName) { 550 if rsConfig.Members[i].Priority == PrimaryPriority { 551 return true 552 } 553 } 554 } 555 return false 556 } 557 558 func (mgr *Manager) Promote(ctx context.Context, cluster *dcs.Cluster) error { 559 rsConfig, err := mgr.GetReplSetConfig(ctx) 560 if rsConfig == nil { 561 mgr.Logger.Error(err, "Get replSet config failed") 562 return err 563 } 564 565 for i := range rsConfig.Members { 566 if strings.HasPrefix(rsConfig.Members[i].Host, mgr.CurrentMemberName) { 567 if rsConfig.Members[i].Priority == PrimaryPriority { 568 mgr.Logger.Info("Current member already has the highest priority!") 569 return nil 570 } 571 572 rsConfig.Members[i].Priority = PrimaryPriority 573 } else if rsConfig.Members[i].Priority == PrimaryPriority { 574 rsConfig.Members[i].Priority = SecondaryPriority 575 } 576 } 577 578 rsConfig.Version++ 579 580 hosts := mgr.GetMemberAddrsFromRSConfig(rsConfig) 581 client, err := NewReplSetClient(ctx, hosts) 582 if err != nil { 583 return err 584 } 585 defer client.Disconnect(ctx) //nolint:errcheck 586 mgr.Logger.Info("reconfig replset", "config", rsConfig) 587 return SetReplSetConfig(ctx, client, rsConfig) 588 } 589 590 func (mgr *Manager) Demote(context.Context) error { 591 // mongodb do premote and demote in one action, here do nothing. 592 return nil 593 } 594 595 func (mgr *Manager) Follow(ctx context.Context, cluster *dcs.Cluster) error { 596 return nil 597 } 598 599 func (mgr *Manager) GetHealthiestMember(cluster *dcs.Cluster, candidate string) *dcs.Member { 600 rsStatus, _ := mgr.GetReplSetStatus(context.TODO()) 601 if rsStatus == nil { 602 return nil 603 } 604 healthyMembers := make([]string, 0, len(rsStatus.Members)) 605 var leader string 606 for _, member := range rsStatus.Members { 607 if member.Health == 1 { 608 memberName := strings.Split(member.Name, ".")[0] 609 if memberName == candidate { 610 return cluster.GetMemberWithName(candidate) 611 } 612 healthyMembers = append(healthyMembers, memberName) 613 if member.State == 1 { 614 leader = memberName 615 } 616 } 617 } 618 619 if candidate != "" { 620 mgr.Logger.Info("no health member for candidate", "candidate", candidate) 621 return nil 622 } 623 624 if leader != "" { 625 return cluster.GetMemberWithName(leader) 626 } 627 628 // TODO: use lag and other info to pick the healthiest member 629 r := rand.New(rand.NewSource(time.Now().UnixNano())) 630 healthiestMember := healthyMembers[r.Intn(len(healthyMembers))] 631 return cluster.GetMemberWithName(healthiestMember) 632 633 } 634 635 func (mgr *Manager) HasOtherHealthyLeader(ctx context.Context, cluster *dcs.Cluster) *dcs.Member { 636 rsStatus, _ := mgr.GetReplSetStatus(ctx) 637 if rsStatus == nil { 638 return nil 639 } 640 healthMembers := map[string]struct{}{} 641 var otherLeader string 642 for _, member := range rsStatus.Members { 643 memberName := strings.Split(member.Name, ".")[0] 644 if member.State == 1 || member.State == 2 { 645 healthMembers[memberName] = struct{}{} 646 } 647 648 if member.State != 1 { 649 continue 650 } 651 if memberName != mgr.CurrentMemberName { 652 otherLeader = memberName 653 } 654 } 655 if otherLeader != "" { 656 return cluster.GetMemberWithName(otherLeader) 657 } 658 659 rsConfig, err := mgr.GetReplSetConfig(ctx) 660 if rsConfig == nil { 661 mgr.Logger.Error(err, "Get replSet config failed") 662 return nil 663 } 664 665 for _, mb := range rsConfig.Members { 666 memberName := strings.Split(mb.Host, ".")[0] 667 if mb.Priority == PrimaryPriority && memberName != mgr.CurrentMemberName { 668 if _, ok := healthMembers[memberName]; ok { 669 otherLeader = memberName 670 } 671 } 672 } 673 674 if otherLeader != "" { 675 return cluster.GetMemberWithName(otherLeader) 676 } 677 678 return nil 679 } 680 681 // HasOtherHealthyMembers Are there any healthy members other than the leader? 682 func (mgr *Manager) HasOtherHealthyMembers(ctx context.Context, cluster *dcs.Cluster, leader string) []*dcs.Member { 683 members := make([]*dcs.Member, 0) 684 rsStatus, _ := mgr.GetReplSetStatus(ctx) 685 if rsStatus == nil { 686 return members 687 } 688 689 for _, member := range rsStatus.Members { 690 if member.Health != 1 { 691 continue 692 } 693 memberName := strings.Split(member.Name, ".")[0] 694 if memberName == leader { 695 continue 696 } 697 member := cluster.GetMemberWithName(memberName) 698 if member != nil { 699 members = append(members, member) 700 } 701 } 702 703 return members 704 } 705 706 func (mgr *Manager) Lock(ctx context.Context, reason string) error { 707 mgr.Logger.Info(fmt.Sprintf("Lock db: %s", reason)) 708 m := bson.D{ 709 {Key: "fsync", Value: 1}, 710 {Key: "lock", Value: true}, 711 {Key: "comment", Value: reason}, 712 } 713 lockResp := LockResp{} 714 715 response := mgr.Client.Database("admin").RunCommand(ctx, m) 716 if response.Err() != nil { 717 mgr.Logger.Error(response.Err(), fmt.Sprintf("Lock db (%s) failed", reason)) 718 return response.Err() 719 } 720 if err := response.Decode(&lockResp); err != nil { 721 err := errors.Wrap(err, "failed to decode lock response") 722 return err 723 } 724 725 if lockResp.OK != 1 { 726 err := errors.Errorf("mongo says: %s", lockResp.Errmsg) 727 return err 728 } 729 mgr.IsLocked = true 730 mgr.Logger.Info(fmt.Sprintf("Lock db success times: %d", lockResp.LockCount)) 731 return nil 732 } 733 734 func (mgr *Manager) Unlock(ctx context.Context) error { 735 mgr.Logger.Info("Unlock db") 736 m := bson.M{"fsyncUnlock": 1} 737 unlockResp := LockResp{} 738 response := mgr.Client.Database("admin").RunCommand(ctx, m) 739 if response.Err() != nil { 740 mgr.Logger.Error(response.Err(), "Unlock db failed") 741 return response.Err() 742 } 743 if err := response.Decode(&unlockResp); err != nil { 744 err := errors.Wrap(err, "failed to decode unlock response") 745 return err 746 } 747 748 if unlockResp.OK != 1 { 749 err := errors.Errorf("mongo says: %s", unlockResp.Errmsg) 750 return err 751 } 752 for unlockResp.LockCount > 0 { 753 response = mgr.Client.Database("admin").RunCommand(ctx, m) 754 if response.Err() != nil { 755 mgr.Logger.Error(response.Err(), "Unlock db failed") 756 return response.Err() 757 } 758 if err := response.Decode(&unlockResp); err != nil { 759 err := errors.Wrap(err, "failed to decode unlock response") 760 return err 761 } 762 } 763 mgr.IsLocked = false 764 mgr.Logger.Info("Unlock db success") 765 return nil 766 }