Subject: Re: 30000 users?
To: Jordan K. Hubbard <jkh@time.cdrom.com>
From: Michael Graff <explorer@flame.org>
List: port-i386
Date: 03/02/1997 14:01:47
"Jordan K. Hubbard" <jkh@time.cdrom.com> writes:

> Unless someone has optimized the way passwd database manipulation is
> done in NetBSD, every time one of those users changes their password
> or otherwise causes the database to be rebuilt, great delays will
> occur as your 30K password database is rebuilt.

One thing to consider here is this.  When using a hash format Berkeley
DB file you can't get above about 100 inserts/sec.  If you switch to a
btree format you get many more.

Perhaps we have outgrown the hash format?

To insert 30,000 "made up" names (this is 1/4 the work pwd_mkdb does,
since it inserts 4 copies -- by name and uid in each of the private and
public databases) into a hash database:

2.578u 1.837s 0:28.53 15.4% 0+0k 561+2953io 9pf+0w

That's 29 seconds time four == 114.12 seconds.

To do the same thing with a btree:

1.896u 0.127s 0:02.20 91.3% 0+0k 26+288io 13pf+0w

Or 8.8 seconds for a real password database rebuild.

--Michael

Here's the btree flavor of the program.  Compile with either -DUSE_HASH
or -DUSE_BTREE.  The 30000.words file consists of 30000 random 4-8 character
names, selected at random from usenet postings.  They are unordered and
consist of only lowercase letters.

#include <sys/param.h>
#include <sys/stat.h>

#include <db.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <util.h>

HASHINFO hashinfo = {
        4096,           /* bsize */
        32,             /* ffactor */
        256,            /* nelem */
        2048 * 1024,    /* cachesize */
        NULL,           /* hash() */
        0               /* lorder */
};

int
main(int argc, char **argv)
{
  static struct passwd pwd;                       /* password structure */
  DB *dp, *edp;
  DBT data, key;
  FILE *fp, *oldfp;
  char buf[1024];
  char buf2[1024];
  char keybuf[1024];
  int  uid;
  
  fp = fopen("30000.words", "r");
  if (fp == NULL)
    errx(1, "Cannot open 30000.words");

#ifdef USE_HASH
  dp = dbopen("30000.words.hash", O_RDWR|O_CREAT, 0600, DB_HASH, &hashinfo);
#endif
#ifdef USE_BTREE
  dp = dbopen("30000.words.btree", O_RDWR|O_CREAT, 0600, DB_BTREE, NULL);
#endif

  while (fgets(buf, 1024, fp) != NULL) {
    buf[strlen(buf) - 1] = '\0';
    snprintf(buf2, 1024,
	     "%s:%s:%d:3001:qmail:/var/qmail/alias:/sbin/nologin",
	     buf, buf, uid);

    snprintf(keybuf, 1024, "%s", buf);

    data.data = buf2;
    data.size = strlen(buf2);
    key.data = keybuf;
    key.size = strlen(keybuf);
    if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
      errx(1, "put");

    uid++;
  }

  (dp->close)(dp);
}