[exim-cvs] HintsDB: Support transactions with a TDB backend.

Góra strony
Delete this message
Reply to this message
Autor: Exim Git Commits Mailing List
Data:  
Dla: exim-cvs
Temat: [exim-cvs] HintsDB: Support transactions with a TDB backend.
Gitweb: https://git.exim.org/exim.git/commitdiff/3cee6033bae86d254e51b583d34f6b559a6d95ea
Commit:     3cee6033bae86d254e51b583d34f6b559a6d95ea
Parent:     4cbf0dff96031f9b53cf6d8a3c4b2001d467a57d
Author:     Jeremy Harris <jgh146exb@???>
AuthorDate: Thu Jul 18 14:50:14 2024 +0100
Committer:  Jeremy Harris <jgh146exb@???>
CommitDate: Thu Jul 18 18:47:17 2024 +0100

    HintsDB: Support transactions with a TDB backend.
---
 doc/doc-txt/ChangeLog          |  10 +-
 src/src/dbfn.c                 |  58 ++++-----
 src/src/dbfunctions.h          |  11 --
 src/src/deliver.c              | 277 +++++++++++++++++++++++------------------
 src/src/exim_dbmbuild.c        |  21 ++++
 src/src/exim_dbutil.c          |  41 ++++--
 src/src/hintsdb.h              |  52 +++++---
 src/src/hintsdb/hints_bdb.h    |  26 +++-
 src/src/hintsdb/hints_gdbm.h   |  11 +-
 src/src/hintsdb/hints_ndbm.h   |  11 +-
 src/src/hintsdb/hints_sqlite.h |  10 +-
 src/src/hintsdb/hints_tdb.h    |  89 ++++++++++---
 src/src/hintsdb_structs.h      |   1 +
 src/src/queue.c                |  30 +++--
 src/src/retry.c                | 104 +++++++++++-----
 test/runtest                   |  37 ++++--
 test/stderr/0275               |   6 -
 test/stderr/0278               |   6 -
 test/stderr/0357               |  10 +-
 test/stderr/0358               |   6 +-
 test/stderr/0361               |  12 --
 test/stderr/0386               |  12 --
 test/stderr/0388               | 101 ++++++---------
 test/stderr/0398               |  45 +++----
 test/stderr/0402               |  21 ----
 test/stderr/0403               |  18 ---
 test/stderr/0404               |  15 ---
 test/stderr/0408               |   6 -
 test/stderr/0432               |  49 ++++----
 test/stderr/0450               |   4 +-
 test/stderr/0476               |   2 +-
 test/stderr/0487               |   6 -
 test/stderr/0512               |  12 +-
 test/stderr/0554               |   6 +-
 test/stderr/0623               |   8 +-
 test/stderr/0628               |   6 -
 test/stderr/0909               |   8 +-
 test/stderr/2600               |   6 -
 test/stderr/2610               |   6 -
 test/stderr/2620               |   6 -
 test/stderr/4052               |   4 +-
 test/stderr/5004               |   9 --
 test/stderr/5005               | 116 ++++++-----------
 test/stderr/5006               |   6 -
 44 files changed, 658 insertions(+), 643 deletions(-)

diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog
index fadb4995a..abf90ca1f 100644
--- a/doc/doc-txt/ChangeLog
+++ b/doc/doc-txt/ChangeLog
@@ -10,11 +10,11 @@ JH/01 Use fewer forks & execs for sending many messages to a single host.
       we can loop there.  A two-phase queue run will benefit, particularly for
       mailinglist and smarthost cases.
 
-JH/02 Add transaction support for hintsdbs. The sole initial provider is
-      sqlite, and is used for the wait-transport and retry DBs. Transactions
-      imply locking internal to the DB. We no longer need a separate lockfile,
-      can keep the DB handle open for extended periods, yet potentially benefit
-      from concurrency on non-conflicting record uses.
+JH/02 Add transaction support for hintsdbs. The providers supported are tdb and
+      sqlite. Transactions are used for the wait-transport and retry DBs.
+      They imply locking internal to the DB.  We no longer need a separate
+      lockfile, can keep the DB handle open for extended periods, yet
+      potentially benefit from concurrency on non-conflicting record uses.
 
 JH/03 With dkim_verify_minimal, avoid calling the DKIM ACL after the first
       good verify.
diff --git a/src/src/dbfn.c b/src/src/dbfn.c
index c2c92cf44..1f6989164 100644
--- a/src/src/dbfn.c
+++ b/src/src/dbfn.c
@@ -38,8 +38,14 @@ are separate open and close functions. However, the calling modules should
 arrange to hold the locks for the bare minimum of time.
 
 API:
-  dbfn_open
-  dbfn_close
+  exim_lockfile_needed            facilities predicate
+  dbfn_open                takes lockfile or opens transaction
+  dbfn_open_multi            only if transactions supported;
+                    no lock or transaction taken
+  dbfn_close                release lockfile or transaction
+  dbfn_close_multi
+  dbfn_transaction_start        only if transactions supported
+  dbfn_transaction_commit
   dbfn_read_with_length
   dbfn_read_enforce_length
   dbfn_write
@@ -166,6 +172,7 @@ exists, there is no error. */
 
 dlen = snprintf(CS dirname, sizeof(dirname), "%s/db", spool_directory);
 
+dbblock->readonly = (flags & O_ACCMODE) == O_RDONLY;
 dbblock->lockfd = -1;
 if (!exim_lockfile_needed())
   db_dir_make(panic);
@@ -194,7 +201,10 @@ open call. */
 snprintf(CS filename, sizeof(filename), "%.*s/%s", dlen, dirname, name);
 
 priv_drop_temp(exim_uid, exim_gid);
-dbblock->dbptr = exim_dbopen(filename, dirname, flags & O_ACCMODE, EXIMDB_MODE);
+dbblock->dbptr = dbblock->readonly && !exim_lockfile_needed()
+  ? exim_dbopen_multi(filename, dirname, flags & O_ACCMODE, EXIMDB_MODE)
+  : exim_dbopen(filename, dirname, flags & O_ACCMODE, EXIMDB_MODE);
+
 if (!dbblock->dbptr && errno == ENOENT && flags & O_CREAT)
   {
   DEBUG(D_hints_lookup)
@@ -220,21 +230,13 @@ if (!dbblock->dbptr)
           filename));
   (void)close(dbblock->lockfd);
   dbblock->lockfd = -1;
-  errno = save_errno;
-  DEBUG(D_hints_lookup) acl_level--;
-  return NULL;
+  dbblock = NULL;
   }
 
-DEBUG(D_hints_lookup)
-  debug_printf_indent("opened hints database %s: flags=%s%s\n", filename,
-    (flags & O_ACCMODE) == O_RDONLY ? "O_RDONLY"
-    : (flags & O_ACCMODE) == O_RDWR ? "O_RDWR"
-    : "??",
-    flags & O_CREAT ? "|O_CREAT" : "");
-
 /* Pass back the block containing the opened database handle and the open fd
 for the lock. */
 
+DEBUG(D_hints_lookup) acl_level--;
 return dbblock;
 }
 
@@ -284,21 +286,11 @@ if (!dbblock->dbptr)
     DEBUG(D_hints_lookup)
       debug_printf_indent("%s\n", CS string_open_failed("DB file %s",
           filename));
-  errno = save_errno;
-  DEBUG(D_hints_lookup) acl_level--;
-  return NULL;
+  dbblock =  NULL;
   }
 
-DEBUG(D_hints_lookup)
-  debug_printf_indent("opened hints database %s for transactions: NOLOCK flags=%s%s\n",
-    filename,
-    (flags & O_ACCMODE) == O_RDONLY ? "O_RDONLY"
-    : (flags & O_ACCMODE) == O_RDWR ? "O_RDWR"
-    : "??",
-    flags & O_CREAT ? "|O_CREAT" : "");
-
 /* Pass back the block containing the opened database handle */
-
+DEBUG(D_hints_lookup) acl_level--;
 return dbblock;
 }
 
@@ -307,13 +299,13 @@ BOOL
 dbfn_transaction_start(open_db * dbp)
 {
 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_transaction_start\n");
-return exim_dbtransaction_start(dbp->dbptr);
+if (!dbp->readonly) return exim_dbtransaction_start(dbp->dbptr);
 }
 void
 dbfn_transaction_commit(open_db * dbp)
 {
 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_transaction_commit\n");
-exim_dbtransaction_commit(dbp->dbptr);
+if (!dbp->readonly) exim_dbtransaction_commit(dbp->dbptr);
 }
 
 
@@ -334,14 +326,15 @@ dbfn_close(open_db * dbp)
 {
 int * fdp = &dbp->lockfd;
 
-exim_dbclose(dbp->dbptr);
+if (dbp->readonly && !exim_lockfile_needed())
+  exim_dbclose_multi(dbp->dbptr);
+else
+  exim_dbclose(dbp->dbptr);
+
 if (*fdp >= 0) (void)close(*fdp);
 DEBUG(D_hints_lookup)
-  {
   debug_printf_indent("closed hints database%s\n",
               *fdp < 0 ? "" : " and lockfile");
-  acl_level--;
-  }
 *fdp = -1;
 }
 
@@ -351,10 +344,7 @@ dbfn_close_multi(open_db * dbp)
 {
 exim_dbclose_multi(dbp->dbptr);
 DEBUG(D_hints_lookup)
-  {
   debug_printf_indent("closed hints database\n");
-  acl_level--;
-  }
 }
 
 
diff --git a/src/src/dbfunctions.h b/src/src/dbfunctions.h
index 0aa3b777c..08f8c2311 100644
--- a/src/src/dbfunctions.h
+++ b/src/src/dbfunctions.h
@@ -28,16 +28,5 @@ void     dbfn_transaction_commit(open_db *);
 
 #define  dbfn_read(a, b) dbfn_read_with_length(a, b, NULL)
 
-/* Berkeley DB uses a callback function to pass back error details. Its API
-changed at release 4.3. */
-
-#if defined(USE_DB) && defined(DB_VERSION_STRING)
-# if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3)
-void     dbfn_bdb_error_callback(const DB_ENV *, const char *, const char *);
-# else
-void     dbfn_bdb_error_callback(const char *, char *);
-# endif
-#endif
-
 #endif
 /* End of dbfunctions.h */
diff --git a/src/src/deliver.c b/src/src/deliver.c
index b04fe6f12..ee7650243 100644
--- a/src/src/deliver.c
+++ b/src/src/deliver.c
@@ -2896,7 +2896,12 @@ while (addr_local)
   of these checks, rather than for all local deliveries, because some local
   deliveries (e.g. to pipes) can take a substantial time. */
 
-  if (!(dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE, TRUE)))
+  if (continue_retry_db && continue_retry_db != (open_db *)-1)
+    {
+    DEBUG(D_hints_lookup) debug_printf("using cached retry hintsdb handle\n");
+    dbm_file = continue_retry_db;
+    }
+  else if (!(dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE, TRUE)))
     DEBUG(D_deliver|D_retry|D_hints_lookup)
       debug_printf("no retry data available\n");
 
@@ -2905,61 +2910,66 @@ while (addr_local)
   while (addr2)
     {
     BOOL ok = TRUE;   /* to deliver this address */
-    uschar *retry_key;
-
-    /* Set up the retry key to include the domain or not, and change its
-    leading character from "R" to "T". Must make a copy before doing this,
-    because the old key may be pointed to from a "delete" retry item after
-    a routing delay. */
 
-    retry_key = string_copy(
-      tp->retry_use_local_part ? addr2->address_retry_key :
-        addr2->domain_retry_key);
-    *retry_key = 'T';
+    if (f.queue_2stage)
+      {
+      DEBUG(D_deliver)
+    debug_printf_indent("no router retry check (ph1 qrun)\n");
+      }
+    else
+      {
+      /* Set up the retry key to include the domain or not, and change its
+      leading character from "R" to "T". Must make a copy before doing this,
+      because the old key may be pointed to from a "delete" retry item after
+      a routing delay. */
+      uschar * retry_key = string_copy(tp->retry_use_local_part
+            ? addr2->address_retry_key : addr2->domain_retry_key);
+      *retry_key = 'T';
 
-    /* Inspect the retry data. If there is no hints file, delivery happens. */
+      /* Inspect the retry data. If there is no hints file, delivery happens. */
 
-    if (dbm_file)
-      {
-      dbdata_retry * retry_record = dbfn_read(dbm_file, retry_key);
+      if (dbm_file)
+    {
+    dbdata_retry * retry_record = dbfn_read(dbm_file, retry_key);
 
-      /* If there is no retry record, delivery happens. If there is,
-      remember it exists so it can be deleted after a successful delivery. */
+    /* If there is no retry record, delivery happens. If there is,
+    remember it exists so it can be deleted after a successful delivery. */
 
-      if (retry_record)
-        {
-        setflag(addr2, af_lt_retry_exists);
+    if (retry_record)
+      {
+      setflag(addr2, af_lt_retry_exists);
 
-        /* A retry record exists for this address. If queue running and not
-        forcing, inspect its contents. If the record is too old, or if its
-        retry time has come, or if it has passed its cutoff time, delivery
-        will go ahead. */
+      /* A retry record exists for this address. If queue running and not
+      forcing, inspect its contents. If the record is too old, or if its
+      retry time has come, or if it has passed its cutoff time, delivery
+      will go ahead. */
 
-        DEBUG(D_retry)
-          {
-          debug_printf("retry record exists: age=%s ",
-            readconf_printtime(now - retry_record->time_stamp));
-          debug_printf("(max %s)\n", readconf_printtime(retry_data_expire));
-          debug_printf("  time to retry = %s expired = %d\n",
-            readconf_printtime(retry_record->next_try - now),
-            retry_record->expired);
-          }
+      DEBUG(D_retry)
+        {
+        debug_printf("retry record exists: age=%s ",
+          readconf_printtime(now - retry_record->time_stamp));
+        debug_printf("(max %s)\n", readconf_printtime(retry_data_expire));
+        debug_printf("  time to retry = %s expired = %d\n",
+          readconf_printtime(retry_record->next_try - now),
+          retry_record->expired);
+        }
 
-        if (f.queue_running && !f.deliver_force)
-          {
-          ok = (now - retry_record->time_stamp > retry_data_expire)
-        || (now >= retry_record->next_try)
-        || retry_record->expired;
+      if (f.queue_running && !f.deliver_force)
+        {
+        ok = (now - retry_record->time_stamp > retry_data_expire)
+          || (now >= retry_record->next_try)
+          || retry_record->expired;
 
-          /* If we haven't reached the retry time, there is one more check
-          to do, which is for the ultimate address timeout. */
+        /* If we haven't reached the retry time, there is one more check
+        to do, which is for the ultimate address timeout. */
 
-          if (!ok)
-            ok = retry_ultimate_address_timeout(retry_key, addr2->domain,
-                retry_record, now);
-          }
-        }
-      else DEBUG(D_retry) debug_printf("no retry record exists\n");
+        if (!ok)
+          ok = retry_ultimate_address_timeout(retry_key, addr2->domain,
+          retry_record, now);
+        }
+      }
+    else DEBUG(D_retry) debug_printf("no retry record exists\n");
+    }
       }
 
     /* This address is to be delivered. Leave it on the chain. */
@@ -2985,7 +2995,11 @@ while (addr_local)
       }
     }
 
