@@ -524,26 +524,72 @@ func (c *RaftCluster) PromoteMember(id types.ID, shouldApplyV3 ShouldApplyV3) {
524524 isLearner .Set (0 )
525525 }
526526
527- if c .be != nil && shouldApplyV3 {
528- c .members [id ].RaftAttributes .IsLearner = false
529- c .updateMembershipMetric (id , true )
530- c .be .MustSaveMemberToBackend (c .members [id ])
527+ if c .be != nil {
528+ m := c .members [id ]
529+ if shouldApplyV3 {
530+ m .RaftAttributes .IsLearner = false
531+ c .updateMembershipMetric (id , true )
532+ c .be .MustSaveMemberToBackend (m )
531533
532- c .lg .Info (
533- "promote member" ,
534- zap .String ("cluster-id" , c .cid .String ()),
535- zap .String ("local-member-id" , c .localID .String ()),
536- zap .String ("promoted-member-id" , id .String ()),
537- )
534+ c .lg .Info (
535+ "promote member" ,
536+ zap .String ("cluster-id" , c .cid .String ()),
537+ zap .String ("local-member-id" , c .localID .String ()),
538+ zap .String ("promoted-member-id" , id .String ()),
539+ )
540+ } else {
541+ // Workaround the issues which have already been affected by
542+ // https://github.com/etcd-io/etcd/issues/19557. The learner
543+ // promotion request had been applied to v3store, but not saved
544+ // to v2snapshot yet when in 3.5. Once upgrading to 3.6, the
545+ // patch here ensure the issue can be automatically fixed.
546+ if m .IsLearner {
547+ m .RaftAttributes .IsLearner = false
548+ c .lg .Info ("Forcibly apply member promotion request" , zap .String ("member" , fmt .Sprintf ("%+v" , * m )))
549+ c .be .MustSaveMemberToBackend (m )
550+ } else {
551+ c .lg .Info (
552+ "ignore already promoted member" ,
553+ zap .String ("cluster-id" , c .cid .String ()),
554+ zap .String ("local-member-id" , c .localID .String ()),
555+ )
556+ }
557+ }
538558 } else {
539559 c .lg .Info (
540- "ignore already promoted member" ,
560+ "ignore already promoted member due to backend being nil " ,
541561 zap .String ("cluster-id" , c .cid .String ()),
542562 zap .String ("local-member-id" , c .localID .String ()),
543563 )
544564 }
545565}
546566
567+ // SyncLearnerPromotionIfNeeded provides a workaround solution to fix the issues
568+ // which have already been affected by https://github.com/etcd-io/etcd/issues/19557.
569+ func (c * RaftCluster ) SyncLearnerPromotionIfNeeded () {
570+ c .Lock ()
571+ defer c .Unlock ()
572+
573+ v2Members , _ := membersFromStore (c .lg , c .v2store )
574+ v3Members , _ := c .be .MustReadMembersFromBackend ()
575+
576+ for id , v3Member := range v3Members {
577+ v2Member , ok := v2Members [id ]
578+ if ! ok {
579+ // This isn't an error. The conf change on the member hasn't been saved to the v2 snapshot yet.
580+ c .lg .Info ("Detected member only in v3store but missing in v2store" , zap .String ("member" , fmt .Sprintf ("%+v" , * v3Member )))
581+ continue
582+ }
583+
584+ if ! v2Member .IsLearner && v3Member .IsLearner {
585+ syncedV3Member := v3Member .Clone ()
586+ syncedV3Member .IsLearner = false
587+ c .lg .Warn ("Syncing member in v3store" , zap .String ("member" , fmt .Sprintf ("%+v" , * syncedV3Member )))
588+ c .be .MustHackySaveMemberToBackend (syncedV3Member )
589+ }
590+ }
591+ }
592+
547593func (c * RaftCluster ) UpdateRaftAttributes (id types.ID , raftAttr RaftAttributes , shouldApplyV3 ShouldApplyV3 ) {
548594 c .Lock ()
549595 defer c .Unlock ()
0 commit comments