diff --git a/pkg/controller/perconaservermongodb/mgo.go b/pkg/controller/perconaservermongodb/mgo.go index e9c5d032b3..03f83b83f1 100644 --- a/pkg/controller/perconaservermongodb/mgo.go +++ b/pkg/controller/perconaservermongodb/mgo.go @@ -376,14 +376,8 @@ func (r *ReconcilePerconaServerMongoDB) getConfigMemberForExternalNode(id int, e func (r *ReconcilePerconaServerMongoDB) updateConfigMembers(ctx context.Context, cli mongo.Client, cr *api.PerconaServerMongoDB, rs *api.ReplsetSpec) (map[string]api.ReplsetMemberStatus, int, error) { log := logf.FromContext(ctx) // Primary with a Secondary and an Arbiter (PSA) - unsafePSA := false rsMembers := make(map[string]api.ReplsetMemberStatus) - - if cr.CompareVersion("1.15.0") <= 0 { - unsafePSA = cr.Spec.UnsafeConf && rs.Arbiter.Enabled && rs.Arbiter.Size == 1 && !rs.NonVoting.Enabled && rs.Size == 2 - } else { - unsafePSA = cr.Spec.Unsafe.ReplsetSize && rs.Arbiter.Enabled && rs.Arbiter.Size == 1 && !rs.NonVoting.Enabled && rs.Size == 2 - } + unsafePSA := cr.Spec.Unsafe.ReplsetSize && rs.Arbiter.Enabled && rs.Arbiter.Size == 1 && !rs.NonVoting.Enabled && rs.Size == 2 pods, err := psmdb.GetRSPods(ctx, r.client, cr, rs.Name) if err != nil { @@ -470,13 +464,20 @@ func (r *ReconcilePerconaServerMongoDB) updateConfigMembers(ctx context.Context, } } - if cnf.Members.AddNew(ctx, members) { + if cnf.Members.RemoveArbiterIfNeeded(ctx, unsafePSA) { + cnf.Version++ + + log.Info("Removing arbiter members", "replset", rs.Name) + + if err := cli.WriteConfig(ctx, cnf, false); err != nil { + return rsMembers, 0, errors.Wrap(err, "remove arbiter if needed: write mongo config") + } + } else if cnf.Members.AddNew(ctx, members) { cnf.Version++ log.Info("Adding new nodes", "replset", rs.Name) - err = cli.WriteConfig(ctx, cnf, false) - if err != nil { + if err := cli.WriteConfig(ctx, cnf, false); err != nil { return rsMembers, 0, errors.Wrap(err, "add new: write mongo config") } } diff --git a/pkg/psmdb/mongo/mongo.go b/pkg/psmdb/mongo/mongo.go index f524b8b34e..f84939b265 100644 --- a/pkg/psmdb/mongo/mongo.go +++ b/pkg/psmdb/mongo/mongo.go @@ -260,7 +260,6 @@ func (client *mongoClient) UpdateRole(ctx context.Context, db string, role Role) } return nil - } func (client *mongoClient) GetRole(ctx context.Context, db, role string) (*Role, error) { @@ -901,6 +900,37 @@ func (m *ConfigMembers) AddNew(ctx context.Context, from ConfigMembers) bool { return false } +func (m *ConfigMembers) RemoveArbiterIfNeeded(ctx context.Context, unsafePSA bool) bool { + if !unsafePSA { + return false + } + + votingMembers := 0 + hasArbiter := false + for _, member := range *m { + if member.ArbiterOnly { + hasArbiter = true + continue + } + if member.Votes > 0 { + votingMembers++ + } + } + if !hasArbiter || votingMembers > 1 { + return false + } + log := logf.FromContext(ctx) + + for i := len(*m) - 1; i >= 0 && len(*m) > 1; i-- { + member := []ConfigMember(*m)[i] + if member.ArbiterOnly { + log.Info("Removing arbiter member because of 1 writable node", "_id", member.ID, "host", member.Host) + *m = append([]ConfigMember(*m)[:i], []ConfigMember(*m)[i+1:]...) + } + } + return true +} + func (m *ConfigMembers) setMemberVoteAndPriority(i int, votes, priority int) { (*m)[i].Votes = votes (*m)[i].Priority = priority @@ -990,7 +1020,7 @@ func (m *ConfigMembers) SetVotes(compareWith ConfigMembers, unsafePSA bool) { return } - if votes%2 == 0 { + if votes%2 == 0 && !unsafePSA { for j := lastVoteIdx; j >= 0; j-- { if []ConfigMember(*m)[j].Votes == 0 { continue diff --git a/pkg/psmdb/mongo/mongo_test.go b/pkg/psmdb/mongo/mongo_test.go index b485fc8f3c..0495b5c1c3 100644 --- a/pkg/psmdb/mongo/mongo_test.go +++ b/pkg/psmdb/mongo/mongo_test.go @@ -209,6 +209,32 @@ func TestVoting(t *testing.T) { }, true, }, + { + "2 members (unsafe PSA start): 2 rs0 without arbiter", + &mongo.ConfigMembers{ + mongo.ConfigMember{ + Host: "host0", + Votes: mongo.DefaultVotes, + Priority: mongo.DefaultPriority, + }, + mongo.ConfigMember{ + Host: "host1", + Votes: mongo.DefaultVotes, + Priority: mongo.DefaultPriority, + }, + }, + &mongo.ConfigMembers{ + mongo.ConfigMember{ + Votes: mongo.DefaultVotes, + Priority: mongo.DefaultPriority, + }, + mongo.ConfigMember{ + Votes: mongo.DefaultVotes, + Priority: mongo.DefaultPriority, + }, + }, + true, + }, { "4 members: 4 rs0", &mongo.ConfigMembers{ @@ -1297,7 +1323,7 @@ func TestVoting(t *testing.T) { } assert.Falsef(t, votes > mongo.MaxVotingMembers, "there should be max (%d) votes in replset", mongo.MaxVotingMembers) - if votes != 0 { + if votes != 0 && !c.unsafePSA { assert.Falsef(t, votes%2 == 0, "total votes (%d) should be an odd number", votes) } })