@@ -6,6 +6,7 @@ import akka.actor._
6
6
import akka .cluster ._
7
7
import cats .effect .SyncIO
8
8
import cats .implicits ._
9
+ import com .swissborg .lithium .implicits ._
9
10
import com .swissborg .lithium .reporter ._
10
11
import com .swissborg .lithium .strategy ._
11
12
@@ -38,6 +39,7 @@ private[lithium] class SplitBrainResolver(private val _strategy: Strategy[SyncIO
38
39
39
40
private val cluster : Cluster = Cluster (context.system)
40
41
private val selfUniqueAddress : UniqueAddress = cluster.selfUniqueAddress
42
+ private val selfMember : Member = cluster.selfMember
41
43
42
44
private val strategy : Union [SyncIO , Strategy , IndirectlyConnected ] =
43
45
new Union (_strategy, new IndirectlyConnected )
@@ -47,20 +49,42 @@ private[lithium] class SplitBrainResolver(private val _strategy: Strategy[SyncIO
47
49
@ SuppressWarnings (Array (" org.wartremover.warts.Any" ))
48
50
override def receive : Receive = {
49
51
case SplitBrainResolver .ResolveSplitBrain (worldView) =>
50
- resolveSplitBrain(worldView).unsafeRunSync()
52
+ cluster.state.leader match {
53
+ case Some (leader) if leader === selfUniqueAddress.address =>
54
+ resolveSplitBrain(worldView, false ).unsafeRunSync()
55
+
56
+ case None =>
57
+ // There is no leader, only down self if part of the nodes to down
58
+ resolveSplitBrain(worldView, true ).unsafeRunSync()
59
+
60
+ case _ =>
61
+ // member is not the leader, do nothing.
62
+ log.debug(" [{}}] is not the leader. The leader will handle the split-brain." , selfMember)
63
+ }
51
64
52
65
case SplitBrainResolver .DownAll (worldView) =>
53
- downAll(worldView).unsafeRunSync()
66
+ cluster.state.leader match {
67
+ case Some (leader) if leader === selfUniqueAddress.address =>
68
+ downAll(worldView, false ).unsafeRunSync()
69
+
70
+ case None =>
71
+ // There is no leader, only down self if part of the nodes to down
72
+ downAll(worldView, true ).unsafeRunSync()
73
+
74
+ case _ =>
75
+ // member is not the leader, do nothing.
76
+ log.debug(" [{}}] is not the leader. The leader will down all the nodes." , selfMember)
77
+ }
54
78
}
55
79
56
80
/**
57
81
* Handle the partition using the [[Union ]] of the configured
58
82
* strategy and the [[IndirectlyConnected ]].
59
83
*/
60
- private def resolveSplitBrain (worldView : WorldView ): SyncIO [Unit ] =
84
+ private def resolveSplitBrain (worldView : WorldView , downSelfOnly : Boolean ): SyncIO [Unit ] =
61
85
for {
62
86
_ <- SyncIO (
63
- log.info (
87
+ log.warning (
64
88
""" [{}] Received request to handle a split-brain...
65
89
|-- Worldview --
66
90
|Reachable nodes:
@@ -70,22 +94,22 @@ private[lithium] class SplitBrainResolver(private val _strategy: Strategy[SyncIO
70
94
|Indirectly-connected nodes:
71
95
| {}
72
96
|""" .stripMargin,
73
- selfUniqueAddress ,
97
+ selfMember ,
74
98
worldView.reachableNodes.mkString_(" \n " ),
75
99
worldView.unreachableNodes.mkString_(" \n " ),
76
100
worldView.indirectlyConnectedNodes.mkString_(" \n " )
77
101
)
78
102
)
79
- _ <- runStrategy(strategy, worldView)
103
+ _ <- runStrategy(strategy, worldView, downSelfOnly )
80
104
} yield ()
81
105
82
106
/**
83
107
* Handle the partition by downing all the members.
84
108
*/
85
- private def downAll (worldView : WorldView ): SyncIO [Unit ] =
109
+ private def downAll (worldView : WorldView , downSelfOnly : Boolean ): SyncIO [Unit ] =
86
110
for {
87
111
_ <- SyncIO (
88
- log.info (
112
+ log.warning (
89
113
""" [{}] Received request to down all the nodes...
90
114
|-- Worldview --
91
115
|Reachable nodes:
@@ -95,13 +119,13 @@ private[lithium] class SplitBrainResolver(private val _strategy: Strategy[SyncIO
95
119
|Indirectly-connected nodes:
96
120
| {}
97
121
|""" .stripMargin,
98
- selfUniqueAddress ,
122
+ selfMember ,
99
123
worldView.reachableNodes.mkString_(" \n " ),
100
124
worldView.unreachableNodes.mkString_(" \n " ),
101
125
worldView.indirectlyConnectedNodes.mkString_(" \n " )
102
126
)
103
127
)
104
- _ <- runStrategy(downAll, worldView)
128
+ _ <- runStrategy(downAll, worldView, downSelfOnly )
105
129
} yield ()
106
130
107
131
/**
@@ -110,16 +134,35 @@ private[lithium] class SplitBrainResolver(private val _strategy: Strategy[SyncIO
110
134
* Enable `nonJoiningOnly` so that joining and weakly-up
111
135
* members do not run the strategy.
112
136
*/
113
- private def runStrategy (strategy : Strategy [SyncIO ], worldView : WorldView ): SyncIO [Unit ] = {
114
- def execute (decision : Decision ): SyncIO [Unit ] =
115
- for {
116
- _ <- SyncIO (
117
- log.info(""" [{}] Downing the nodes:
118
- | {}""" .stripMargin, selfUniqueAddress, Decision .allNodesToDown(decision).mkString_(" \n " ))
137
+ private def runStrategy (strategy : Strategy [SyncIO ], worldView : WorldView , downSelfOnly : Boolean ): SyncIO [Unit ] = {
138
+ def execute (decision : Decision ): SyncIO [Unit ] = {
139
+
140
+ val nodesToDown =
141
+ if (downSelfOnly) decision.nodesToDown.filter(_.uniqueAddress === selfUniqueAddress)
142
+ else decision.nodesToDown
143
+
144
+ if (nodesToDown.nonEmpty) {
145
+ for {
146
+ _ <- SyncIO (
147
+ log.warning(
148
+ """ [{}] Downing the nodes:
149
+ | {}{}
150
+ |""" .stripMargin,
151
+ selfMember,
152
+ nodesToDown.mkString_(" \n " ),
153
+ if (downSelfOnly) " \n Note: no leader, only the self node will be downed." else " "
154
+ )
155
+ )
156
+ _ <- nodesToDown.toList.traverse_(node => SyncIO (cluster.down(node.address)))
157
+ } yield ()
158
+ } else {
159
+ SyncIO (
160
+ log.warning(" [{}] No nodes to down. {}" ,
161
+ selfMember,
162
+ if (downSelfOnly) " \n Note: no leader, only the self node can be downed." else " " )
119
163
)
120
- _ <- decision.nodesToDown.toList.traverse_(node => SyncIO (cluster.down(node.address)))
121
-
122
- } yield ()
164
+ }
165
+ }
123
166
124
167
strategy
125
168
.takeDecision(worldView)
0 commit comments