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

com.caucho.v5.db.journal.JournalStore Maven / Gradle / Ivy

There is a newer version: 1.0.1
Show newest version
/*
 * Copyright (c) 1998-2015 Caucho Technology -- all rights reserved
 *
 * This file is part of Baratine(TM)
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Baratine is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Baratine is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Baratine; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.v5.db.journal;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import com.caucho.v5.amp.Amp;
import com.caucho.v5.amp.ServicesAmp;
import com.caucho.v5.io.TempBuffer;
import com.caucho.v5.store.io.InStore;
import com.caucho.v5.store.io.OutStore;
import com.caucho.v5.store.io.StoreBuilder;
import com.caucho.v5.store.io.StoreReadWrite;
import com.caucho.v5.util.BitsUtil;
import com.caucho.v5.util.ConcurrentArrayList;
import com.caucho.v5.util.L10N;

/**
 * The store manages the block-based journal store file.
 * 
 * Journals are shared with the same file. Each journal has a unique
 * key.
 */
public class JournalStore
{
  final static long JOURNAL_MAGIC;

  private static final L10N L = new L10N(JournalStore.class);
  
  static final int BLOCK_SIZE = 8 * 1024;
  static final int KEY_LENGTH = 32;
  
  private final StoreReadWrite _store;
  
  private final Path _path;
  
  private final int _segmentSize;
  private final int _blocksPerSegment;
  
  private final int _headLength = 256;
  private final long _tailAddress;
  
  private ConcurrentHashMap _journalMap
    = new ConcurrentHashMap<>();
    
  private ConcurrentArrayList _segmentList
    = new ConcurrentArrayList<>(JournalSegment.class);
    
  private ArrayList _freeSegmentList
    = new ArrayList<>();
    
  private JournalSegment _systemSegment;
  private long _address;
  
  JournalStore(Builder builder)
    throws IOException
  {
    _path = builder.getPath();
    
    StoreBuilder storeBuilder = new StoreBuilder(_path);
    storeBuilder.mmap(builder.isMmap());
    storeBuilder.ampManager(builder.getRampManager());
    
    _store = storeBuilder.build();
    
    long segmentSize = builder.getSegmentSize();
    segmentSize += (BLOCK_SIZE - 1);
    segmentSize -= segmentSize % BLOCK_SIZE;
    
    if (segmentSize > Integer.MAX_VALUE / 2) {
      throw new IllegalArgumentException();
    }
      
    _segmentSize = (int) segmentSize;
    _blocksPerSegment = (int) (segmentSize / BLOCK_SIZE);
    
    _tailAddress = _segmentSize - BLOCK_SIZE;
    
    if (Files.isReadable(_path)) {
      _store.init();
      initImpl();
    }
    else {
      _store.create();
      createImpl();
    }
  }

  /**
   * Creates an independent store.
   */
  public static JournalStore create(Path path)
    throws IOException
  {
    return create(path, true);
  }

  /**
   * Creates an independent store.
   */
  public static JournalStore createNoMmap(Path path)
    throws IOException
  {
    return create(path, false);
  }

  /**
   * Creates an independent store.
   */
  public static JournalStore createMmap(Path path)
    throws IOException
  {
    return create(path, true);
  }

  /**
   * Creates an independent store.
   */
  public static JournalStore create(Path path, boolean isMmap)
    throws IOException
  {
    // RampManager rampManager = Ramp.newManager();
    
    long segmentSize = 4 * 1024 * 1024;
    
    JournalStore.Builder builder = JournalStore.Builder.create(path);
    
    builder.segmentSize(segmentSize);
    // builder.rampManager(rampManager);
    builder.mmap(isMmap);
    
    JournalStore store = builder.build();

    return store;
  }

  public int getSegmentSize()
  {
    return _segmentSize;
  }

  public long getTailAddress()
  {
    return _tailAddress;
  }
  
  public long getHeadLength()
  {
    return _headLength;
  }

  public JournalGroup openJournal(String name)
  {
    JournalGroup journal = _journalMap.get(name);
    
    if (journal == null) {
      journal = new JournalGroup(this, name);
      
      _journalMap.putIfAbsent(name, journal);
      
      journal = _journalMap.get(name);
    }
    
    return journal;
  }
  
  public JournalStream openJournalStream(String name)
  {
    return new JournalStreamImpl(openJournal(name));
  }
  
  ArrayList getReplaySegments(byte []key)
  {
    ArrayList list = new ArrayList<>();
    
    for (JournalSegment segment : _segmentList) {
      if (segment.getSequence() > 0
          && Arrays.equals(key, segment.getKey())) {
        list.add(segment);
      }
    }
    
    return list;
  }

  public JournalSegment openSegment(JournalGroup journal, long sequence)
  {
    return allocateSegment(journal.getKey(), sequence);
  }

