It turns out that file databases don't get very large, and that
sqlite3 can be quite fast with an in-memory database. It also turns
out that dumping the database to disk on exit (or during idle times)
is pretty cheap compared to constant updates.

So: We add "--enable-memory-db", which defaults to on if you have
sqlite 3.7 or later, and off for 3.6 (because 3.6 has horrible
performance with in-memory db on some hosts we tried).
---
 ChangeLog.txt   |    5 +++
 Makefile.in     |    5 ++-
 configure       |   32 +++++++++++++++
 pseudo_db.c     |  119 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 pseudo_db.h     |    1 +
 pseudo_server.c |    2 +
 6 files changed, 159 insertions(+), 5 deletions(-)

diff --git a/ChangeLog.txt b/ChangeLog.txt
index 8f52cc9..ed9e5fa 100644
--- a/ChangeLog.txt
+++ b/ChangeLog.txt
@@ -1,3 +1,8 @@
+2013-02-15:
+       * (seebs) Add support for in-memory DB. This, plus upcoming
+         fsync-related changes, are expected to be big enough to justify
+         calling this 1.5.
+
 2013-02-13:
         * (seebs) calling link while chrooted could in some cases result
           in the root path not being prepended at all. One more try!
diff --git a/Makefile.in b/Makefile.in
index e0cd7a9..094deba 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -23,11 +23,12 @@ LIBDIR=@LIBDIR@
 SUFFIX=@SUFFIX@
 SQLITE=@SQLITE@
 SQLITE_LIB=@SQLITE_LIB@
+SQLITE_MEMORY=@SQLITE_MEMORY@
 BITS=@BITS@
 ARCH_FLAGS=@ARCH_FLAGS@
 MARK64=@MARK64@
 RPATH=@RPATH@
-VERSION=1.4.5
+VERSION=1.5
 
 LIB=@LIB@
 BIN=bin
@@ -37,7 +38,7 @@ LOCALSTATEDIR=$(PREFIX)/$(LOCALSTATE)
 
 CFLAGS_BASE=-pipe -std=gnu99 -Wall -W -Wextra
 CFLAGS_CODE=-fPIC -D_LARGEFILE64_SOURCE -D_ATFILE_SOURCE $(ARCH_FLAGS)
-CFLAGS_DEFS=-DPSEUDO_PREFIX='"$(PREFIX)"' -DPSEUDO_SUFFIX='"$(SUFFIX)"' 
-DPSEUDO_BINDIR='"$(BIN)"' -DPSEUDO_LIBDIR='"$(LIB)"' 
-DPSEUDO_LOCALSTATEDIR='"$(LOCALSTATE)"' -DPSEUDO_VERSION='"$(VERSION)"'
+CFLAGS_DEFS=-DPSEUDO_PREFIX='"$(PREFIX)"' -DPSEUDO_SUFFIX='"$(SUFFIX)"' 
-DPSEUDO_BINDIR='"$(BIN)"' -DPSEUDO_LIBDIR='"$(LIB)"' 
-DPSEUDO_LOCALSTATEDIR='"$(LOCALSTATE)"' -DPSEUDO_VERSION='"$(VERSION)"' 
$(SQLITE_MEMORY)
 CFLAGS_DEBUG=-O2 -g
 CFLAGS_SQL=-L$(SQLITE)/$(SQLITE_LIB) -I$(SQLITE)/include $(RPATH)
 CFLAGS_PSEUDO=$(CFLAGS_BASE) $(CFLAGS_CODE) $(CFLAGS_DEFS) \
diff --git a/configure b/configure
index 9cb7804..44bee74 100755
--- a/configure
+++ b/configure
@@ -25,6 +25,7 @@ opt_arch=x86
 opt_bits=
 opt_sqlite=/usr
 opt_rpath=
+opt_memory=
 
 compile_x86_32=-m32
 compile_x86_64=-m64
@@ -35,6 +36,7 @@ usage()
     echo >&2 "  configure --prefix=..."
     echo >&2 "           [--libdir=...]"
     echo >&2 "           [--suffix=...]"
+    echo >&2 "           [--enable-memory-db]"
     echo >&2 "           [--with-sqlite=...]"
     echo >&2 "           [--with-sqlite-lib=...]"
     echo >&2 "           [--enable-static-sqlite]"
@@ -69,6 +71,12 @@ do
         sqlite_ldarg='$(SQLITE)/$(SQLITE_LIB)/libsqlite3.a'
         use_maybe_rpath=false
         ;;
+    --enable-memory-db=no)
+        opt_memory=false
+        ;;
+    --enable-memory-db=yes | --enable-memory-db)
+        opt_memory=true
+       ;;
     --with-sqlite=*)
         opt_sqlite=${arg#--with-sqlite=}
         # assign new value if unset
@@ -170,6 +178,29 @@ if [ "${SQLITE3_VERSION}" -lt "03006000" ]; then
     exit 1
 fi
 
+if [ -z "$opt_memory" ]; then
+    if [ "${SQLITE3_VERSION}" -lt "03007000" ]; then
+        echo "Disabling in-memory database by default (sqlite too old)."
+       opt_memory=false
+    else
+        echo "Enabling in-memory database by default."
+       opt_memory=true
+    fi
+fi
+
+if $opt_memory; then
+    if [ "${SQLITE3_VERSION}" -lt "03007000" ]; then
+       cat >&2 <<EOF
+WARNING: sqlite prior to 3.7 has been known to perform exceedingly poorly
+with the in-memory database option. You asked for it, you get it, but if
+you get horrible performance, try turning it off.
+EOF
+    fi
+    SQLITE_MEMORY="-DUSE_MEMORY_DB"
+else
+    SQLITE_MEMORY=""
+fi
+
 sed -e '
   s,@PREFIX@,'"$opt_prefix"',g
   s,@LIBDIR@,'"$opt_libdir"',g
@@ -179,6 +210,7 @@ sed -e '
   s,@ARCH_FLAGS@,'"$arch_flags"',g
   s,@SQLITE_LDARG@,'"$sqlite_ldarg"',g
   s,@SQLITE_LIB@,'"$opt_sqlite_lib"',g
+  s,@SQLITE_MEMORY@,'"$SQLITE_MEMORY"',g
   s!@RPATH@!'"$opt_rpath"'!g
   s,@MARK64@,'"$opt_mark64"',g
   s,@ARCH@,'"$opt_arch"',g
diff --git a/pseudo_db.c b/pseudo_db.c
index 540a3c2..21f2f53 100644
--- a/pseudo_db.c
+++ b/pseudo_db.c
@@ -1,7 +1,7 @@
 /*
  * pseudo_db.c, sqlite3 interface
  * 
- * Copyright (c) 2008-2010 Wind River Systems, Inc.
+ * Copyright (c) 2008-2010,2013 Wind River Systems, Inc.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the Lesser GNU General Public License version 2.1 as
@@ -50,6 +50,10 @@ struct pdb_file_list {
        sqlite3_stmt *stmt;
 };
 
+static int file_db_dirty = 0;
+#ifdef USE_MEMORY_DB
+static sqlite3 *real_file_db = 0;
+#endif
 static sqlite3 *file_db = 0;
 static sqlite3 *log_db = 0;
 
@@ -118,6 +122,10 @@ static struct sql_index {
 static char *file_pragmas[] = {
        "PRAGMA legacy_file_format = OFF;",
        "PRAGMA journal_mode = OFF;",
+       /* the default page size produces painfully bad behavior
+        * for memory databases with some versions of sqlite.
+        */
+       "PRAGMA page_size = 8192;",
        "PRAGMA locking_mode = EXCLUSIVE;",
        /* Setting this to NORMAL makes pseudo noticably slower
         * than fakeroot, but is perhaps more secure.  However,
@@ -139,7 +147,6 @@ static char *log_pragmas[] = {
        NULL
 };
 
-
 /* table migrations: */
 /* If there is no migration table, we assume "version -1" -- the
  * version shipped with wrlinux 3.0, which had no version
@@ -239,6 +246,17 @@ static struct database_info db_infos[] = {
                file_migrations,
                file_pragmas,
                file_setups,
+#ifdef USE_MEMORY_DB
+               &real_file_db
+       },
+       {
+               ":memory:",
+               file_indexes,
+               file_tables,
+               file_migrations,
+               file_pragmas,
+               file_setups,
+#endif
                &file_db
        },
        { 0, 0, 0, 0, 0, 0, 0 }
@@ -263,6 +281,67 @@ dberr(sqlite3 *db, char *fmt, ...) {
        }
 }
 
+#ifdef USE_MEMORY_DB
+
+static void
+pdb_backup() {
+        sqlite3_backup *pBackup;
+        /* no point in doing this if we don't have a database to back up,
+        * or nothing's changed.
+        */
+        if (!file_db || !real_file_db || !file_db_dirty)
+                return;
+
+       pBackup = sqlite3_backup_init(real_file_db, "main", file_db, "main");
+       if (pBackup) {
+               int rc;
+               (void)sqlite3_backup_step(pBackup, -1);
+               rc = sqlite3_backup_finish(pBackup);
+               if (rc != SQLITE_OK) {
+                       dberr(real_file_db, "error during flush to disk");
+               }
+       }
+       file_db_dirty = 0;
+}
+
+static void
+pdb_restore() {
+        sqlite3_backup *pBackup;
+        /* no point in doing this if we don't have a database to back up */
+        if (!file_db || !real_file_db)
+                return;
+
+       pBackup = sqlite3_backup_init(file_db, "main", real_file_db, "main");
+       if (pBackup) {
+               int rc;
+               (void)sqlite3_backup_step(pBackup, -1);
+               rc = sqlite3_backup_finish(pBackup);
+               if (rc != SQLITE_OK) {
+                       dberr(file_db, "error during load from disk");
+               }
+       }
+       file_db_dirty = 0;
+}
+
+int
+pdb_maybe_backup(void) {
+        static int occasional = 0;
+        if (file_db && real_file_db) {
+                occasional = (occasional + 1) % 10;
+                if (occasional == 0) {
+                        pdb_backup();
+                        return 1;
+                }
+        }
+        return 0;
+}
+#else /* USE_MEMORY_DB */
+int
+pdb_maybe_backup(void) {
+       return 0;
+}
+#endif
+
 /* those who enjoy children, sausages, and databases, should not watch
  * them being made.
  */
