chiark / gitweb /
new compaction entrypoints impl'd
authorian <ian>
Wed, 25 Jan 2006 23:18:04 +0000 (23:18 +0000)
committerian <ian>
Wed, 25 Jan 2006 23:18:04 +0000 (23:18 +0000)
cdb/cdb.tct
cdb/writeable.c

index e33607b..d113af9 100644 (file)
@@ -106,7 +106,7 @@ Table cdbwr Cdbwr_SubCommand
                db      iddata(&cdbtcl_rwdatabases)
        compact-check 0
                db      iddata(&cdbtcl_rwdatabases)
-       compact-onupdate 0 # this is the default
+       compact-auto 0 # this is the default
                db      iddata(&cdbtcl_rwdatabases)
        compact-explicit 0
                db      iddata(&cdbtcl_rwdatabases)
index 4230e62..0334a45 100644 (file)
@@ -140,7 +140,6 @@ typedef struct Rw {
   int ix, autocompact;
   int cdb_fd, lock_fd;
   struct cdb cdb; /* valid iff cdb_fd >= 0 */
-  off_t cdb_bytes; /* valid iff cdb_fd >= 0 */
   FILE *logfile;
   HashTable logincore;
   Pathbuf pbsome, pbother;
@@ -153,6 +152,7 @@ static int rw_close(Tcl_Interp *ip, Rw *rw) {
 
   rc= TCL_OK;
   ht_destroy(&rw->logincore);
+  if (rw->cdb_fd >= 0) cdb_free(&rw->cdb);
   maybe_close(rw->cdb_fd);
   maybe_close(rw->lock_fd);
 
@@ -358,6 +358,19 @@ static int infocb(Tcl_Interp *ip, Rw *rw, const char *arg1,
   
 /*---------- Opening ----------*/
 
+static int cdbinit(Tcl_Interp *ip, Rw *rw) {
+  /* On entry, cdb_fd >=0 but cdb is _undefined_/
+   * On exit, either cdb_fd<0 or cdb is initialised */
+  int r, rc;
+  
+  r= cdb_init(&rw->cdb, rw->cdb_fd);
+  if (r) {
+    rc= cht_posixerr(ip, errno, "failed to initialise cdb reader");
+    close(rw->cdb_fd);  rw->cdb_fd= -1;  return rc;
+  }
+  return TCL_OK;
+}
+
 int cht_do_cdbwr_open(ClientData cd, Tcl_Interp *ip, const char *pathb,
                      Tcl_Obj *on_info, Tcl_Obj *on_lexminval,
                      void **result) {
@@ -392,11 +405,7 @@ int cht_do_cdbwr_open(ClientData cd, Tcl_Interp *ip, const char *pathb,
 
   rw->cdb_fd= open(pathbuf_sfx(&rw->pbsome,".cdb"), O_RDONLY);
   if (rw->cdb_fd >=0) {
-    r= cdb_init(&rw->cdb, rw->cdb_fd);
-    if (r) {
-      rc= cht_posixerr(ip, errno, "failed to initialise cdb reader");
-      close(rw->cdb_fd);  rw->cdb_fd= -1;  goto x_rc;
-    }
+    rc= cdbinit(ip, rw);  if (rc) goto x_rc;
   } else if (errno == ENOENT) {
     if (rw->mainsz) {
       rc= cht_staticerr(ip, ".cdb does not exist but .main is nonempty -"
@@ -474,7 +483,7 @@ int cht_do_cdbwr_open(ClientData cd, Tcl_Interp *ip, const char *pathb,
   return rc;
 }
 
-/*==================== COMPACTING ====================*/
+/*==================== COMPACTION ====================*/
 
 struct ht_forall_ctx {
   struct cdb_make cdbm;
@@ -527,7 +536,7 @@ static int addto_main(const char *key, HashValue *val,
 
 /*---------- compact main entrypoint ----------*/
 
-static int compact_core(Tcl_Interp *ip, Rw *rw, unsigned long logsize,
+static int compact_core(Tcl_Interp *ip, Rw *rw, unsigned long logsz,
                        long *reccount_r) {
   /* creates new .cdb and .main
    * closes logfile
@@ -554,7 +563,7 @@ static int compact_core(Tcl_Interp *ip, Rw *rw, unsigned long logsize,
   rw->logfile= 0;
   
   rc= infocb(ip, rw, "compact-start", "log=%luby main=%luby",
-            logsize, (unsigned long)rw->mainsz);
+            logsz, (unsigned long)rw->mainsz);
   if (rc) goto x_rc;
 
   if (rw->on_lexminval.llength) {
@@ -655,16 +664,18 @@ static int compact_core(Tcl_Interp *ip, Rw *rw, unsigned long logsize,
 
   /* done! */
   
-  rc= infocb(ip, rw, "compact-end", "log=%luby main=%luby nrecs=%l",
-            logsize, (unsigned long)rw->mainsz, *a.reccount);
+  rc= infocb(ip, rw, "compact-end", "main=%luby nrecs=%l",
+            (unsigned long)rw->mainsz, *a.reccount);
   if (rc) goto x_rc;
 
-  rc= TCL_OK;
+  return rc;
+
 x_rc:
   if (a.mainfile) fclose(a.mainfile);
   if (cdbmaking) cdb_make_finish(&a.cdbm);
   maybe_close(cdbfd);
   remove(pathbuf_sfx(&rw->pbsome,".tmp")); /* for tidyness */
+  return rc;
 }
 
 /*---------- Closing ----------*/
@@ -716,4 +727,59 @@ int cht_do_cdbwr_close(ClientData cd, Tcl_Interp *ip, void *rw_v) {
   return rc;
 }
 
-/*==================== Other entrypoints ====================*/
+/*---------- Other compaction-related entrypoints ----------*/
+
+static int compact_keeopen(Tcl_Interp *ip, Rw *rw, int force) {
+  off_t logsz;
+  long reccount;
+  int rc, r;
+
+  logsz= ftello(rw->logfile);
+  if (logsz < 0) return cht_posixerr(ip, errno, "ftell .log"
+                                      " during compact check or force");
+
+  if (!force && logsz < rw->mainsz / 10 + 1000) return TCL_OK;
+
+  rc= compact_core(ip, rw, logsz, &reccount);  if (rc) goto x_rc;
+
+  maybe_close(rw->cdb_fd);
+  rw->cdb_fd= -1;
+  ht_destroy(&rw->logincore);
+  ht_setup(&rw->logincore);
+
+  rw->cdb_fd= open(pathbuf_sfx(&rw->pbsome,".cdb"), O_RDONLY);
+  if (rw->cdb_fd < 0) PE("reopen .cdb after compact");
+
+  rc= cdbinit(ip, rw);  if (rc) goto x_rc;
+
+  rw->logfile= fopen(pathbuf_sfx(&rw->pbsome,".log"), "w");
+  if (!rw->logfile) PE("reopen .log after compact");
+
+  r= fsync(fileno(rw->logfile));  if (r) PE("fsync .log after compact reopen");
+
+  return TCL_OK;
+
+x_rc:
+  /* doom! all updates fail after this (because rw->logfile is 0), and
+   * we may be usin a lot more RAM than would be ideal.  Program will
+   * have to reopen if it really wants sanity. */
+  return rc;
+}
+
+int cht_do_cdbwr_compact_force(ClientData cd, Tcl_Interp *ip, void *rw_v) {
+  return compact_keeopen(ip, rw_v, 1);
+}
+int cht_do_cdbwr_compact_check(ClientData cd, Tcl_Interp *ip, void *rw_v) {
+  return compact_keeopen(ip, rw_v, 0);
+}
+
+int cht_do_cdbwr_compact_explicit(ClientData cd, Tcl_Interp *ip, void *rw_v) {
+  Rw *rw= rw_v;
+  rw->autocompact= 0;
+  return TCL_OK;
+}
+int cht_do_cdbwr_compact_auto(ClientData cd, Tcl_Interp *ip, void *rw_v) {
+  Rw *rw= rw_v;
+  rw->autocompact= 1;
+  return TCL_OK;
+}