Class TwoCopyBarrierBuffer

java.lang.Object
com.aoapps.persistence.AbstractPersistentBuffer
com.aoapps.persistence.TwoCopyBarrierBuffer
All Implemented Interfaces:
PersistentBuffer, Closeable, AutoCloseable

public class TwoCopyBarrierBuffer extends AbstractPersistentBuffer

Java does not support write barriers without a complete force call, this class works-around this issue by maintaining two copies of the file and updating the older copy to be the newer copy occasionally on barrier(false) and immediately on barrier(true) (if protectionLevel is high enough).

All instances also share a Timer to perform automatic background flushing of caches. Automatic flushing is single-threaded to favor low load averages over timely flushes.

This class also acts as a write cache that may batch or delay writes for potentially long periods of time. This is useful for media such as flash memory where write throughput is quite limited and the number of writes on the media is also limited. This also turns many random writes into fewer, sequential writes. This may help spinning platter-based media.

Since this class is intended for flash-based media, it will not rewrite the same data to a section of a file. Even if this means that it has to read the section of the file, compare the values, and then write only when changed. This is an attempt to help the wear leveling technology built into the media by dispatching a minimum number of writes.

An additional benefit may be that reads from cached data are performed directly from the write cache, although this is not the purpose of this buffer. Writes that would not change anything, however, are not cached and would not help as a read cache.

Two copies of the file are maintained. The most recent version of the file will normally be named with the regular name, but other versions will exist with .old or .new appended to the filename. The exact order of update is:

  1. Rename filename.old to filename.new
  2. Write new version of all data to filename.new
  3. Rename filename to filename.old
  4. Rename filename.new to filename

The filename states, in order:

          Complete      Complete Old  Partial
 Normal:  filename      filename.old
          filename                    filename.new
          filename.new  filename.old
 Normal:  filename      filename.old

 

This implementation assumes an atomic file rename for correct recovery.

To reduce the chance of data loss, this registers a JVM shutdown hook to flush all caches on JVM shutdown. If it takes a long time to flush the data this can cause significant delays when stopping a JVM.

TODO: Should we run on top of RandomAccessBuffer/LargeMappedPersistentBuffer/MappedPersistentBuffer for memory-mapped read performance?

Author:
AO Industries, Inc.
  • Constructor Details

    • TwoCopyBarrierBuffer

      public TwoCopyBarrierBuffer() throws IOException
      Creates a read-write buffer backed by temporary files. The protection level is set to ProtectionLevel.NONE. The temporary file will be deleted when this buffer is closed or on JVM shutdown. Uses default sectorSize of 4096, asynchronous commit delay of 5 seconds, and synchronous commit delay of 60 seconds. A shutdown hook is not registered.
      Throws:
      IOException
    • TwoCopyBarrierBuffer

      public TwoCopyBarrierBuffer(String name) throws IOException
      Creates a read-write buffer with ProtectionLevel.BARRIER protection level. Uses default sectorSize of 4096, asynchronous commit delay of 5 seconds, and synchronous commit delay of 60 seconds.
      Throws:
      IOException
    • TwoCopyBarrierBuffer

      public TwoCopyBarrierBuffer(String name, ProtectionLevel protectionLevel) throws IOException
      Creates a buffer. Uses default sectorSize of 4096, asynchronous commit delay of 5 seconds, and synchronous commit delay of 60 seconds.
      Throws:
      IOException
    • TwoCopyBarrierBuffer

      public TwoCopyBarrierBuffer(File file) throws IOException
      Creates a read-write buffer with ProtectionLevel.BARRIER protection level. Uses default sectorSize of 4096, asynchronous commit delay of 5 seconds, and synchronous commit delay of 60 seconds.
      Throws:
      IOException
    • TwoCopyBarrierBuffer

      public TwoCopyBarrierBuffer(File file, ProtectionLevel protectionLevel) throws IOException
      Creates a buffer. Uses default sectorSize of 4096, asynchronous commit delay of 5 seconds, and synchronous commit delay of 60 seconds.
      Throws:
      IOException
    • TwoCopyBarrierBuffer

      public TwoCopyBarrierBuffer(File file, ProtectionLevel protectionLevel, int sectorSize, long asynchronousCommitDelay, long synchronousCommitDelay) throws IOException
      Creates a buffer. Synchronizes the two copies of the file. Populates the oldWriteCache with any data the doesn't match the newer version of the file. This means that both files are read completely at start-up in order to provide the most efficient synchronization later.
      Parameters:
      file - The base filename, will be appended with ".old" and ".new" while committing changes.
      protectionLevel - The protection level for this buffer.
      sectorSize - The size of the sectors cached and written. For best results this should match the underlying filesystem block size. Must be a power of two >= 1.
      asynchronousCommitDelay - The number of milliseconds before a background thread syncs uncommitted data to the underlying storage. A value of Long.MAX_VALUE will avoid any overhead of background thread management.
      synchronousCommitDelay - The number of milliseconds before a the calling thread syncs uncommitted data to the underlying storage.
      Throws:
      IOException
  • Method Details