-  if (dbm_file) dbfn_close(dbm_file);
+  if (dbm_file)
+    if (dbm_file != continue_retry_db)
+      { dbfn_close(dbm_file); dbm_file = NULL; }
+    else
+      DEBUG(D_hints_lookup) debug_printf("retaining retry hintsdb handle\n");
 
   /* If there are no addresses left on the chain, they all deferred. Loop
   for the next set of addresses. */
@@ -7440,8 +7454,13 @@ while (addr_new)           /* Loop until all addresses dealt with */
     {
     /* If we have transaction-capable hintsdbs, open the retry db without
     locking, and leave open for the transport process and for subsequent
-    deliveries. If the open fails, tag that explicitly for the transport but
-    retry the open next time around, in case it was created in the interim. */
+    deliveries. Use a writeable open as we can keep it open all the way through
+    to writing retry records if needed due to message fails.
+    If the open fails, tag that explicitly for the transport but retry the open
+    next time around, in case it was created in the interim.
+    If non-transaction, we are only reading records at this stage and
+    we close the db before running the transport.
+    Either way we do a non-creating open. */
 
     if (continue_retry_db == (open_db *)-1)
       continue_retry_db = NULL;
@@ -7451,7 +7470,7 @@ while (addr_new)           /* Loop until all addresses dealt with */
       DEBUG(D_hints_lookup) debug_printf("using cached retry hintsdb handle\n");
       dbm_file = continue_retry_db;
       }
-    else if (!exim_lockfile_needed() && continue_transport)
+    else if (!exim_lockfile_needed())
       {
       dbm_file = dbfn_open_multi(US"retry", O_RDWR, &dbblock);
       continue_retry_db = dbm_file ? dbm_file : (open_db *)-1;
@@ -7471,7 +7490,7 @@ while (addr_new)           /* Loop until all addresses dealt with */
     {
     int rc;
     tree_node * tnode;
-    dbdata_retry * domain_retry_record, * address_retry_record;
+    dbdata_retry * domain_retry_record = NULL, * address_retry_record = NULL;
 
     addr = addr_new;
     addr_new = addr->next;
@@ -7700,76 +7719,82 @@ while (addr_new)           /* Loop until all addresses dealt with */
       continue;
       }
 
-    /* Get the routing retry status, saving the two retry keys (with and
-    without the local part) for subsequent use. If there is no retry record for
-    the standard address routing retry key, we look for the same key with the
-    sender attached, because this form is used by the smtp transport after a
-    4xx response to RCPT when address_retry_include_sender is true. */
-
-    DEBUG(D_retry)
+    if (f.queue_2stage)
       {
-      debug_printf_indent("checking router retry status\n");
-      acl_level++;
+      DEBUG(D_deliver)
+    debug_printf_indent("no router retry check (ph1 qrun)\n");
       }
-    addr->domain_retry_key = string_sprintf("R:%s", addr->domain);
-    addr->address_retry_key = string_sprintf("R:%s@%s", addr->local_part,
-      addr->domain);
-
-    if (dbm_file)
+    else
       {
-      domain_retry_record = dbfn_read(dbm_file, addr->domain_retry_key);
-      if (  domain_retry_record
-         && now - domain_retry_record->time_stamp > retry_data_expire
-     )
+      /* Get the routing retry status, saving the two retry keys (with and
+      without the local part) for subsequent use. If there is no retry record
+      for the standard address routing retry key, we look for the same key with
+      the sender attached, because this form is used by the smtp transport after
+      a 4xx response to RCPT when address_retry_include_sender is true. */
+
+      DEBUG(D_deliver|D_retry)
     {
-    DEBUG(D_deliver|D_retry)
-      debug_printf_indent("domain retry record present but expired\n");
-        domain_retry_record = NULL;    /* Ignore if too old */
+    debug_printf_indent("checking router retry status\n");
+    acl_level++;
     }
+      addr->domain_retry_key = string_sprintf("R:%s", addr->domain);
+      addr->address_retry_key = string_sprintf("R:%s@%s", addr->local_part,
+    addr->domain);
 
-      address_retry_record = dbfn_read(dbm_file, addr->address_retry_key);
-      if (  address_retry_record
-         && now - address_retry_record->time_stamp > retry_data_expire
-     )
+      if (dbm_file)
     {
-    DEBUG(D_deliver|D_retry)
-      debug_printf_indent("address retry record present but expired\n");
-        address_retry_record = NULL;   /* Ignore if too old */
-    }
+    domain_retry_record = dbfn_read(dbm_file, addr->domain_retry_key);
+    if (  domain_retry_record
+       && now - domain_retry_record->time_stamp > retry_data_expire
+       )
+      {
+      DEBUG(D_deliver|D_retry)
+        debug_printf_indent("domain retry record present but expired\n");
+      domain_retry_record = NULL;    /* Ignore if too old */
+      }
 
-      if (!address_retry_record)
-        {
-        uschar *altkey = string_sprintf("%s:<%s>", addr->address_retry_key,
-          sender_address);
-        address_retry_record = dbfn_read(dbm_file, altkey);
-        if (  address_retry_record
-       && now - address_retry_record->time_stamp > retry_data_expire)
+    address_retry_record = dbfn_read(dbm_file, addr->address_retry_key);
+    if (  address_retry_record
+       && now - address_retry_record->time_stamp > retry_data_expire
+       )
       {
       DEBUG(D_deliver|D_retry)
-        debug_printf_indent("address<sender> retry record present but expired\n");
-          address_retry_record = NULL;   /* Ignore if too old */
+        debug_printf_indent("address retry record present but expired\n");
+      address_retry_record = NULL;   /* Ignore if too old */
       }
-        }
-      }
-    else
-      domain_retry_record = address_retry_record = NULL;
 
-    DEBUG(D_deliver|D_retry)
-      {
-      if (!domain_retry_record)
-    debug_printf_indent("no   domain  retry record\n");
-      else
-    debug_printf_indent("have domain  retry record; next_try = now%+d\n",
-              f.running_in_test_harness ? 0 :
-              (int)(domain_retry_record->next_try - now));
+    if (!address_retry_record)
+      {
+      uschar *altkey = string_sprintf("%s:<%s>", addr->address_retry_key,
+        sender_address);
+      address_retry_record = dbfn_read(dbm_file, altkey);
+      if (  address_retry_record
+         && now - address_retry_record->time_stamp > retry_data_expire)
+        {
+        DEBUG(D_deliver|D_retry)
+          debug_printf_indent("address<sender> retry record present but expired\n");
+        address_retry_record = NULL;   /* Ignore if too old */
+        }
+      }
+    }
 
-      if (!address_retry_record)
-    debug_printf_indent("no   address retry record\n");
-      else
-    debug_printf_indent("have address retry record; next_try = now%+d\n",
-              f.running_in_test_harness ? 0 :
-              (int)(address_retry_record->next_try - now));
-      acl_level--;
+      DEBUG(D_deliver|D_retry)
+    {
+    if (!domain_retry_record)
+      debug_printf_indent("no   domain  retry record\n");
+    else
+      debug_printf_indent("have domain  retry record; next_try = now%+d\n",
+            f.running_in_test_harness ? 0 :
+            (int)(domain_retry_record->next_try - now));
+
+    if (!address_retry_record)
+      debug_printf_indent("no   address retry record\n");
+    else
+      debug_printf_indent("have address retry record; next_try = now%+d\n",
+            f.running_in_test_harness ? 0 :
+            (int)(address_retry_record->next_try - now));
+    acl_level--;
+    }
       }
 
     /* If we are sending a message down an existing SMTP connection, we must
@@ -7873,11 +7898,15 @@ while (addr_new)           /* Loop until all addresses dealt with */
       }
     }
 
-  /* The database is closed while routing is actually happening. Requests to
-  update it are put on a chain and all processed together at the end. */
+  /* If not transaction-capable, the database is closed while routing is
+  actually happening. Requests to update it are put on a chain and all processed
+  together at the end. */
 
-  if (dbm_file && !continue_retry_db)
-    { dbfn_close(dbm_file); dbm_file = NULL; }
+  if (dbm_file)
+    if (exim_lockfile_needed())
+      { dbfn_close(dbm_file); dbm_file = NULL; }
+    else
+      DEBUG(D_hints_lookup) debug_printf("retaining retry hintsdb handle\n");
 
   /* If queue_domains is set, we don't even want to try routing addresses in
   those domains. During queue runs, queue_domains is forced to be unset.
@@ -8048,9 +8077,6 @@ while (addr_new)           /* Loop until all addresses dealt with */
   }    /* Loop to process any child addresses that the routers created, and
        any rerouted addresses that got put back on the new chain. */
 
-if (dbm_file)        /* Can only be continue_retry_db */
-  { dbfn_close_multi(continue_retry_db); continue_retry_db = dbm_file = NULL; }
-
 /* Debugging: show the results of the routing */
 
 DEBUG(D_deliver|D_retry|D_route)
@@ -8149,12 +8175,7 @@ if (  mua_wrapper
 
 
 /* If this is a run to continue deliveries to an external channel that is
-already set up, defer any local deliveries.
-
-jgh 2020/12/20: I don't see why; locals should be quick.
-The defer goes back to version 1.62 in 1997.  A local being still deliverable
-during a continued run might result from something like a defer during the
-original delivery, eg. in a DB lookup.  Unlikely but possible.
+already set up, defer any local deliveries because we are handling remotes.
 
 To avoid delaying a local when combined with a callout-hold for a remote
 delivery, test continue_sequence rather than continue_transport. */
@@ -8402,9 +8423,10 @@ if (mua_wrapper)
 
 /* In a normal configuration, we now update the retry database. This is done in
 one fell swoop at the end in order not to keep opening and closing (and
-locking) the database. The code for handling retries is hived off into a
-separate module for convenience. We pass it the addresses of the various
-chains, because deferred addresses can get moved onto the failed chain if the
+locking) the database (at least, for non-transaction-capable DBs.
+The code for handling retries is hived off into a separate module for
+convenience. We pass it the addresses of the various chains,
+because deferred addresses can get moved onto the failed chain if the
 retry cutoff time has expired for all alternative destinations. Bypass the
 updating of the database if the -N flag is set, which is a debugging thing that
 prevents actual delivery. */
@@ -8506,6 +8528,13 @@ f.disable_logging = FALSE;  /* In case left set */
 
 DELIVERY_TIDYUP:
 
+if (dbm_file)        /* Can only be continue_retry_db */
+  {
+  DEBUG(D_hints_lookup) debug_printf("final close of cached retry db\n");
+  dbfn_close_multi(continue_retry_db);
+  continue_retry_db = dbm_file = NULL;
+  }
+
 /* If there are now no deferred addresses, we are done. Preserve the
 message log if so configured, and we are using them. Otherwise, sling it.
 Then delete the message itself. */
diff --git a/src/src/exim_dbmbuild.c b/src/src/exim_dbmbuild.c
index 05387aa3f..f8fbd2b8c 100644
--- a/src/src/exim_dbmbuild.c
+++ b/src/src/exim_dbmbuild.c
@@ -88,6 +88,27 @@ if it is made static. */
 const uschar *hex_digits = CUS"0123456789abcdef";
 
 
+/*******************
+*   Debug output   *
+*******************/
+
+unsigned int debug_selector = 0;    /* set -1 for debugging */
+
+void
+debug_printf(const char * fmt, ...)
+{
+va_list ap;
+va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap);
+}
+void
+debug_printf_indent(const char * fmt, ...)
+{
+va_list ap;
+va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap);
+}
+
+
+
 #ifdef STRERROR_FROM_ERRLIST
 /* Some old-fashioned systems still around (e.g. SunOS4) don't have strerror()
 in their libraries, but can provide the same facility by this simple
diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c
index dd1444593..f3123c80b 100644
--- a/src/src/exim_dbutil.c
+++ b/src/src/exim_dbutil.c
@@ -112,6 +112,25 @@ exit(EXIT_FAILURE);
 
 
 
+/*******************
+*   Debug output   *
+*******************/
+
+unsigned int debug_selector = 0;    /* set -1 for debugging */
+
+void
+debug_printf(const char * fmt, ...)
+{
+va_list ap;
+va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap);
+}
+void
+debug_printf_indent(const char * fmt, ...)
+{
+va_list ap;
+va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap);
+}
+
 /*************************************************
 *           Sort out the command arguments       *
 *************************************************/