  /**
   * Creates the store.
   * @throws SQLException 
   */
  private void createImpl()
  {
    _systemSegment = allocateSegment(new byte[32], 1);
    
    _segmentList.add(_systemSegment);
    
    TempBuffer tBuf = TempBuffer.allocateLarge();
    byte []buffer = tBuf.buffer();
    
    Arrays.fill(buffer, (byte) 0);
    
    int offset = 0;
    
    BitsUtil.writeLong(buffer, offset, JOURNAL_MAGIC);
    offset += 8;
    
    BitsUtil.writeLong(buffer, offset, _segmentSize);
    offset += 8;
    
    BitsUtil.writeLong(buffer, offset, _tailAddress);
    offset += 8;
    
    BitsUtil.writeInt(buffer, offset, KEY_LENGTH);
    offset += 4;
    
    try (OutStore os = _store.openWrite(0, buffer.length)) {
      os.write(0, buffer, 0, buffer.length);
    }
    
    tBuf.freeSelf();
  }

  void initImpl()
  {
    int segmentCount = (int) (_store.fileSize() / _segmentSize);
    
    _address = segmentCount * _segmentSize;
    
    TempBuffer tBuf = TempBuffer.allocateLarge();
    byte []buffer = tBuf.buffer();
    
    for (int i = 1; i < segmentCount; i++) {
      long segmentAddress = i * _segmentSize;

      try (InStore is = _store.openRead(segmentAddress, _segmentSize)) {
        is.read(segmentAddress + _tailAddress, buffer, 0, buffer.length);
        
        int offset = 0;
        
        long initSequence = BitsUtil.readLong(buffer, offset);
        offset += 8;
        
        byte []key = new byte[KEY_LENGTH];
        System.arraycopy(buffer, offset, key, 0, key.length);
        offset += key.length;
        
        long sequence = BitsUtil.readLong(buffer, offset);
        offset += 8;
        
        JournalSegment segment
          = new JournalSegment(this, key, segmentAddress, 
                               initSequence, sequence);
        
        _segmentList.add(segment);
        
        if (initSequence == 0) {
          _freeSegmentList.add(segment);
        }
      }
    }
  }
  
  InStore openRead(long offset, int size)
  {
    return _store.openRead(offset, size);
  }
  
  OutStore openWrite(long offset, int size)
  {
    return _store.openWrite(offset, size);
  }

  void free(JournalSegment segment)
  {
    _freeSegmentList.add(segment);
  }
  
  private synchronized JournalSegment allocateSegment(byte []key,
                                                      long sequence)
  {
    long address = _address;
    
    JournalSegment segment;
    
    if (_freeSegmentList.size() > 0) {
      segment = _freeSegmentList.remove(0);
      _segmentList.remove(segment);
      
      address = segment.getAddress();
    }
    else {
      _address += _segmentSize;
    }
    
    try (OutStore os = _store.openWrite(address, _segmentSize)) {
      segment = new JournalSegment(this, key, address,
                                   sequence, sequence);
      
      TempBuffer tBuf = TempBuffer.allocate();

      byte []buffer = tBuf.buffer();
      Arrays.fill(buffer, (byte) 0);

      System.arraycopy(key, 0, buffer, 8, key.length);

      os.write(address, buffer, 0, buffer.length);

      segment.writeTail(os);
      
      tBuf.freeSelf();
      
      _segmentList.add(segment);
      
      return segment;
    }
  }
  
  public void close()
  {
    for (JournalGroup journal : _journalMap.values()) {
      try {
        journal.close();
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    
    _store.close();
  }
  
  @Override
  public String toString()
  {
    return getClass().getSimpleName() + "[" + _path + "]";
  }
  
  public static class Builder {
    private final Path _path;
    private long _segmentSize = 4 * 1024 * 1024;
    private boolean _isMmap = true;
    private ServicesAmp _rampManager;
    
    public Builder(Path path)
    {
      Objects.requireNonNull(path);
      
      _path = path;
    }
    
    public static Builder create(Path path)
    {
      return new Builder(path);
    }
    
    public Path getPath()
    {
      return _path;
    }
    
    public Builder segmentSize(long segmentSize)
    {
      if ((segmentSize % BLOCK_SIZE) != 0 || segmentSize <= 0) {
        throw new IllegalArgumentException(L.l("0x{0} is an invalid segment size",
                                               Long.toHexString(segmentSize)));
      }

      _segmentSize = segmentSize;
      
      return this;
    }
    
    public long getSegmentSize()
    {
      return _segmentSize;
    }
    
    public Builder mmap(boolean isMmap)
    {
      _isMmap = isMmap;
      
      return this;
    }
    
    public boolean isMmap()
    {
      return _isMmap;
    }
    
    public Builder rampManager(ServicesAmp manager)
    {
      _rampManager = manager;
      
      return this;
    }
    
    public ServicesAmp getRampManager()
    {
      return _rampManager;
    }
    
    public JournalStore build()
      throws IOException
    {
      if (_rampManager == null) {
        _rampManager = ServicesAmp.newManager().get();
      }
      
      return new JournalStore(this);
    }
  }
  
  static {
    byte []magicBytes = "BarJo080".getBytes();
    
    JOURNAL_MAGIC = BitsUtil.readLong(magicBytes, 0);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy