Merge branch 'master' into paz-927
authorAdam Dickmeiss <adam@indexdata.dk>
Fri, 5 Sep 2014 11:52:51 +0000 (13:52 +0200)
committerAdam Dickmeiss <adam@indexdata.dk>
Fri, 5 Sep 2014 11:52:51 +0000 (13:52 +0200)
1  2 
src/client.c
src/client.h
src/connection.c
src/pazpar2_config.c
src/pazpar2_config.h
src/session.c
win/makefile

diff --combined src/client.c
@@@ -71,6 -71,8 +71,8 @@@ Foundation, Inc., 51 Franklin St, Fift
  #include "relevance.h"
  #include "incref.h"
  
+ #define XDOC_CACHE_SIZE 100
  static YAZ_MUTEX g_mutex = 0;
  static int no_clients = 0;
  
@@@ -104,7 -106,9 +106,9 @@@ struct client 
      Odr_int hits;
      int record_offset;
      int show_stat_no;
-     int filtered; // When using local:, this will count the number of filtered records.
+     int filtered; /* number of records ignored for local filtering */
+     int ingest_failures; /* number of records where XSLT/other failed */
+     int record_failures; /* number of records where ZOOM reported error */
      int maxrecs;
      int startrecs;
      int diagnostic;
      int same_search;
      char *sort_strategy;
      char *sort_criteria;
+     xmlDoc **xdoc;
  };
  
  struct suggestions {
@@@ -192,6 -197,50 +197,50 @@@ void client_set_state(struct client *cl
      }
  }
  
+ static void client_init_xdoc(struct client *cl)
+ {
+     int i;
+     cl->xdoc = xmalloc(sizeof(*cl->xdoc) * XDOC_CACHE_SIZE);
+     for (i = 0; i < XDOC_CACHE_SIZE; i++)
+         cl->xdoc[i] = 0;
+ }
+ static void client_destroy_xdoc(struct client *cl)
+ {
+     int i;
+     assert(cl->xdoc);
+     for (i = 0; i < XDOC_CACHE_SIZE; i++)
+         if (cl->xdoc[i])
+             xmlFreeDoc(cl->xdoc[i]);
+     xfree(cl->xdoc);
+ }
+ xmlDoc *client_get_xdoc(struct client *cl, int record_no)
+ {
+     assert(cl->xdoc);
+     if (record_no >= 0 && record_no < XDOC_CACHE_SIZE)
+         return cl->xdoc[record_no];
+     return 0;
+ }
+ void client_store_xdoc(struct client *cl, int record_no, xmlDoc *xdoc)
+ {
+     assert(cl->xdoc);
+     if (record_no >= 0 && record_no < XDOC_CACHE_SIZE)
+     {
+         if (cl->xdoc[record_no])
+             xmlFreeDoc(cl->xdoc[record_no]);
+         cl->xdoc[record_no] = xdoc;
+     }
+     else
+     {
+         xmlFreeDoc(xdoc);
+     }
+ }
  static void client_show_raw_error(struct client *cl, const char *addinfo);
  
  struct connection *client_get_connection(struct client *cl)
