forked from lix-project/lix
* Automatically upgrade the Berkeley DB environment if necessary.
This commit is contained in:
parent
88dea78cdf
commit
8f57634c14
|
@ -165,127 +165,152 @@ 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"));
|
|
||||||
|
|
||||||
|
|
||||||
/* Create the database environment object. */
|
/* Create the database environment object. */
|
||||||
DbEnv * env = 0; /* !!! close on error */
|
DbEnv * env = 0; /* !!! close on error */
|
||||||
env = new DbEnv(0);
|
env = new DbEnv(0);
|
||||||
|
|
||||||
/* Smaller log files. */
|
/* Smaller log files. */
|
||||||
env->set_lg_bsize(32 * 1024); /* default */
|
env->set_lg_bsize(32 * 1024); /* default */
|
||||||
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: Cannot
|
||||||
/* Increase the locking limits. If you ever get `Dbc::get:
|
allocate memory' or similar, especially while running
|
||||||
Cannot allocate memory' or similar, especially while
|
`nix-store --verify', just increase the following number, then
|
||||||
running `nix-store --verify', just increase the following
|
run db_recover on the database to remove the existing DB
|
||||||
number, then run db_recover on the database to remove the
|
environment (since changes only take effect on new
|
||||||
existing DB environment (since changes only take effect on
|
environments). */
|
||||||
new 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 still
|
||||||
shouldn't sync when DB_TXN_WRITE_NOSYNC is used, but it
|
fsync()s sometimes. */
|
||||||
still 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 */
|
||||||
|
|
||||||
/* Open the accessor count file. */
|
/* Open the accessor count file. */
|
||||||
string accessorsPath = path + "/accessor_count";
|
string accessorsPath = path + "/accessor_count";
|
||||||
fdAccessors = ::open(accessorsPath.c_str(), O_RDWR | O_CREAT, 0666);
|
fdAccessors = ::open(accessorsPath.c_str(), O_RDWR | O_CREAT, 0666);
|
||||||
if (fdAccessors == -1)
|
if (fdAccessors == -1)
|
||||||
if (errno == EACCES)
|
if (errno == EACCES)
|
||||||
throw DbNoPermission(
|
throw DbNoPermission(
|
||||||
format("permission denied to database in `%1%'") % accessorsPath);
|
format("permission denied to database in `%1%'") % accessorsPath);
|
||||||
else
|
else
|
||||||
throw SysError(format("opening file `%1%'") % accessorsPath);
|
throw SysError(format("opening file `%1%'") % accessorsPath);
|
||||||
|
|
||||||
/* Open the lock file. */
|
/* Open the lock file. */
|
||||||
string lockPath = path + "/access_lock";
|
string lockPath = path + "/access_lock";
|
||||||
fdLock = ::open(lockPath.c_str(), O_RDWR | O_CREAT, 0666);
|
fdLock = ::open(lockPath.c_str(), O_RDWR | O_CREAT, 0666);
|
||||||
if (fdLock == -1)
|
if (fdLock == -1)
|
||||||
throw SysError(format("opening lock file `%1%'") % lockPath);
|
throw SysError(format("opening lock file `%1%'") % lockPath);
|
||||||
|
|
||||||
/* Try to acquire a write lock. */
|
/* Try to acquire a write lock. */
|
||||||
debug(format("attempting write lock on `%1%'") % lockPath);
|
debug(format("attempting write lock on `%1%'") % lockPath);
|
||||||
if (lockFile(fdLock, ltWrite, false)) { /* don't wait */
|
if (lockFile(fdLock, ltWrite, false)) { /* don't wait */
|
||||||
|
|
||||||
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. */
|
||||||
|
|
||||||
int n = getAccessorCount(fdAccessors);
|
if (removeOldEnv) {
|
||||||
|
printMsg(lvlError, "removing old Berkeley DB database environment...");
|
||||||
if (n != 0) {
|
env->remove(path.c_str(), DB_FORCE);
|
||||||
printMsg(lvlTalkative,
|
return;
|
||||||
format("accessor count is %1%, running recovery") % n);
|
}
|
||||||
|
|
||||||
/* Open the environment after running recovery. */
|
int n = getAccessorCount(fdAccessors);
|
||||||
openEnv(env, path, DB_RECOVER);
|
|
||||||
}
|
if (n != 0) {
|
||||||
|
printMsg(lvlTalkative,
|
||||||
|
format("accessor count is %1%, running recovery") % n);
|
||||||
|
|
||||||
else
|
/* Open the environment after running recovery. */
|
||||||
/* Open the environment normally. */
|
openEnv(env, path, DB_RECOVER);
|
||||||
openEnv(env, path, 0);
|
}
|
||||||
|
|
||||||
setAccessorCount(fdAccessors, 1);
|
else
|
||||||
|
|
||||||
/* Downgrade to a read lock. */
|
|
||||||
debug(format("downgrading to read lock on `%1%'") % lockPath);
|
|
||||||
lockFile(fdLock, ltRead, true);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
/* There are other accessors. */
|
|
||||||
debug(format("write lock refused"));
|
|
||||||
|
|
||||||
/* Acquire a read lock. */
|
|
||||||
debug(format("acquiring read lock on `%1%'") % lockPath);
|
|
||||||
lockFile(fdLock, ltRead, true); /* wait indefinitely */
|
|
||||||
|
|
||||||
/* Increment the accessor count. */
|
|
||||||
lockFile(fdAccessors, ltWrite, true);
|
|
||||||
int n = getAccessorCount(fdAccessors) + 1;
|
|
||||||
setAccessorCount(fdAccessors, n);
|
|
||||||
debug(format("incremented accessor count to %1%") % n);
|
|
||||||
lockFile(fdAccessors, ltNone, true);
|
|
||||||
|
|
||||||
/* Open the environment normally. */
|
/* Open the environment normally. */
|
||||||
openEnv(env, path, 0);
|
openEnv(env, path, 0);
|
||||||
}
|
|
||||||
|
|
||||||
this->env = env;
|
setAccessorCount(fdAccessors, 1);
|
||||||
|
|
||||||
|
/* Downgrade to a read lock. */
|
||||||
|
debug(format("downgrading to read lock on `%1%'") % lockPath);
|
||||||
|
lockFile(fdLock, ltRead, true);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/* There are other accessors. */
|
||||||
|
debug(format("write lock refused"));
|
||||||
|
|
||||||
|
/* Acquire a read lock. */
|
||||||
|
debug(format("acquiring read lock on `%1%'") % lockPath);
|
||||||
|
lockFile(fdLock, ltRead, true); /* wait indefinitely */
|
||||||
|
|
||||||
} catch (DbException e) { rethrow(e); }
|
/* Increment the accessor count. */
|
||||||
|
lockFile(fdAccessors, ltWrite, true);
|
||||||
|
int n = getAccessorCount(fdAccessors) + 1;
|
||||||
|
setAccessorCount(fdAccessors, n);
|
||||||
|
debug(format("incremented accessor count to %1%") % n);
|
||||||
|
lockFile(fdAccessors, ltNone, true);
|
||||||
|
|
||||||
|
/* Open the environment normally. */
|
||||||
|
openEnv(env, path, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in a new issue