@@ -287,7 +306,6 @@ dbfn_open(const uschar * name, int flags, open_db * dbblock,
 {
 int rc;
 struct flock lock_data;
-BOOL read_only = (flags & (O_WRONLY|O_RDWR)) == O_RDONLY;
 uschar * dirname, * filename;
 
 /* The first thing to do is to open a separate file on which to lock. This
@@ -301,6 +319,7 @@ if (  asprintf(CSS &dirname, "%s/db", spool_directory) < 0
    || asprintf(CSS &filename, "%s/%s.lockfile", dirname, name) < 0)
   return NULL;
 
+dbblock->readonly = (flags & (O_WRONLY|O_RDWR)) == O_RDONLY;
 dbblock->lockfd = -1;
 if (exim_lockfile_needed())
   {
@@ -314,7 +333,7 @@ if (exim_lockfile_needed())
   /* Now we must get a lock on the opened lock file; do this with a blocking
   lock that times out. */
 
-  lock_data.l_type = read_only ? F_RDLCK : F_WRLCK;
+  lock_data.l_type = dbblock->readonly ? F_RDLCK : F_WRLCK;
   lock_data.l_whence = lock_data.l_start = lock_data.l_len = 0;
 
   sigalrm_seen = FALSE;
@@ -327,7 +346,7 @@ if (exim_lockfile_needed())
   if (rc < 0)
     {
     printf("** Failed to get %s lock for %s: %s",
-      read_only ? "read" : "write",
+      dbblock->readonly ? "read" : "write",
       filename,
       errno == ETIMEDOUT ? "timed out" : strerror(errno));
     (void)close(dbblock->lockfd);
@@ -340,10 +359,12 @@ if (exim_lockfile_needed())
 
 if (asprintf(CSS &filename, "%s/%s", dirname, name) < 0) return NULL;
 
-if (!(dbblock->dbptr = exim_dbopen(filename, dirname, flags, 0)))
+if (!(dbblock->dbptr = dbblock->readonly && !exim_lockfile_needed()
+              ? exim_dbopen_multi(filename, dirname, flags, 0)
+              : exim_dbopen(filename, dirname, flags, 0)))
   {
   printf("** Failed to open hintsdb file %s for %s: %s%s\n", filename,
-    read_only ? "reading" : "writing", strerror(errno),
+    dbblock->readonly ? "reading" : "writing", strerror(errno),
 #ifdef USE_DB
     " (or Berkeley DB error while opening)"
 #else
@@ -374,8 +395,13 @@ Returns:  nothing
 void
 dbfn_close(open_db * dbp)
 {
-exim_dbclose(dbp->dbptr);
-if (dbp->lockfd >= 0) (void) close(dbp->lockfd);
+if (dbp->readonly && !exim_lockfile_needed())
+  exim_dbclose_multi(dbp->dbptr);
+else
+  exim_dbclose(dbp->dbptr);
+
+if (dbp->lockfd >= 0)
+  (void) close(dbp->lockfd);
 }
 
 
@@ -422,6 +448,7 @@ we should store the taint status along with the data. */
 dlen = exim_datum_size_get(&result_datum);
 yield = store_get(dlen, GET_TAINTED);
 memcpy(yield, exim_datum_data_get(&result_datum), dlen);
+DEBUG(D_hints_lookup) debug_printf_indent("dbfn_read: size %u return\n", dlen);
 if (length) *length = dlen;
 
 exim_datum_free(&result_datum);    /* Some DBM libs require freeing */
diff --git a/src/src/hintsdb.h b/src/src/hintsdb.h
index ba50ae1b9..a35791409 100644
--- a/src/src/hintsdb.h
+++ b/src/src/hintsdb.h
@@ -25,7 +25,11 @@ The API is:
   Functions:
     exim_lockfile_needed     API semantics predicate
     exim_dbopen
+    exim_dbopen_multi        only for no-lockfile-needed
     exim_dbclose
+    exim_dbclose_multi        only for no-lockfile-needed
+    exim_dbtransaction_start    only for no-lockfile-needed
+    exim_dbtransaction_commit    only for no-lockfile-needed
     exim_dbget
     exim_dbput
     exim_dbputb            non-overwriting put
@@ -66,6 +70,9 @@ required by Exim's process transitions)?
 #ifndef HINTSDB_H
 #define HINTSDB_H
 
+/* Include file ordering problem */
+extern void    debug_printf_indent(const char *, ...) PRINTF_FUNCTION(1,2);
+
 
 #ifdef USE_SQLITE
 # if defined(USE_DB) || defined(USE_GDBM) || defined(USE_TDB)
@@ -103,20 +110,6 @@ the default is the NDBM interface (which seems to be a wrapper for GDBM) */
 
 
 
-#if defined(COMPILE_UTILITY) || defined(MACRO_PREDEF)
-
-static inline EXIM_DB *
-exim_dbopen(const uschar * name, const uschar * dirname, int flags,
-  unsigned mode)
-{
-return exim_dbopen__(name, dirname, flags, mode);
-}
-
-static inline void
-exim_dbclose(EXIM_DB * dbp)
-{ exim_dbclose__(dbp); }
-
-#else    /*  exim mainline code */
 
 /* Wrappers for open/close with debug tracing */
 
@@ -147,14 +140,43 @@ DEBUG(D_hints_lookup) debug_printf_indent("returned from EXIM_DBOPEN: %p\n", dbp
 return dbp;
 }
 
+static inline EXIM_DB *
+exim_dbopen_multi(const uschar * name, const uschar * dirname, int flags,
+  unsigned mode)
+{
+void * dbp;
+DEBUG(D_hints_lookup)
+  debug_printf_indent("EXIM_DBOPEN_MULTI: file <%s> dir <%s> flags=%s\n",
+    name, dirname,
+    flags == O_RDONLY ? "O_RDONLY"
+    : flags == O_RDWR ? "O_RDWR"
+    : flags == (O_RDWR|O_CREAT) ? "O_RDWR|O_CREAT"
+    : "??");
+if (is_tainted(name) || is_tainted(dirname))
+  {
+  log_write(0, LOG_MAIN|LOG_PANIC, "Tainted name for DB file not permitted");
+  dbp = NULL;
+  }
+else
+  dbp = exim_dbopen_multi__(name, dirname, flags, mode);
+
+DEBUG(D_hints_lookup) debug_printf_indent("returned from EXIM_DBOPEN_MULTI: %p\n", dbp);
+return dbp;
+}
+
 static inline void
 exim_dbclose(EXIM_DB * dbp)
 {
 DEBUG(D_hints_lookup) debug_printf_indent("EXIM_DBCLOSE(%p)\n", dbp);
 exim_dbclose__(dbp);
 }
+static inline void
+exim_dbclose_multi(EXIM_DB * dbp)
+{
+DEBUG(D_hints_lookup) debug_printf_indent("EXIM_DBCLOSE_MULTI(%p)\n", dbp);
+exim_dbclose_multi__(dbp);
+}
 
-#endif        /* defined(COMPILE_UTILITY) || defined(MACRO_PREDEF) */
 
 /********************* End of dbm library definitions **********************/
 
diff --git a/src/src/hintsdb/hints_bdb.h b/src/src/hintsdb/hints_bdb.h
index e629cce4e..48573e907 100644
--- a/src/src/hintsdb/hints_bdb.h
+++ b/src/src/hintsdb/hints_bdb.h
@@ -48,6 +48,21 @@ definition of DB_VERSION_STRING, which is present in versions 2.x onwards. */
 #    define DB_FORCESYNC 0
 #   endif
 
+
+
+/* Berkeley DB uses a callback function to pass back error details. Its API
+changed at release 4.3. */
+
+#if defined(DB_VERSION_STRING)
+# if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3)
+static void     dbfn_bdb_error_callback(const DB_ENV *, const char *, const char *);
+# else
+static void     dbfn_bdb_error_callback(const char *, char *);
+# endif
+#endif
+
+
+
 /* Error callback */
 /* For Berkeley DB >= 2, we can define a function to be called in case of DB
 errors. This should help with debugging strange DB problems, e.g. getting "File
@@ -73,9 +88,9 @@ return TRUE;
 }
 
 static inline EXIM_DB *
-exim_dbopen_multi(const uschar * name, const uschar * dirname, int flags,
+exim_dbopen_multi__(const uschar * name, const uschar * dirname, int flags,
   unsigned mode) { return NULL; }
-static inline void exim_dbclose_multi(EXIM_DB * dbp) {}
+static inline void exim_dbclose_multi__(EXIM_DB * dbp) {}
 static inline BOOL exim_dbtransaction_start(EXIM_DB * dbp) { return FALSE; }
 static inline void exim_dbtransaction_commit(EXIM_DB * dbp) {}
 
@@ -106,6 +121,9 @@ if (db_create(&b, dbp, 0) == 0)
           mode) == 0
       )
     return dbp;
+  else DEBUG(D_hints_lookup)
+    debug_printf_indent("bdb_open(flags 0x%x mode %04o) %s\n",
+          flags, mode, strerror(errno));
 
   b->close(b, 0);
   }
@@ -233,9 +251,9 @@ return TRUE;
 }
 
 static inline EXIM_DB *
-exim_dbopen_multi(const uschar * name, const uschar * dirname, int flags,
+exim_dbopen_multi__(const uschar * name, const uschar * dirname, int flags,
   unsigned mode) { return NULL; }
-static inline void exim_dbclose_multi(EXIM_DB * dbp) {}
+static inline void exim_dbclose_multi__(EXIM_DB * dbp) {}
 static inline BOOL exim_dbtransaction_start(EXIM_DB * dbp) { return FALSE; }
 static inline void exim_dbtransaction_commit(EXIM_DB * dbp) {}
 
diff --git a/src/src/hintsdb/hints_gdbm.h b/src/src/hintsdb/hints_gdbm.h
index b406d45e0..6b3789979 100644
--- a/src/src/hintsdb/hints_gdbm.h
+++ b/src/src/hintsdb/hints_gdbm.h
@@ -41,9 +41,9 @@ return TRUE;
 }
 
 static inline EXIM_DB *
-exim_dbopen_multi(const uschar * name, const uschar * dirname, int flags,
+exim_dbopen_multi__(const uschar * name, const uschar * dirname, int flags,
   unsigned mode) { return NULL; }
-static inline void exim_dbclose_multi(EXIM_DB * dbp) {}
+static inline void exim_dbclose_multi__(EXIM_DB * dbp) {}
 static inline BOOL exim_dbtransaction_start(EXIM_DB * dbp) { return FALSE; }
 static inline void exim_dbtransaction_commit(EXIM_DB * dbp) {}
 
@@ -60,7 +60,12 @@ if (dbp)
     flags & O_CREAT ? GDBM_WRCREAT
     : flags & (O_RDWR|O_WRONLY) ? GDBM_WRITER : GDBM_READER,
     mode, 0);
-  if (dbp->gdbm) return dbp;
+  if (dbp->gdbm)
+    return dbp;
+
+  DEBUG(D_hints_lookup)
+    debug_printf_indent("gdbm_open(flags 0x%x mode %04o) %s\n",
+          flags, mode, strerror(errno));
   free(dbp);
   }
 return NULL;
diff --git a/src/src/hintsdb/hints_ndbm.h b/src/src/hintsdb/hints_ndbm.h
index fb1db57a8..389abd9e2 100644
--- a/src/src/hintsdb/hints_ndbm.h
+++ b/src/src/hintsdb/hints_ndbm.h
@@ -36,9 +36,9 @@ return TRUE;
 }
 
 static inline EXIM_DB *
-exim_dbopen_multi(const uschar * name, const uschar * dirname, int flags,
+exim_dbopen_multi__(const uschar * name, const uschar * dirname, int flags,
   unsigned mode) { return NULL; }
-static inline void exim_dbclose_multi(EXIM_DB * dbp) {}
+static inline void exim_dbclose_multi__(EXIM_DB * dbp) {}
 static inline BOOL exim_dbtransaction_start(EXIM_DB * dbp) { return FALSE; }
 static inline void exim_dbtransaction_commit(EXIM_DB * dbp) {}
 
@@ -54,9 +54,10 @@ exim_dbopen__(const uschar * name, const uschar * dirname, int flags,
 struct stat st;
 if (!(flags & O_CREAT) || lstat(CCS name, &st) != 0 && errno == ENOENT)
   return dbm_open(CS name, flags, mode);
-#ifndef COMPILE_UTILITY
-debug_printf("%s %d errno %s\n", __FUNCTION__, __LINE__, strerror(errno));
-#endif
+
+DEBUG(D_hints_lookup)
+  debug_printf_indent("ndbm_open(flags 0x%x mode %04o) %s\n",
+          flags, mode, strerror(errno));
 errno = (st.st_mode & S_IFMT) == S_IFDIR ? EISDIR : EEXIST;
 return NULL;
 }
diff --git a/src/src/hintsdb/hints_sqlite.h b/src/src/hintsdb/hints_sqlite.h
index da3bc2bff..262a414f6 100644
--- a/src/src/hintsdb/hints_sqlite.h
+++ b/src/src/hintsdb/hints_sqlite.h
@@ -35,11 +35,12 @@ return FALSE;    /* We do transaction; no extra locking needed */
 
 /* EXIM_DBOPEN - return pointer to an EXIM_DB, NULL if failed */
 static inline EXIM_DB *
-exim_dbopen_multi(const uschar * name, const uschar * dirname, int flags,
+exim_dbopen_multi__(const uschar * name, const uschar * dirname, int flags,
   unsigned mode)
 {
 EXIM_DB * dbp;
 int ret, sflags = flags & O_RDWR ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY;
+
 if (flags & O_CREAT) sflags |= SQLITE_OPEN_CREATE;
 if ((ret = sqlite3_open_v2(CCS name, &dbp, sflags, NULL)) == SQLITE_OK)
   {
@@ -51,8 +52,9 @@ if ((ret = sqlite3_open_v2(CCS name, &dbp, sflags, NULL)) == SQLITE_OK)
   if (ret != SQLITE_OK)
     sqlite3_close(dbp);
   }
-//else
-//  fprintf(stderr, "sqlite3_open_v2: %s\n", sqlite3_errmsg(dbp));
+else DEBUG(D_hints_lookup)
+  debug_printf_indent("sqlite_open(flags 0x%x mode %04o) %s\n",
+              flags, mode, sqlite3_errmsg(dbp));
 return ret == SQLITE_OK ? dbp : NULL;
 }
 
@@ -286,7 +288,7 @@ store_free(cursor);
 
 /* EXIM_DBCLOSE */
 static inline void
-exim_dbclose_multi(EXIM_DB * dbp)
+exim_dbclose_multi__(EXIM_DB * dbp)
 {
 sqlite3_close(dbp);
 }
diff --git a/src/src/hintsdb/hints_tdb.h b/src/src/hintsdb/hints_tdb.h
index 436597115..2b4fcc7f6 100644
--- a/src/src/hintsdb/hints_tdb.h
+++ b/src/src/hintsdb/hints_tdb.h
@@ -11,9 +11,6 @@
 backend provider. */
 
 /* ************************* tdb interface ************************ */
-/*XXX https://manpages.org/tdb/3 mentions concurrent writes.
-Could we lose the file lock? */
-
 # include <tdb.h>
 
 /* Basic DB type */
@@ -34,22 +31,39 @@ tdb_traverse to be called) */
 static inline BOOL
 exim_lockfile_needed(void)
 {
-return TRUE;
+return FALSE;        /* Transactions are supported */
 }
 
-static inline EXIM_DB *
-exim_dbopen_multi(const uschar * name, const uschar * dirname, int flags,
-  unsigned mode) { return NULL; }
-static inline void exim_dbclose_multi(EXIM_DB * dbp) {}
-static inline BOOL exim_dbtransaction_start(EXIM_DB * dbp) { return FALSE; }
-static inline void exim_dbtransaction_commit(EXIM_DB * dbp) {}
 
 /* EXIM_DBOPEN - return pointer to an EXIM_DB, NULL if failed */
 static inline EXIM_DB *
 exim_dbopen__(const uschar * name, const uschar * dirname, int flags,
   unsigned mode)
 {
-return tdb_open(CS name, 0, TDB_DEFAULT, flags, mode);
+EXIM_DB * db = tdb_open(CS name, 0, TDB_DEFAULT, flags, mode);
+int e;
+
+DEBUG(D_hints_lookup) if (!db)
+  debug_printf_indent("tdb_open(flags 0x%x mode %04o) %s\n",
+          flags, mode, strerror(errno));
+if (!db || tdb_transaction_start(db) == 0) return db;
+e = errno;
+DEBUG(D_hints_lookup) if (db)
+  debug_printf_indent("tdb_transaction_start: %s\n", tdb_errorstr(db));
+tdb_close(db);
+errno = e;
+return NULL;
+}
+
+static inline EXIM_DB *
+exim_dbopen_multi__(const uschar * name, const uschar * dirname, int flags,
+  unsigned mode)
+{
+EXIM_DB * db = tdb_open(CS name, 0, TDB_DEFAULT, flags, mode);
+DEBUG(D_hints_lookup) if (!db)
+  debug_printf_indent("tdb_open(flags 0x%x mode %04o) %s\n",
+          flags, mode, strerror(errno));
+return db;
 }
 
 /* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */
@@ -60,10 +74,36 @@ exim_dbget(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res)
 return res->dptr != NULL;
 }
 
+
+static inline BOOL
+exim_dbtransaction_start(EXIM_DB * db)
+{
+BOOL ok = tdb_transaction_start(db) == 0;
+DEBUG(D_hints_lookup) if (!ok)
+  debug_printf_indent("tdb_transaction_start: %s\n", tdb_errorstr(db));
+return ok;
+}
+
+static inline void
+exim_dbtransaction_commit(EXIM_DB * db)
+{
+BOOL ok = tdb_transaction_commit(db) == 0;
+DEBUG(D_hints_lookup) if (!ok)
+  debug_printf_indent("tdb_transaction_commit: %s\n", tdb_errorstr(db));
+return;
+}
+
+
+
 /* EXIM_DBPUT - returns nothing useful, assumes replace mode */
 static inline int
 exim_dbput(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
-{ return tdb_store(dbp, *key, *data, TDB_REPLACE); }
+{
+int rc = tdb_store(dbp, *key, *data, TDB_REPLACE);
+DEBUG(D_hints_lookup) if (rc != 0)
+  debug_printf_indent("tdb_store: %s\n", tdb_errorstr(dbp));
+return rc;
+}
 
 /* EXIM_DBPUTB - non-overwriting for use by dbmbuild */
 static inline int
@@ -109,12 +149,33 @@ return key->dptr != NULL;
 /* EXIM_DBDELETE_CURSOR - terminate scanning operation. */
 static inline void
 exim_dbdelete_cursor(EXIM_CURSOR * cursor)
-{ store_free(cursor); }
+{
+#ifdef COMPILE_UTILITY
+free(cursor);
+#else
+store_free(cursor);
+#endif
+}
 
 /* EXIM_DBCLOSE */
+static inline void
+exim_dbclose_multi__(EXIM_DB * db)
+{
+int rc = tdb_close(db);
+DEBUG(D_hints_lookup) if (rc != 0)
+  debug_printf_indent("tdb_close: %s\n", tdb_errorstr(db));
+}
+
 static inline void
 exim_dbclose__(EXIM_DB * db)
-{ tdb_close(db); }
+{
+int rc = tdb_transaction_commit(db);
+DEBUG(D_hints_lookup) if (rc != 0)
+  debug_printf_indent("tdb_transaction_commit: %s\n", tdb_errorstr(db));
+rc = tdb_close(db);
+DEBUG(D_hints_lookup) if (rc != 0)
+  debug_printf_indent("tdb_close: %s\n", tdb_errorstr(db));
+}
 
 /* Datum access */
 
diff --git a/src/src/hintsdb_structs.h b/src/src/hintsdb_structs.h
index 5f50dfd5f..5adb68eb5 100644
--- a/src/src/hintsdb_structs.h
+++ b/src/src/hintsdb_structs.h
@@ -21,6 +21,7 @@ that relates to it. */
 typedef struct {
   void *    dbptr;
   int        lockfd;
+  BOOL        readonly;
 } open_db;
 
 
diff --git a/src/src/queue.c b/src/src/queue.c
index 3073ee780..57e5eb769 100644
--- a/src/src/queue.c
+++ b/src/src/queue.c
@@ -513,23 +513,31 @@ for (int i = queue_run_in_order ? -1 : 0;
           (double)load_average/1000.0,
           (double)deliver_queue_load_max/1000.0);
 
-    /* If initial of a 2-phase run, maintain a set of child procs
-    to get disk parallelism */
+    /* If initial of a 2-phase run (and not under the test-harness)
+    maintain a set of child procs to get disk parallelism */
 
     if (q->queue_2stage && !queue_run_in_order)
       {
       int i;
-      if (qpid[f.running_in_test_harness ? 0 : nelem(qpid) - 1])
-    {
-    DEBUG(D_queue_run) debug_printf("q2stage waiting for child %d\n", (int)qpid[0]);
+      if (qpid[
+#ifndef MEASURE_TIMING
+          f.running_in_test_harness ? 0 :
+#endif
+          nelem(qpid) - 1])
+    {        /* The child table is maxed out; wait for the oldest */
+    DEBUG(D_queue_run)
+      debug_printf("q2stage waiting for child %d\n", (int)qpid[0]);
     waitpid(qpid[0], NULL, 0);
-    DEBUG(D_queue_run) debug_printf("q2stage reaped child %d\n", (int)qpid[0]);
-    if (f.running_in_test_harness) i = 0;
-    else for (i = 0; i < nelem(qpid) - 1; i++) qpid[i] = qpid[i+1];
+    DEBUG(D_queue_run)
+      debug_printf("q2stage reaped child %d\n", (int)qpid[0]);
+#ifndef MEASURE_TIMING
+    if (f.running_in_test_harness) i = 0; else
+#endif
+      for (i = 0; i < nelem(qpid) - 1; i++) qpid[i] = qpid[i+1];
     qpid[i] = 0;
     }
       else
-    for (i = 0; qpid[i]; ) i++;
+    for (i = 0; qpid[i]; ) i++;        /* find first spare slot */
       if ((qpid[i] = exim_fork(US"qrun-phase-one")))
     continue;    /* parent loops around */
       }
@@ -565,7 +573,8 @@ for (int i = queue_run_in_order ? -1 : 0;
       follow. If the message is chosen for delivery, the header is read again
       in the deliver_message() function, in a subprocess. */
 
-      if (spool_read_header(fq->text, FALSE, TRUE) != spool_read_OK) goto go_around;
+      if (spool_read_header(fq->text, FALSE, TRUE) != spool_read_OK)
+    goto go_around;
       f.dont_deliver = orig_dont_deliver;
 
       /* Now decide if we want to deliver this message. As we have read the
@@ -739,6 +748,7 @@ single_item_retry:
     set_process_info("running queue");
 
     /* If initial of a 2-phase run, we are a child - so just exit */
+
     if (q->queue_2stage && !queue_run_in_order)
       exim_exit(EXIT_SUCCESS);
 
diff --git a/src/src/retry.c b/src/src/retry.c
index e86b1afe8..c32bd85b7 100644
--- a/src/src/retry.c
+++ b/src/src/retry.c
@@ -153,19 +153,23 @@ retry_check_address(const uschar *domain, host_item *host, uschar *portstring,
 BOOL yield = FALSE;
 time_t now = time(NULL);
 const uschar * host_key, * message_key;
-open_db dbblock, * dbm_file;
+open_db dbblock, * dbm_file = NULL;
 tree_node * node;
 dbdata_retry * host_retry_record, * message_retry_record;
 
 *retry_host_key = *retry_message_key = NULL;
 
-DEBUG(D_transport|D_retry) debug_printf("checking retry status of %s\n", host->name);
-
 /* Do nothing if status already set; otherwise initialize status as usable. */
 
 if (host->status != hstatus_unknown) return FALSE;
 host->status = hstatus_usable;
 
+DEBUG(D_transport|D_retry)
+  {
+  debug_printf_indent("checking retry status of %s\n", host->name);
+  acl_level++;
+  }
+
 /* Generate the host key for the unusable tree and the retry database. Ensure
 host names are lower cased (that's what %S does).
 Generate the message-specific key too.
@@ -182,11 +186,12 @@ the retry database when it is updated). */
 
 if ((node = tree_search(tree_unusable, host_key)))
   {
-  DEBUG(D_transport|D_retry) debug_printf("found in tree of unusables\n");
-  host->status = (node->data.val > 255)?
-    hstatus_unusable_expired : hstatus_unusable;
+  DEBUG(D_transport|D_retry)
+    debug_printf_indent("found in tree of unusables\n");
+  host->status = node->data.val > 255
+    ? hstatus_unusable_expired : hstatus_unusable;
   host->why = node->data.val & 255;
-  return FALSE;
+  goto out;
   }
 
 /* Open the retry database, giving up if there isn't one. Otherwise, search for
@@ -194,40 +199,49 @@ the retry records, and then close the database again. */
 
 if (!continue_retry_db)
   dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE, TRUE);
-else if ((dbm_file = continue_retry_db) == (open_db *)-1)
-  dbm_file = NULL;
+else if (continue_retry_db != (open_db *)-1)
+  {
+  DEBUG(D_hints_lookup)
+    debug_printf_indent(" using cached retry hintsdb handle\n");
+  dbm_file = continue_retry_db;
+  }
+else DEBUG(D_hints_lookup)
+    debug_printf_indent(" using cached retry hintsdb nonpresence\n");
 
 if (!dbm_file)
   {
   DEBUG(D_deliver|D_retry|D_hints_lookup)
-    debug_printf("no retry data available\n");
-  return FALSE;
+    debug_printf_indent("no retry data available\n");
+  goto out;
   }
 host_retry_record = dbfn_read(dbm_file, host_key);
 message_retry_record = dbfn_read(dbm_file, message_key);
 if (!continue_retry_db)
   dbfn_close(dbm_file);
+else
+  DEBUG(D_hints_lookup) debug_printf_indent("retaining retry hintsdb handle\n");
 
 /* Ignore the data if it is too old - too long since it was written */
 
 if (!host_retry_record)
   {
-  DEBUG(D_transport|D_retry) debug_printf("no host retry record\n");
+  DEBUG(D_transport|D_retry) debug_printf_indent("no host retry record\n");
   }
 else if (now - host_retry_record->time_stamp > retry_data_expire)
   {
   host_retry_record = NULL;
-  DEBUG(D_transport|D_retry) debug_printf("host retry record too old\n");
+  DEBUG(D_transport|D_retry) debug_printf_indent("host retry record too old\n");
   }
 
 if (!message_retry_record)
   {
-  DEBUG(D_transport|D_retry) debug_printf("no message retry record\n");
+  DEBUG(D_transport|D_retry) debug_printf_indent("no message retry record\n");
   }
 else if (now - message_retry_record->time_stamp > retry_data_expire)
   {
   message_retry_record = NULL;
-  DEBUG(D_transport|D_retry) debug_printf("message retry record too old\n");
+  DEBUG(D_transport|D_retry)
+    debug_printf_indent("message retry record too old\n");
   }
 
 /* If there's a host-specific retry record, check for reaching the retry
@@ -249,7 +263,7 @@ if (host_retry_record)
     if (!host_retry_record->expired &&
         retry_ultimate_address_timeout(host_key, domain,
           host_retry_record, now))
-      return FALSE;
+      goto out;
 
     /* We have not hit the ultimate address timeout; host is unusable. */
 
@@ -257,7 +271,7 @@ if (host_retry_record)
       hstatus_unusable_expired : hstatus_unusable;
     host->why = hwhy_retry;
     host->last_try = host_retry_record->last_try;
-    return FALSE;
+    goto out;
     }
 
   /* Host is usable; set return TRUE if expired. */
@@ -280,10 +294,12 @@ if (message_retry_record)
       host->status = hstatus_unusable;
       host->why = hwhy_retry;
       }
-    return FALSE;
+    yield = FALSE; goto out;
     }
   }
 
+out:
+DEBUG(D_transport|D_retry) acl_level--;
 return yield;
 }
 
@@ -531,6 +547,7 @@ return yield;
 /* Update the retry data for any directing/routing/transporting that was
 deferred, or delete it for those that succeeded after a previous defer. This is
 done all in one go to minimize opening/closing/locking of the database file.
+Called (only) from deliver_message().
 
 Note that, because SMTP delivery involves a list of destinations to try, there
 may be defer-type retry information for some of them even when the message was
@@ -590,8 +607,7 @@ for (int i = 0; i < 3; i++)
 
     for (addr = endaddr; addr; addr = addr->parent)
       {
-      int update_count = 0;
-      int timedout_count = 0;
+      int update_count = 0, timedout_count = 0;
 
       DEBUG(D_retry)
     {
@@ -617,14 +633,20 @@ for (int i = 0; i < 3; i++)
         reached their retry next try time. */
 
         if (!dbm_file)
-          dbm_file = dbfn_open(US"retry", O_RDWR|O_CREAT, &dbblock, TRUE, TRUE);
-
-        if (!dbm_file)
-          {
-          DEBUG(D_deliver|D_retry|D_hints_lookup)
-            debug_printf_indent("retry database not available for updating\n");
-          return;
-          }
+      if (continue_retry_db && continue_retry_db != (open_db *)-1)
+        {
+        DEBUG(D_hints_lookup)
+          debug_printf_indent("using cached retry hintsdb handle\n");
+        dbm_file = continue_retry_db;
+        }
+      else if (!(dbm_file = exim_lockfile_needed()
+            ? dbfn_open(US"retry", O_RDWR|O_CREAT, &dbblock, TRUE, TRUE)
+            : dbfn_open_multi(US"retry", O_RDWR|O_CREAT, &dbblock)))
+        {
+        DEBUG(D_deliver|D_retry|D_hints_lookup)
+          debug_printf_indent("retry db not available for updating\n");
+        return;
+        }
 
         /* If there are no deferred addresses, that is, if this message is
         completing, and the retry item is for a message-specific SMTP error,
@@ -699,6 +721,12 @@ for (int i = 0; i < 3; i++)
       message_length = EXIM_DB_RLIMIT;
       }
 
+    /* For a transaction-capable DB, open one for the read,write
+    sequence used for this retry record */
+
+    if (!exim_lockfile_needed())
+      dbfn_transaction_start(dbm_file);
+
         /* Read a retry record from the database or construct a new one.
         Ignore an old one if it is too old since it was last updated. */
 
@@ -888,8 +916,12 @@ for (int i = 0; i < 3; i++)
           debug_printf(" %s\n", retry_record->text);
           }
 
-        (void)dbfn_write(dbm_file, rti->key, retry_record,
-          sizeof(dbdata_retry) + message_length);
+        if (dbfn_write(dbm_file, rti->key, retry_record,
+              sizeof(dbdata_retry) + message_length) != 0)
+      DEBUG(D_retry) debug_printf_indent("retry record write failed\n");
+
+    if (!exim_lockfile_needed())
+      dbfn_transaction_commit(dbm_file);
         }                            /* Loop for each retry item */
       DEBUG(D_retry) acl_level--;
 
@@ -971,9 +1003,17 @@ for (int i = 0; i < 3; i++)
 
 /* Close and unlock the database */
 
-if (dbm_file) dbfn_close(dbm_file);
+if (dbm_file)
+  if (dbm_file != continue_retry_db)
+      if (exim_lockfile_needed())
+    dbfn_close(dbm_file);
+      else
+    dbfn_close_multi(dbm_file);
+  else DEBUG(D_hints_lookup)
+    debug_printf_indent("retaining retry hintsdb handle\n");
 
-DEBUG(D_retry) { acl_level--; debug_printf_indent("end of retry processing\n"); }
+DEBUG(D_retry)
+  { acl_level--; debug_printf_indent("end of retry processing\n"); }
 }
 
 /* End of retry.c */
diff --git a/test/runtest b/test/runtest
index a41298f73..6a00a4642 100755
--- a/test/runtest
+++ b/test/runtest
@@ -1178,6 +1178,30 @@ RESET_AFTER_EXTRA_LINE_READ:
     next if /lock(?:ing|ed) .*\/spool\/db\/[^.]+\.lockfile$/;
     s/closed hints database\K and lockfile$//;
 
+    # Hints DBs with transactions are provider-dependent, and flow changes
+    # to take advantage of them need different opens and different flags.
+    # Drop all the debug output for opens and closes.
+    if (/EXIM_DBOPEN(_MULTI)?: file <.*spool\/db\/retry>/)
+      {
+      $_ = <IN>;
+      next if (/returned from EXIM_DBOPEN(_MULTI)?: 0x[[:xdigit:]]+$/);
+      $_ = <IN>;
+      <IN> if (/returned from EXIM_DBOPEN(_MULTI)?: \(nil\)$/);
+      next;
+      }
+    if (/EXIM_DBCLOSE(_MULTI)?/) { <IN>; next; }
+    next if /retaining retry hintsdb handle$/;
+    next if /using cached retry hintsdb (?:handle|nonpresence)$/;
+    if (/final close of cached retry db$/) { <IN>; <IN>; next; }
+    next if /dbfn_transaction_(?:start|commit)$/;
+
+    # Various hintsdb backends
+    s/(?:bdb|tdb|gdbm|ndbm|sqlite)
+      _open\(flags\ 0x(\d)
+      \ mode\ 0640\)
+      (?=\ No\ such\ file\ or\ directory$)
+     /hintsdb_open(flags 0x$1 mode 0640)/x;
+
     # Lines with a leading pid.  Only handle >= 4-digit PIDs to avoid converting SMTP respose codes
     s/^\s*(\d{4,})\s(?!(?:previous message|in\s|bytes remain in|SMTP accept process running))/new_value($1, "p%s", \$next_pid) . ' '/e;
 
