Package com.cloudhopper.datastore.tokyo

Source Code of com.cloudhopper.datastore.tokyo.TokyoDataStore

package com.cloudhopper.datastore.tokyo;

/*
* #%L
* ch-datastore
* %%
* Copyright (C) 2012 Cloudhopper by Twitter
* %%
* 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.
* #L%
*/

import com.cloudhopper.commons.util.HexUtil;
import com.cloudhopper.commons.util.StringUtil;
import com.cloudhopper.datastore.*;
import java.io.File;
import java.util.Timer;
import java.util.TimerTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tokyocabinet.BDB;

/**
* Tokyo Cabinet backed implementation of a DataStore.
*
* @author joelauer
*/
public class TokyoDataStore extends BaseDataStore implements AscendingIteratorSupport {
    private final static Logger logger = LoggerFactory.getLogger(TokyoDataStore.class);

    // path used to generate a full path to the database file
    private File path;
    // the underlying b-tree database
    private BDB bdb;

    // for performance, the database is not synchronized on every put() and take()
    // if -1, sync() is disabled
    // if 0, sync() is called on ever put() and take()
    // if > 0, the amount of ms between a sync() call is made, set to 5 seconds by default
    private long synchronizeInterval;
    private Timer synchronizeTimer;
    private TimerTask synchronizeTimerTask;

    public TokyoDataStore() {
        super("TC-BDB");
        // defaults
        synchronizeInterval = 5000;
    }

    /**
     * Returns the underlying B-Tree database.  If not opened, this will return
     * null.
     * @return Null if not open, otherwise the B-Tree database.
     */
    public BDB getDatabase() {
        return bdb;
    }

    /**
     * Gets the interval between sync() calls to underlying Tokyo cabinet db.
     * For performance, by default, the database is not synchronized on every
     * put() and take().  If -1, sync() is disabled. If 0, sync() is called on
     * every put() and take().  Throughput will be dramatically decreased, but
     * it'll guarantee the data makes it into the file.  If > 0, its the length
     * of ms between a sync() call is made, set to 5 seconds by default
     * @return The synchronization interval in milliseconds
     */
    public long getSynchronizeInterval() {
        return synchronizeInterval;
    }

    /**
     *
     * @throws QueueStoreException Thrown if the data store is already open
     *      when this method is called.
     */
    public void setSynchronizeInterval(long value) {
        this.synchronizeInterval = value;
    }

    private File getDatabaseFile() {
        return new File(getDirectory(), getName() + ".bdb");
    }

    @Override
    public void doDelete() throws DataStoreFatalException {
        File databaseFile = getDatabaseFile();
        try {
            databaseFile.delete();
        } catch (Exception e) {
            throw new DataStoreFatalException("Unable to delete " + databaseFile.getPath(), e);
        }
    }

    @Override
    public void doOpen() throws DataStoreFatalException {
        // create a tokyo cabinet backed queue
        bdb = new BDB();

        // just leave comparison as LEXICAL, don't set it to anything else

  /*
  // comparator that does a byte at a time, up to the length of the shorter key
  bdb.setcomparator(new tokyocabinet.BDBCMP() {
    public int compare(byte[] a, byte[] b) {
        int itlen = a.length;
        if (itlen > b.length) itlen = b.length;
        for (int i=0;i<itlen;i++) {
      if (a[i] == b[i]) continue;
      else return a[i] - b[i];
        }
        return 0;
    }
      });
  */

        // try to open the database
        path = getDatabaseFile();

        if (!bdb.open(path.getAbsolutePath(), BDB.OWRITER | BDB.OCREAT)) {
            // error occurred while opening, throw error
            int ecode = bdb.ecode();
            bdb = null;
            throw new DataStoreFatalException("Unable to open " + toString() + " @ " + path + " {code=" + ecode + ", message=" + BDB.errmsg(ecode) + "}");
        }

        if (this.synchronizeInterval < 0) {
            logger.debug("Automatic sync disabled {synchronizeInterval < 0} for " + toString());
        } else if (this.synchronizeInterval == 0) {
            logger.debug("Automatic sync enabled for every transaction {synchronizeInterval == 0} for " + toString());
        } else {
            // schedule task to print results every 5 seconds
            synchronizeTimer = new Timer("TokyoDataStoreSync-" + getName(), true);
            synchronizeTimerTask = new TimerTask() {
                @Override
                public void run() {
                    try {
                        if (bdb != null) {
                            logger.trace("Synchronized data store [name=" + getName() + "]");
                            bdb.sync();
                        } else {
                            // cancel this task!
                            logger.warn("Cancelling synchronizeTimerTask since bdb was null, this may be normal if data store shutting down");
                            this.cancel();
                        }
                    } catch (Exception e) {
                        logger.error("", e);
                    }
                }
            };

            synchronizeTimer.schedule(synchronizeTimerTask, synchronizeInterval, synchronizeInterval);

            logger.debug("Automatic sync enabled every " + synchronizeInterval + " ms for " + toString());
        }
    }

