Package com.trifork.bitcask

Source Code of com.trifork.bitcask.BitCask

/**
* This file is part of Bitcask Java
*
* Copyright (c) 2011 by Trifork
*
* 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.trifork.bitcask;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

import com.google.protobuf.ByteString;
import com.trifork.bitcask.BitCaskLock.Type;

public class BitCask {

  private static final ByteString TOMBSTONE = ByteString
      .copyFromUtf8("bitcask_tombstone");

  /** bc_state */
  File dirname;
  BitCaskFile write_file = BitCaskFile.FRESH_FILE;
  BitCaskLock write_lock;
  Map<File, BitCaskFile> read_files = new HashMap<File, BitCaskFile>();
  long max_file_size;
  BitCaskOptions opts;
  BitCaskKeyDir keydir;

  public static BitCask open(File dirname, BitCaskOptions opts)
      throws Exception {
    BitCask result = new BitCask();

    BitCaskFile.ensuredir(new File(dirname, "bitcask"));

    if (opts.is_read_write()) {
      BitCaskLock.delete_stale_lock(Type.WRITE, dirname);
      result.write_file = BitCaskFile.FRESH_FILE;
    }

    result.dirname = dirname;

    BitCaskKeyDir keydir;
    keydir = BitCaskKeyDir.keydir_new(dirname, opts.open_timeout_secs);
    result.keydir = keydir;
    if (!keydir.is_ready()) {
      File[] files = result.readable_files();
      BitCask.scan_key_files(files, keydir);
      keydir.mark_ready();
    }

    result.max_file_size = opts.max_file_size;
    result.dirname = dirname;
    result.opts = opts;

    return result;
  }

  public void close() throws IOException {

    // release?
    keydir = null;

    for (BitCaskFile read_file : read_files.values()) {
      read_file.close();
    }

    read_files.clear();

    if (write_file == null || write_file == BitCaskFile.FRESH_FILE) {
      // ok
    } else {
      write_file.close();
      write_lock.release();
    }
  }

  public void put(ByteString key, ByteString value) throws IOException {
    if (write_file == null) {
      throw new IOException("read only");
    }

    switch (write_file.check_write(key, value, max_file_size)) {
    case WRAP: {
      write_file.close_for_writing();
      BitCaskFile last_write_file = write_file;
      BitCaskFile nwf = BitCaskFile.create(dirname);
      write_lock.write_activefile(nwf);

      write_file = nwf;
      read_files.put(last_write_file.filename, last_write_file);
      break;
    }

    case FRESH:
      // time to start our first write file
    {
      BitCaskLock wl = BitCaskLock.acquire(Type.WRITE, dirname);
      BitCaskFile nwf = BitCaskFile.create(dirname);
      wl.write_activefile(nwf);

      this.write_lock = wl;
      this.write_file = nwf;

      break;
    }

    case OK:
      // we're good to go
    }

    BitCaskEntry entry = write_file.write(key, value);
    keydir.put(key, entry);
  }

  public ByteString get(ByteString key) throws IOException {
    return get(key, 2);
  }

  private ByteString get(ByteString key, int try_num) throws IOException {
    BitCaskEntry entry = keydir.get(key);
    if (entry == null) {
      return null;
    }

    if (entry.tstamp < opts.expiry_time()) {
      return null;
    }

    BitCaskFile file_state = get_filestate(entry.file_id);
    /** merging deleted file between keydir.get and here */
    if (file_state == null) {
      Thread.yield();
      return get(key, try_num - 1);
    }

    ByteString[] kv = file_state.read(entry.offset, entry.total_sz);

    if (kv[1].equals(TOMBSTONE)) {
      return null;
    } else {
      return kv[1];
    }
  }

  private BitCaskFile get_filestate(int fileId) throws IOException {

    File fname = BitCaskFile.mk_filename(dirname, fileId);
    BitCaskFile f = read_files.get(fname);
    if (f != null) {
      return f;
    }

    f = BitCaskFile.open(dirname, fileId);
    read_files.put(fname, f);

    return f;
  }

  private static final Comparator<? super File> REVERSE_DATA_FILE_COMPARATOR = new Comparator<File>() {

    @Override
    public int compare(File file0, File file1) {
      int i0 = BitCaskFile.tstamp(file0);
      int i1 = BitCaskFile.tstamp(file1);

      if (i0 < i1)
        return 1;
      if (i0 == i1)
        return 0;

      return -1;
    }
  };

  File[] readable_files() {

    final File writing_file = BitCaskLock.read_activefile(Type.WRITE,
        dirname);
    final File merging_file = BitCaskLock.read_activefile(Type.MERGE,
        dirname);

    return list_data_files(writing_file, merging_file);
  }

  static Pattern DATA_FILE = Pattern.compile("[0-9]+.bitcask.data");

  private File[] list_data_files(final File writing_file,
      final File merging_file) {
    File[] files = dirname.listFiles(new FileFilter() {
      @Override
      public boolean accept(File f) {
        if (f == writing_file || f == merging_file)
          return false;

        return DATA_FILE.matcher(f.getName()).matches();
      }
    });

    Arrays.sort(files, 0, files.length, REVERSE_DATA_FILE_COMPARATOR);

    return files;
  }

  static void scan_key_files(File[] files, final BitCaskKeyDir keydir)
      throws Exception {

    for (File f : files) {

      final BitCaskFile file = BitCaskFile.open(f);

      file.fold_keys(new KeyIter<Void>() {
        @Override
        public Void each(ByteString key, int tstamp, long entryPos,
            int entrySize, Void acc) throws Exception {

          keydir.put(key, new BitCaskEntry(file.file_id, tstamp,
              entryPos, entrySize));

          return null;
        }
      }, null);
     
      file.close();
    }
  }

  public void put(String key, String value) throws IOException {
    put(ByteString.copyFromUtf8(key), ByteString.copyFromUtf8(value));
  }

  public String getString(String key) throws IOException {
    ByteString val = get(ByteString.copyFromUtf8(key));
    if (val == null)
      return null;
    return val.toStringUtf8();
  }

  public <T> T fold(final KeyValueIter<T> entryIter, T acc) throws IOException {

    final int expiry_time = opts.expiry_time();
    EntryIter<T> iter = new EntryIter<T>() {

      @Override
      public T each(ByteString key, ByteString value, int tstamp,
          long entryPos, int entrySize, T acc) {

       
        if (tstamp < expiry_time) {
          return acc;
        }
       
        BitCaskEntry ent = keydir.get(key);
        if (entryPos != ent.offset) {
          return acc;
        }

        if (value.equals(TOMBSTONE)) {
          return acc;
        }

        return entryIter.each(key, value, acc);
      }
    };

    BitCaskFile[] files = open_fold_files();
    if (files != null) {
      for (int i = 0; i < files.length; i++) {
        acc = files[i].fold(iter, acc);
      }
    }

    return acc;
  }

  private BitCaskFile[] open_fold_files() {
    // TODO: retries ?
    File[] files = list_data_files(null, null);
    return open_files(files);
  }

  BitCaskFile[] open_files(File[] files) {
    BitCaskFile[] out = new BitCaskFile[files.length];

    for (int i = 0; i < out.length; i++) {

      try {
        out[i] = BitCaskFile.open(files[i]);
      } catch (IOException e) {

        for (int j = 0; j < i; j++) {
          try {
            out[j].close();
          } catch (IOException e1) {
          }
        }

        return null;
      }
    }

    return out;
  }

}
TOP

Related Classes of com.trifork.bitcask.BitCask

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.