@@ -1390,19 +1414,6 @@ RESET_AFTER_EXTRA_LINE_READ:
 
     next if /failed to load readline:/;
 
-    # Some DBM libraries seem to make DBM files on opening with O_RDWR without
-    # O_CREAT; other's don't. In the latter case there is some debugging output
-    # which is not present in the former. Skip the relevant lines (there are
-    # three of them).
-
-    if (/returned from EXIM_DBOPEN: \(nil\)/)
-      {
-      $_ .= <IN>;
-      s?\Q$parm_cwd\E?TESTSUITE?g;
-      if (/TESTSUITE\/spool\/db\/\S+ appears not to exist: trying to create/)
-    { $_ = <IN>; next; }
-      }
-
     # Some tests turn on +expand debugging to check on expansions.
     # Unfortunately, the Received: expansion varies, depending on whether TLS
     # is compiled or not. So we must remove the relevant debugging if it is.
diff --git a/test/stderr/0275 b/test/stderr/0275
index c4638ea60..aa6187d0d 100644
--- a/test/stderr/0275
+++ b/test/stderr/0275
@@ -259,9 +259,6 @@ body_linecount=0 message_linecount=8
 DSN: set orcpt:   flags: 0x0
 Delivery address list:
   userx@??? 
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
 no retry data available

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: userx@???
@@ -459,9 +456,6 @@ After routing:
search_tidyup called
>>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>

--------> userx@??? <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
search_tidyup called
changed uid/gid: local delivery to userx <userx@???> transport=t1
diff --git a/test/stderr/0278 b/test/stderr/0278
index 58729d516..fd94ee0c0 100644
--- a/test/stderr/0278
+++ b/test/stderr/0278
@@ -164,9 +164,6 @@ body_linecount=0 message_linecount=8
DSN: set orcpt: flags: 0x0
Delivery address list:
CALLER@???
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: CALLER@???
@@ -259,9 +256,6 @@ After routing:
search_tidyup called
>>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>

--------> CALLER@??? <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
search_tidyup called
changed uid/gid: local delivery to CALLER <CALLER@???> transport=t1
diff --git a/test/stderr/0357 b/test/stderr/0357
index a861c737d..4048d801f 100644
--- a/test/stderr/0357
+++ b/test/stderr/0357
@@ -25,7 +25,7 @@ After routing:
Failed addresses:
Deferred addresses:
checking retry status of 127.0.0.1
-no retry data available
+ no retry data available
added retry item for R:userx@???:<CALLER@???>: errno=-44 more_errno=dd,A flags=0
cmdlog: '220:EHLO:250:MAIL:250:RCPT:451:QUIT+:250'
>>>>>>>>>>>>>>>> Exim pid=p1239 (transport) terminating with rc=0 >>>>>>>>>>>>>>>>

@@ -69,8 +69,8 @@ After routing:
Failed addresses:
Deferred addresses:
checking retry status of 127.0.0.1
-no host retry record
-no message retry record
+ no host retry record
+ no message retry record
added retry item for R:userx@???:<CALLER@???>: errno=-44 more_errno=dd,A flags=0
cmdlog: '220:EHLO:250:MAIL:250:RCPT:451:QUIT+:250'
>>>>>>>>>>>>>>>> Exim pid=p1240 (transport) terminating with rc=0 >>>>>>>>>>>>>>>>

@@ -127,8 +127,8 @@ After routing:
Failed addresses:
Deferred addresses:
checking retry status of 127.0.0.1
-no host retry record
-no message retry record
+ no host retry record
+ no message retry record
added retry item for R:userx@???:<CALLER@???>: errno=-44 more_errno=dd,A flags=0
cmdlog: '220:EHLO:250:MAIL:250:RCPT:451:QUIT+:250'
>>>>>>>>>>>>>>>> Exim pid=p1242 (transport) terminating with rc=0 >>>>>>>>>>>>>>>>

diff --git a/test/stderr/0358 b/test/stderr/0358
index b4c68382b..d51bcf1a9 100644
--- a/test/stderr/0358
+++ b/test/stderr/0358
@@ -31,7 +31,7 @@ After routing:
Failed addresses:
Deferred addresses:
checking retry status of 127.0.0.1
-no retry data available
+ no retry data available
added retry item for R:userx@???:<CALLER@???>: errno=-44 more_errno=dd,A flags=0
added retry item for R:usery@???:<CALLER@???>: errno=-44 more_errno=dd,A flags=0
cmdlog: '220:EHLO:250:MAIL:250:RCPT:451:RCPT:451:QUIT+:250'
@@ -95,8 +95,8 @@ After routing:
Failed addresses:
Deferred addresses:
checking retry status of 127.0.0.1
-no host retry record
-no message retry record
+ no host retry record
+ no message retry record
added retry item for R:userx@???:<CALLER@???>: errno=-44 more_errno=dd,A flags=0
added retry item for R:usery@???:<CALLER@???>: errno=-44 more_errno=dd,A flags=0
cmdlog: '220:EHLO:250:MAIL:250:RCPT:451:RCPT:451:QUIT+:250'
diff --git a/test/stderr/0361 b/test/stderr/0361
index d54cdc43d..171d925e6 100644
--- a/test/stderr/0361
+++ b/test/stderr/0361
@@ -78,9 +78,6 @@ body_linecount=0 message_linecount=8
DSN: set orcpt: flags: 0x0
Delivery address list:
kilos@???
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: kilos@???
@@ -136,9 +133,6 @@ rewriting header lines
rewrite_one_header: type=F:
From: CALLER_NAME <CALLER@???>
re-routed to kilos@???
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: kilos@???
@@ -182,9 +176,6 @@ r3 router generated kilos@???
routed by r3 router
envelope to: kilos@???
transport: <none>
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: kilos@???
@@ -235,9 +226,6 @@ After routing:
search_tidyup called
>>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>

--------> kilos@??? <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
search_tidyup called
changed uid/gid: local delivery to kilos <kilos@???> transport=t2
diff --git a/test/stderr/0386 b/test/stderr/0386
index db54ea8f4..fa1cfb63e 100644
--- a/test/stderr/0386
+++ b/test/stderr/0386
@@ -312,9 +312,6 @@ body_linecount=1 message_linecount=8
DSN: set orcpt: flags: 0x0
Delivery address list:
2@b
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: 2@b
@@ -348,9 +345,6 @@ After routing:
search_tidyup called
>>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>

--------> 2@b <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
search_tidyup called
changed uid/gid: local delivery to 2 <2@b> transport=t1
@@ -508,9 +502,6 @@ body_linecount=1 message_linecount=8
DSN: set orcpt: flags: 0x0
Delivery address list:
2@b
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: 2@b
@@ -544,9 +535,6 @@ After routing:
search_tidyup called
>>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>

--------> 2@b <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
search_tidyup called
changed uid/gid: local delivery to 2 <2@b> transport=t1
diff --git a/test/stderr/0388 b/test/stderr/0388
index 152c91a27..d924179fa 100644
--- a/test/stderr/0388
+++ b/test/stderr/0388
@@ -7,9 +7,6 @@ admin user
dropping to exim gid; retaining priv uid
set_process_info: pppp delivering specified messages
set_process_info: pppp delivering 10HmaX-000000005vi-0000
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

 Considering: x@y
@@ -77,10 +74,7 @@ changed uid/gid: remote delivery to x@y with transport=smtp
   uid=EXIM_UID gid=EXIM_GID pid=p1235
 set_process_info: pppp delivering 10HmaX-000000005vi-0000 using smtp
 checking retry status of 127.0.0.1
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
-no retry data available
+ no retry data available
 127.0.0.1 in serialize_hosts? no (option unset)
 set_process_info: pppp delivering 10HmaX-000000005vi-0000 to 127.0.0.1 [127.0.0.1]:PORT_S (x@y)
 Connecting to 127.0.0.1 [127.0.0.1]:PORT_S ...
@@ -118,10 +112,7 @@ set_process_info: pppp delivering 10HmaX-000000005vi-0000: just tried 127.0.0.1
  *@127.0.0.1 in "*"? yes (matched "*")
 Clearing TFO as not first host for message
 checking retry status of V4NET.0.0.0
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
-no retry data available
+ no retry data available
 V4NET.0.0.0 in serialize_hosts? no (option unset)
 set_process_info: pppp delivering 10HmaX-000000005vi-0000 to V4NET.0.0.0 [V4NET.0.0.0]:PORT_S (x@y)
 Connecting to V4NET.0.0.0 [V4NET.0.0.0]:PORT_S ...
@@ -152,45 +143,40 @@ Processing retry items
  Failed addresses:
  Deferred addresses:
   x@y
-   ╎EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDWR
-   ╎returned from EXIM_DBOPEN: 0xAAAAAAAA
-   ╎opened hints database TESTSUITE/spool/db/retry: flags=O_RDWR|O_CREAT
-   ╎x@y in "*"?
+   x@y in "*"?
+   ╎list element: *
+   ╎address match test: subject=x@y pattern=*
+   ╎y in "*"?
    ╎ list element: *
-   ╎ address match test: subject=x@y pattern=*
-   ╎ y in "*"?
-   ╎  list element: *
-   ╎  y in "*"? yes (matched "*")
-   ╎ x@y in "*"? yes (matched "*")
-   ╎retry for R:x@y = * 0 0
-   ╎dbfn_read: key=R:x@y
-   ╎dbfn_read: null return
-   ╎failing_interval=ttt message_age=ttt
-   ╎Writing retry data for R:x@y
-   ╎  first failed=dddd last try=dddd next try=+1 expired=1
-   ╎  errno=-44 more_errno=dd,A H=127.0.0.1 [127.0.0.1]: SMTP error from remote mail server after RCPT TO:<x@y>: 451 Temporary error
-   ╎dbfn_write: key=R:x@y datalen nn
-   ╎*@V4NET.0.0.0 in "*"?
+   ╎ y in "*"? yes (matched "*")
+   ╎x@y in "*"? yes (matched "*")
+   retry for R:x@y = * 0 0
+   dbfn_read: key=R:x@y
+   dbfn_read: null return
+   failing_interval=ttt message_age=ttt
+   Writing retry data for R:x@y
+     first failed=dddd last try=dddd next try=+1 expired=1
+     errno=-44 more_errno=dd,A H=127.0.0.1 [127.0.0.1]: SMTP error from remote mail server after RCPT TO:<x@y>: 451 Temporary error
+   dbfn_write: key=R:x@y datalen nn
+   *@V4NET.0.0.0 in "*"?
+   ╎list element: *
+   ╎address match test: subject=*@V4NET.0.0.0 pattern=*
+   ╎V4NET.0.0.0 in "*"?
    ╎ list element: *
-   ╎ address match test: subject=*@V4NET.0.0.0 pattern=*
-   ╎ V4NET.0.0.0 in "*"?
-   ╎  list element: *
-   ╎  V4NET.0.0.0 in "*"? yes (matched "*")
-   ╎ *@V4NET.0.0.0 in "*"? yes (matched "*")
-   ╎retry for T:[V4NET.0.0.0]:V4NET.0.0.0:PORT_S (y) = * 0 0
-   ╎dbfn_read: key=T:[V4NET.0.0.0]:V4NET.0.0.0:PORT_S
-   ╎dbfn_read: null return
-   ╎failing_interval=ttt message_age=ttt
-   ╎on queue longer than maximum retry
-   ╎Writing retry data for T:[V4NET.0.0.0]:V4NET.0.0.0:PORT_S
-   ╎  first failed=dddd last try=dddd next try=+0 expired=0
-   ╎  errno=dd more_errno=dd,A Network Error
-   ╎dbfn_write: key=T:[V4NET.0.0.0]:V4NET.0.0.0:PORT_S datalen nn
-   timed out: all retries expired
+   ╎ V4NET.0.0.0 in "*"? yes (matched "*")
+   ╎*@V4NET.0.0.0 in "*"? yes (matched "*")
+   retry for T:[V4NET.0.0.0]:V4NET.0.0.0:PORT_S (y) = * 0 0
+   dbfn_read: key=T:[V4NET.0.0.0]:V4NET.0.0.0:PORT_S
+   dbfn_read: null return
+   failing_interval=ttt message_age=ttt
+   on queue longer than maximum retry
+   Writing retry data for T:[V4NET.0.0.0]:V4NET.0.0.0:PORT_S
+     first failed=dddd last try=dddd next try=+0 expired=0
+     errno=dd more_errno=dd,A Network Error
+   dbfn_write: key=T:[V4NET.0.0.0]:V4NET.0.0.0:PORT_S datalen nn
+  timed out: all retries expired
 LOG: MAIN
   ** x@y: retry timeout exceeded
-  EXIM_DBCLOSE(0xAAAAAAAA)
-  closed hints database
 end of retry processing
 exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -C TESTSUITE/test-config -d=0xd7715ced -MCd bounce-message -odi -odi -t -oem -oi -f <> -E10HmaX-000000005vi-0000
 Exim version x.yz ....
@@ -263,24 +249,19 @@ admin user
 dropping to exim gid; retaining priv uid
 set_process_info: pppp delivering specified messages
 set_process_info: pppp delivering 10HmaY-000000005vi-0000
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: 0xAAAAAAAA
- opened hints database TESTSUITE/spool/db/retry: flags=O_RDONLY

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: CALLER@???
unique = CALLER@???
- checking router retry status
- dbfn_read: key=R:myhost.test.ex
- dbfn_read: null return
- dbfn_read: key=R:CALLER@???
- dbfn_read: null return
- dbfn_read: key=R:CALLER@???:<>
- dbfn_read: null return
- no domain retry record
- no address retry record
+checking router retry status
+ dbfn_read: key=R:myhost.test.ex
+ dbfn_read: null return
+ dbfn_read: key=R:CALLER@???
+ dbfn_read: null return
+ dbfn_read: key=R:CALLER@???:<>
+ dbfn_read: null return
+ no domain retry record
+ no address retry record
CALLER@???: queued for routing
- EXIM_DBCLOSE(0xAAAAAAAA)
- closed hints database
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

routing CALLER@???
--------> r0 router <--------
diff --git a/test/stderr/0398 b/test/stderr/0398
index 169c332ad..86fbb5acd 100644
--- a/test/stderr/0398
+++ b/test/stderr/0398
@@ -141,15 +141,12 @@ routed by r2 router
Attempting full verification using callout
EXIM_DBOPEN: file <TESTSUITE/spool/db/callout> dir <TESTSUITE/spool/db> flags=O_RDWR
returned from EXIM_DBOPEN: 0xAAAAAAAA
- opened hints database TESTSUITE/spool/db/callout: flags=O_RDWR|O_CREAT
- dbfn_read: key=remote
- dbfn_read: size nnn return
- callout cache: found domain record for remote
- dbfn_read: key=qq@remote
- dbfn_read: null return
- callout cache: no address record found for qq@remote
- EXIM_DBCLOSE(0xAAAAAAAA)
- closed hints database
+dbfn_read: key=remote
+dbfn_read: size nnn return
+callout cache: found domain record for remote
+dbfn_read: key=qq@remote
+dbfn_read: null return
+callout cache: no address record found for qq@remote
interface=NULL port=PORT_S
Connecting to 127.0.0.1 [127.0.0.1]:PORT_S ...
127.0.0.1 in hosts_try_fastopen?
@@ -177,14 +174,11 @@ cmd buf flush ddd bytes
cmdlog: '220:EHLO:250:MAIL:250:RCPT:550:QUIT:250'
EXIM_DBOPEN: file <TESTSUITE/spool/db/callout> dir <TESTSUITE/spool/db> flags=O_RDWR
returned from EXIM_DBOPEN: 0xAAAAAAAA
- opened hints database TESTSUITE/spool/db/callout: flags=O_RDWR|O_CREAT
- dbfn_write: key=remote datalen nn
- wrote callout cache domain record for remote:
- result=1 postmaster=0 random=0
- dbfn_write: key=qq@remote datalen nn
- wrote negative callout cache address record for qq@remote
- EXIM_DBCLOSE(0xAAAAAAAA)
- closed hints database
+dbfn_write: key=remote datalen nn
+wrote callout cache domain record for remote:
+ result=1 postmaster=0 random=0
+dbfn_write: key=qq@remote datalen nn
+wrote negative callout cache address record for qq@remote
----------- end verify ------------
l_message: $acl_verify_message
warn: condition test succeeded in ACL "rcpt"
@@ -274,16 +268,13 @@ routed by r2 router
Attempting full verification using callout
EXIM_DBOPEN: file <TESTSUITE/spool/db/callout> dir <TESTSUITE/spool/db> flags=O_RDWR
returned from EXIM_DBOPEN: 0xAAAAAAAA
- opened hints database TESTSUITE/spool/db/callout: flags=O_RDWR|O_CREAT
- dbfn_read: key=remote
- dbfn_read: size nnn return
- callout cache: found domain record for remote
- dbfn_read: key=qq@remote
- dbfn_read: size nnn return
- callout cache: found address record for qq@remote
- callout cache: address record is negative
- EXIM_DBCLOSE(0xAAAAAAAA)
- closed hints database
+dbfn_read: key=remote
+dbfn_read: size nnn return
+callout cache: found domain record for remote
+dbfn_read: key=qq@remote
+dbfn_read: size nnn return
+callout cache: found address record for qq@remote
+callout cache: address record is negative
----------- end verify ------------
l_message: $acl_verify_message
warn: condition test succeeded in ACL "rcpt"
diff --git a/test/stderr/0402 b/test/stderr/0402
index bf80809c8..a9dab0cdc 100644
--- a/test/stderr/0402
+++ b/test/stderr/0402
@@ -447,9 +447,6 @@ Delivery address list:
userz@???
rd+CALLER@???
rd+usery@???
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: CALLER@???
@@ -724,9 +721,6 @@ try option unseen
routed by r1 router
envelope to: CALLER@???
transport: t1
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: TESTSUITE/test-mail/junk
@@ -750,9 +744,6 @@ After routing:
search_tidyup called
>>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>

--------> TESTSUITE/test-mail/junk <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
try option max_parallel
try option return_path
@@ -849,9 +840,6 @@ rd+usery@???: children all complete
LOG: MAIN
=> TESTSUITE/test-mail/junk <rd+usery@???> R=r5 T=ft1
--------> TESTSUITE/test-mail/junk <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
try option max_parallel
try option return_path
@@ -940,9 +928,6 @@ rd+CALLER@???: children all complete
LOG: MAIN
=> TESTSUITE/test-mail/junk <rd+CALLER@???> R=r4 T=ft1
--------> CALLER@??? <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
try option max_parallel
try option return_path
@@ -974,9 +959,6 @@ CALLER@??? delivered
LOG: MAIN
=> CALLER <CALLER@???> R=r1 T=t1
--------> usery@??? <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
try option max_parallel
try option return_path
@@ -1016,9 +998,6 @@ usery@??? delivered
LOG: MAIN
=> usery <usery@???> R=r2 T=t1
--------> userz@??? <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
try option max_parallel
try option return_path
diff --git a/test/stderr/0403 b/test/stderr/0403
index 79dde380b..449b63a6d 100644
--- a/test/stderr/0403
+++ b/test/stderr/0403
@@ -72,9 +72,6 @@ body_linecount=0 message_linecount=8
DSN: set orcpt: flags: 0x0
Delivery address list:
userx@???
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: userx@???
@@ -160,9 +157,6 @@ r2 router generated TESTSUITE/test-mail/junk
routed by r2 router
envelope to: userx@???
transport: <none>
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: TESTSUITE/test-mail/junk
@@ -178,9 +172,6 @@ After routing:
search_tidyup called
>>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>

--------> TESTSUITE/test-mail/junk <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
search_tidyup called
changed uid/gid: local delivery to TESTSUITE/test-mail/junk <TESTSUITE/test-mail/junk> transport=t1
@@ -311,9 +302,6 @@ body_linecount=0 message_linecount=8
DSN: set orcpt: flags: 0x0
Delivery address list:
charlie@???
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: charlie@???
@@ -377,9 +365,6 @@ r1 router generated TESTSUITE/test-mail/junk
routed by r1 router
envelope to: charlie@???
transport: <none>
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: TESTSUITE/test-mail/junk
@@ -395,9 +380,6 @@ After routing:
search_tidyup called
>>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>

--------> TESTSUITE/test-mail/junk <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
search_tidyup called
changed uid/gid: local delivery to TESTSUITE/test-mail/junk <TESTSUITE/test-mail/junk> transport=t1
diff --git a/test/stderr/0404 b/test/stderr/0404
index e1dbb4656..b3b91e227 100644
--- a/test/stderr/0404
+++ b/test/stderr/0404
@@ -173,9 +173,6 @@ body_linecount=0 message_linecount=160
DSN: set orcpt: flags: 0x0
Delivery address list:
userx@???
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: userx@???
@@ -218,9 +215,6 @@ r2 router generated >sender@???,sender@???,sender@???,sender@???
routed by r2 router
envelope to: userx@???
transport: <none>
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: >sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???, ...
@@ -236,9 +230,6 @@ After routing:
search_tidyup called
>>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>

--------> >sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???, ... <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
search_tidyup called
changed uid/gid: local delivery to >sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???, ... <>sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???,sender@???, ...> transport=t1
@@ -2762,9 +2753,6 @@ Delivery address list:
sender@???
sender@???
sender@???
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: sender@???
@@ -19191,9 +19179,6 @@ sender@??? is a duplicate address: discarded
sender@??? is a duplicate address: discarded
>>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>

--------> sender@??? <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
search_tidyup called
changed uid/gid: local delivery to sender <sender@???> transport=t2
diff --git a/test/stderr/0408 b/test/stderr/0408
index 6666f2209..38c348765 100644
--- a/test/stderr/0408
+++ b/test/stderr/0408
@@ -72,9 +72,6 @@ body_linecount=0 message_linecount=8
DSN: set orcpt: flags: 0x0
Delivery address list:
userx@???
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: userx@???
@@ -140,9 +137,6 @@ After routing:
search_tidyup called
>>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>

--------> userx@??? <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
search_tidyup called
changed uid/gid: local delivery to userx <userx@???> transport=t1
diff --git a/test/stderr/0432 b/test/stderr/0432
index 162877b7a..b6629f893 100644
--- a/test/stderr/0432
+++ b/test/stderr/0432
@@ -93,16 +93,17 @@ get[host|ipnode]byname[2] looked up these IP addresses:
name=127.0.0.1 address=127.0.0.1
Attempting full verification using callout
EXIM_DBOPEN: file <TESTSUITE/spool/db/callout> dir <TESTSUITE/spool/db> flags=O_RDWR
+ hintsdb_open(flags 0x2 mode 0640) No such file or directory
+ returned from EXIM_DBOPEN: (nil)
+ TESTSUITE/spool/db/callout appears not to exist: trying to create
+ EXIM_DBOPEN: file <TESTSUITE/spool/db/callout> dir <TESTSUITE/spool/db> flags=O_RDWR|O_CREAT
returned from EXIM_DBOPEN: 0xAAAAAAAA
- opened hints database TESTSUITE/spool/db/callout: flags=O_RDWR|O_CREAT
- dbfn_read: key=y
- dbfn_read: null return
- callout cache: no domain record found for y
- dbfn_read: key=x@y
- dbfn_read: null return
- callout cache: no address record found for x@y
- EXIM_DBCLOSE(0xAAAAAAAA)
- closed hints database
+dbfn_read: key=y
+dbfn_read: null return
+callout cache: no domain record found for y
+dbfn_read: key=x@y
+dbfn_read: null return
+callout cache: no address record found for x@y
interface=NULL port=PORT_S
Connecting to 127.0.0.1 [127.0.0.1]:PORT_S ...
127.0.0.1 in hosts_try_fastopen?
@@ -130,14 +131,11 @@ cmd buf flush ddd bytes
cmdlog: '220:EHLO:250:MAIL:250:RCPT:250:QUIT:220'
EXIM_DBOPEN: file <TESTSUITE/spool/db/callout> dir <TESTSUITE/spool/db> flags=O_RDWR
returned from EXIM_DBOPEN: 0xAAAAAAAA
- opened hints database TESTSUITE/spool/db/callout: flags=O_RDWR|O_CREAT
- dbfn_write: key=y datalen nn
- wrote callout cache domain record for y:
- result=1 postmaster=0 random=0
- dbfn_write: key=x@y datalen nn
- wrote positive callout cache address record for x@y
- EXIM_DBCLOSE(0xAAAAAAAA)
- closed hints database
+dbfn_write: key=y datalen nn
+wrote callout cache domain record for y:
+ result=1 postmaster=0 random=0
+dbfn_write: key=x@y datalen nn
+wrote positive callout cache address record for x@y
----------- end verify ------------
sender x@y verified ok
accept: condition test succeeded in ACL "mail"
@@ -216,16 +214,13 @@ get[host|ipnode]byname[2] looked up these IP addresses:
Attempting full verification using callout
EXIM_DBOPEN: file <TESTSUITE/spool/db/callout> dir <TESTSUITE/spool/db> flags=O_RDWR
returned from EXIM_DBOPEN: 0xAAAAAAAA
- opened hints database TESTSUITE/spool/db/callout: flags=O_RDWR|O_CREAT
- dbfn_read: key=y
- dbfn_read: size nnn return
- callout cache: found domain record for y
- dbfn_read: key=x@y
- dbfn_read: size nnn return
- callout cache: found address record for x@y
- callout cache: address record is positive
- EXIM_DBCLOSE(0xAAAAAAAA)
- closed hints database
+dbfn_read: key=y
+dbfn_read: size nnn return
+callout cache: found domain record for y
+dbfn_read: key=x@y
+dbfn_read: size nnn return
+callout cache: found address record for x@y
+callout cache: address record is positive
----------- end verify ------------
sender x@y verified ok
accept: condition test succeeded in ACL "mail"
diff --git a/test/stderr/0450 b/test/stderr/0450
index 75b2e88ff..dec7fe0ac 100644
--- a/test/stderr/0450
+++ b/test/stderr/0450
@@ -49,8 +49,8 @@ t1 transport entered
using the transport's hosts: 127.0.0.1
getting address for 127.0.0.1
checking retry status of 127.0.0.1
-no host retry record
-no message retry record
+ no host retry record
+ no message retry record
127.0.0.1 [127.0.0.1]:1112 retry-status = usable
delivering 10HmaX-000000005vi-0000 to 127.0.0.1 [127.0.0.1] (userx@???)
Connecting to 127.0.0.1 [127.0.0.1]:PORT_D2 ...
diff --git a/test/stderr/0476 b/test/stderr/0476
index e83c006cb..3a321a727 100644
--- a/test/stderr/0476
+++ b/test/stderr/0476
@@ -125,7 +125,7 @@ t1 transport entered
hostlist:
'127.0.0.1' IP 127.0.0.1 port -1
checking retry status of 127.0.0.1
-no message retry record
+ no message retry record
127.0.0.1 [127.0.0.1]:1111 retry-status = unusable
all IP addresses skipped or deferred at least one address
updating wait-t1 database
diff --git a/test/stderr/0487 b/test/stderr/0487
index 3b692c2c5..868741f5b 100644
--- a/test/stderr/0487
+++ b/test/stderr/0487
@@ -106,9 +106,6 @@ body_linecount=1 message_linecount=9
DSN: set orcpt: flags: 0x0
Delivery address list:
userx@???
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: userx@???
@@ -142,9 +139,6 @@ After routing:
search_tidyup called
>>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>

--------> userx@??? <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
search_tidyup called
changed uid/gid: local delivery to userx <userx@???> transport=t1
diff --git a/test/stderr/0512 b/test/stderr/0512
index e7fe36f86..1d4a61b53 100644
--- a/test/stderr/0512
+++ b/test/stderr/0512
@@ -14,7 +14,7 @@ t1 transport entered
using the transport's hosts: 127.0.0.1 : 127.0.0.1 : 127.0.0.1 : 127.0.0.1
getting address for 127.0.0.1
checking retry status of 127.0.0.1
-no message retry record
+ no message retry record
127.0.0.1 [127.0.0.1]:1111 retry-status = usable
delivering 10HmaX-000000005vi-0000 to 127.0.0.1 [127.0.0.1] (userx@???)
hosts_max_try limit reached with this host
@@ -28,7 +28,7 @@ temporary delivery error(s) override hosts_max_try (message older than host's re
Clearing TFO as not first host for message
getting address for 127.0.0.1
checking retry status of 127.0.0.1
-no message retry record
+ no message retry record
127.0.0.1 [127.0.0.1]:1111 retry-status = usable
delivering 10HmaX-000000005vi-0000 to 127.0.0.1 [127.0.0.1] (userx@???)
hosts_max_try limit reached with this host
@@ -42,7 +42,7 @@ temporary delivery error(s) override hosts_max_try (message older than host's re
Clearing TFO as not first host for message
getting address for 127.0.0.1
checking retry status of 127.0.0.1
-no message retry record
+ no message retry record
127.0.0.1 [127.0.0.1]:1111 retry-status = usable
delivering 10HmaX-000000005vi-0000 to 127.0.0.1 [127.0.0.1] (userx@???)
hosts_max_try limit reached with this host
@@ -56,7 +56,7 @@ temporary delivery error(s) override hosts_max_try (message older than host's re
Clearing TFO as not first host for message
getting address for 127.0.0.1
checking retry status of 127.0.0.1
-no message retry record
+ no message retry record
127.0.0.1 [127.0.0.1]:1111 retry-status = usable
delivering 10HmaX-000000005vi-0000 to 127.0.0.1 [127.0.0.1] (userx@???)
hosts_max_try limit reached with this host
@@ -119,7 +119,7 @@ t1 transport entered
using the transport's hosts: 127.0.0.1 : 127.0.0.1 : 127.0.0.1 : 127.0.0.1
getting address for 127.0.0.1
checking retry status of 127.0.0.1
-no message retry record
+ no message retry record
127.0.0.1 [127.0.0.1]:1111 retry-status = usable
delivering 10HmaZ-000000005vi-0000 to 127.0.0.1 [127.0.0.1] (userx@???)
hosts_max_try limit reached with this host
@@ -133,7 +133,7 @@ temporary delivery error(s) override hosts_max_try (message older than host's re
Clearing TFO as not first host for message
getting address for 127.0.0.1
checking retry status of 127.0.0.1
-no message retry record
+ no message retry record
127.0.0.1 [127.0.0.1]:1111 retry-status = usable
delivering 10HmaZ-000000005vi-0000 to 127.0.0.1 [127.0.0.1] (userx@???)
hosts_max_try limit reached with this host
diff --git a/test/stderr/0554 b/test/stderr/0554
index f9bbb7dd0..d8ee0171a 100644
--- a/test/stderr/0554
+++ b/test/stderr/0554
@@ -17,7 +17,7 @@ After routing:
Failed addresses:
Deferred addresses:
checking retry status of 127.0.0.1
-no retry data available
+ no retry data available
added retry item for R:x@y:<CALLER@???>: errno=-44 more_errno=dd,A flags=0
cmdlog: '220:EHLO:250:MAIL:250:RCPT:451:QUIT+:250'
>>>>>>>>>>>>>>>> Exim pid=p1235 (transport) terminating with rc=0 >>>>>>>>>>>>>>>>

@@ -58,8 +58,8 @@ After routing:
   Failed addresses:
   Deferred addresses:
 checking retry status of 127.0.0.1
-no host retry record
-no message retry record
+ no host retry record
+ no message retry record
 added retry item for R:x@y:<CALLER@???>: errno=dd more_errno=dd,A flags=1
 added retry item for R:x@y: errno=dd more_errno=dd,A flags=1
 cmdlog: '220:EHLO:250:MAIL:250:RCPT:250:DATA:354:.:250:QUIT+:250'
diff --git a/test/stderr/0623 b/test/stderr/0623
index 3786f7168..659aa892c 100644
--- a/test/stderr/0623
+++ b/test/stderr/0623
@@ -85,8 +85,8 @@ send_to_server transport entered
 using the transport's hosts: 127.0.0.1
 getting address for 127.0.0.1
 checking retry status of 127.0.0.1
-no host retry record
-no message retry record
+ no host retry record
+ no message retry record
 127.0.0.1 [127.0.0.1]:1111 retry-status = usable
 delivering 10HmbA-000000005vi-0000 to 127.0.0.1 [127.0.0.1] (permreject@???)
 Connecting to 127.0.0.1 [127.0.0.1]:PORT_D ...
@@ -155,8 +155,8 @@ send_to_server transport entered
 using the transport's hosts: 127.0.0.1
 getting address for 127.0.0.1
 checking retry status of 127.0.0.1
-no host retry record
-no message retry record
+ no host retry record
+ no message retry record
 127.0.0.1 [127.0.0.1]:1111 retry-status = usable
 delivering 10HmbB-000000005vi-0000 to 127.0.0.1 [127.0.0.1] (permreject@???)
 Connecting to 127.0.0.1 [127.0.0.1]:PORT_D ...
diff --git a/test/stderr/0628 b/test/stderr/0628
index 219218484..225347acc 100644
--- a/test/stderr/0628
+++ b/test/stderr/0628
@@ -53,9 +53,6 @@
 01:01:01 p1239  DSN: set orcpt:   flags: 0x0
 01:01:01 p1239  Delivery address list:
 01:01:01 p1239    dest@??? 
-01:01:01 p1239   EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
-01:01:01 p1239   returned from EXIM_DBOPEN: (nil)
-01:01:01 p1239   failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
 01:01:01 p1239  no retry data available
 01:01:01 p1239  >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 01:01:01 p1239  Considering: dest@???
@@ -158,9 +155,6 @@
 01:01:01 p1241  DSN: set orcpt:   flags: 0x0
 01:01:01 p1241  Delivery address list:
 01:01:01 p1241    dest2@??? 
-01:01:01 p1241   EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
-01:01:01 p1241   returned from EXIM_DBOPEN: (nil)
-01:01:01 p1241   failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
 01:01:01 p1241  no retry data available
 01:01:01 p1241  >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 01:01:01 p1241  Considering: dest2@???
diff --git a/test/stderr/0909 b/test/stderr/0909
index 3ba13b3b0..ffaf71b8c 100644
--- a/test/stderr/0909
+++ b/test/stderr/0909
@@ -232,8 +232,8 @@ send_to_server transport entered
 using the transport's hosts: 127.0.0.1
 getting address for 127.0.0.1
 checking retry status of 127.0.0.1
-no host retry record
-no message retry record
+ no host retry record
+ no message retry record
 127.0.0.1 [127.0.0.1]:1111 retry-status = usable
 delivering 10HmbA-000000005vi-0000 to 127.0.0.1 [127.0.0.1] (permreject@???)
 Connecting to 127.0.0.1 [127.0.0.1]:PORT_D ...
@@ -306,8 +306,8 @@ send_to_server transport entered
 using the transport's hosts: 127.0.0.1
 getting address for 127.0.0.1
 checking retry status of 127.0.0.1
-no host retry record
-no message retry record
+ no host retry record
+ no message retry record
 127.0.0.1 [127.0.0.1]:1111 retry-status = usable
 delivering 10HmbB-000000005vi-0000 to 127.0.0.1 [127.0.0.1] (dataloss@???)
 Connecting to 127.0.0.1 [127.0.0.1]:PORT_D ...
diff --git a/test/stderr/2600 b/test/stderr/2600
index b7b55388c..dee2ef5c7 100644
--- a/test/stderr/2600
+++ b/test/stderr/2600
@@ -586,9 +586,6 @@ body_linecount=1 message_linecount=8
 DSN: set orcpt:   flags: 0x0
 Delivery address list:
   userx@??? 
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
 no retry data available

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: userx@???
@@ -633,9 +630,6 @@ After routing:
search_tidyup called
>>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>

--------> userx@??? <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
search_tidyup called
changed uid/gid: local delivery to userx <userx@???> transport=t1
diff --git a/test/stderr/2610 b/test/stderr/2610
index a4a46c552..93c24bf58 100644
--- a/test/stderr/2610
+++ b/test/stderr/2610
@@ -808,9 +808,6 @@ body_linecount=1 message_linecount=8
DSN: set orcpt: flags: 0x0
Delivery address list:
ph10@???
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: ph10@???
@@ -876,9 +873,6 @@ search_tidyup called
close MYSQL connection: 127.0.0.1:PORT_N/test/root
>>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>

--------> ph10@??? <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
search_tidyup called
changed uid/gid: local delivery to ph10 <ph10@???> transport=t1
diff --git a/test/stderr/2620 b/test/stderr/2620
index 40fc08f8a..7193592a9 100644
--- a/test/stderr/2620
+++ b/test/stderr/2620
@@ -727,9 +727,6 @@ body_linecount=1 message_linecount=8
DSN: set orcpt: flags: 0x0
Delivery address list:
CALLER@???
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: CALLER@???
@@ -776,9 +773,6 @@ search_tidyup called
close PGSQL connection: localhost:PORT_N/test/CALLER
>>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>

--------> CALLER@??? <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
search_tidyup called
changed uid/gid: local delivery to CALLER <CALLER@???> transport=t1
diff --git a/test/stderr/4052 b/test/stderr/4052
index f90265149..31baec047 100644
--- a/test/stderr/4052
+++ b/test/stderr/4052
@@ -17,8 +17,8 @@ smtp transport entered
hostlist:
'127.0.0.1' IP 127.0.0.1 port PORT_D
checking retry status of 127.0.0.1
-no host retry record
-no message retry record
+ no host retry record
+ no message retry record
127.0.0.1 [127.0.0.1]:1111 retry-status = usable
delivering 10HmbP-000000005vi-0000 to 127.0.0.1 [127.0.0.1] (extchange@???)
Transport port=25 replaced by host-specific port=PORT_D
diff --git a/test/stderr/5004 b/test/stderr/5004
index bf1ed20db..fdeed81ac 100644
--- a/test/stderr/5004
+++ b/test/stderr/5004
@@ -76,9 +76,6 @@ body_linecount=1 message_linecount=8
DSN: set orcpt: flags: 0x0
Delivery address list:
userx@???
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: userx@???
@@ -113,9 +110,6 @@ r1 router generated TESTSUITE/test-mail
routed by r1 router
envelope to: userx@???
transport: <none>
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: TESTSUITE/test-mail
@@ -131,9 +125,6 @@ After routing:
search_tidyup called
>>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>

--------> TESTSUITE/test-mail <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
search_tidyup called
changed uid/gid: local delivery to TESTSUITE/test-mail <TESTSUITE/test-mail> transport=t1
diff --git a/test/stderr/5005 b/test/stderr/5005
index 2e1c3b08d..9f3d8c172 100644
--- a/test/stderr/5005
+++ b/test/stderr/5005
@@ -72,9 +72,6 @@ body_linecount=1 message_linecount=8
DSN: set orcpt: flags: 0x0
Delivery address list:
nofile@???
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: nofile@???
@@ -108,9 +105,6 @@ After routing:
search_tidyup called
>>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>

--------> nofile@??? <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
search_tidyup called
changed uid/gid: local delivery to nofile <nofile@???> transport=t1
@@ -262,9 +256,6 @@ body_linecount=1 message_linecount=8
DSN: set orcpt: flags: 0x0
Delivery address list:
userx@???
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: userx@???
@@ -298,9 +289,6 @@ After routing:
search_tidyup called
>>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>

--------> userx@??? <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
search_tidyup called
changed uid/gid: local delivery to userx <userx@???> transport=t1
@@ -454,9 +442,6 @@ body_linecount=1 message_linecount=8
DSN: set orcpt: flags: 0x0
Delivery address list:
userx@???
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: userx@???
@@ -490,9 +475,6 @@ After routing:
search_tidyup called
>>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>

 --------> userx@??? <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
 no retry data available
 search_tidyup called
 changed uid/gid: local delivery to userx <userx@???> transport=t1
@@ -550,26 +532,21 @@ Processing retry items
  Failed addresses:
  Deferred addresses:
   userx@???
-   ╎EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDWR
-   ╎returned from EXIM_DBOPEN: 0xAAAAAAAA
-   ╎opened hints database TESTSUITE/spool/db/retry: flags=O_RDWR|O_CREAT
-   ╎userx@??? in "*"?
+   userx@??? in "*"?
+   ╎list element: *
+   ╎address match test: subject=userx@??? pattern=*
+   ╎test.ex in "*"?
    ╎ list element: *
-   ╎ address match test: subject=userx@??? pattern=*
-   ╎ test.ex in "*"?
-   ╎  list element: *
-   ╎  test.ex in "*"? yes (matched "*")
-   ╎ userx@??? in "*"? yes (matched "*")
-   ╎retry for T:userx@??? = * 0 0
-   ╎dbfn_read: key=T:userx@???
-   ╎dbfn_read: null return
-   ╎failing_interval=ttt message_age=ttt
-   ╎Writing retry data for T:userx@???
-   ╎  first failed=dddd last try=dddd next try=+86400 expired=0
-   ╎  errno=-22 more_errno=dd mailbox is full (MTA-imposed quota exceeded while writing to tmp/MAILDIR.myhost.test.ex)
-   ╎dbfn_write: key=T:userx@??? datalen nn
-  EXIM_DBCLOSE(0xAAAAAAAA)
-  closed hints database
+   ╎ test.ex in "*"? yes (matched "*")
+   ╎userx@??? in "*"? yes (matched "*")
+   retry for T:userx@??? = * 0 0
+   dbfn_read: key=T:userx@???
+   dbfn_read: null return
+   failing_interval=ttt message_age=ttt
+   Writing retry data for T:userx@???
+     first failed=dddd last try=dddd next try=+86400 expired=0
+     errno=-22 more_errno=dd mailbox is full (MTA-imposed quota exceeded while writing to tmp/MAILDIR.myhost.test.ex)
+   dbfn_write: key=T:userx@??? datalen nn
 end of retry processing
 delivery deferred: update_spool=1 header_rewritten=0
 Writing spool header file: TESTSUITE/spool//input//hdr.10HmaZ-000000005vi-0000
@@ -655,24 +632,19 @@ body_linecount=1 message_linecount=8
 DSN: set orcpt:   flags: 0x0
 Delivery address list:
   userx@??? 
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: 0xAAAAAAAA
- opened hints database TESTSUITE/spool/db/retry: flags=O_RDONLY

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: userx@???
unique = userx@???
- checking router retry status
- dbfn_read: key=R:test.ex
- dbfn_read: null return
- dbfn_read: key=R:userx@???
- dbfn_read: null return
- dbfn_read: key=R:userx@???:<CALLER@???>
- dbfn_read: null return
- no domain retry record
- no address retry record
+checking router retry status
+ dbfn_read: key=R:test.ex
+ dbfn_read: null return
+ dbfn_read: key=R:userx@???
+ dbfn_read: null return
+ dbfn_read: key=R:userx@???:<CALLER@???>
+ dbfn_read: null return
+ no domain retry record
+ no address retry record
userx@???: queued for routing
- EXIM_DBCLOSE(0xAAAAAAAA)
- closed hints database
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

routing userx@???
--------> r1 router <--------
@@ -698,15 +670,10 @@ After routing:
search_tidyup called
>>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>

 --------> userx@??? <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: 0xAAAAAAAA
- opened hints database TESTSUITE/spool/db/retry: flags=O_RDONLY
- dbfn_read: key=T:userx@???
- dbfn_read: size nnn return
+dbfn_read: key=T:userx@???
+dbfn_read: size nnn return
 retry record exists: age=ttt (max 1w)
   time to retry = tttt expired = 0
- EXIM_DBCLOSE(0xAAAAAAAA)
- closed hints database
 search_tidyup called
 changed uid/gid: local delivery to userx <userx@???> transport=t1
   uid=CALLER_UID gid=CALLER_GID pid=p1245
@@ -750,26 +717,21 @@ Processing retry items
  Failed addresses:
  Deferred addresses:
   userx@???
-   ╎EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDWR
-   ╎returned from EXIM_DBOPEN: 0xAAAAAAAA
-   ╎opened hints database TESTSUITE/spool/db/retry: flags=O_RDWR|O_CREAT
-   ╎userx@??? in "*"?
+   userx@??? in "*"?
+   ╎list element: *
+   ╎address match test: subject=userx@??? pattern=*
+   ╎test.ex in "*"?
    ╎ list element: *
-   ╎ address match test: subject=userx@??? pattern=*
-   ╎ test.ex in "*"?
-   ╎  list element: *
-   ╎  test.ex in "*"? yes (matched "*")
-   ╎ userx@??? in "*"? yes (matched "*")
-   ╎retry for T:userx@??? = * 0 0
-   ╎dbfn_read: key=T:userx@???
-   ╎dbfn_read: size nnn return
-   ╎failing_interval=ttt message_age=ttt
-   ╎Writing retry data for T:userx@???
-   ╎  first failed=dddd last try=dddd next try=+86400 expired=0
-   ╎  errno=-22 more_errno=dd mailbox is full (MTA-imposed quota exceeded while writing to tmp/MAILDIR.myhost.test.ex)
-   ╎dbfn_write: key=T:userx@??? datalen nn
-  EXIM_DBCLOSE(0xAAAAAAAA)
-  closed hints database
+   ╎ test.ex in "*"? yes (matched "*")
+   ╎userx@??? in "*"? yes (matched "*")
+   retry for T:userx@??? = * 0 0
+   dbfn_read: key=T:userx@???
+   dbfn_read: size nnn return
+   failing_interval=ttt message_age=ttt
+   Writing retry data for T:userx@???
+     first failed=dddd last try=dddd next try=+86400 expired=0
+     errno=-22 more_errno=dd mailbox is full (MTA-imposed quota exceeded while writing to tmp/MAILDIR.myhost.test.ex)
+   dbfn_write: key=T:userx@??? datalen nn
 end of retry processing
 delivery deferred: update_spool=1 header_rewritten=0
 Writing spool header file: TESTSUITE/spool//input//hdr.10HmbA-000000005vi-0000
diff --git a/test/stderr/5006 b/test/stderr/5006
index f13b5b879..d9e4556ef 100644
--- a/test/stderr/5006
+++ b/test/stderr/5006
@@ -72,9 +72,6 @@ body_linecount=1 message_linecount=8
 DSN: set orcpt:   flags: 0x0
 Delivery address list:
   userx@??? 
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
 no retry data available

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Considering: userx@???
@@ -108,9 +105,6 @@ After routing:
search_tidyup called
>>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>

--------> userx@??? <--------
- EXIM_DBOPEN: file <TESTSUITE/spool/db/retry> dir <TESTSUITE/spool/db> flags=O_RDONLY
- returned from EXIM_DBOPEN: (nil)
- failed to open DB file TESTSUITE/spool/db/retry: No such file or directory
no retry data available
search_tidyup called
changed uid/gid: local delivery to userx <userx@???> transport=t1

--
## subscription configuration (requires account):
## https://lists.exim.org/mailman3/postorius/lists/exim-cvs.lists.exim.org/
## unsubscribe (doesn't require an account):
## exim-cvs-unsubscribe@???
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/