@@@ -523,23 -572,24 +572,24 @@@ void client_search_response(struct clie
      struct connection *co = cl->connection;
      ZOOM_connection link = connection_get_link(co);
      ZOOM_resultset resultset = cl->resultset;
+     struct session *se = client_get_session(cl);
  
      const char *error, *addinfo = 0;
  
      if (ZOOM_connection_error(link, &error, &addinfo))
      {
          cl->hits = 0;
+         session_log(se, YLOG_WARN, "%s: Error %s (%s)",
+                     client_get_id(cl), error, addinfo);
          client_set_state(cl, Client_Error);
-         yaz_log(YLOG_WARN, "Search error %s (%s): %s",
-                 error, addinfo, client_get_id(cl));
      }
      else
      {
          client_report_facets(cl, resultset);
          cl->record_offset = cl->startrecs;
          cl->hits = ZOOM_resultset_size(resultset);
-         yaz_log(YLOG_DEBUG, "client_search_response: hits " ODR_INT_PRINTF,
-                 cl->hits);
+         session_log(se, YLOG_LOG, "%s: hits: " ODR_INT_PRINTF,
+                     client_get_id(cl), cl->hits);
          if (cl->suggestions)
              client_suggestions_destroy(cl);
          cl->suggestions =
@@@ -571,16 -621,38 +621,38 @@@ static void client_record_ingest(struc
      ZOOM_record rec = 0;
      ZOOM_resultset resultset = cl->resultset;
      struct session *se = client_get_session(cl);
+     xmlDoc *xdoc;
+     int offset = cl->record_offset + 1; /* 0 versus 1 numbered offsets */
  
-     if ((rec = ZOOM_resultset_record_immediate(resultset, cl->record_offset)))
+     xdoc = client_get_xdoc(cl, offset);
+     if (xdoc)
+     {
+         if (cl->session)
+         {
+             NMEM nmem = nmem_create();
+             int rc = ingest_xml_record(cl, xdoc, offset, nmem, 1);
+             if (rc == -1)
+             {
+                 session_log(se, YLOG_WARN,
+                             "%s: #%d: failed to ingest xdoc",
+                             client_get_id(cl), offset);
+                 cl->ingest_failures++;
+             }
+             else if (rc == -2)
+                 cl->filtered++;
+             nmem_destroy(nmem);
+         }
+     }
+     else if ((rec = ZOOM_resultset_record_immediate(resultset,
+                                                     cl->record_offset)))
      {
-         int offset = ++cl->record_offset;
          if (cl->session == 0)
              ;  /* no operation */
          else if (ZOOM_record_error(rec, &msg, &addinfo, 0))
          {
              session_log(se, YLOG_WARN, "Record error %s (%s): %s #%d",
                          msg, addinfo, client_get_id(cl), offset);
+             cl->record_failures++;
          }
          else
          {
              if (!xmlrec)
              {
                  const char *rec_syn =  ZOOM_record_get(rec, "syntax", NULL);
-                 session_log(se, YLOG_WARN, "ZOOM_record_get failed from %s #%d",
+                 session_log(se, YLOG_WARN, "%s: #%d: ZOOM_record_get failed",
                              client_get_id(cl), offset);
                  session_log(se, YLOG_LOG, "pz:nativesyntax=%s . "
                              "ZOOM record type=%s . Actual record syntax=%s",
                              s ? s : "null", type,
                              rec_syn ? rec_syn : "null");
+                 cl->ingest_failures++;
              }
              else
              {
                  /* OK = 0, -1 = failure, -2 = Filtered */
-                 int rc = ingest_record(cl, xmlrec, cl->record_offset, nmem);
+                 int rc = ingest_record(cl, xmlrec, offset, nmem);
                  if (rc == -1)
                  {
                      const char *rec_syn =  ZOOM_record_get(rec, "syntax", NULL);
                      session_log(se, YLOG_WARN,
-                                 "Failed to ingest record from %s #%d",
+                                 "%s: #%d: failed to ingest record",
                                  client_get_id(cl), offset);
                      session_log(se, YLOG_LOG, "pz:nativesyntax=%s . "
                                  "ZOOM record type=%s . Actual record syntax=%s",
                                  s ? s : "null", type,
                                  rec_syn ? rec_syn : "null");
+                     cl->ingest_failures++;
                  }
-                 if (rc == -2)
-                     cl->filtered += 1;
+                 else if (rc == -2)
+                     cl->filtered++;
              }
              nmem_destroy(nmem);
          }
      else
      {
          session_log(se, YLOG_WARN, "Got NULL record from %s #%d",
-                     client_get_id(cl), cl->record_offset);
+                     client_get_id(cl), offset);
      }
+     cl->record_offset++;
  }
  
  void client_record_response(struct client *cl, int *got_records)
  
      if (ZOOM_connection_error(link, &error, &addinfo))
      {
+         struct session *se = client_get_session(cl);
+         session_log(se, YLOG_WARN, "%s: Error %s (%s)",
+                     client_get_id(cl), error, addinfo);
          client_set_state(cl, Client_Error);
-         yaz_log(YLOG_WARN, "Search error %s (%s): %s",
-             error, addinfo, client_get_id(cl));
      }
      else
      {
@@@ -673,7 -749,7 +749,7 @@@ int client_reingest(struct client *cl
  {
      int i = cl->startrecs;
      int to = cl->record_offset;
-     cl->filtered = 0;
+     cl->record_failures = cl->ingest_failures = cl->filtered = 0;
  
      cl->record_offset = i;
      for (; i < to; i++)
@@@ -867,13 -943,13 +943,13 @@@ int client_start_search(struct client *
      /* Nothing has changed and we already have a result */
      if (cl->same_search == 1 && rc_prep_connection == 2)
      {
-         session_log(se, YLOG_LOG, "client %s resuse result", client_get_id(cl));
+         session_log(se, YLOG_LOG, "%s: reuse result", client_get_id(cl));
          client_report_facets(cl, cl->resultset);
          return client_reingest(cl);
      }
      else if (!rc_prep_connection)
      {
-         session_log(se, YLOG_LOG, "client %s postponing search: No connection",
+         session_log(se, YLOG_LOG, "%s: postponing search: No connection",
                      client_get_id(cl));
          client_set_state_nb(cl, Client_Working);
          return -1;
      link = connection_get_link(co);
      assert(link);
  
-     session_log(se, YLOG_LOG, "client %s new search", client_get_id(cl));
+     session_log(se, YLOG_LOG, "%s: new search", client_get_id(cl));
  
      cl->diagnostic = 0;
-     cl->filtered = 0;
+     cl->record_failures = cl->ingest_failures = cl->filtered = 0;
+     client_destroy_xdoc(cl);
+     client_init_xdoc(cl);
  
      if (extra_args && *extra_args)
          ZOOM_connection_option_set(link, "extraArgs", extra_args);
      query = ZOOM_query_create();
      if (cl->cqlquery)
      {
-         yaz_log(YLOG_LOG, "Client %s: Search CQL: %s", client_get_id(cl),
-                 cl->cqlquery);
+         session_log(se, YLOG_LOG, "%s: Search CQL: %s", client_get_id(cl),
+                     cl->cqlquery);
          ZOOM_query_cql(query, cl->cqlquery);
          if (*opt_sort)
              ZOOM_query_sortby(query, opt_sort);
      }
      else
      {
-         yaz_log(YLOG_LOG, "Client %s: Search PQF: %s", client_get_id(cl),
-                 cl->pquery);
+         session_log(se, YLOG_LOG, "%s: Search PQF: %s", client_get_id(cl),
+                     cl->pquery);
          ZOOM_query_prefix(query, cl->pquery);
      }
      if (cl->sort_strategy && cl->sort_criteria) {
@@@ -1001,6 -1079,7 +1079,7 @@@ struct client *client_create(const cha
      cl->sort_criteria = 0;
      assert(id);
      cl->id = xstrdup(id);
+     client_init_xdoc(cl);
      client_use(1);
  
      yaz_log(YLOG_DEBUG, "client_create c=%p %s", cl, id);
@@@ -1046,6 -1125,7 +1125,7 @@@ int client_destroy(struct client *c
              assert(!c->connection);
              facet_limits_destroy(c->facet_limits);
  
+             client_destroy_xdoc(c);
              if (c->resultset)
              {
                  ZOOM_resultset_destroy(c->resultset);
@@@ -1083,12 -1163,6 +1163,12 @@@ void client_disconnect(struct client *c
      client_set_connection(cl, 0);
  }
  
 +void client_mark_dead(struct client *cl)
 +{
 +    if (cl->connection)
 +        connection_mark_dead(cl->connection);
 +}
 +
  // Initialize CCL map for a target
  static CCL_bibset prepare_cclmap(struct client *cl, CCL_bibset base_bibset)
  {
@@@ -1601,16 -1675,18 +1681,18 @@@ Odr_int client_get_approximation(struc
      return cl->hits;
  }
  
- int client_get_num_records(struct client *cl)
+ int client_get_num_records(struct client *cl, int *filtered, int *ingest,
+                            int *failed)
  {
+     if (filtered)
+         *filtered = cl->filtered;
+     if (ingest)
+         *ingest = cl->ingest_failures;
+     if (failed)
+         *failed = cl->record_failures;
      return cl->record_offset;
  }
  
- int client_get_num_records_filtered(struct client *cl)
- {
-     return cl->filtered;
- }
  void client_set_diagnostic(struct client *cl, int diagnostic,
                             const char *message, const char *addinfo)
  {
diff --combined src/client.h
@@@ -69,7 -69,6 +69,7 @@@ int client_destroy(struct client *c)
  
  void client_set_connection(struct client *cl, struct connection *con);
  void client_disconnect(struct client *cl);
 +void client_mark_dead(struct client *cl);
  int client_prep_connection(struct client *cl,
                             int operation_timeout, int session_timeout,
                             iochan_man_t iochan,
@@@ -88,8 -87,8 +88,8 @@@ int client_parse_query(struct client *c
                         facet_limits_t facet_limits, const char **error_msg);
  Odr_int client_get_hits(struct client *cl);
  Odr_int client_get_approximation(struct client *cl);
- int client_get_num_records(struct client *cl);
- int client_get_num_records_filtered(struct client *cl);
+ int client_get_num_records(struct client *cl, int *filtered, int *ingest,
+                            int *failed);
  int client_get_diagnostic(struct client *cl,
                            const char **message, const char **addinfo);
  void client_set_diagnostic(struct client *cl, int diagnostic,
@@@ -113,6 -112,8 +113,8 @@@ const char *client_get_facet_limit_loca
  
  void client_update_show_stat(struct client *cl, int cmd);
  
+ void client_store_xdoc(struct client *cl, int record_no, xmlDoc *xdoc);
  #endif
  
  /*
diff --combined src/connection.c
@@@ -43,6 -43,7 +43,6 @@@ Foundation, Inc., 51 Franklin St, Fift
  #include <yaz/tcpip.h>
  #include "connection.h"
  #include "session.h"
 -#include "host.h"
  #include "client.h"
  #include "settings.h"
  
@@@ -88,6 -89,7 +88,6 @@@ int connections_count(void
  struct connection {
      IOCHAN iochan;
      ZOOM_connection link;
 -    struct host *host;
      struct client *client;
      char *zproxy;
      char *url;
  
  static int connection_connect(struct connection *con, iochan_man_t iochan_man);
  
 -static int connection_is_idle(struct connection *co)
 -{
 -    ZOOM_connection link = co->link;
 -    int event;
 -
 -    if (co->state != Conn_Open || !link)
 -        return 0;
 -
 -    if (!ZOOM_connection_is_idle(link))
 -        return 0;
 -    event = ZOOM_connection_peek_event(link);
 -    if (event == ZOOM_EVENT_NONE)
 -        return 1;
 -    else
 -        return 0;
 -}
 -
  ZOOM_connection connection_get_link(struct connection *co)
  {
      return co->link;
  }
  
 -static void remove_connection_from_host(struct connection *con)
 -{
 -    struct connection **conp = &con->host->connections;
 -    assert(con);
 -    while (*conp)
 -    {
 -        if (*conp == con)
 -        {
 -            *conp = (*conp)->next;
 -            break;
 -        }
 -        conp = &(*conp)->next;
 -    }
 -    yaz_cond_broadcast(con->host->cond_ready);
 +void connection_mark_dead(struct connection *co)
 +{ 
 +    iochan_settimeout(co->iochan, 1);
  }
  
  // Close connection and recycle structure
@@@ -138,7 -168,7 +138,7 @@@ static void connection_destroy(struct c
  // client's database
  static struct connection *connection_create(struct client *cl,
                                              const char *url,
 -                                            struct host *host,
 +                                            const char *zproxy,
                                              int operation_timeout,
                                              int session_timeout,
                                              iochan_man_t iochan_man)
      struct connection *co;
  
      co = xmalloc(sizeof(*co));
 -    co->host = host;
  
      co->client = cl;
      co->url = xstrdup(url);
      co->zproxy = 0;
 +    if (zproxy)
 +        co->zproxy = xstrdup(zproxy);
 +
      client_set_connection(cl, co);
      co->link = 0;
      co->state = Conn_Closed;
      co->operation_timeout = operation_timeout;
      co->session_timeout = session_timeout;
  
 -    if (host->ipport)
 -        connection_connect(co, iochan_man);
 -
 -    yaz_mutex_enter(host->mutex);
 -    co->next = co->host->connections;
 -    co->host->connections = co;
 -    yaz_mutex_leave(host->mutex);
 +    connection_connect(co, iochan_man);
  
      connection_use(1);
      return co;
@@@ -194,8 -228,10 +194,10 @@@ static void non_block_events(struct con
                  int err;
                  if ((err = ZOOM_connection_error(link, &error, &addinfo)))
                  {
-                     yaz_log(YLOG_LOG, "Error %s from %s",
-                             error, client_get_id(cl));
+                     struct session *se = client_get_session(cl);
+                     session_log(se, YLOG_WARN, "%s: Error %s (%s)",
+                                 client_get_id(cl), error, addinfo);
                      client_set_diagnostic(cl, err, error, addinfo);
                      client_set_state(cl, Client_Error);
                  }
                      iochan_settimeout(iochan, co->session_timeout);
                      client_set_state(cl, Client_Idle);
                  }
 -                yaz_cond_broadcast(co->host->cond_ready);
              }
              break;
          case ZOOM_EVENT_SEND_DATA:
          case ZOOM_EVENT_RECV_APDU:
              break;
          case ZOOM_EVENT_CONNECT:
-             yaz_log(YLOG_LOG, "Connected to %s", client_get_id(cl));
              co->state = Conn_Open;
              break;
          case ZOOM_EVENT_RECV_SEARCH:
          case ZOOM_EVENT_RECV_RECORD:
              client_record_response(cl, &got_records);
              break;
 +        case ZOOM_EVENT_NONE:
 +            break;
          default:
              yaz_log(YLOG_LOG, "Unhandled event (%d) from %s",
                      ev, client_get_id(cl));
      }
  }
  
 +static void iochan_update(struct connection *co)
 +{
 +    if (co->link)
 +    {
 +        int m = ZOOM_connection_get_mask(co->link);
 +
 +        if (m == 0)
 +            m = ZOOM_SELECT_READ;
 +        iochan_setflags(co->iochan, m);
 +        iochan_setfd(co->iochan, ZOOM_connection_get_socket(co->link));
 +    }
 +}
 +
  void connection_continue(struct connection *co)
  {
      int r = ZOOM_connection_exec_task(co->link);
          client_unlock(cl);
      }
      else
 -    {
 -        iochan_setflags(co->iochan, ZOOM_connection_get_mask(co->link));
 -        iochan_setfd(co->iochan, ZOOM_connection_get_socket(co->link));
 -    }
 +        iochan_update(co);
  }
  
  static void connection_handler(IOCHAN iochan, int event)
  {
      struct connection *co = iochan_getdata(iochan);
      struct client *cl;
 -    struct host *host = co->host;
  
 -    yaz_mutex_enter(host->mutex);
      cl = co->client;
      if (!cl)
      {
             a closed connection from the target.. Or, perhaps, an unexpected
             package.. We will just close the connection */
          yaz_log(YLOG_LOG, "timeout connection %p event=%d", co, event);
 -        remove_connection_from_host(co);
 -        yaz_mutex_leave(host->mutex);
          connection_destroy(co);
      }
      else if (event & EVENT_TIMEOUT)
          non_block_events(co);
          client_unlock(cl);
  
 -        remove_connection_from_host(co);
 -        yaz_mutex_leave(host->mutex);
          connection_destroy(co);
      }
      else
      {
 -        yaz_mutex_leave(host->mutex);
 -
 +        if (ZOOM_connection_is_idle(co->link))
 +        {
 +            connection_destroy(co);
 +            return;
 +        }
          client_lock(cl);
          non_block_events(co);
  
          non_block_events(co);
          client_unlock(cl);
  
 -        if (co->link)
 -        {
 -            iochan_setflags(iochan, ZOOM_connection_get_mask(co->link));
 -            iochan_setfd(iochan, ZOOM_connection_get_socket(co->link));
 -        }
 -    }
 -}
 -
 -
 -// Disassociate connection from client
 -static void connection_release(struct connection *co)
 -{
 -    struct client *cl = co->client;
 -
 -    if (!cl)
 -        return;
 -    client_set_connection(cl, 0);
 -    co->client = 0;
 -}
 -
 -void connect_resolver_host(struct host *host, iochan_man_t iochan_man)
 -{
 -    struct connection *con;
 -
 -    yaz_mutex_enter(host->mutex);
 -    con = host->connections;
 -    while (con)
 -    {
 -        if (con->state == Conn_Closed)
 -        {
 -            if (!host->ipport || !con->client) /* unresolved or no client */
 -            {
 -                remove_connection_from_host(con);
 -                yaz_mutex_leave(host->mutex);
 -                connection_destroy(con);
 -            }
 -            else
 -            {
 -                struct session_database *sdb = client_get_database(con->client);
 -                if (sdb)
 -                {
 -                    yaz_mutex_leave(host->mutex);
 -                    client_start_search(con->client);
 -                }
 -                else
 -                {
 -                    remove_connection_from_host(con);
 -                    yaz_mutex_leave(host->mutex);
 -                    connection_destroy(con);
 -                }
 -            }
 -            /* start all over .. at some point it will be NULL */
 -            yaz_mutex_enter(host->mutex);
 -            con = host->connections;
 -        }
 -        else
 -        {
 -            con = con->next;
 -        }
 +        iochan_update(co);
      }
 -    yaz_mutex_leave(host->mutex);
 -}
 -
 -static struct host *connection_get_host(struct connection *con)
 -{
 -    return con->host;
  }
  
  static int connection_connect(struct connection *con, iochan_man_t iochan_man)
  {
 -    struct host *host = connection_get_host(con);
      ZOOM_options zoptions = ZOOM_options_create();
      const char *auth;
      const char *charset;
      if (redis && *redis)
          ZOOM_options_set(zoptions, "redis", redis);
  
 -    assert(host->ipport);
 -    if (host->proxy)
 -    {
 -        yaz_log(YLOG_LOG, "proxy=%s", host->ipport);
 -        ZOOM_options_set(zoptions, "proxy", host->ipport);
 -    }
 -    else
 +    if (con->zproxy)
      {
 -        assert(host->tproxy);
 -        yaz_log(YLOG_LOG, "tproxy=%s", host->ipport);
 -        ZOOM_options_set(zoptions, "tproxy", host->ipport);
 +        yaz_log(YLOG_LOG, "proxy=%s", con->zproxy);
 +        ZOOM_options_set(zoptions, "proxy", con->zproxy);
      }
 -
      if (apdulog && *apdulog)
          ZOOM_options_set(zoptions, "apdulog", apdulog);
  
@@@ -433,6 -533,9 +434,6 @@@ int client_prep_connection(struct clien
      struct session_database *sdb = client_get_database(cl);
      const char *zproxy = session_setting_oneval(sdb, PZ_ZPROXY);
      const char *url = session_setting_oneval(sdb, PZ_URL);
 -    const char *sru = session_setting_oneval(sdb, PZ_SRU);
 -    struct host *host = 0;
 -    int default_port = *sru ? 80 : 210;
  
      if (zproxy && zproxy[0] == '\0')
          zproxy = 0;
      if (!url || !*url)
          url = sdb->database->id;
  
 -    host = find_host(client_get_session(cl)->service->server->database_hosts,
 -                     url, zproxy, default_port, iochan_man);
 -
      yaz_log(YLOG_DEBUG, "client_prep_connection: target=%s url=%s",
              client_get_id(cl), url);
 -    if (!host)
 -        return 0;
  
      co = client_get_connection(cl);
 -    if (co)
 -    {
 -        assert(co->host);
 -        if (co->host == host && client_get_state(cl) == Client_Idle)
 -        {
 -            return 2;
 -        }
 -        connection_release(co);
 -        co = 0;
 -    }
      if (!co)
      {
 -        int max_connections = 0;
 -        int reuse_connections = 1;
 -        const char *v = session_setting_oneval(client_get_database(cl),
 -                                               PZ_MAX_CONNECTIONS);
 -        if (v && *v)
 -            max_connections = atoi(v);
 -
 -        v = session_setting_oneval(client_get_database(cl),
 -                PZ_REUSE_CONNECTIONS);
 -        if (v && *v)
 -            reuse_connections = atoi(v);
 -
 -        // See if someone else has an idle connection
 -        // We should look at timestamps here to select the longest-idle connection
 -        yaz_mutex_enter(host->mutex);
 -        while (1)
 -        {
 -            int num_connections = 0;
 -            for (co = host->connections; co; co = co->next)
 -                num_connections++;
 -            if (reuse_connections)
 -            {
 -                for (co = host->connections; co; co = co->next)
 -                {
 -                    if (connection_is_idle(co) &&
 -                        !strcmp(url, co->url) &&
 -                        (!co->client || client_get_state(co->client) == Client_Idle) &&
 -                        !strcmp(ZOOM_connection_option_get(co->link, "user"),
 -                                session_setting_oneval(client_get_database(cl),
 -                                                       PZ_AUTHENTICATION)))
 -                    {
 -                        if (zproxy == 0 && co->zproxy == 0)
 -                            break;
 -                        if (zproxy && co->zproxy && !strcmp(zproxy, co->zproxy))
 -                            break;
 -                    }
 -                }
 -                if (co)
 -                {
 -                    yaz_log(YLOG_LOG, "num_connections = %d (reusing)", num_connections);
 -                    break;
 -                }
 -            }
 -            if (max_connections <= 0 || num_connections < max_connections)
 -            {
 -                yaz_log(YLOG_LOG, "num_connections = %d (new); max = %d",
 -                        num_connections, max_connections);
 -                break;
 -            }
 -            yaz_log(YLOG_LOG, "num_connections = %d (waiting) max = %d",
 -                    num_connections, max_connections);
 -            if (yaz_cond_wait(host->cond_ready, host->mutex, abstime))
 -            {
 -                yaz_log(YLOG_LOG, "out of connections %s", client_get_id(cl));
 -                client_set_state(cl, Client_Error);
 -                yaz_mutex_leave(host->mutex);
 -                return 0;
 -            }
 -        }
 -        if (co)
 -        {
 -            yaz_log(YLOG_LOG,  "%p Connection reuse. state: %d", co, co->state);
 -            connection_release(co);
 -            client_set_connection(cl, co);
 -            co->client = cl;
 -            /* ensure that connection is only assigned to this client
 -               by marking the client non Idle */
 -            client_set_state(cl, Client_Working);
 -            yaz_mutex_leave(host->mutex);
 -            co->operation_timeout = operation_timeout;
 -            co->session_timeout = session_timeout;
 -            /* tells ZOOM to reconnect if necessary. Disabled becuase
 -               the ZOOM_connection_connect flushes the task queue */
 -            ZOOM_connection_connect(co->link, 0, 0);
 -        }
 -        else
 -        {
 -            yaz_mutex_leave(host->mutex);
 -            co = connection_create(cl, url, host,
 -                                   operation_timeout, session_timeout,
 -                                   iochan_man);
 -        }
 -        assert(co->host);
 +        co = connection_create(cl, url, zproxy,
 +                               operation_timeout, session_timeout,
 +                               iochan_man);
      }
  
      if (co && co->link)
diff --combined src/pazpar2_config.c
@@@ -56,6 -56,7 +56,6 @@@ struct conf_confi
      WRBUF confdir;
      char *path;
      iochan_man_t iochan_man;
 -    database_hosts_t database_hosts;
  };
  
  struct service_xslt
@@@ -871,6 -872,7 +871,6 @@@ static struct conf_server *server_creat
      server->charsets = 0;
      server->http_server = 0;
      server->iochan_man = 0;
 -    server->database_hosts = config->database_hosts;
      server->settings_fname = 0;
  
      if (server_id)
@@@ -1274,7 -1276,7 +1274,7 @@@ static int parse_config(struct conf_con
      return 0;
  }
  
- struct conf_config *config_create(const char *fname, int verbose)
+ struct conf_config *config_create(const char *fname)
  {
      xmlDoc *doc = xmlReadFile(fname,
                                NULL,
      config->path = nmem_strdup(nmem, ".");
      config->no_threads = 0;
      config->iochan_man = 0;
 -    config->database_hosts = database_hosts_create();
  
      config->confdir = wrbuf_alloc();
      if ((p = strrchr(fname,
      r = yaz_xml_include_simple(n, wrbuf_cstr(config->confdir));
      if (r == 0) /* OK */
      {
-         if (verbose)
-         {
-             yaz_log(YLOG_LOG, "Configuration %s after include processing",
-                     fname);
+         yaz_log(YLOG_LOG, "Configuration %s after include processing",
+                 fname);
  #if LIBXML_VERSION >= 20600
-             xmlDocFormatDump(yaz_log_file(), doc, 0);
+         xmlDocFormatDump(yaz_log_file(), doc, 0);
  #else
-             xmlDocDump(yaz_log_file(), doc);
+         xmlDocDump(yaz_log_file(), doc);
  #endif
-         }
          r = parse_config(config, n);
      }
      xmlFreeDoc(doc);
@@@ -1373,6 -1373,7 +1370,6 @@@ void config_destroy(struct conf_config 
              struct conf_server *s_next = server->next;
              server_destroy(server);
              server = s_next;
 -            database_hosts_destroy(&config->database_hosts);
          }
          wrbuf_destroy(config->confdir);
          nmem_destroy(config->nmem);
diff --combined src/pazpar2_config.h
@@@ -28,6 -28,7 +28,6 @@@ Foundation, Inc., 51 Franklin St, Fift
  #include "charsets.h"
  #include "http.h"
  #include "database.h"
 -#include "host.h"
  
  enum conf_metadata_type {
      Metadata_type_generic,    // Generic text field
@@@ -153,9 -154,10 +153,9 @@@ struct conf_serve
      struct conf_config *config;
      http_server_t http_server;
      iochan_man_t iochan_man;
 -    database_hosts_t database_hosts;
  };
  
- struct conf_config *config_create(const char *fname, int verbose);
+ struct conf_config *config_create(const char *fname);
  void config_destroy(struct conf_config *config);
  void config_process_events(struct conf_config *config);
  void info_services(struct conf_server *server, WRBUF w);
diff --combined src/session.c
@@@ -57,7 -57,7 +57,7 @@@ Foundation, Inc., 51 Franklin St, Fift
  #include <yaz/oid_db.h>
  #include <yaz/snprintf.h>
  
- #define USE_TIMING 1
+ #define USE_TIMING 0
  #if USE_TIMING
  #include <yaz/timing.h>
  #endif
@@@ -508,6 -508,7 +508,6 @@@ static void select_targets_callback(str
          l->next = se->clients_cached;
          se->clients_cached = l;
      }
 -    /* set session always. If may be 0 if client is not active */
      client_set_session(cl, se);
  
      l = xmalloc(sizeof(*l));
@@@ -556,7 -557,6 +556,7 @@@ static void session_remove_cached_clien
          client_lock(l->client);
          client_set_session(l->client, 0);
          client_set_database(l->client, 0);
 +        client_mark_dead(l->client);
          client_unlock(l->client);
          client_destroy(l->client);
          xfree(l);
@@@ -1038,13 -1038,13 +1038,13 @@@ static struct hitsbytarget *hitsbytarge
          WRBUF w = wrbuf_alloc();
          const char *name = session_setting_oneval(client_get_database(cl),
                                                    PZ_NAME);
          res[*count].id = client_get_id(cl);
          res[*count].name = *name ? name : "Unknown";
          res[*count].hits = client_get_hits(cl);
          res[*count].approximation = client_get_approximation(cl);
-         res[*count].records = client_get_num_records(cl);
-         res[*count].filtered = client_get_num_records_filtered(cl);
+         res[*count].records = client_get_num_records(cl,
+                                                      &res[*count].filtered,
+                                                      0, 0);
          res[*count].diagnostic =
              client_get_diagnostic(cl, &res[*count].message,
                                    &res[*count].addinfo);
@@@ -1280,8 -1280,24 +1280,24 @@@ int session_fetch_more(struct session *
              }
              else
              {
-                 session_log(se, YLOG_LOG, "%s: no more to fetch",
-                             client_get_id(cl));
+                 int filtered;
+                 int ingest_failures;
+                 int record_failures;
+                 int num = client_get_num_records(
+                     cl, &filtered, &ingest_failures, &record_failures);
+                 session_log(se, YLOG_LOG, "%s: hits=" ODR_INT_PRINTF
+                             " fetched=%d filtered=%d",
+                             client_get_id(cl),
+                             client_get_hits(cl),
+                             num, filtered);
+                 if (ingest_failures || record_failures)
+                 {
+                     session_log(se, YLOG_WARN, "%s:"
+                                 " ingest failures=%d record failures=%d",
+                                 client_get_id(cl),
+                                 ingest_failures, record_failures);
+                 }
              }
          }
          else
@@@ -1687,9 -1703,6 +1703,6 @@@ static int ingest_sub_record(struct cli
  {
      int ret = 0;
      struct session *se = client_get_session(cl);
-     struct conf_service *service = se->service;
-     insert_settings_values(sdb, xdoc, root, service);
  
      if (!check_record_filter(root, sdb))
      {
@@@ -1722,9 -1735,19 +1735,19 @@@ int ingest_record(struct client *cl, co
      struct session_database *sdb = client_get_database(cl);
      struct conf_service *service = se->service;
      xmlDoc *xdoc = normalize_record(se, sdb, service, rec, nmem);
-     int r = 0;
-     xmlNode *root;
+     int r = ingest_xml_record(cl, xdoc, record_no, nmem, 0);
+     client_store_xdoc(cl, record_no, xdoc);
+     return r;
+ }
  
+ int ingest_xml_record(struct client *cl, xmlDoc *xdoc,
+                       int record_no, NMEM nmem, int cached_copy)
+ {
+     struct session *se = client_get_session(cl);
+     struct session_database *sdb = client_get_database(cl);
+     struct conf_service *service = se->service;
+     xmlNode *root;
+     int r = 0;
      if (!xdoc)
          return -1;
  
              if (sroot->type == XML_ELEMENT_NODE &&
                  !strcmp((const char *) sroot->name, "record"))
              {
+                 if (!cached_copy)
+                     insert_settings_values(sdb, xdoc, root, service);
                  r = ingest_sub_record(cl, xdoc, sroot, record_no, nmem, sdb,
                                        mk);
              }
              mk->value = nmem_strdup(nmem, mergekey_norm);
              mk->next = 0;
  
+             if (!cached_copy)
+                 insert_settings_values(sdb, xdoc, root, service);
              r = ingest_sub_record(cl, xdoc, root, record_no, nmem, sdb, mk);
          }
      }
                      (const char *) root->name);
          r = -1;
      }
-     xmlFreeDoc(xdoc);
      return r;
  }
  
@@@ -2067,8 -2093,6 +2093,6 @@@ static int ingest_to_cluster(struct cli
  
      if (check_limit_local(cl, record, record_no))
      {
-         session_log(se, YLOG_LOG, "Facet filtered out record no %d from %s",
-                     record_no, sdb->database->id);
          if (type)
              xmlFree(type);
          if (value)
diff --combined win/makefile
@@@ -5,7 -5,7 +5,7 @@@
  DEBUG=0   # 0 for release, 1 for debug
  USE_MANIFEST = 1 # Can be enabled Visual Studio 2005/2008
  PACKAGE_NAME=pazpar2
- PACKAGE_VERSION=1.7.2
+ PACKAGE_VERSION=1.7.4
  
  # YAZ
  YAZ_DIR=..\..\yaz
@@@ -179,6 -179,8 +179,6 @@@ LNKOPT= $(COMMON_LNK_OPTIONS) $(RELEASE
  # Source and object modules
  
  PAZPAR2_OBJS = \
 -   "$(OBJDIR)\getaddrinfo.obj" \
 -   "$(OBJDIR)\host.obj" \
     "$(OBJDIR)\pazpar2.obj" \
     "$(OBJDIR)\pazpar2_config.obj" \
     "$(OBJDIR)\http.obj" \