@@ -448,6 +527,12 @@ make_tables(sqlite3 *db,
 static void
 cleanup_db(void) {
        pseudo_debug(1, "server exiting\n");
+#ifdef USE_MEMORY_DB
+        if (real_file_db) {
+                pdb_backup();
+                sqlite3_close(real_file_db);
+       }
+#endif
        if (file_db)
                sqlite3_close(file_db);
        if (log_db)
@@ -478,7 +563,12 @@ get_db(struct database_info *dbinfo) {
                return 0;
 
        dbfile = pseudo_localstatedir_path(dbinfo->pathname);
-       rc = sqlite3_open(dbfile, &db);
+#ifdef USE_MEMORY_DB
+        if (!strcmp(dbinfo->pathname, ":memory:")) {
+                rc = sqlite3_open(dbinfo->pathname, &db);
+        } else
+#endif
+                rc = sqlite3_open(dbfile, &db);
        free(dbfile);
        if (rc) {
                pseudo_diag("Failed: %s\n", sqlite3_errmsg(db));
@@ -528,6 +618,11 @@ static int
 get_dbs(void) {
        int err = 0;
        int i;
+#ifdef USE_MEMORY_DB
+        int already_loaded = 0;
+        if (file_db)
+                already_loaded = 1;
+#endif
        for (i = 0; db_infos[i].db; ++i) {
                if (get_db(&db_infos[i])) {
                        pseudo_diag("Error getting '%s' database.\n",
@@ -535,6 +630,10 @@ get_dbs(void) {
                        err = 1;
                }
        }
+#ifdef USE_MEMORY_DB
+        if (!already_loaded && file_db)
+                pdb_restore();
+#endif
        return err;
 }
 
@@ -1082,6 +1181,7 @@ pdb_delete(pseudo_query_t *traits, unsigned long fields) {
 
        /* no need to return it, so... */
        if (stmt) {
+               file_db_dirty = 1;
                int rc = sqlite3_step(stmt);
                if (rc != SQLITE_DONE) {
                        dberr(log_db, "deletion failed");
@@ -1292,6 +1392,7 @@ pdb_link_file(pseudo_msg_t *msg) {
                (msg->pathlen ? msg->path : "<nil> (as NAMELESS FILE)"),
                (unsigned long long) msg->dev, (unsigned long long) msg->ino,
                (int) msg->mode, msg->uid);
+       file_db_dirty = 1;
        rc = sqlite3_step(insert);
        if (rc != SQLITE_DONE) {
                dberr(file_db, "insert may have failed (rc %d)", rc);
@@ -1324,6 +1425,7 @@ pdb_unlink_file_dev(pseudo_msg_t *msg) {
        }
        sqlite3_bind_int(sql_delete, 1, msg->dev);
        sqlite3_bind_int(sql_delete, 2, msg->ino);
+       file_db_dirty = 1;
        rc = sqlite3_step(sql_delete);
        if (rc != SQLITE_DONE) {
                dberr(file_db, "delete by inode may have failed");
@@ -1359,6 +1461,7 @@ pdb_update_file_path(pseudo_msg_t *msg) {
        sqlite3_bind_text(update, 1, msg->path, -1, SQLITE_STATIC);
        sqlite3_bind_int(update, 2, msg->dev);
        sqlite3_bind_int(update, 3, msg->ino);
+       file_db_dirty = 1;
        rc = sqlite3_step(update);
        if (rc != SQLITE_DONE) {
                dberr(file_db, "update path by inode may have failed");
@@ -1396,6 +1499,7 @@ pdb_may_unlink_file(pseudo_msg_t *msg, int deleting) {
                pseudo_debug(1, "cannot mark a file for pending deletion 
without a path.");
                return 1;
        }
+       file_db_dirty = 1;
        rc = sqlite3_step(mark_file);
        if (rc != SQLITE_DONE) {
                dberr(file_db, "mark for deletion may have failed");
@@ -1439,6 +1543,7 @@ pdb_cancel_unlink_file(pseudo_msg_t *msg) {
                pseudo_debug(1, "cannot unmark a file for pending deletion 
without a path.");
                return 1;
        }
+       file_db_dirty = 1;
        rc = sqlite3_step(mark_file);
        if (rc != SQLITE_DONE) {
                dberr(file_db, "unmark for deletion may have failed");
@@ -1475,6 +1580,7 @@ pdb_did_unlink_files(int deleting) {
                return 0;
        }
        sqlite3_bind_int(delete_exact, 1, deleting);
+       file_db_dirty = 1;
        rc = sqlite3_step(delete_exact);
        if (rc != SQLITE_DONE) {
                dberr(file_db, "cleanup of files marked for deletion may have 
failed");
@@ -1510,6 +1616,7 @@ pdb_did_unlink_file(char *path, int deleting) {
        }
        sqlite3_bind_text(delete_exact, 1, path, -1, SQLITE_STATIC);
        sqlite3_bind_int(delete_exact, 2, deleting);
+       file_db_dirty = 1;
        rc = sqlite3_step(delete_exact);
        if (rc != SQLITE_DONE) {
                dberr(file_db, "cleanup of file marked for deletion may have 
failed");
@@ -1548,6 +1655,7 @@ pdb_unlink_file(pseudo_msg_t *msg) {
                pseudo_debug(1, "cannot unlink a file without a path.");
                return 1;
        }
+       file_db_dirty = 1;
        rc = sqlite3_step(delete_exact);
        if (rc != SQLITE_DONE) {
                dberr(file_db, "delete exact by path may have failed");
@@ -1595,6 +1703,7 @@ pdb_unlink_contents(pseudo_msg_t *msg) {
                pseudo_debug(1, "cannot unlink a file without a path.");
                return 1;
        }
+       file_db_dirty = 1;
        rc = sqlite3_step(delete_sub);
        if (rc != SQLITE_DONE) {
                dberr(file_db, "delete sub by path may have failed");
@@ -1662,6 +1771,7 @@ pdb_rename_file(const char *oldpath, pseudo_msg_t *msg) {
 
        rc = sqlite3_exec(file_db, "BEGIN;", NULL, NULL, NULL);
 
+       file_db_dirty = 1;
        rc = sqlite3_step(update_exact);
        if (rc != SQLITE_DONE) {
                dberr(file_db, "update exact may have failed: rc %d", rc);
@@ -1712,6 +1822,7 @@ pdb_renumber_all(dev_t from, dev_t to) {
                dberr(file_db, "error binding device numbers to update");
        }
 
+       file_db_dirty = 1;
        rc = sqlite3_step(update);
        if (rc != SQLITE_DONE) {
                dberr(file_db, "update may have failed: rc %d", rc);
@@ -1762,6 +1873,7 @@ pdb_update_inode(pseudo_msg_t *msg) {
                dberr(file_db, "error binding %s to select", msg->path);
        }
 
+       file_db_dirty = 1;
        rc = sqlite3_step(update);
        if (rc != SQLITE_DONE) {
                dberr(file_db, "update may have failed: rc %d", rc);
@@ -1806,6 +1918,7 @@ pdb_update_file(pseudo_msg_t *msg) {
        sqlite3_bind_int(update, 5, msg->dev);
        sqlite3_bind_int(update, 6, msg->ino);
 
+       file_db_dirty = 1;
        rc = sqlite3_step(update);
        if (rc != SQLITE_DONE) {
                dberr(file_db, "update may have failed: rc %d", rc);
diff --git a/pseudo_db.h b/pseudo_db.h
index fe2fb12..9e0382a 100644
--- a/pseudo_db.h
+++ b/pseudo_db.h
@@ -37,6 +37,7 @@ typedef struct {
        char *program;
 } log_entry;
 
+extern int pdb_maybe_backup(void);
 extern int pdb_cancel_unlink_file(pseudo_msg_t *msg);
 extern int pdb_did_unlink_file(char *path, int deleting);
 extern int pdb_did_unlink_files(int deleting);
diff --git a/pseudo_server.c b/pseudo_server.c
index f241242..4af5265 100644
--- a/pseudo_server.c
+++ b/pseudo_server.c
@@ -416,6 +416,8 @@ pseudo_server_loop(void) {
                         */
                        if (active_clients == 1) {
                                loop_timeout -= LOOP_DELAY;
+                                /* maybe flush database to disk */
+                                pdb_maybe_backup();
                                if (loop_timeout <= 0) {
                                        pseudo_debug(1, "no more clients, got 
bored.\n");
                                        die_peacefully = 1;
-- 
1.7.9.5


_______________________________________________
Openembedded-core mailing list
Openembedded-core@lists.openembedded.org
http://lists.linuxtogo.org/cgi-bin/mailman/listinfo/openembedded-core

Reply via email to