All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.comcast.xfinity.sirius.uberstore.data.UberDataFile.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.data

import com.comcast.xfinity.sirius.api.impl.OrderedEvent
import com.comcast.xfinity.sirius.uberstore.common.Fnv1aChecksummer

import scala.annotation.tailrec

object UberDataFile {

  /**
   * Create a fully wired UberDataFile
   *
   * Uses:
   *  - UberStoreBinaryFileOps for file operations
   *  - Fnv1aChecksummer for checksums
   *  - BinaryEventCodec for encoding events
   *
   * @param dataFileName the data file name, this will
   *          be created if it does not exist
   *
   * @return fully constructed UberDataFile
   */
  def apply(dataFileName: String, fileHandleFactory: UberDataFileHandleFactory): UberDataFile = {
    val fileOps = new UberStoreBinaryFileOps with Fnv1aChecksummer
    val codec = new BinaryEventCodec
    new UberDataFile(dataFileName, fileHandleFactory, fileOps, codec)
  }
}

/**
 * Lower level file access for UberStore data files.
 *
 * @param fileHandleFactory the UberDataFile.UberFileDesc to provide handles
 *          to the underlying file. Extracted out for testing.
 * @param fileOps service class providing low level file operations
 * @param codec OrderedEventCodec for transforming OrderedEvents
 */
// TODO: use trait to hide this constructor but keep type visible?
private[uberstore] class UberDataFile(dataFileName: String,
                                      fileHandleFactory: UberDataFileHandleFactory,
                                      fileOps: UberStoreFileOps,
                                      codec: OrderedEventCodec) {

  val writeHandle: UberDataFileWriteHandle = fileHandleFactory.createWriteHandle(dataFileName)

  var isClosed = false

  /**
   * Write an event to this file
   *
   * @param event the OrderedEvent to persist
   */
  def writeEvent(event: OrderedEvent): Long = {
    if (isClosed) {
      throw new IllegalStateException("Attempting to write to closed UberDataFile")
    }

    fileOps.put(writeHandle, codec.serialize(event))
  }

  /**
   * Fold left over this entire file
   *
   * @param acc0 initial accumulator value
   * @param foldFun fold function
   */
  def foldLeft[T](acc0: T)(foldFun: (T, Long, OrderedEvent) => T): T = {
    foldLeftRange(0, Long.MaxValue)(acc0)(foldFun)
  }

  /**
   * Fold left starting at baseOffset, until the file pointer is at or beyond endOff.
   *
   * The caller is expected to put in a sane baseOff which corresponds with the start
   * of an actual event.
   *
   * This is a low low level API function that should not be taken lightly
   *
   * @param baseOff starting offset in the file to start at
   * @param endOff offset at or after which the operation should conclude, inclusive
   * @param acc0 initial accumulator value
   * @param foldFun the fold function
   *
   * @return T the final accumulator value
   */
  def foldLeftRange[T](baseOff: Long, endOff: Long)(acc0: T)(foldFun: (T, Long, OrderedEvent) => T): T = {
    val readHandle = fileHandleFactory.createReadHandle(dataFileName, baseOff)
    try {
      foldLeftUntil(readHandle, endOff, acc0, foldFun)
    } finally {
      readHandle.close()
    }
  }

  // private low low low level fold left
  @tailrec
  private def foldLeftUntil[T](readHandle: UberDataFileReadHandle, maxOffset: Long, acc: T, foldFun: (T, Long, OrderedEvent) => T): T = {
    val offset = readHandle.offset()
    if (offset > maxOffset) {
      acc
    } else {
      fileOps.readNext(readHandle) match {
        case None => acc
        case Some(bytes) =>
          val accNew = foldFun(acc, offset, codec.deserialize(bytes))
          foldLeftUntil(readHandle, maxOffset, accNew, foldFun)
      }
    }
  }

  /**
   * Close open file handles.  Only touching writeHandle here, since readHandles are opened and then
   * closed in a finally of the same block.  This UberDataFile should not be used after close is called.
   */
  def close() {
    if (!isClosed) {
      writeHandle.close()
      isClosed = true
    }
  }

  override def finalize() {
    close()
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy