about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2003-07-31T16·05+0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2003-07-31T16·05+0000
commit06d3d7355d1b0ec05e61d2e7fe67f8d7153c1ff9 (patch)
tree424a162babe37ef113f8f908715e095f180d001f /src
parent177a7782aee4c4789ad5377b5993bfa0b692282e (diff)
* Enclose most operations that update the database in transactions.
* Open all database tables (Db objects) at initialisation time, not
  every time they are used.  This is necessary because tables have to
  outlive all transactions that refer to them.

Diffstat (limited to 'src')
-rw-r--r--src/db.cc131
-rw-r--r--src/db.hh25
-rw-r--r--src/globals.cc16
-rw-r--r--src/globals.hh8
-rw-r--r--src/normalise.cc4
-rw-r--r--src/store.cc57
6 files changed, 145 insertions, 96 deletions
diff --git a/src/db.cc b/src/db.cc
index 77ad93a86dce..61ecb203a5fc 100644
--- a/src/db.cc
+++ b/src/db.cc
@@ -5,15 +5,6 @@
 
 
 /* Wrapper class to ensure proper destruction. */
-class DestroyDb
-{
-    Db * db;
-public:
-    DestroyDb(Db * _db) : db(_db) { }
-    ~DestroyDb() { db->close(0); delete db; }
-};
-
-
 class DestroyDbc 
 {
     Dbc * dbc;
@@ -38,55 +29,60 @@ Transaction::Transaction()
 Transaction::Transaction(Database & db)
 {
     db.requireEnv();
-    db.env->txn_begin(0, &txn, 0);
+    try {
+        db.env->txn_begin(0, &txn, 0);
+    } catch (DbException e) { rethrow(e); }
 }
 
 
 Transaction::~Transaction()
 {
-    if (txn) {
-        txn->abort();
-        txn = 0;
-    }
+    if (txn) abort();
 }
 
 
 void Transaction::commit()
 {
     if (!txn) throw Error("commit called on null transaction");
-    txn->commit(0);
+    debug(format("committing transaction %1%") % (void *) txn);
+    DbTxn * txn2 = txn;
     txn = 0;
+    try {
+        txn2->commit(0);
+    } catch (DbException e) { rethrow(e); }
 }
 
 
-void Database::requireEnv()
+void Transaction::abort()
 {
-    if (!env) throw Error("database environment not open");
+    if (!txn) throw Error("abort called on null transaction");
+    debug(format("aborting transaction %1%") % (void *) txn);
+    DbTxn * txn2 = txn;
+    txn = 0;
+    try {
+        txn2->abort();
+    } catch (DbException e) { rethrow(e); }
 }
 
 
-Db * Database::openDB(const Transaction & txn,
-    const string & table, bool create)
+void Database::requireEnv()
 {
-    requireEnv();
-
-    Db * db = new Db(env, 0);
+    if (!env) throw Error("database environment not open");
+}
 
-    try {
-        // !!! fixme when switching to BDB 4.1: use txn.
-        db->open(table.c_str(), 0, 
-            DB_HASH, create ? DB_CREATE : 0, 0666);
-    } catch (...) {
-        delete db;
-        throw;
-    }
 
-    return db;
+Db * Database::getDb(TableId table)
+{
+    map<TableId, Db *>::iterator i = tables.find(table);
+    if (i == tables.end())
+        throw Error("unknown table id");
+    return i->second;
 }
 
 
 Database::Database()
     : env(0)
+    , nextId(1)
 {
 }
 
@@ -95,8 +91,23 @@ Database::~Database()
 {
     if (env) {
         debug(format("closing database environment"));
-        env->txn_checkpoint(0, 0, 0);
-        env->close(0);
+
+        try {
+
+            for (map<TableId, Db *>::iterator i = tables.begin();
+                 i != tables.end(); i++)
+            {
+                debug(format("closing table %1%") % i->first);
+                Db * db = i->second;
+                db->close(0);
+                delete db;
+            }
+
+            env->txn_checkpoint(0, 0, 0);
+            env->close(0);
+
+        } catch (DbException e) { rethrow(e); }
+
         delete env;
     }
 }
@@ -112,8 +123,9 @@ void Database::open(const string & path)
 
         env->set_lg_bsize(32 * 1024); /* default */
         env->set_lg_max(256 * 1024); /* must be > 4 * lg_bsize */
+        env->set_lk_detect(DB_LOCK_DEFAULT);
         
-        env->open(path.c_str(), 
+        env->open(path.c_str(),
             DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN |
             DB_CREATE,
             0666);
@@ -122,22 +134,36 @@ void Database::open(const string & path)
 }
 
 
-void Database::createTable(const string & table)
+TableId Database::openTable(const string & tableName)
 {
+    requireEnv();
+    TableId table = nextId++;
+
     try {
-        Db * db = openDB(noTxn, table, true);
-        DestroyDb destroyDb(db);
+
+        Db * db = new Db(env, 0);
+
+        try {
+            // !!! fixme when switching to BDB 4.1: use txn.
+            db->open(tableName.c_str(), 0, DB_HASH, DB_CREATE, 0666);
+        } catch (...) {
+            delete db;
+            throw;
+        }
+
+        tables[table] = db;
+
     } catch (DbException e) { rethrow(e); }
+
+    return table;
 }
 
 
-bool Database::queryString(const Transaction & txn, const string & table, 
+bool Database::queryString(const Transaction & txn, TableId table, 
     const string & key, string & data)
 {
     try {
-
-        Db * db = openDB(txn, table, false);
-        DestroyDb destroyDb(db);
+        Db * db = getDb(table);
 
         Dbt kt((void *) key.c_str(), key.length());
         Dbt dt;
@@ -156,7 +182,7 @@ bool Database::queryString(const Transaction & txn, const string & table,
 }
 
 
-bool Database::queryStrings(const Transaction & txn, const string & table, 
+bool Database::queryStrings(const Transaction & txn, TableId table, 
     const string & key, Strings & data)
 {
     string d;
@@ -190,13 +216,11 @@ bool Database::queryStrings(const Transaction & txn, const string & table,
 }
 
 
-void Database::setString(const Transaction & txn, const string & table,
+void Database::setString(const Transaction & txn, TableId table,
     const string & key, const string & data)
 {
     try {
-        Db * db = openDB(txn, table, false);
-        DestroyDb destroyDb(db);
-
+        Db * db = getDb(table);
         Dbt kt((void *) key.c_str(), key.length());
         Dbt dt((void *) data.c_str(), data.length());
         db->put(txn.txn, &kt, &dt, 0);
@@ -204,7 +228,7 @@ void Database::setString(const Transaction & txn, const string & table,
 }
 
 
-void Database::setStrings(const Transaction & txn, const string & table,
+void Database::setStrings(const Transaction & txn, TableId table,
     const string & key, const Strings & data)
 {
     string d;
@@ -227,28 +251,25 @@ void Database::setStrings(const Transaction & txn, const string & table,
 }
 
 
-void Database::delPair(const Transaction & txn, const string & table,
+void Database::delPair(const Transaction & txn, TableId table,
     const string & key)
 {
     try {
-        Db * db = openDB(txn, table, false);
-        DestroyDb destroyDb(db);
+        Db * db = getDb(table);
         Dbt kt((void *) key.c_str(), key.length());
         db->del(txn.txn, &kt, 0);
     } catch (DbException e) { rethrow(e); }
 }
 
 
-void Database::enumTable(const Transaction & txn, const string & table,
+void Database::enumTable(const Transaction & txn, TableId table,
     Strings & keys)
 {
     try {
-
-        Db * db = openDB(txn, table, false);
-        DestroyDb destroyDb(db);
+        Db * db = getDb(table);
 
         Dbc * dbc;
-        db->cursor(0, &dbc, 0);
+        db->cursor(txn.txn, &dbc, 0);
         DestroyDbc destroyDbc(dbc);
 
         Dbt kt, dt;
diff --git a/src/db.hh b/src/db.hh
index 57b6e4d8ebfb..4bac943e554d 100644
--- a/src/db.hh
+++ b/src/db.hh
@@ -3,6 +3,7 @@
 
 #include <string>
 #include <list>
+#include <map>
 
 #include <db_cxx.h>
 
@@ -26,6 +27,7 @@ public:
     Transaction(Database & _db);
     ~Transaction();
 
+    void abort();
     void commit();
 };
 
@@ -33,6 +35,9 @@ public:
 #define noTxn Transaction()
 
 
+typedef unsigned int TableId; /* table handles */
+
+
 class Database
 {
     friend class Transaction;
@@ -40,10 +45,12 @@ class Database
 private:
     DbEnv * env;
 
+    TableId nextId;
+    map<TableId, Db *> tables;
+
     void requireEnv();
 
-    Db * openDB(const Transaction & txn,
-        const string & table, bool create);
+    Db * getDb(TableId table);
 
 public:
     Database();
@@ -51,24 +58,24 @@ public:
     
     void open(const string & path);
 
-    void createTable(const string & table);
+    TableId openTable(const string & table);
 
-    bool queryString(const Transaction & txn, const string & table, 
+    bool queryString(const Transaction & txn, TableId table, 
         const string & key, string & data);
 
-    bool queryStrings(const Transaction & txn, const string & table, 
+    bool queryStrings(const Transaction & txn, TableId table, 
         const string & key, Strings & data);
 
-    void setString(const Transaction & txn, const string & table,
+    void setString(const Transaction & txn, TableId table,
         const string & key, const string & data);
 
-    void setStrings(const Transaction & txn, const string & table,
+    void setStrings(const Transaction & txn, TableId table,
         const string & key, const Strings & data);
 
-    void delPair(const Transaction & txn, const string & table,
+    void delPair(const Transaction & txn, TableId table,
         const string & key);
 
-    void enumTable(const Transaction & txn, const string & table,
+    void enumTable(const Transaction & txn, TableId table,
         Strings & keys);
 };
 
diff --git a/src/globals.cc b/src/globals.cc
index 8c3ec38283ec..1ec0c4f9ba21 100644
--- a/src/globals.cc
+++ b/src/globals.cc
@@ -5,10 +5,10 @@
 Database nixDB;
 
 
-string dbPath2Id = "path2id";
-string dbId2Paths = "id2paths";
-string dbSuccessors = "successors";
-string dbSubstitutes = "substitutes";
+TableId dbPath2Id;
+TableId dbId2Paths;
+TableId dbSuccessors;
+TableId dbSubstitutes;
 
 
 string nixStore = "/UNINIT";
@@ -20,13 +20,13 @@ string nixDBPath = "/UNINIT";
 void openDB()
 {
     nixDB.open(nixDBPath);
+    dbPath2Id = nixDB.openTable("path2id");
+    dbId2Paths = nixDB.openTable("id2paths");
+    dbSuccessors = nixDB.openTable("successors");
+    dbSubstitutes = nixDB.openTable("substitutes");
 }
 
 
 void initDB()
 {
-    nixDB.createTable(dbPath2Id);
-    nixDB.createTable(dbId2Paths);
-    nixDB.createTable(dbSuccessors);
-    nixDB.createTable(dbSubstitutes);
 }
diff --git a/src/globals.hh b/src/globals.hh
index 9df827622237..2c4d3392077d 100644
--- a/src/globals.hh
+++ b/src/globals.hh
@@ -17,13 +17,13 @@ extern Database nixDB;
 
    Each pair (p, id) records that path $p$ contains an expansion of
    $id$. */
-extern string dbPath2Id;
+extern TableId dbPath2Id;
 
 
 /* dbId2Paths :: FSId -> [Path]
 
    A mapping from ids to lists of paths. */
-extern string dbId2Paths;
+extern TableId dbId2Paths;
 
 
 /* dbSuccessors :: FSId -> FSId
@@ -35,7 +35,7 @@ extern string dbId2Paths;
    Note that a term $y$ is successor of $x$ iff there exists a
    sequence of rewrite steps that rewrites $x$ into $y$.
 */
-extern string dbSuccessors;
+extern TableId dbSuccessors;
 
 
 /* dbSubstitutes :: FSId -> [FSId]
@@ -51,7 +51,7 @@ extern string dbSuccessors;
    this case might be an fstate expression that fetches the Nix
    archive. 
 */
-extern string dbSubstitutes;
+extern TableId dbSubstitutes;
 
 
 /* Path names. */
diff --git a/src/normalise.cc b/src/normalise.cc
index 5a8cb9a0d0ff..e8fc6fc55078 100644
--- a/src/normalise.cc
+++ b/src/normalise.cc
@@ -9,7 +9,9 @@
 
 void registerSuccessor(const FSId & id1, const FSId & id2)
 {
-    nixDB.setString(noTxn, dbSuccessors, id1, id2);
+    Transaction txn(nixDB);
+    nixDB.setString(txn, dbSuccessors, id1, id2);
+    txn.commit();
 }
 
 
diff --git a/src/store.cc b/src/store.cc
index 3dc625a7b239..6d7861d0beb1 100644
--- a/src/store.cc
+++ b/src/store.cc
@@ -32,6 +32,8 @@ struct CopySource : RestoreSource
 
 void copyPath(string src, string dst)
 {
+    debug(format("copying `%1%' to `%2%'") % src % dst);
+
     /* Unfortunately C++ doesn't support coprocedures, so we have no
        nice way to chain CopySink and CopySource together.  Instead we
        fork off a child to run the sink.  (Fork-less platforms should
@@ -96,54 +98,69 @@ void registerSubstitute(const FSId & srcId, const FSId & subId)
     /* For now, accept only one substitute per id. */
     Strings subs;
     subs.push_back(subId);
-    nixDB.setStrings(noTxn, dbSubstitutes, srcId, subs);
+    
+    Transaction txn(nixDB);
+    nixDB.setStrings(txn, dbSubstitutes, srcId, subs);
+    txn.commit();
 }
 
 
 void registerPath(const string & _path, const FSId & id)
 {
     string path(canonPath(_path));
+    Transaction txn(nixDB);
 
-    nixDB.setString(noTxn, dbPath2Id, path, id);
+    debug(format("registering path `%1%' with id %2%")
+        % path % (string) id);
 
-    Strings paths;
-    nixDB.queryStrings(noTxn, dbId2Paths, id, paths); /* non-existence = ok */
+    string oldId;
+    if (nixDB.queryString(txn, dbPath2Id, path, oldId)) {
+        txn.abort();
+        if (id != parseHash(oldId))
+            throw Error(format("path `%1%' already contains id %2%")
+                % path % oldId);
+        return;
+    }
 
-    for (Strings::iterator it = paths.begin();
-         it != paths.end(); it++)
-        if (*it == path) return;
+    nixDB.setString(txn, dbPath2Id, path, id);
+
+    Strings paths;
+    nixDB.queryStrings(txn, dbId2Paths, id, paths); /* non-existence = ok */
     
     paths.push_back(path);
     
-    nixDB.setStrings(noTxn, dbId2Paths, id, paths);
+    nixDB.setStrings(txn, dbId2Paths, id, paths);
+
+    txn.commit();
 }
 
 
 void unregisterPath(const string & _path)
 {
     string path(canonPath(_path));
+    Transaction txn(nixDB);
+
+    debug(format("unregistering path `%1%'") % path);
 
     string _id;
-    if (!nixDB.queryString(noTxn, dbPath2Id, path, _id)) return;
+    if (!nixDB.queryString(txn, dbPath2Id, path, _id)) {
+        txn.abort();
+        return;
+    }
     FSId id(parseHash(_id));
 
-    nixDB.delPair(noTxn, dbPath2Id, path);
+    nixDB.delPair(txn, dbPath2Id, path);
 
-    /* begin transaction */
-    
     Strings paths, paths2;
-    nixDB.queryStrings(noTxn, dbId2Paths, id, paths); /* non-existence = ok */
+    nixDB.queryStrings(txn, dbId2Paths, id, paths); /* non-existence = ok */
 
-    bool changed = false;
     for (Strings::iterator it = paths.begin();
          it != paths.end(); it++)
-        if (*it != path) paths2.push_back(*it); else changed = true;
+        if (*it != path) paths2.push_back(*it);
 
-    if (changed)
-        nixDB.setStrings(noTxn, dbId2Paths, id, paths2);
-
-    /* end transaction */
+    nixDB.setStrings(txn, dbId2Paths, id, paths2);
 
+    txn.commit();
 }
 
 
@@ -230,6 +247,8 @@ string expandId(const FSId & id, const string & target,
 void addToStore(string srcPath, string & dstPath, FSId & id,
     bool deterministicName)
 {
+    debug(format("adding `%1%' to the store") % srcPath);
+
     srcPath = absPath(srcPath);
     id = hashPath(srcPath);