Пример #1
0
db_t db_init(const size_t size, const char * const path, const int flags, const int mode, int mdb_flags, int ndbi, dbi_t *dbis, size_t padsize){
	int fd = -1, err = 0xdeadbeef;
	struct stat st;

	db_t db;
	db = malloc(size < sizeof(*db) ? sizeof(*db) : size);
	assert(db);

	// always do this
	mdb_flags |= MDB_NOSUBDIR;

	int r;
	// ignore MDB_RDONLY - key off of OS flags
	// see docs for open() - O_RDONLY is not a bit!
	if((flags & 0x3) == O_RDONLY){
		mdb_flags |= MDB_RDONLY;
		// disable NOSYNC/NOMETASYNC for readonly, as that burns another file descriptor
		mdb_flags &= ~(MDB_NOSYNC|MDB_NOMETASYNC);
	}else{
		mdb_flags &= ~MDB_RDONLY;
	}


	// default to 100mb minimum pad
	db->padsize = padsize ? padsize : (1<<27);

	db->env = NULL;
	db->txns = 0;
	db->handles = NULL;
	db->updated = 0;

	r = pthread_mutex_init(&db->mutex, NULL);
	FAIL(r, err, errno, fail);

	r = pthread_cond_init(&db->cond, NULL);
	FAIL(r, err, errno, fail);

	// unless MDB_RDONLY is specified, lmdb will automatically create non-existant databases,
	// which is not what I want. Try to emulate standard unix open() flags:
	fd = open(path, flags, mode);
	FAIL(-1 == fd, err, errno, fail);

	r = mdb_env_create(&db->env);
	FAIL(r, err, r, fail);

	r = mdb_env_set_maxdbs(db->env, ndbi);
	FAIL(r, err, r, fail);

	size_t mapsize;
	do{
		r = fstat(fd, &st);
		FAIL(r, err, errno, fail);

		if(!st.st_size &&( flags & O_CREAT))
			db->updated = 1;

		// pad out such that we have at least 1gb of map overhead
		mapsize = (1 + st.st_size / db->padsize) * db->padsize;

		r = mdb_env_set_mapsize(db->env, mapsize);
	}while(MDB_SUCCESS != r);
	close(fd);
	fd = -1;

	r = mdb_env_open(db->env, path, mdb_flags, mode);

	// mdb_env_open can return EAGAIN somehow, but I think it really means:
	if(EAGAIN == r)
		r = EMFILE;
	FAIL(r, err, r, fail);

	r = mdb_env_get_fd(db->env, &db->fd);
	FAIL(r, err, r, fail);

	if(dbis && ndbi){
		db->handles = malloc(sizeof(db->handles[0]) * ndbi);
		FAIL(!db->handles, err, errno, fail);

		// open the indexes - try read-only first
		unsigned int txn_flags = MDB_RDONLY;
		txn_t txn = db_txn_new(db, NULL, txn_flags);

		int i;
		for(i = 0; i < ndbi; i++){
			int dbflags = dbis[i].flags;
retry:
			dbflags = (txn_flags & MDB_RDONLY) ? (dbflags & ~MDB_CREATE) : (dbflags | MDB_CREATE);
			r = mdb_dbi_open(txn->txn, dbis[i].name, dbflags, &db->handles[i]);
			if(MDB_SUCCESS != r){
				if(MDB_NOTFOUND == r && (txn_flags & MDB_RDONLY)){
					// we were in read-only and a sub-db was missing
					// end txn
					txn_commit(txn);
					// switch to read-write
					txn_flags &= ~MDB_RDONLY;
					txn = db_txn_new(db, NULL, txn_flags);
					// and pick up where we left off
					goto retry;
				}else{
					txn_abort(txn);
				}
				FAIL(r, err, r, fail);
			}
		}
		r = txn_commit(txn);
		FAIL(r, err, r, fail);
	}

	return db;

fail:
	db_close(db);
	if(fd != -1){
		if((flags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
			unlink(path);
		close(fd);
	}
	errno = err;
	return NULL;
}
Пример #2
0
int     slmdb_open(SLMDB *slmdb, const char *path, int open_flags,
		           int lmdb_flags, int slmdb_flags)
{
    struct stat st;
    MDB_env *env;
    MDB_txn *txn;
    MDB_dbi dbi;
    int     db_fd;
    int     status;

    /*
     * Create LMDB environment.
     */
    if ((status = mdb_env_create(&env)) != 0)
	return (status);

    /*
     * Make sure that the memory map has room to store and commit an initial
     * "drop" transaction as well as fixed database metadata. We have no way
     * to recover from errors before the first application-level I/O request.
     */
#define SLMDB_FUDGE      10240

    if (slmdb->curr_limit < SLMDB_FUDGE)
	slmdb->curr_limit = SLMDB_FUDGE;
    if (stat(path, &st) == 0
	&& st.st_size > slmdb->curr_limit - SLMDB_FUDGE) {
	if (st.st_size > slmdb->hard_limit)
	    slmdb->hard_limit = st.st_size;
	if (st.st_size < slmdb->hard_limit - SLMDB_FUDGE)
	    slmdb->curr_limit = st.st_size + SLMDB_FUDGE;
	else
	    slmdb->curr_limit = slmdb->hard_limit;
    }

    /*
     * mdb_open() requires a txn, but since the default DB always exists in
     * an LMDB environment, we usually don't need to do anything else with
     * the txn. It is currently used for truncate and for bulk transactions.
     */
    if ((status = mdb_env_set_mapsize(env, slmdb->curr_limit)) != 0
	|| (status = mdb_env_open(env, path, lmdb_flags, 0644)) != 0
	|| (status = mdb_txn_begin(env, (MDB_txn *) 0,
				   lmdb_flags & MDB_RDONLY, &txn)) != 0
	|| (status = mdb_open(txn, (const char *) 0, 0, &dbi)) != 0
	|| (status = mdb_env_get_fd(env, &db_fd)) != 0) {
	mdb_env_close(env);
	return (status);
    }

    /*
     * Bundle up.
     */
    slmdb->open_flags = open_flags;
    slmdb->lmdb_flags = lmdb_flags;
    slmdb->slmdb_flags = slmdb_flags;
    slmdb->env = env;
    slmdb->dbi = dbi;
    slmdb->db_fd = db_fd;
    slmdb->cursor = 0;
    slmdb_saved_key_init(slmdb);
    slmdb->api_retry_count = 0;
    slmdb->bulk_retry_count = 0;
    slmdb->api_retry_limit = SLMDB_DEF_API_RETRY_LIMIT;
    slmdb->bulk_retry_limit = SLMDB_DEF_BULK_RETRY_LIMIT;
    slmdb->longjmp_fn = 0;
    slmdb->notify_fn = 0;
    slmdb->assert_fn = 0;
    slmdb->cb_context = 0;
    slmdb->txn = txn;

    if ((status = slmdb_prepare(slmdb)) != 0)
	mdb_env_close(env);

    return (status);
}