15
15
*/
16
16
package com .comcast .xfinity .sirius .api .impl .state
17
17
18
- import akka .actor .{Props , Actor , ActorRef }
18
+ import akka .actor .{Actor , ActorRef , Props }
19
19
import com .comcast .xfinity .sirius .writeaheadlog .SiriusLog
20
20
import com .comcast .xfinity .sirius .api .{SiriusConfiguration , SiriusResult }
21
21
import com .comcast .xfinity .sirius .api .impl ._
22
22
import com .comcast .xfinity .sirius .admin .MonitoringHooks
23
23
24
+ import scala .collection .mutable .ListBuffer
25
+
24
26
object SiriusPersistenceActor {
25
27
26
28
/**
27
29
* Trait encapsulating _queries_ into Sirius's log's state,
28
30
* that is, the state of persisted data
29
31
*/
30
32
sealed trait LogQuery
33
+ sealed trait LogQuerySubrange extends LogQuery {
34
+ def begin : Long
35
+ def end : Long
36
+ }
31
37
32
38
case object GetLogSize extends LogQuery
33
39
/**
@@ -40,7 +46,19 @@ object SiriusPersistenceActor {
40
46
* @param begin first sequence number of the range
41
47
* @param end last sequence number of the range, inclusive
42
48
*/
43
- case class GetLogSubrange (begin : Long , end : Long ) extends LogQuery
49
+ case class GetLogSubrange (begin : Long , end : Long ) extends LogQuerySubrange
50
+ /**
51
+ * Message for directly requesting a chunk of the log from a node.
52
+ *
53
+ * SiriusPersistenceActor is expected to reply with a LogSubrange
54
+ * when receiving this message. This range should be as complete
55
+ * as possible.
56
+ *
57
+ * @param begin first sequence number of the range
58
+ * @param end last sequence number of the range, inclusive
59
+ * @param limit the maximum number of events
60
+ */
61
+ case class GetLogSubrangeWithLimit (begin : Long , end : Long , limit : Long ) extends LogQuerySubrange
44
62
45
63
trait LogSubrange
46
64
trait PopulatedSubrange extends LogSubrange {
@@ -129,21 +147,11 @@ class SiriusPersistenceActor(stateActor: ActorRef,
129
147
130
148
lastWriteTime = thisWriteTime
131
149
132
- case GetLogSubrange (rangeStart, rangeEnd) if rangeEnd < siriusLog.getNextSeq => // we can answer fully
133
- val events = siriusLog.foldLeftRange(rangeStart, rangeEnd)(List [OrderedEvent ]())(
134
- (acc, event) => event :: acc
135
- ).reverse
136
- sender ! CompleteSubrange (rangeStart, rangeEnd, events)
150
+ case GetLogSubrange (start, end) =>
151
+ sender ! querySubrange(start, end, Long .MaxValue )
137
152
138
- case GetLogSubrange (rangeStart, rangeEnd) if siriusLog.getNextSeq <= rangeStart => // we can't send anything useful back
139
- sender ! EmptySubrange
140
-
141
- case GetLogSubrange (rangeStart, rangeEnd) => // we can respond partially
142
- val lastSeq = siriusLog.getNextSeq - 1
143
- val events = siriusLog.foldLeftRange(rangeStart, lastSeq)(List [OrderedEvent ]())(
144
- (acc, event) => event :: acc
145
- ).reverse
146
- sender ! PartialSubrange (rangeStart, lastSeq, events)
153
+ case GetLogSubrangeWithLimit (start, end, limit) =>
154
+ sender ! querySubrange(start, end, limit)
147
155
148
156
case GetNextLogSeq =>
149
157
sender ! siriusLog.getNextSeq
@@ -156,6 +164,46 @@ class SiriusPersistenceActor(stateActor: ActorRef,
156
164
case _ : SiriusResult =>
157
165
}
158
166
167
+ private def querySubrange (rangeStart : Long , rangeEnd : Long , limit : Long ): LogSubrange = {
168
+ val nextSeq = siriusLog.getNextSeq
169
+ val lastSeq = nextSeq - 1
170
+ if (limit <= 0 || rangeEnd < rangeStart || rangeEnd <= 0 || rangeStart > lastSeq) {
171
+ // parameters are out of range, can't return anything useful
172
+ EmptySubrange
173
+ } else {
174
+ val endSeq = if (rangeEnd > lastSeq) lastSeq else rangeEnd
175
+ if (limit > (endSeq - rangeStart)) {
176
+ // the limit is larger than the subrange window, so do not enforce
177
+ val events = siriusLog.foldLeftRange(rangeStart, endSeq)(ListBuffer .empty[OrderedEvent ])(
178
+ (acc, evt) => acc += evt
179
+ ).toList
180
+ if (endSeq < rangeEnd) {
181
+ // the end of the range extends beyond the end of the log, so can only partially answer
182
+ PartialSubrange (rangeStart, endSeq, events)
183
+ } else {
184
+ // the range is entirely within the log, so can fully answer
185
+ CompleteSubrange (rangeStart, endSeq, events)
186
+ }
187
+ } else {
188
+ // the limit is smaller than the subrange window
189
+ val buffer = siriusLog.foldLeftRangeWhile(rangeStart, endSeq)(ListBuffer .empty[OrderedEvent ])(
190
+ buffer => buffer.size < limit
191
+ )(
192
+ (acc, evt) => acc += evt
193
+ )
194
+ if (buffer.size < limit && endSeq < rangeEnd) {
195
+ // the end of the subrange extended part the end of the log
196
+ // and the buffer was not filled to the limit, so we can only partially respond
197
+ PartialSubrange (rangeStart, endSeq, buffer.toList)
198
+ } else {
199
+ // the buffer was filled to the limit, so completely respond using the sequence of the
200
+ // last event as the end of the range
201
+ CompleteSubrange (rangeStart, buffer.last.sequence, buffer.toList)
202
+ }
203
+ }
204
+ }
205
+ }
206
+
159
207
/**
160
208
* Monitoring hooks
161
209
*/
0 commit comments