    @Override
    public void doClose() throws DataStoreFatalException {
        try {
            // make sure sync thread is cancelled
            if (synchronizeTimerTask != null) {
                try {
                    synchronizeTimerTask.cancel();
                } catch (Exception e) {
                    // ignore this
                }
                synchronizeTimerTask = null;
            }

            if (synchronizeTimer != null) {
                try {
                    synchronizeTimer.cancel();
                } catch (Exception e) {
                    // ignore this
                }
                synchronizeTimer = null;
            }

            if (bdb != null) {
                if (!bdb.close()) {
                    // error occurred while opening, throw error
                    int ecode = bdb.ecode();
                    throw new DataStoreFatalException("Unable to properly close DataStore {code=" + ecode + ", message=" + BDB.errmsg(ecode) + "}");
                }
            }
        } finally {
            bdb = null;
        }
    }

    @Override
    public void doSetRecord(byte[] key, byte[] value) throws DataStoreFatalException {
        // bdb.put = returns true if value put in and didn't replace anything
        //           returns false if it failed OR if it had to replace a value
        if (!bdb.put(key, value)) {
            int ecode = bdb.ecode();
            // this code may be 0 if the value was replaced
            if (ecode == 0) {
                // value replaced, ignoring
            } else {
                throw new DataStoreFatalException("Unable to set value and key [0x" + HexUtil.toHexString(key) + "] [code=" + ecode + ", message=" + BDB.errmsg(ecode) + "]");
            }
        }

        // if synchronize interval is zero, sync() on every put() and take()
        if (this.synchronizeInterval == 0) {
            // syncing the b-tree database
            bdb.sync();
        }
    }

    @Override
    public byte[] doGetRecord(byte[] key) throws RecordNotFoundException, DataStoreFatalException {
        byte[] value = bdb.get(key);

        if (value == null) {
            throw new RecordNotFoundException("The record for key [0x" + HexUtil.toHexString(key) + "] was not found");
        }

        return value;
    }

    @Override
    public void doDeleteRecord(byte[] key) throws RecordNotFoundException, DataStoreFatalException {
        if (!bdb.out(key)) {
            int ecode = bdb.ecode();
            // this code may be 0 if the value was replaced
            if (ecode == 0) {
                // value deleted, ignoring error code
            } else if (ecode == 22) {
                //[code=22, message=no record found]
                throw new RecordNotFoundException("The record for key [0x" + HexUtil.toHexString(key) + "] was not found");
            } else {
                throw new DataStoreFatalException("Unable to delete record with key [0x" + HexUtil.toHexString(key) + "] [code=" + ecode + ", message=" + BDB.errmsg(ecode) + "]");
            }
        }

        // if synchronize interval is zero, sync() on every put() and take()
        if (this.synchronizeInterval == 0) {
            // syncing the b-tree database
            bdb.sync();
        }
    }

    public DataStoreIterator doGetAscendingIterator() throws DataStoreFatalException {
        return new TokyoDataStoreIterator(getDatabase());
    }
}
TOP

Related Classes of com.cloudhopper.datastore.tokyo.TokyoDataStore

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.