* Automatically upgrade the Berkeley DB environment if necessary.

This commit is contained in:
Eelco Dolstra 2005-05-09 15:25:47 +00:00
parent 88dea78cdf
commit 8f57634c14
2 changed files with 134 additions and 107 deletions

View file

@ -165,12 +165,10 @@ static int my_fsync(int fd)
} }
void Database::open(const string & path) void Database::open2(const string & path, bool removeOldEnv)
{ {
if (env) throw Error(format("environment already open")); if (env) throw Error(format("environment already open"));
try {
debug(format("opening database environment")); debug(format("opening database environment"));
@ -183,39 +181,37 @@ void Database::open(const string & path)
env->set_lg_max(256 * 1024); /* must be > 4 * lg_bsize */ env->set_lg_max(256 * 1024); /* must be > 4 * lg_bsize */
/* Write the log, but don't sync. This protects transactions /* Write the log, but don't sync. This protects transactions
against application crashes, but if the system crashes, against application crashes, but if the system crashes, some
some transactions may be undone. An acceptable risk, I transactions may be undone. An acceptable risk, I think. */
think. */
env->set_flags(DB_TXN_WRITE_NOSYNC | DB_LOG_AUTOREMOVE, 1); env->set_flags(DB_TXN_WRITE_NOSYNC | DB_LOG_AUTOREMOVE, 1);
/* Increase the locking limits. If you ever get `Dbc::get: /* Increase the locking limits. If you ever get `Dbc::get: Cannot
Cannot allocate memory' or similar, especially while allocate memory' or similar, especially while running
running `nix-store --verify', just increase the following `nix-store --verify', just increase the following number, then
number, then run db_recover on the database to remove the run db_recover on the database to remove the existing DB
existing DB environment (since changes only take effect on environment (since changes only take effect on new
new environments). */ environments). */
env->set_lk_max_locks(10000); env->set_lk_max_locks(10000);
env->set_lk_max_lockers(10000); env->set_lk_max_lockers(10000);
env->set_lk_max_objects(10000); env->set_lk_max_objects(10000);
env->set_lk_detect(DB_LOCK_DEFAULT); env->set_lk_detect(DB_LOCK_DEFAULT);
/* Dangerous, probably, but from the docs it *seems* that BDB /* Dangerous, probably, but from the docs it *seems* that BDB
shouldn't sync when DB_TXN_WRITE_NOSYNC is used, but it shouldn't sync when DB_TXN_WRITE_NOSYNC is used, but it still
still fsync()s sometimes. */ fsync()s sometimes. */
db_env_set_func_fsync(my_fsync); db_env_set_func_fsync(my_fsync);
/* The following code provides automatic recovery of the /* The following code provides automatic recovery of the database
database environment. Recovery is necessary when a process environment. Recovery is necessary when a process dies while
dies while it has the database open. To detect this, it has the database open. To detect this, processes atomically
processes atomically increment a counter when they open the increment a counter when they open the database, and decrement
database, and decrement it when they close it. If we see it when they close it. If we see that counter is > 0 but no
that counter is > 0 but no processes are accessing the processes are accessing the database---determined by attempting
database---determined by attempting to obtain a write lock to obtain a write lock on a lock file on which all accessors
on a lock file on which all accessors have a read lock---we have a read lock---we must run recovery. Note that this also
must run recovery. Note that this also ensures that we ensures that we only run recovery when there are no other
only run recovery when there are no other accessors (which accessors (which could cause database corruption). */
could cause database corruption). */
/* !!! close fdAccessors / fdLock on exception */ /* !!! close fdAccessors / fdLock on exception */
@ -241,8 +237,14 @@ void Database::open(const string & path)
debug(format("write lock granted")); debug(format("write lock granted"));
/* We have a write lock, which means that there are no /* We have a write lock, which means that there are no other
other readers or writers. */ readers or writers. */
if (removeOldEnv) {
printMsg(lvlError, "removing old Berkeley DB database environment...");
env->remove(path.c_str(), DB_FORCE);
return;
}
int n = getAccessorCount(fdAccessors); int n = getAccessorCount(fdAccessors);
@ -284,8 +286,31 @@ void Database::open(const string & path)
} }
this->env = env; this->env = env;
}
void Database::open(const string & path)
{
try {
open2(path, false);
} catch (DbException e) {
if (e.get_errno() == DB_VERSION_MISMATCH) {
/* Remove the environment while we are holding the global
lock. If things go wrong there, we bail out. !!!
there is some leakage here op DbEnv and lock
handles. */
open2(path, true);
/* Try again. */
open2(path, false);
}
else
rethrow(e);
}
} catch (DbException e) { rethrow(e); }
} }

View file

@ -58,6 +58,8 @@ private:
Db * getDb(TableId table); Db * getDb(TableId table);
void open2(const string & path, bool removeOldEnv);
public: public:
Database(); Database();
~Database(); ~Database();