#define MDB_NORDAHEAD 0x800000
/** don't initialize malloc'd memory before writing to datafile */
#define MDB_NOMEMINIT 0x1000000
- /** use the previous meta page rather than the latest one */
-#define MDB_PREVMETA 0x2000000
+ /** use the previous snapshot rather than the latest one */
+#define MDB_PREVSNAPSHOT 0x2000000
/** don't use a single mmap, remap individual chunks (needs MDB_RPAGE_CACHE) */
#define MDB_REMAP_CHUNKS 0x4000000
/** @} */
* caller is expected to overwrite all of the memory that was
* reserved in that case.
* This flag may be changed at any time using #mdb_env_set_flags().
- * <li>#MDB_PREVMETA
- * Open the environment with the previous meta page rather than the latest
+ * <li>#MDB_PREVSNAPSHOT
+ * Open the environment with the previous snapshot rather than the latest
* one. This loses the latest transaction, but may help work around some
- * types of corruption.
+ * types of corruption. If opened with write access, this must be the
+ * only process using the environment. This flag is automatically reset
+ * after a write transaction is successfully committed.
* </ul>
* @param[in] mode The UNIX permissions to set on created files and semaphores.
* This parameter is ignored on Windows.
return MDB_SUCCESS;
}
+static int ESECT mdb_env_share_locks(MDB_env *env, int *excl);
+
int
mdb_txn_commit(MDB_txn *txn)
{
if ((rc = mdb_env_write_meta(txn)))
goto fail;
end_mode = MDB_END_COMMITTED|MDB_END_UPDATE;
+ if (env->me_flags & MDB_PREVSNAPSHOT) {
+ if (!(env->me_flags & MDB_NOLOCK)) {
+ int excl;
+ rc = mdb_env_share_locks(env, &excl);
+ if (rc)
+ goto fail;
+ }
+ env->me_flags ^= MDB_PREVSNAPSHOT;
+ }
done:
mdb_txn_end(txn, end_mode);
mdb_env_pick_meta(const MDB_env *env)
{
MDB_meta *const *metas = env->me_metas;
- return metas[ metas[0]->mm_txnid < metas[1]->mm_txnid ];
+ return metas[ (metas[0]->mm_txnid < metas[1]->mm_txnid) ^
+ ((env->me_flags & MDB_PREVSNAPSHOT) != 0) ];
}
int ESECT
#endif
env->me_maxpg = env->me_mapsize / env->me_psize;
+ if (env->me_txns)
+ env->me_txns->mti_txnid = meta.mm_txnid;
+
#if MDB_DEBUG
{
MDB_meta *meta = mdb_env_pick_meta(env);
mdb_env_share_locks(MDB_env *env, int *excl)
{
int rc = 0;
- MDB_meta *meta = mdb_env_pick_meta(env);
-
- env->me_txns->mti_txnid = meta->mm_txnid;
#ifdef _WIN32
{
/*f*/ MDB_FIXEDMAP, /*h*/ MDB_NORDAHEAD, /*i*/ MDB_NOMEMINIT,
/*l*/ MDB_NOLOCK, /*m*/ MDB_NOMETASYNC, /*n*/ MDB_NOSUBDIR,
/*r*/ MDB_RDONLY, /*s*/ MDB_NOSYNC, /*t*/ MDB_NOTLS,
- /*v*/ MDB_PREVMETA, /*w*/ MDB_WRITEMAP,
+ /*v*/ MDB_PREVSNAPSHOT, /*w*/ MDB_WRITEMAP,
};
unsigned flags = 0;
const char *s, *opts = getenv("LMDB_FLAGS");
*/
#define CHANGEABLE (MDB_NOSYNC|MDB_NOMETASYNC|MDB_MAPASYNC|MDB_NOMEMINIT)
#define CHANGELESS (MDB_FIXEDMAP|MDB_NOSUBDIR|MDB_RDONLY| \
- MDB_WRITEMAP|MDB_NOTLS|MDB_NOLOCK|MDB_NORDAHEAD|MDB_PREVMETA|MDB_REMAP_CHUNKS)
+ MDB_WRITEMAP|MDB_NOTLS|MDB_NOLOCK|MDB_NORDAHEAD|MDB_PREVSNAPSHOT|MDB_REMAP_CHUNKS)
#define EXPOSED (CHANGEABLE|CHANGELESS | MDB_ENCRYPT)
#if VALID_FLAGS & PERSISTENT_FLAGS & EXPOSED
rc = mdb_env_setup_locks(env, &fname, mode, &excl);
if (rc)
goto leave;
+ if ((flags & MDB_PREVSNAPSHOT) && !excl) {
+ rc = EAGAIN;
+ goto leave;
+ }
}
rc = mdb_fopen(env, &fname,
goto leave;
}
- if ((rc = mdb_env_open2(env, flags & MDB_PREVMETA)) == MDB_SUCCESS) {
+ if ((rc = mdb_env_open2(env, flags & MDB_PREVSNAPSHOT)) == MDB_SUCCESS) {
if (!(flags & (MDB_RDONLY|MDB_WRITEMAP))) {
/* Synchronous fd for meta writes. Needed even with
* MDB_NOSYNC/MDB_NOMETASYNC, in case these get reset.
goto leave;
}
DPRINTF(("opened dbenv %p", (void *) env));
- if (excl > 0) {
+ if (excl > 0 && !(flags & MDB_PREVSNAPSHOT)) {
rc = mdb_env_share_locks(env, &excl);
if (rc)
goto leave;