long position = token.get_file_position();
// get a portion reader for the current segment and the position denoted by the token
SegmentReader reader = segment.portionReader(position, root.getReadingPageSize());
LogPage page = new LogPage(new LogBatch(Lists.<LogRecord>newArrayList()));
// iterate the portion and load the records in the returned page
RecordIterator iterator = reader.iterator();
while (iterator.hasNext()) {
LogRecord next = iterator.next();
if ((!next.is_set_index_code()) || next.get_index_code().equals(log.code)) {
page.get_batch().add_to_records(next);
}
}
page.set_next_page_token(token);
long newPosition = position + iterator.getSafelyRead();
boolean finished = newPosition == segment.length();
if (finished) {
switch (segment.type) {
case OPTIMIZED:
// the optimized case is special because we already figured out the
// next step at the beggining. still, if we were going to continue
// with a live segment and there's an index segment for it already
// let's switch to speed up the read process.
if (token.get_next_type() == PageType.live) {
Segment indexSegment = log.getSegment(token.get_next_timestamp());
if (indexSegment != null) {
// there's an index segment to read, much better than live :)
token.set_next_type(PageType.index);
}
}
break;
case SORTED:
boolean foundCurrent = false;
Segment nextIndexSegment = null;
// let's look for the first index segment after the one we just read
for (Segment s : log.getSegments()) {
if (foundCurrent) {
nextIndexSegment = s;
break;
}
if (s.timestamp == segment.timestamp) {
foundCurrent = true;
}
}
if (!foundCurrent) {
// the file we just read to build the page has dissapeared. we can't
// guarantee that the next one didn't dissappear as well. ABORT!!!
logger.error("Unable to find sorted segment for timestamp %s", token.get_timestamp());
return revertFromScratch();
}
if (nextIndexSegment != null) {
// we know we should continue with another index segment
token.set_next_type(PageType.index);
token.set_next_timestamp(nextIndexSegment.timestamp);
} else {
// we are out of index segments. we can safely assume that we have read
// all dealt data, so next segment should be the first undealt live segment
token.set_next_type(PageType.live);
token.set_next_timestamp(nextTimestamp);
}
break;
case RAW:
token.set_next_type(PageType.live);
try {
Segment s;
s = liveLog.findFollowingSegment(segment.timestamp);
if (s == null) {
// end of read process
page.unset_next_page_token();
} else {
token.set_next_timestamp(s.timestamp);
}
} catch (MissingSegmentException e) {
throw new RuntimeException("INVALID TOKEN", e);
}
break;
}
token.unset_timestamp();
token.unset_type();
token.unset_file_position();
} else {
switch (segment.type) {
case OPTIMIZED: token.set_type(PageType.optimized); break;
case SORTED: token.set_type(PageType.index); break;
case RAW: token.set_type(PageType.live); break;
}
token.set_timestamp(segment.timestamp);
token.set_file_position(newPosition);
}
logger.info("Returning page with %d records for index %s for segment %s from %d to %d, next token %s", page.get_batch().get_records_size(), log.code, segment, position, newPosition, token);
return page;
}