@@ -32,6 +32,7 @@ object ActiveActive {
32
32
33
33
def initialized : Receive = {
34
34
case SeqCommand (seq, cmd, replyTo) =>
35
+ // tracking of sequence numbers and resends is elided here
35
36
cmd match {
36
37
case Put (key, value, r) =>
37
38
map += key -> value
@@ -53,18 +54,19 @@ object ActiveActive {
53
54
def add (res : SeqResult ): ReplyState
54
55
def isFinished : Boolean = missing.isEmpty
55
56
}
56
- private case class Unknown (deadline : Deadline , replies : Set [SeqResult ], missing : Set [ActorRef ]) extends ReplyState {
57
+ private case class Unknown (deadline : Deadline , replies : Set [SeqResult ], missing : Set [ActorRef ], quorum : Int ) extends ReplyState {
57
58
override def add (res : SeqResult ): ReplyState = {
58
- val quorum = (missing.size + 1 ) / 2
59
59
val nextReplies = replies + res
60
+ val nextMissing = missing - res.replica
60
61
if (nextReplies.size >= quorum) {
61
62
val answer = replies.toSeq.groupBy(_.res).collectFirst { case (k, s) if s.size >= quorum => s.head }
62
63
if (answer.isDefined) {
63
64
val right = answer.get
64
65
val wrong = replies.collect { case SeqResult (_, res, replica, _) if res != right => replica }
65
- Known (deadline, right, wrong, missing - res.replica)
66
- } else Unknown (deadline, nextReplies, missing - res.replica)
67
- } else Unknown (deadline, nextReplies, missing - res.replica)
66
+ Known (deadline, right, wrong, nextMissing)
67
+ } else if (nextMissing.isEmpty) Known .fromUnknown(deadline, nextReplies)
68
+ else Unknown (deadline, nextReplies, nextMissing, quorum)
69
+ } else Unknown (deadline, nextReplies, nextMissing, quorum)
68
70
}
69
71
}
70
72
private case class Known (deadline : Deadline , reply : SeqResult , wrong : Set [ActorRef ], missing : Set [ActorRef ]) extends ReplyState {
@@ -73,6 +75,18 @@ object ActiveActive {
73
75
Known (deadline, reply, nextWrong, missing - res.replica)
74
76
}
75
77
}
78
+ private object Known {
79
+ def fromUnknown (deadline : Deadline , replies : Set [SeqResult ]): Known = {
80
+ // did not reach consensus on this one, pick simple majority
81
+ val counts = replies.groupBy(_.res)
82
+ val biggest = counts.iterator.map(_._2.size).max
83
+ val winners = counts.collectFirst {
84
+ case (res, win) if win.size == biggest => win
85
+ }.get
86
+ val losers = (replies -- winners).map(_.replica)
87
+ Known (deadline, winners.head, losers, Set .empty)
88
+ }
89
+ }
76
90
77
91
class Coordinator (N : Int ) extends Actor {
78
92
private var replicas = (1 to N ).map(_ => newReplica()).toSet
@@ -81,7 +95,7 @@ object ActiveActive {
81
95
private var nextReply = 0
82
96
83
97
override def supervisorStrategy = SupervisorStrategy .stoppingStrategy
84
-
98
+
85
99
private def newReplica (): ActorRef =
86
100
context.watch(context.actorOf(Replica .props))
87
101
@@ -92,7 +106,7 @@ object ActiveActive {
92
106
case cmd : Command =>
93
107
val c = SeqCommand (seqNr.next, cmd, self)
94
108
replicas foreach (_ ! c)
95
- replies += c.seq -> Unknown (5 seconds fromNow, Set .empty, replicas)
109
+ replies += c.seq -> Unknown (5 seconds fromNow, Set .empty, replicas, (replicas.size + 1 ) / 2 )
96
110
case res : SeqResult if replies.contains(res.seq) && replicas.contains(res.replica) =>
97
111
val prevState = replies(res.seq)
98
112
val nextState = prevState.add(res)
@@ -111,18 +125,10 @@ object ActiveActive {
111
125
val expired = replies.iterator.takeWhile(_._2.deadline <= now)
112
126
for ((seq, state) <- expired) {
113
127
state match {
114
- case Unknown (deadline, received, missing) =>
115
- // did not reach consensus on this one, pick simple majority
116
- val counts = received.groupBy(_.res)
117
- val biggest = counts.iterator.map(_._2.size).max
118
- val winners = counts.collectFirst {
119
- case (res, win) if win.size == biggest => win
120
- }.get
121
- val losers = (received -- winners).map(_.replica)
122
- // don’t wait for further responses
123
- replies += seq -> Known (deadline, winners.head, losers, Set .empty)
128
+ case Unknown (deadline, received, _, _) =>
129
+ val forced = Known .fromUnknown(deadline, received)
130
+ replies += seq -> forced
124
131
case Known (deadline, reply, wrong, missing) =>
125
- // don’t wait for further responses
126
132
replies += seq -> Known (deadline, reply, wrong, Set .empty)
127
133
}
128
134
}
0 commit comments