From: JIM@brownvm.brown.edu (James Mathiesen)
Newsgroups: news.software.b
Subject: Re: Multi-FS news spool layout question
Date: Tue, 15 Dec 1992 16:16:00 EST
Organization: Brown University - Providence, Rhode Island  USA
NNTP-Posting-Host: brownvm.brown.edu
News-Software: BNN via BNN_POST v1.0 beta

You may have the same gottcha we found last week.  (We run INN 1.2 on Ultrix
4.2 with two RZ58 drives, so we have pretty similar systems)

Our symptoms were a machine that froze at random intervals for
up to 20 seconds with innd in disk-wait.

In our case the problem was the interaction between dbz and innd.
innd calls dbzincore() to keep the history.pag index in memory.
After every 10 updates of the .pag file, innd calls dbzsync() to
update the .pag on disk.  The problem is that dbzsync simply
writes the entire 6 meg .pag file out -- about 100 times an hour.
No wonder the disk was busy all the time...

A quick-and-dirty solution is to comment out the dbzincore(1); in
$inn/innd/his.c  This will stop the crippling disk writes, at the
cost of not having history.pag buffered in memory.

A more elegent solution is to let dbz() have a write-through cache,
this lets .pag be kept in memory, but updates are written to disk
as they occur.  I'm enclosing patches below to dbz.c, dbz.h and
his.c that add and use dbzwritethough().

While we were working on finding this problem, we also noticed
that the default tag mask used by dbz.c is inappropriate for sites
with a history file larger than 16 meg.  The patches to
expire.c and makehistory.c provide an alternate mask of 0x78000000
that will be used for points up to 134meg in the history file.
We found that this change dramatically reduced the number of
false reads from the history file when looking up by message-id:
You'll probably want to check out the comments in dbz.c, figure
hwo big you are likely to let your history file get and then
pick an appropriate mask.  0x7C000000 would be a good choice if
your history file won't be exceeding 67meg.  (Note that if
your history file exceeds the bounds of the tag mask things will
just slow down a bit, not break.)

We're apparently not the first to be burned by dbzsync().  A patch
for dbzwritethrough() has been around for a while and the author sent
it to me recently.  Unfortunately I lost the mail and patch (sorry) but
they were very similar to this.

I'm still new to Unix (although not to systems stuff) so
apologies if these patches don't actually work.  They are simple
enough you should be able to apply them by hand if you have to.

james mathiesen
jim@brownvm.brown.edu

p.s. we run our INN with the spool partitioned across two RZ58 drives,
there are symlinks all over the place and it doesn't appear to be
hurting performance any.  The symlinks only have to be de-referenced
when a file is opened and inn sits with its current directory at the
top of the spool so it's probably bypassed most of your symlinks anyway.

*** dbz.c.OLD	Fri Dec 11 16:58:33 1992
--- dbz.c	Sun Dec 13 11:55:53 1992
***************
*** 233,238 ****
--- 233,245 ----
  #endif

  /*
+  * If writethrough is set changes to the incore index are
+  * written to disk as they are made.  This keeps inn from writing
+  * a 6 meg index when it calls dbzsynch() several times a minute.
+  */
+ static int writethrough = 0;
+
+ /*
   * Stdio buffer for .pag reads.  Buffering more than about 16 does not help
   * significantly at the densities we try to maintain, and the much larger
   * buffers that most stdios default to are much more expensive to fill.
***************
*** 823,829 ****
  	if (!written)
  		return(0);

! 	if (corepag != NULL) {
  		if (putcore(corepag, bufpagf) < 0) {
  			DEBUG(("dbzsync: putcore failed\n"));
  			ret = -1;
--- 830,836 ----
  	if (!written)
  		return(0);

! 	if (corepag != NULL && !writethrough) {
  		if (putcore(corepag, bufpagf) < 0) {
  			DEBUG(("dbzsync: putcore failed\n"));
  			ret = -1;
***************
*** 1445,1451 ****
  	if (corepag != NULL && place < conf.tsize) {
  		corepag[place] = value;
  		DEBUG(("set: incore\n"));
! 		return(0);
  	}

  	/* seek to spot */
--- 1452,1458 ----
  	if (corepag != NULL && place < conf.tsize) {
  		corepag[place] = value;
  		DEBUG(("set: incore\n"));
! 		if (!writethrough) return(0);
  	}

  	/* seek to spot */
***************
*** 1782,1784 ****
--- 1789,1803 ----
  	return(-1);
  }
  #endif
+
+ /*
+  - dbzwritethrough - use a writethrough cache
+  */
+ int
+ dbzwritethrough(value)
+ int value;
+ {
+ 	register int old = writethrough;
+ 	writethrough = value;
+ 	return(old);
+ }
*** dbz.h.OLD	Mon Oct 19 17:45:06 1992
--- dbz.h	Sun Dec 13 11:54:00 1992
***************
*** 23,28 ****
--- 23,29 ----
  extern int dbzincore();
  extern int dbzcancel();
  extern int dbzdebug();
+ extern int dbzwritethrough();

  /*
   * In principle we could handle unlimited-length keys by operating a chunk
*** his.c.OLD	Mon Sep 14 15:21:23 1992
--- his.c	Sun Dec 13 12:38:50 1992
***************
*** 34,39 ****
--- 34,40 ----
  	CloseOnExec(HISreadfd);

  	/* Open the DBZ file. */
+ 	(void)dbzwritethrough(1);
  	(void)dbzincore(1);
  	if (dbminit(HIShistpath) < 0) {
  	    syslog(L_FATAL, "%s cant dbminit %s %m", HIShistpath, LogName);
*** expire.c.OLD	Mon Sep 14 15:21:09 1992
--- expire.c	Wed Dec  9 18:23:51 1992
***************
*** 951,957 ****
  		    NHistory, NHistorydir, NHistorypag);
  	(void)dbzincore(1);
  	if (IgnoreOld) {
! 	    if (dbzfresh(NHistory, dbzsize(0L), '\t', 'C', 0L) < 0) {
  		(void)fprintf(stderr, "Can't create database, %s\n",
  			strerror(errno));
  		exit(1);
--- 951,957 ----
  		    NHistory, NHistorydir, NHistorypag);
  	(void)dbzincore(1);
  	if (IgnoreOld) {
! 	    if (dbzfresh(NHistory, dbzsize(0L), '\t', 'C', 0x78000000) < 0) {
  		(void)fprintf(stderr, "Can't create database, %s\n",
  			strerror(errno));
  		exit(1);
*** makehistory.c.OLD	Mon Sep 14 15:21:10 1992
--- makehistory.c	Wed Dec  9 18:26:11 1992
***************
*** 124,130 ****
      /* Open the new database, using the old file if desired and possible. */
      (void)dbzincore(1);
      if (IgnoreOld) {
! 	if (dbzfresh(p, dbzsize(size), HIS_FIELDSEP, 'C', 0L) < 0) {
  	    (void)fprintf(stderr, "Can't do dbzfresh, %s\n",
  		    strerror(errno));
  	    if (temp[0])
--- 124,130 ----
      /* Open the new database, using the old file if desired and possible. */
      (void)dbzincore(1);
      if (IgnoreOld) {
! 	if (dbzfresh(p, dbzsize(size), HIS_FIELDSEP, 'C', 0x78000000) < 0) {
  	    (void)fprintf(stderr, "Can't do dbzfresh, %s\n",
  		    strerror(errno));
  	    if (temp[0])
--
internet: jim@brownvm.brown.edu