func (m *MDBStore) DeleteRange(minIdx, maxIdx uint64) error {
	tx, dbis, err := m.startTxn(false, dbLogs)
	if err != nil {
		return err
	}
	defer tx.Abort()
	var num int
DELETE:
	num, err = m.innerDeleteRange(tx, dbis, minIdx, maxIdx)
	if err != nil {
		return err
	}
	if num > 0 {
		goto DELETE
	}
	return tx.Commit()
}
Deletes a range of log entries. The range is inclusive.
package raftmdb
import (
	"fmt"
	"github.com/socketplane/socketplane/Godeps/_workspace/src/github.com/armon/gomdb"
	"github.com/socketplane/socketplane/Godeps/_workspace/src/github.com/hashicorp/raft"
	"os"
	"path/filepath"
)
const (
	dbLogs       = "logs"
	dbConf       = "conf"
	dbMaxMapSize = 128 * 1024 * 1024  
)
var mdbPath = "mdb/"
type MDBStore struct {
	env     *mdb.Env
	path    string
	maxSize uint64
}
func NewMDBStore(base string) (*MDBStore, error) {
	return NewMDBStoreWithSize(base, 0)
}
func NewMDBStoreWithSize(base string, maxSize uint64) (*MDBStore, error) {
	path := filepath.Join(base, mdbPath)
	if err := os.MkdirAll(path, 0755); err != nil {
		return nil, err
	}
	if maxSize == 0 {
		maxSize = dbMaxMapSize
	}
	env, err := mdb.NewEnv()
	if err != nil {
		return nil, err
	}
	store := &MDBStore{
		env:     env,
		path:    path,
		maxSize: maxSize,
	}
	if err := store.initialize(); err != nil {
		env.Close()
		return nil, err
	}
	return store, nil
}
func (m *MDBStore) initialize() error {
	if err := m.env.SetMaxDBs(mdb.DBI(2)); err != nil {
		return err
	}
	if err := m.env.SetMapSize(m.maxSize); err != nil {
		return err
	}
	if err := m.env.Open(m.path, mdb.NOTLS, 0755); err != nil {
		return err
	}
	tx, _, err := m.startTxn(false, dbLogs, dbConf)
	if err != nil {
		tx.Abort()
		return err
	}
	return tx.Commit()
}
func (m *MDBStore) Close() error {
	m.env.Close()
	return nil
}
func (m *MDBStore) startTxn(readonly bool, open ...string) (*mdb.Txn, []mdb.DBI, error) {
	var txFlags uint = 0
	var dbFlags uint = 0
	if readonly {
		txFlags |= mdb.RDONLY
	} else {
		dbFlags |= mdb.CREATE
	}
	tx, err := m.env.BeginTxn(nil, txFlags)
	if err != nil {
		return nil, nil, err
	}
	var dbs []mdb.DBI
	for _, name := range open {
		dbi, err := tx.DBIOpen(name, dbFlags)
		if err != nil {
			tx.Abort()
			return nil, nil, err
		}
		dbs = append(dbs, dbi)
	}
	return tx, dbs, nil
}
func (m *MDBStore) FirstIndex() (uint64, error) {
	tx, dbis, err := m.startTxn(true, dbLogs)
	if err != nil {
		return 0, err
	}
	defer tx.Abort()
	cursor, err := tx.CursorOpen(dbis[0])
	if err != nil {
		return 0, err
	}
	defer cursor.Close()
	key, _, err := cursor.Get(nil, mdb.FIRST)
	if err == mdb.NotFound {
		return 0, nil
	} else if err != nil {
		return 0, err
	}
	return bytesToUint64(key), nil
}
func (m *MDBStore) LastIndex() (uint64, error) {
	tx, dbis, err := m.startTxn(true, dbLogs)
	if err != nil {
		return 0, err
	}
	defer tx.Abort()
	cursor, err := tx.CursorOpen(dbis[0])
	if err != nil {
		return 0, err
	}
	defer cursor.Close()
	key, _, err := cursor.Get(nil, mdb.LAST)
	if err == mdb.NotFound {
		return 0, nil
	} else if err != nil {
		return 0, err
	}
	return bytesToUint64(key), nil
}
func (m *MDBStore) GetLog(index uint64, logOut *raft.Log) error {
	key := uint64ToBytes(index)
	tx, dbis, err := m.startTxn(true, dbLogs)
	if err != nil {
		return err
	}
	defer tx.Abort()
	val, err := tx.Get(dbis[0], key)
	if err == mdb.NotFound {
		return raft.ErrLogNotFound
	} else if err != nil {
		return err
	}
	return decodeMsgPack(val, logOut)
}
func (m *MDBStore) StoreLog(log *raft.Log) error {
	return m.StoreLogs([]*raft.Log{log})
}
func (m *MDBStore) StoreLogs(logs []*raft.Log) error {
	tx, dbis, err := m.startTxn(false, dbLogs)
	if err != nil {
		return err
	}
	for _, log := range logs {
		key := uint64ToBytes(log.Index)
		val, err := encodeMsgPack(log)
		if err != nil {
			tx.Abort()
			return err
		}
		if err := tx.Put(dbis[0], key, val.Bytes(), 0); err != nil {
			tx.Abort()
			return err
		}
	}
	return tx.Commit()
}