
com.comcast.xfinity.sirius.uberstore.UberPair.scala Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2012-2014 Comcast Cable Communications Management, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.comcast.xfinity.sirius.uberstore
import com.comcast.xfinity.sirius.api.impl.OrderedEvent
import com.comcast.xfinity.sirius.uberstore.data.{UberDataFile, UberDataFileHandleFactory}
import com.comcast.xfinity.sirius.uberstore.seqindex.{DiskOnlySeqIndex, SeqIndex}
object UberPair {
/**
* Create an UberStoreFilePair based in baseDir. baseDir is NOT
* created, it must exist. The files within baseDir will
* be created if they do not exist however.
*
* @param baseDir directory to base the UberStoreFilePair in
*
* @return an instantiated UberStoreFilePair
*/
def apply(baseDir: String, startingSeq: Long, fileHandleFactory: UberDataFileHandleFactory): UberPair = {
val baseName = "%s/%s".format(baseDir, startingSeq)
val dataFile = UberDataFile("%s.data".format(baseName), fileHandleFactory)
val index = DiskOnlySeqIndex("%s.index".format(baseName))
repairIndex(index, dataFile)
new UberPair(dataFile, index)
}
/**
* Recovers missing entries at the end of index from dataFile.
*
* Assumes that UberDataFile is proper, that is events are there in order,
* and there are no dups.
*
* Has the side effect of updating index.
*
* @param index the SeqIndex to update
* @param dataFile the UberDataFile to update
*/
private[uberstore] def repairIndex(index: SeqIndex, dataFile: UberDataFile) {
val (includeFirst, lastOffset) = index.getMaxSeq match {
case None => (true, 0L)
case Some(seq) => (false, index.getOffsetFor(seq).get) // has to exist
}
dataFile.foldLeftRange(lastOffset, Long.MaxValue)(includeFirst) (
(shouldInclude, off, evt) => {
if (shouldInclude) {
index.put(evt.sequence, off)
}
true
}
)
}
}
/**
* THIS SHOULD NOT BE USED DIRECTLY, use UberStoreFilePair#apply instead,
* it will do all of the proper wiring. This very well may be
* made private in the future!
*
* Expectedly high performance sequence number based append only
* storage. Stores all data in dataFile, and sequence -> data
* mappings in index.
*
* @param dataFile the UberDataFile to store data in
* @param index the SeqIndex to use
*/
class UberPair(dataFile: UberDataFile, index: SeqIndex) {
def writeEntry(event: OrderedEvent) {
if (isClosed) {
throw new IllegalStateException("Attempting to write to closed UberStoreFilePair")
}
if (event.sequence < getNextSeq) {
throw new IllegalArgumentException("Writing events out of order is bad news bears")
}
val offset = dataFile.writeEvent(event)
index.put(event.sequence, offset)
}
def getNextSeq = index.getMaxSeq match {
case None => 1L
case Some(seq) => seq + 1
}
def foldLeft[T](acc0: T)(foldFun: (T, OrderedEvent) => T): T =
foldLeftRange(0, Long.MaxValue)(acc0)(foldFun)
def foldLeftRange[T](startSeq: Long, endSeq: Long)(acc0: T)(foldFun: (T, OrderedEvent) => T): T = {
val (startOffset, endOffset) = index.getOffsetRange(startSeq, endSeq)
dataFile.foldLeftRange(startOffset, endOffset)(acc0)(
(acc, _, evt) => foldFun(acc, evt)
)
}
/**
* Close underlying file handles or connections. This UberStoreFilePair should not be used after
* close is called.
*/
def close() {
if (!dataFile.isClosed) {
dataFile.close()
}
if (!index.isClosed) {
index.close()
}
}
/**
* Consider this closed if either of its underlying objects are closed, no good writes
* will be able to go through in that case.
*
* @return whether this is "closed," i.e., unable to be written to
*/
def isClosed =
dataFile.isClosed || index.isClosed
override def finalize() {
close()
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy