ITS#8704 add MDB_PREVSNAPSHOT flag to mdb_env_open

used to open the previous snapshot, in case the latest one
is corrupted
This commit is contained in:
Howard Chu 2017-08-12 12:21:25 +01:00
parent 86226754a9
commit 9c6eb75c65
No known key found for this signature in database
GPG key ID: FD2A70B44AB11BA7
8 changed files with 68 additions and 15 deletions

View file

@ -311,6 +311,8 @@ typedef void (MDB_rel_func)(MDB_val *item, void *oldptr, void *newptr, void *rel
#define MDB_NORDAHEAD 0x800000
/** don't initialize malloc'd memory before writing to datafile */
#define MDB_NOMEMINIT 0x1000000
/** use the previous snapshot rather than the latest one */
#define MDB_PREVSNAPSHOT 0x2000000
/** @} */
/** @defgroup mdb_dbi_open Database Flags
@ -622,6 +624,12 @@ int mdb_env_create(MDB_env **env);
* 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_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. 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.

View file

@ -1468,7 +1468,7 @@ static int mdb_page_merge(MDB_cursor *csrc, MDB_cursor *cdst);
static int mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata,
pgno_t newpgno, unsigned int nflags);
static int mdb_env_read_header(MDB_env *env, MDB_meta *meta);
static int mdb_env_read_header(MDB_env *env, int prev, MDB_meta *meta);
static MDB_meta *mdb_env_pick_meta(const MDB_env *env);
static int mdb_env_write_meta(MDB_txn *txn);
#ifdef MDB_USE_POSIX_MUTEX /* Drop unused excl arg */
@ -3632,6 +3632,8 @@ done:
return MDB_SUCCESS;
}
static int ESECT mdb_env_share_locks(MDB_env *env, int *excl);
int
mdb_txn_commit(MDB_txn *txn)
{
@ -3854,6 +3856,15 @@ 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);
@ -3867,11 +3878,12 @@ fail:
/** Read the environment parameters of a DB environment before
* mapping it into memory.
* @param[in] env the environment handle
* @param[in] prev whether to read the backup meta page
* @param[out] meta address of where to store the meta information
* @return 0 on success, non-zero on failure.
*/
static int ESECT
mdb_env_read_header(MDB_env *env, MDB_meta *meta)
mdb_env_read_header(MDB_env *env, int prev, MDB_meta *meta)
{
MDB_metabuf pbuf;
MDB_page *p;
@ -3922,7 +3934,7 @@ mdb_env_read_header(MDB_env *env, MDB_meta *meta)
return MDB_VERSION_MISMATCH;
}
if (off == 0 || m->mm_txnid > meta->mm_txnid)
if (off == 0 || (prev ? m->mm_txnid < meta->mm_txnid : m->mm_txnid > meta->mm_txnid))
*meta = *m;
}
return 0;
@ -4131,7 +4143,8 @@ static MDB_meta *
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
@ -4366,7 +4379,7 @@ mdb_fsize(HANDLE fd, mdb_size_t *size)
/** Further setup required for opening an LMDB environment
*/
static int ESECT
mdb_env_open2(MDB_env *env)
mdb_env_open2(MDB_env *env, int prev)
{
unsigned int flags = env->me_flags;
int i, newenv = 0, rc;
@ -4429,7 +4442,7 @@ mdb_env_open2(MDB_env *env)
}
#endif
if ((i = mdb_env_read_header(env, &meta)) != 0) {
if ((i = mdb_env_read_header(env, prev, &meta)) != 0) {
if (i != ENOENT)
return i;
DPUTS("new mdbenv");
@ -4505,6 +4518,9 @@ mdb_env_open2(MDB_env *env)
#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);
@ -4600,9 +4616,6 @@ static int ESECT
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
{
@ -5056,7 +5069,7 @@ fail:
*/
#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_WRITEMAP|MDB_NOTLS|MDB_NOLOCK|MDB_NORDAHEAD|MDB_PREVSNAPSHOT)
#if VALID_FLAGS & PERSISTENT_FLAGS & (CHANGEABLE|CHANGELESS)
# error "Persistent DB flags & env flags overlap, but both go in mm_flags"
@ -5178,9 +5191,13 @@ mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode
rc = mdb_env_setup_locks(env, lpath, mode, &excl);
if (rc)
goto leave;
if ((flags & MDB_PREVSNAPSHOT) && !excl) {
rc = EAGAIN;
goto leave;
}
}
if ((rc = mdb_env_open2(env)) == MDB_SUCCESS) {
if ((rc = mdb_env_open2(env, flags & MDB_PREVSNAPSHOT)) == MDB_SUCCESS) {
if (flags & (MDB_RDONLY|MDB_WRITEMAP)) {
env->me_mfd = env->me_fd;
} else {
@ -5206,7 +5223,7 @@ mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode
}
}
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;

View file

@ -11,6 +11,8 @@ mdb_copy \- LMDB environment copy tool
.BR \-c ]
[\c
.BR \-n ]
[\c
.BR \-v ]
.B srcpath
[\c
.BR dstpath ]
@ -39,6 +41,10 @@ slow down the backup process as it is more CPU-intensive.
.TP
.BR \-n
Open LDMB environment(s) which do not use subdirectories.
.TP
.BR \-v
Use the previous environment state instead of the latest state.
This may be useful if the latest state has been corrupted.
.SH DIAGNOSTICS
Exit status is zero if no errors occur.

View file

@ -38,6 +38,8 @@ int main(int argc,char * argv[])
for (; argc > 1 && argv[1][0] == '-'; argc--, argv++) {
if (argv[1][1] == 'n' && argv[1][2] == '\0')
flags |= MDB_NOSUBDIR;
else if (argv[1][1] == 'v' && argv[1][2] == '\0')
flags |= MDB_PREVSNAPSHOT;
else if (argv[1][1] == 'c' && argv[1][2] == '\0')
cpflags |= MDB_CP_COMPACT;
else if (argv[1][1] == 'V' && argv[1][2] == '\0') {
@ -48,7 +50,7 @@ int main(int argc,char * argv[])
}
if (argc<2 || argc>3) {
fprintf(stderr, "usage: %s [-V] [-c] [-n] srcpath [dstpath]\n", progname);
fprintf(stderr, "usage: %s [-V] [-c] [-n] [-v] srcpath [dstpath]\n", progname);
exit(EXIT_FAILURE);
}

View file

@ -14,6 +14,8 @@ mdb_dump \- LMDB environment export tool
[\c
.BR \-n ]
[\c
.BR \-v ]
[\c
.BR \-p ]
[\c
.BR \-a \ |
@ -42,6 +44,10 @@ names will be listed, no data will be output.
.BR \-n
Dump an LMDB database which does not use subdirectories.
.TP
.BR \-v
Use the previous environment state instead of the latest state.
This may be useful if the latest state has been corrupted.
.TP
.BR \-p
If characters in either the key or data items are printing characters (as
defined by isprint(3)), output them directly. This option permits users to

View file

@ -164,7 +164,7 @@ static int dumpit(MDB_txn *txn, MDB_dbi dbi, char *name)
static void usage(char *prog)
{
fprintf(stderr, "usage: %s [-V] [-f output] [-l] [-n] [-p] [-a|-s subdb] dbpath\n", prog);
fprintf(stderr, "usage: %s [-V] [-f output] [-l] [-n] [-p] [-v] [-a|-s subdb] dbpath\n", prog);
exit(EXIT_FAILURE);
}
@ -188,6 +188,7 @@ int main(int argc, char *argv[])
* -n: use NOSUBDIR flag on env_open
* -p: use printable characters
* -f: write to file instead of stdout
* -v: use previous snapshot
* -V: print version and exit
* (default) dump only the main DB
*/
@ -215,6 +216,9 @@ int main(int argc, char *argv[])
case 'n':
envflags |= MDB_NOSUBDIR;
break;
case 'v':
envflags |= MDB_PREVSNAPSHOT;
break;
case 'p':
mode |= PRINT;
break;

View file

@ -14,6 +14,8 @@ mdb_stat \- LMDB environment status tool
[\c
.BR \-n ]
[\c
.BR \-v ]
[\c
.BR \-r [ r ]]
[\c
.BR \-a \ |
@ -39,6 +41,10 @@ If \fB\-fff\fP is given, display the full list of page IDs in the freelist.
.BR \-n
Display the status of an LMDB database which does not use subdirectories.
.TP
.BR \-v
Use the previous environment state instead of the latest state.
This may be useful if the latest state has been corrupted.
.TP
.BR \-r
Display information about the environment reader table.
Shows the process ID, thread ID, and transaction ID for each active

View file

@ -46,7 +46,7 @@ static void prstat(MDB_stat *ms)
static void usage(char *prog)
{
fprintf(stderr, "usage: %s [-V] [-n] [-e] [-r[r]] [-f[f[f]]] [-a|-s subdb] dbpath\n", prog);
fprintf(stderr, "usage: %s [-V] [-n] [-e] [-r[r]] [-f[f[f]]] [-v] [-a|-s subdb] dbpath\n", prog);
exit(EXIT_FAILURE);
}
@ -73,6 +73,7 @@ int main(int argc, char *argv[])
* -f: print freelist info
* -r: print reader info
* -n: use NOSUBDIR flag on env_open
* -v: use previous snapshot
* -V: print version and exit
* (default) print stat of only the main DB
*/
@ -96,6 +97,9 @@ int main(int argc, char *argv[])
case 'n':
envflags |= MDB_NOSUBDIR;
break;
case 'v':
envflags |= MDB_PREVSNAPSHOT;
break;
case 'r':
rdrinfo++;
break;