List:Commits« Previous MessageNext Message »
From:Georgi Kodinov Date:December 22 2010 5:33pm
Subject:bzr commit into mysql-trunk-wl5259 branch (Georgi.Kodinov:3446) WL#5259
View as plain text  
#At file:///Users/kgeorge/mysql/work/wl5259-trunk/ based on revid:georgi.kodinov@stripped

 3446 Georgi Kodinov	2010-12-22
      WL#5259 : Show contents of host cache
      
      === INFORMATION_SCHEMA.HOST_CACHE ===
      
      CREATE TABLE `HOST_CACHE` (
        `IP_ADDRESS` varchar(64) NOT NULL DEFAULT '',
        `HOSTNAME` varchar(64) DEFAULT NULL,
        `ERRORS` int(10) NOT NULL DEFAULT '0'
      );
      
      
      This table will contain 1 row for every entry in the host cache hash.
      The contents of the table can be efficiently filtered by a wildcard on IP_ADDRESS.
      The HOSTNAME column can be NULL, meaning that the IP_ADDRESS cannot be resolved.
      No SHOW command will be created.
      After FLUSH HOSTS there will be no rows in INFORMATION_SCHEMA.HOST_CACHE.
      
      === Global status variables ===
      
      The following global readonly variables will be added :
       Host_cache_free : free entries in the host cache.
       Host_cache_hits : number of records found in the cache
       Host_cache_inserts : number of record inserted in the cache
       Host_cache_misses : number of records not found in the cache
       Host_cache_prunes : number of times a record is deleted from the cache to free up 
      place for new records
       Host_cache_used : number of active records in the cache
      
      These variable will be reset by FLUSH STATUS. Note that FLUSH HOSTS will have no 
      effect on them. Only Host_cache_free will probably be non-zero after FLUSH STATUS.
      
      === host-cache-size global variable/command line option ===
      
      Valid values : 0-2048. Default : 128
      
      If set at run time will have the join effect of FLUSH STATUS and FLUSH HOSTS and 
      will re-allocate the host cache to a new size.

    added:
      mysql-test/suite/sys_vars/r/host_cache_size_basic.result
      mysql-test/suite/sys_vars/t/host_cache_size_basic.test
    modified:
      mysql-test/r/information_schema.result
      mysql-test/r/information_schema_db.result
      mysql-test/r/mysqld--help-notwin.result
      mysql-test/r/mysqlshow.result
      sql/CMakeLists.txt
      sql/handler.h
      sql/hash_filo.cc
      sql/hash_filo.h
      sql/hostname.cc
      sql/hostname.h
      sql/mysqld.cc
      sql/sql_acl.cc
      sql/sql_show.cc
      sql/sys_vars.cc
=== modified file 'mysql-test/r/information_schema.result'
--- a/mysql-test/r/information_schema.result	2010-12-17 11:28:59 +0000
+++ b/mysql-test/r/information_schema.result	2010-12-22 17:31:51 +0000
@@ -55,6 +55,7 @@ EVENTS
 FILES
 GLOBAL_STATUS
 GLOBAL_VARIABLES
+HOST_CACHE
 KEY_COLUMN_USAGE
 PARAMETERS
 PARTITIONS
@@ -875,7 +876,7 @@ table_schema IN ('mysql', 'INFORMATION_S
 AND table_name not like 'ndb%' AND table_name not like 'innodb_%'
 GROUP BY TABLE_SCHEMA;
 table_schema	count(*)
-information_schema	30
+information_schema	31
 mysql	25
 create table t1 (i int, j int);
 create trigger trg1 before insert on t1 for each row
@@ -1324,6 +1325,7 @@ EVENTS	information_schema.EVENTS	1
 FILES	information_schema.FILES	1
 GLOBAL_STATUS	information_schema.GLOBAL_STATUS	1
 GLOBAL_VARIABLES	information_schema.GLOBAL_VARIABLES	1
+HOST_CACHE	information_schema.HOST_CACHE	1
 KEY_COLUMN_USAGE	information_schema.KEY_COLUMN_USAGE	1
 PARAMETERS	information_schema.PARAMETERS	1
 PARTITIONS	information_schema.PARTITIONS	1

=== modified file 'mysql-test/r/information_schema_db.result'
--- a/mysql-test/r/information_schema_db.result	2010-11-30 17:53:11 +0000
+++ b/mysql-test/r/information_schema_db.result	2010-12-22 17:31:51 +0000
@@ -15,6 +15,7 @@ EVENTS
 FILES
 GLOBAL_STATUS
 GLOBAL_VARIABLES
+HOST_CACHE
 KEY_COLUMN_USAGE
 PARAMETERS
 PARTITIONS

=== modified file 'mysql-test/r/mysqld--help-notwin.result'
--- a/mysql-test/r/mysqld--help-notwin.result	2010-12-07 07:53:39 +0000
+++ b/mysql-test/r/mysqld--help-notwin.result	2010-12-22 17:31:51 +0000
@@ -186,6 +186,7 @@ The following options may be given as th
  The maximum length of the result of function 
  GROUP_CONCAT()
  -?, --help          Display this help and exit.
+ --host-cache-size=# How many hostnames should be cached to avoid resolving.
  --ignore-builtin-innodb 
  Disable initialization of builtin InnoDB plugin
  --init-connect=name Command(s) that are executed for each new connection
@@ -797,6 +798,7 @@ gdb FALSE
 general-log FALSE
 group-concat-max-len 1024
 help TRUE
+host-cache-size 128
 ignore-builtin-innodb FALSE
 init-connect 
 init-file (No default value)

=== modified file 'mysql-test/r/mysqlshow.result'
--- a/mysql-test/r/mysqlshow.result	2010-10-27 14:46:44 +0000
+++ b/mysql-test/r/mysqlshow.result	2010-12-22 17:31:51 +0000
@@ -89,6 +89,7 @@ Database: information_schema
 | FILES                                 |
 | GLOBAL_STATUS                         |
 | GLOBAL_VARIABLES                      |
+| HOST_CACHE                            |
 | KEY_COLUMN_USAGE                      |
 | PARAMETERS                            |
 | PARTITIONS                            |
@@ -132,6 +133,7 @@ Database: INFORMATION_SCHEMA
 | FILES                                 |
 | GLOBAL_STATUS                         |
 | GLOBAL_VARIABLES                      |
+| HOST_CACHE                            |
 | KEY_COLUMN_USAGE                      |
 | PARAMETERS                            |
 | PARTITIONS                            |

=== added file 'mysql-test/suite/sys_vars/r/host_cache_size_basic.result'
--- a/mysql-test/suite/sys_vars/r/host_cache_size_basic.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/sys_vars/r/host_cache_size_basic.result	2010-12-22 17:31:51 +0000
@@ -0,0 +1,30 @@
+SELECT @@global.host_cache_size;
+@@global.host_cache_size
+128
+select @@session.host_cache_size;
+ERROR HY000: Variable 'host_cache_size' is a GLOBAL variable
+show global variables like 'host_cache_size';
+Variable_name	Value
+host_cache_size	128
+show session variables like 'host_cache_size';
+Variable_name	Value
+host_cache_size	128
+select * from INFORMATION_SCHEMA.global_variables where variable_name='host_cache_size';
+VARIABLE_NAME	VARIABLE_VALUE
+HOST_CACHE_SIZE	128
+select * from INFORMATION_SCHEMA.session_variables where variable_name='host_cache_size';
+VARIABLE_NAME	VARIABLE_VALUE
+HOST_CACHE_SIZE	128
+set global host_cache_size = 1024;
+SELECT @@global.host_cache_size;
+@@global.host_cache_size
+1024
+set session host_cache_size = 1024;
+ERROR HY000: Variable 'host_cache_size' is a GLOBAL variable and should be set with SET GLOBAL
+SELECT @@global.host_cache_size;
+@@global.host_cache_size
+1024
+set global host_cache_size = default;
+SELECT @@global.host_cache_size;
+@@global.host_cache_size
+128

=== added file 'mysql-test/suite/sys_vars/t/host_cache_size_basic.test'
--- a/mysql-test/suite/sys_vars/t/host_cache_size_basic.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/sys_vars/t/host_cache_size_basic.test	2010-12-22 17:31:51 +0000
@@ -0,0 +1,18 @@
+SELECT @@global.host_cache_size;
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+select @@session.host_cache_size;
+
+show global variables like 'host_cache_size';
+show session variables like 'host_cache_size';
+select * from INFORMATION_SCHEMA.global_variables where variable_name='host_cache_size';
+select * from INFORMATION_SCHEMA.session_variables where variable_name='host_cache_size';
+
+set global host_cache_size = 1024;
+SELECT @@global.host_cache_size;
+
+--error ER_GLOBAL_VARIABLE
+set session host_cache_size = 1024;
+SELECT @@global.host_cache_size;
+
+set global host_cache_size = default;
+SELECT @@global.host_cache_size;

=== modified file 'sql/CMakeLists.txt'
--- a/sql/CMakeLists.txt	2010-12-17 09:41:21 +0000
+++ b/sql/CMakeLists.txt	2010-12-22 17:31:51 +0000
@@ -80,6 +80,7 @@ SET (SQL_SOURCE
                datadict.cc sql_reload.cc
                sql_partition_admin.cc
                sql_admin.cc sql_alter.cc
+               hash_filo.cc
                ${GEN_SOURCES}
                ${MYSYS_LIBWRAP_SOURCE})
 

=== modified file 'sql/handler.h'
--- a/sql/handler.h	2010-11-18 16:34:56 +0000
+++ b/sql/handler.h	2010-12-22 17:31:51 +0000
@@ -560,6 +560,7 @@ enum enum_schema_tables
   SCH_FILES,
   SCH_GLOBAL_STATUS,
   SCH_GLOBAL_VARIABLES,
+  SCH_HOST_CACHE,
   SCH_KEY_COLUMN_USAGE,
   SCH_OPEN_TABLES,
   SCH_PARAMETERS,

=== modified file 'sql/hash_filo.cc'
--- a/sql/hash_filo.cc	2010-07-02 18:15:21 +0000
+++ b/sql/hash_filo.cc	2010-12-22 17:31:51 +0000
@@ -25,3 +25,152 @@
 
 #include "sql_priv.h"
 #include "hash_filo.h"
+
+
+hash_filo_metrics::hash_filo_metrics()
+{
+  reset();
+}
+
+
+void hash_filo_metrics::reset()
+{
+  hits= misses= inserts= prunes= 0;
+}
+
+
+hash_filo::hash_filo(uint size_arg, uint key_offset_arg , uint key_length_arg,
+	    my_hash_get_key get_key_arg, my_hash_free_key free_element_arg,
+	    CHARSET_INFO *hash_charset_arg)
+  :key_offset(key_offset_arg), key_length(key_length_arg),
+  get_key(get_key_arg), size(size_arg), free_element(free_element_arg),init(0),
+  hash_charset(hash_charset_arg), metrics(NULL)
+{
+  bzero((char*) &cache,sizeof(cache));
+}
+
+
+hash_filo::~hash_filo()
+{
+  if (init)
+  {
+    if (cache.array.buffer)	/* Avoid problems with thread library */
+      (void) my_hash_free(&cache);
+    mysql_mutex_destroy(&lock);
+  }
+}
+
+
+void hash_filo::set_metrics(hash_filo_metrics *in_metrics)
+{
+  metrics= in_metrics;
+}
+
+
+void hash_filo::clear(bool locked)
+{
+  if (!init)
+  {
+    init= 1;
+    mysql_mutex_init(key_hash_filo_lock, &lock, MY_MUTEX_INIT_FAST);
+  }
+  if (!locked)
+    mysql_mutex_lock(&lock);
+  (void) my_hash_free(&cache);
+  (void) my_hash_init(&cache, hash_charset, size,key_offset, 
+                      key_length, get_key, free_element, 0);
+  if (!locked)
+    mysql_mutex_unlock(&lock);
+  newest_element_p=oldest_element_p= 0;
+  if (metrics)
+  {
+    metrics->used= 0;
+    metrics->free= size;
+  }
+}
+
+
+void hash_filo::resize(uint new_size)
+{
+  (void) mysql_mutex_lock(&lock);
+  size= new_size;
+  if (metrics)
+    metrics->reset();
+  clear(1);
+  (void) mysql_mutex_unlock(&lock);
+}
+
+
+hash_filo_element *hash_filo::search(const uchar* key, size_t length)
+{
+  hash_filo_element *entry= (hash_filo_element*)
+    my_hash_search(&cache, key, length);
+  if (entry)
+  {						// Found; link it first
+    if (metrics)
+      statistic_increment(metrics->hits, &metrics->lock);
+    if (entry != newest_element_p)
+    {						// Relink used-chain
+      if (entry == oldest_element_p)
+        oldest_element_p= entry->newer_element_p;
+      else
+      {
+        entry->older_element_p->newer_element_p= entry->newer_element_p;
+        entry->newer_element_p->older_element_p= entry->older_element_p;
+      }
+      if ((entry->older_element_p= newest_element_p))
+        newest_element_p->newer_element_p= entry;
+      newest_element_p= entry;
+    }
+    else
+    {
+      if (metrics)
+        statistic_increment(metrics->misses, &metrics_lock);
+    }
+  }
+  return entry;
+}
+
+
+my_bool hash_filo::add(hash_filo_element *entry)
+{
+
+  if(unlikely(size == 0)) 
+  {
+    /* Our HASH size is 0, so throw away any entries attempted. */
+    if(free_element)
+      (*free_element)(entry);
+    return 0;
+  }
+
+  if (cache.records == size)
+  {
+    hash_filo_element *tmp= oldest_element_p;
+    oldest_element_p= oldest_element_p->newer_element_p;
+    my_hash_delete(&cache,(uchar*) tmp);
+    if (metrics)
+      statistic_increment(metrics->prunes, &metrics->lock);
+  }
+  if (my_hash_insert(&cache,(uchar*) entry))
+  {
+    if (free_element)
+      (*free_element)(entry);		// This should never happen
+    return 1;
+  }
+
+  if (metrics)
+    statistic_increment(metrics->inserts, &metrics->lock);
+  if ((entry->older_element_p= newest_element_p))
+    newest_element_p->newer_element_p= entry;
+  else
+    oldest_element_p= entry;
+  newest_element_p= entry;
+
+  if (metrics)
+  {
+    metrics->used= cache.records;
+    metrics->free= size - cache.records;
+  }
+
+  return 0;
+}

=== modified file 'sql/hash_filo.h'
--- a/sql/hash_filo.h	2010-07-02 02:58:51 +0000
+++ b/sql/hash_filo.h	2010-12-22 17:31:51 +0000
@@ -32,106 +32,59 @@
 
 class hash_filo_element
 {
-  hash_filo_element *next_used,*prev_used;
+  hash_filo_element *newer_element_p, *older_element_p;
  public:
   hash_filo_element() {}
+  hash_filo_element *newer_element() { return newer_element_p; }
+  hash_filo_element *older_element() { return older_element_p; }
+  friend class hash_filo;
+};
+
+
+class hash_filo_metrics
+{
+  pthread_mutex_t lock;
+ public:
+  hash_filo_metrics();
+  void reset();
+
+  ulong hits;
+  ulong misses;
+  ulong inserts;
+  ulong prunes;
+
+  ulong used, free;
+
   friend class hash_filo;
 };
 
 
 class hash_filo
 {
-  const uint size, key_offset, key_length;
+  const uint key_offset, key_length;
   const my_hash_get_key get_key;
+  uint size; 
   my_hash_free_key free_element;
   bool init;
   CHARSET_INFO *hash_charset;
 
-  hash_filo_element *first_link,*last_link;
+  hash_filo_element *newest_element_p, *oldest_element_p;
+  hash_filo_metrics *metrics;
 public:
   mysql_mutex_t lock;
   HASH cache;
 
   hash_filo(uint size_arg, uint key_offset_arg , uint key_length_arg,
 	    my_hash_get_key get_key_arg, my_hash_free_key free_element_arg,
-	    CHARSET_INFO *hash_charset_arg)
-    :size(size_arg), key_offset(key_offset_arg), key_length(key_length_arg),
-    get_key(get_key_arg), free_element(free_element_arg),init(0),
-    hash_charset(hash_charset_arg)
-  {
-    bzero((char*) &cache,sizeof(cache));
-  }
-
-  ~hash_filo()
-  {
-    if (init)
-    {
-      if (cache.array.buffer)	/* Avoid problems with thread library */
-	(void) my_hash_free(&cache);
-      mysql_mutex_destroy(&lock);
-    }
-  }
-  void clear(bool locked=0)
-  {
-    if (!init)
-    {
-      init=1;
-      mysql_mutex_init(key_hash_filo_lock, &lock, MY_MUTEX_INIT_FAST);
-    }
-    if (!locked)
-      mysql_mutex_lock(&lock);
-    (void) my_hash_free(&cache);
-    (void) my_hash_init(&cache,hash_charset,size,key_offset, 
-    		     key_length, get_key, free_element,0);
-    if (!locked)
-      mysql_mutex_unlock(&lock);
-    first_link=last_link=0;
-  }
-
-  hash_filo_element *search(uchar* key, size_t length)
-  {
-    hash_filo_element *entry=(hash_filo_element*)
-      my_hash_search(&cache,(uchar*) key,length);
-    if (entry)
-    {						// Found; link it first
-      if (entry != first_link)
-      {						// Relink used-chain
-	if (entry == last_link)
-	  last_link=entry->prev_used;
-	else
-	{
-	  entry->next_used->prev_used = entry->prev_used;
-	  entry->prev_used->next_used = entry->next_used;
-	}
-	if ((entry->next_used= first_link))
-	  first_link->prev_used=entry;
-	first_link=entry;
-      }
-    }
-    return entry;
-  }
-
-  my_bool add(hash_filo_element *entry)
-  {
-    if (cache.records == size)
-    {
-      hash_filo_element *tmp=last_link;
-      last_link=last_link->prev_used;
-      my_hash_delete(&cache,(uchar*) tmp);
-    }
-    if (my_hash_insert(&cache,(uchar*) entry))
-    {
-      if (free_element)
-	(*free_element)(entry);		// This should never happen
-      return 1;
-    }
-    if ((entry->next_used=first_link))
-      first_link->prev_used=entry;
-    else
-      last_link=entry;
-    first_link=entry;
-    return 0;
-  }
+	    CHARSET_INFO *hash_charset_arg);
+  ~hash_filo();
+  void set_metrics(hash_filo_metrics *in_metrics);
+  hash_filo_element *newest_element() { return newest_element_p; }
+  hash_filo_element *oldest_element() { return oldest_element_p; }
+  void clear(bool locked= 0);
+  void resize(uint new_size);
+  hash_filo_element *search(const uchar * key, size_t length= 0);
+  my_bool add(hash_filo_element *entry);
 };
 
 #endif

=== modified file 'sql/hostname.cc'
--- a/sql/hostname.cc	2010-07-23 20:59:42 +0000
+++ b/sql/hostname.cc	2010-12-22 17:31:51 +0000
@@ -26,11 +26,6 @@
 
 #include "sql_priv.h"
 #include "hostname.h"
-#include "my_global.h"
-#ifndef __WIN__
-#include <netdb.h>        // getservbyname, servent
-#endif
-#include "hash_filo.h"
 #include <m_ctype.h>
 #include "log.h"                                // sql_print_warning,
                                                 // sql_print_information
@@ -48,77 +43,101 @@ extern "C" {					// Because of SCO 3.2V4
 #ifdef	__cplusplus
 }
 #endif
+#include "sql_class.h"
+#include "sql_show.h" // schema_table_store_record
+#include "sql_acl.h"  // wild_case_compare
 
-/*
-  HOST_ENTRY_KEY_SIZE -- size of IP address string in the hash cache.
-*/
-
-#define HOST_ENTRY_KEY_SIZE INET6_ADDRSTRLEN
+static hash_filo *hostname_cache;
+hash_filo_metrics hostname_cache_metrics;
+ulong host_cache_size;
 
-/**
-  An entry in the hostname hash table cache.
+static inline Host_entry *hostname_cache_search(const char *ip_key)
+{
+  return (Host_entry *)hostname_cache->search((const uchar *) ip_key, 0);
+}
 
-  Host name cache does two things:
-    - caches host names to save DNS look ups;
-    - counts connect errors from IP.
 
-  Host name can be NULL (that means DNS look up failed), but connect errors
-  still are counted.
+/*
+  A simple inline function to return the hash key for a given entry.
 */
-
-class Host_entry :public hash_filo_element
+static inline uchar *host_entry_get_key(Host_entry *entry, size_t *length,
+                                       my_bool not_used __attribute__((unused)))
 {
-public:
-  /**
-    Client IP address. This is the key used with the hash table.
-
-    The client IP address is always expressed in IPv6, even when the
-    network IPv6 stack is not present.
-
-    This IP address is never used to connect to a socket.
-  */
-  char ip_key[HOST_ENTRY_KEY_SIZE];
-
-  /**
-    Number of errors during handshake phase from the IP address.
-  */
-  uint connect_errors;
-
-  /**
-    One of the host names for the IP address. May be NULL.
-  */
-  const char *hostname;
-};
+  *length= HOST_ENTRY_KEY_SIZE;
+  return (uchar *) &entry->ip_key;
+}
 
-static hash_filo *hostname_cache;
 
-void hostname_cache_refresh()
+/*
+  A simple inline function to free a hash entry.
+*/
+static inline void host_entry_free_key(Host_entry *entry)
 {
-  hostname_cache->clear();
+  my_free(entry->hostname);
+  my_free((uchar *) entry);
 }
 
+
 bool hostname_cache_init()
 {
   Host_entry tmp;
   uint key_offset= (uint) ((char*) (&tmp.ip_key) - (char*) &tmp);
 
-  if (!(hostname_cache= new hash_filo(HOST_CACHE_SIZE,
+  if (!(hostname_cache= new hash_filo(host_cache_size, 
                                       key_offset, HOST_ENTRY_KEY_SIZE,
-                                      NULL, (my_hash_free_key) free,
-                                      &my_charset_bin)))
+                                      (my_hash_get_key) host_entry_get_key, 
+                                      (my_hash_free_key) host_entry_free_key,
+                                      &my_charset_latin1)))
     return 1;
 
   hostname_cache->clear();
-
+  hostname_cache->set_metrics(&hostname_cache_metrics);
   return 0;
 }
 
+
 void hostname_cache_free()
 {
   delete hostname_cache;
   hostname_cache= NULL;
 }
 
+
+/**
+  Empty the contents of the hostname cache.
+*/
+void hostname_cache_refresh()
+{
+  DBUG_ENTER("hostname_cache_refresh");
+  hostname_cache->clear();
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  Empty the contents of the hostname aggregate stats.
+*/
+void hostname_cache_metrics_reset()
+{
+  DBUG_ENTER("hostname_cache_metrics_reset");
+  mysql_mutex_lock(&hostname_cache->lock);
+  hostname_cache_metrics.reset();
+  mysql_mutex_unlock(&hostname_cache->lock);
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  Resize the hostname cache by resizing the hash table underlying it.
+
+  @param  size  new size of the hostname cache
+*/
+void hostname_cache_resize(uint size)
+{
+  hostname_cache->resize(size);
+}
+
+
 static void prepare_hostname_cache_key(const char *ip_string,
                                        char *ip_key)
 {
@@ -129,65 +148,60 @@ static void prepare_hostname_cache_key(c
   memcpy(ip_key, ip_string, ip_string_length);
 }
 
-static inline Host_entry *hostname_cache_search(const char *ip_key)
-{
-  return (Host_entry *) hostname_cache->search((uchar *) ip_key, 0);
-}
 
-static bool add_hostname_impl(const char *ip_key, const char *hostname)
+static bool hostname_cache_add(const char *ip_key, const char *hostname)
 {
-  if (hostname_cache_search(ip_key))
-    return FALSE;
-
-  size_t hostname_size= hostname ? strlen(hostname) + 1 : 0;
+  bool err_status= TRUE;
+  Host_entry *entry;
 
-  Host_entry *entry= (Host_entry *) malloc(sizeof (Host_entry) + hostname_size);
-
-  if (!entry)
-    return TRUE;
-
-  char *hostname_copy;
+  if (specialflag & SPECIAL_NO_HOST_CACHE)
+    return FALSE;
 
-  memcpy(&entry->ip_key, ip_key, HOST_ENTRY_KEY_SIZE);
+  mysql_mutex_lock(&hostname_cache->lock);
 
-  if (hostname_size)
+  if (!hostname_cache->search((const uchar *) ip_key, 0))
   {
-    hostname_copy= (char *) (entry + 1);
-    memcpy(hostname_copy, hostname, hostname_size);
+    if ((entry= (Host_entry *) my_malloc(sizeof(Host_entry), 
+                                         MYF(MY_WME|MY_ZEROFILL))))
+    {
+      memcpy(&entry->ip_key, ip_key, HOST_ENTRY_KEY_SIZE);
+      entry->hostname= hostname ? my_strdup(hostname, MYF(0)) : NULL;
 
-    DBUG_PRINT("info", ("Adding '%s' -> '%s' to the hostname cache...'",
-                        (const char *) ip_key,
-                        (const char *) hostname_copy));
+      DBUG_PRINT("info", ("Adding '%s' -> '%s' to the hostname cache...'",
+                          ip_key,
+                          hostname ? hostname : "NULL"));
+      err_status= hostname_cache->add(entry);
+    }
   }
   else
-  {
-    hostname_copy= NULL;
-
-    DBUG_PRINT("info", ("Adding '%s' -> NULL to the hostname cache...'",
-                        (const char *) ip_key));
-  }
+    err_status= FALSE;
 
-  entry->hostname= hostname_copy;
-  entry->connect_errors= 0;
-
-  return hostname_cache->add(entry);
+  mysql_mutex_unlock(&hostname_cache->lock);
+  return err_status;
 }
 
-static bool add_hostname(const char *ip_key, const char *hostname)
-{
-  if (specialflag & SPECIAL_NO_HOST_CACHE)
-    return FALSE;
 
-  mysql_mutex_lock(&hostname_cache->lock);
-
-  bool err_status= add_hostname_impl(ip_key, hostname);
+/**
+  Add a denied IP address to the hostname cache, so it can be quickly
+  denied in the future.
 
-  mysql_mutex_unlock(&hostname_cache->lock);
+  @param  ip_key  pointer to struct in_addr with IP address
+*/
 
-  return err_status;
+static inline bool hostname_cache_add_deny(const char *ip_key)
+{
+  DBUG_ENTER("hostname_cache_add_deny");
+  DBUG_RETURN (hostname_cache_add(ip_key, NullS));
 }
 
-void inc_host_errors(const char *ip_string)
+
+/**
+  Increment the error count for an IP address.
+  
+  @param  in  pointer to struct in_addr with IP address
+*/
+
+void hostname_cache_inc_host_errors(const char *ip_string)
 {
   if (!ip_string)
     return;
@@ -206,7 +220,13 @@ void inc_host_errors(const char *ip_stri
 }
 
 
-void reset_host_errors(const char *ip_string)
+/**
+  Reset the error count for an IP address.
+  
+  @param  in  pointer to struct in_addr with IP address
+*/
+
+void hostname_cache_reset_host_errors(const char *ip_string)
 {
   if (!ip_string)
     return;
@@ -343,7 +363,7 @@ bool ip_to_hostname(struct sockaddr_stor
       DBUG_PRINT("info",("IP (%s) has been found in the cache. "
                          "Hostname: '%s'; connect_errors: %d",
                          (const char *) ip_key,
-                         (const char *) (*hostname? *hostname : "null"),
+                         (const char *) (*hostname ? *hostname : "null"),
                          (int) *connect_errors));
 
       mysql_mutex_unlock(&hostname_cache->lock);
@@ -381,7 +401,7 @@ bool ip_to_hostname(struct sockaddr_stor
                       "no reverse address mapping.",
                       (const char *) ip_key);
 
-    err_status= add_hostname(ip_key, NULL);
+    err_status= hostname_cache_add_deny(ip_key);
 
     *hostname= NULL;
     *connect_errors= 0; /* New IP added to the cache. */
@@ -436,7 +456,7 @@ bool ip_to_hostname(struct sockaddr_stor
                       (const char *) ip_key,
                       (const char *) hostname_buffer);
 
-    err_status= add_hostname(ip_key, NULL);
+    err_status= hostname_cache_add_deny(ip_key);
 
     *hostname= NULL;
     *connect_errors= 0; /* New IP added to the cache. */
@@ -468,7 +488,7 @@ bool ip_to_hostname(struct sockaddr_stor
       indefinitely.
     */
 
-    err_status= add_hostname(ip_key, NULL);
+    err_status= hostname_cache_add_deny(ip_key);
 
     *hostname= NULL;
     *connect_errors= 0; /* New IP added to the cache. */
@@ -550,17 +570,72 @@ bool ip_to_hostname(struct sockaddr_stor
 
   if (*hostname)
   {
-    err_status= add_hostname(ip_key, *hostname);
+    err_status= hostname_cache_add(ip_key, *hostname);
+    /* hostname_cache_add calls ::search(), which will increment misses */
+    statistic_decrement(hostname_cache_metrics.misses, &hostname_cache_metrics.lock);
     *connect_errors= 0;
   }
   else
   {
     DBUG_PRINT("error",("Couldn't verify hostname with getaddrinfo()."));
 
-    err_status= add_hostname(ip_key, NULL);
+    err_status= hostname_cache_add_deny(ip_key);
     *hostname= NULL;
     *connect_errors= 0;
   }
 
   DBUG_RETURN(err_status);
 }
+
+
+/**
+  Fill the passed information_schema table with host cache information.
+
+  @param  thd     Ubiquitous thread object.
+  @param  tables  The destination of the host-cache info.
+  @param  cond    (Unused.)
+*/
+int hostname_cache_fill_i_s(THD *thd, TABLE_LIST *tables, Item *cond)
+{
+  DBUG_ENTER("fill_host_cache_i_s");
+  int ret= 0;
+
+  const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS;
+  TABLE *table= tables->table;
+  CHARSET_INFO *cs= system_charset_info;
+
+  mysql_mutex_lock(&hostname_cache->lock);
+
+  Host_entry *cur;
+
+  for (cur= (Host_entry *) hostname_cache->newest_element();
+      cur != NULL; cur= (Host_entry *) cur->older_element())
+  {
+    if (wild && wild[0] && wild_case_compare(cs, cur->ip_key, wild))
+      continue;
+
+    table->field[0]->store(cur->ip_key, strlen(cur->ip_key), system_charset_info);
+    if(cur->hostname)
+    {
+      table->field[1]->store(cur->hostname, strlen(cur->hostname), system_charset_info);
+      table->field[1]->set_notnull();
+    }
+    else
+    {
+      table->field[1]->store(NULL, 0, system_charset_info);
+      table->field[1]->set_null();
+    }
+    table->field[2]->store((longlong)cur->connect_errors, TRUE);
+
+    if (schema_table_store_record(thd, table))
+    {
+      ret= 1;
+      goto clean_up;
+    }
+  }
+
+clean_up:
+  mysql_mutex_unlock(&hostname_cache->lock);
+
+  DBUG_RETURN(ret);
+}

=== modified file 'sql/hostname.h'
--- a/sql/hostname.h	2010-07-02 02:58:51 +0000
+++ b/sql/hostname.h	2010-12-22 17:31:51 +0000
@@ -17,14 +17,68 @@
 #define HOSTNAME_INCLUDED
 
 #include "my_global.h"                          /* uint */
+#include "hash_filo.h"
+#include "table.h"
+#ifndef __WIN__
+#include <netdb.h>        // getservbyname, servent
+#endif
+
+/*
+  HOST_ENTRY_KEY_SIZE -- size of IP address string in the hash cache.
+*/
+
+#define HOST_ENTRY_KEY_SIZE INET6_ADDRSTRLEN
+
+
+/**
+  An entry in the hostname hash table cache.
+
+  Host name cache does two things:
+    - caches host names to save DNS look ups;
+    - counts connect errors from IP.
+
+  Host name can be NULL (that means DNS look up failed), but connect errors
+  still are counted.
+*/
+
+class Host_entry :public hash_filo_element
+{
+public:
+  /**
+    Client IP address. This is the key used with the hash table.
+
+    The client IP address is always expressed in IPv6, even when the
+    network IPv6 stack is not present.
+
+    This IP address is never used to connect to a socket.
+  */
+  char ip_key[HOST_ENTRY_KEY_SIZE];
+
+  /**
+    Number of errors during handshake phase from the IP address.
+  */
+  uint connect_errors;
+
+  /**
+    One of the host names for the IP address. May be NULL.
+  */
+  char *hostname;
+};
+
+
+extern hash_filo_metrics hostname_cache_metrics;
+extern ulong host_cache_size;
 
 bool ip_to_hostname(struct sockaddr_storage *ip_storage,
                     const char *ip_string,
                     char **hostname, uint *connect_errors);
-void inc_host_errors(const char *ip_string);
-void reset_host_errors(const char *ip_string);
+void hostname_cache_inc_host_errors(const char *ip_string);
+void hostname_cache_reset_host_errors(const char *ip_string);
 bool hostname_cache_init();
 void hostname_cache_free();
 void hostname_cache_refresh(void);
+void hostname_cache_metrics_reset(void);
+void hostname_cache_resize(uint size);
+int hostname_cache_fill_i_s(THD *thd, TABLE_LIST *tables, Item *cond);
 
 #endif /* HOSTNAME_INCLUDED */

=== modified file 'sql/mysqld.cc'
--- a/sql/mysqld.cc	2010-12-17 11:28:59 +0000
+++ b/sql/mysqld.cc	2010-12-22 17:31:51 +0000
@@ -6674,6 +6674,12 @@ SHOW_VAR status_vars[]= {
   {"Handler_savepoint_rollback",(char*) offsetof(STATUS_VAR, ha_savepoint_rollback_count), SHOW_LONG_STATUS},
   {"Handler_update",           (char*) offsetof(STATUS_VAR, ha_update_count), SHOW_LONG_STATUS},
   {"Handler_write",            (char*) offsetof(STATUS_VAR, ha_write_count), SHOW_LONG_STATUS},
+  {"Host_cache_free",          (char*) &hostname_cache_metrics.free, SHOW_LONG_NOFLUSH},
+  {"Host_cache_hits",          (char*) &hostname_cache_metrics.hits, SHOW_LONG_NOFLUSH},
+  {"Host_cache_inserts",       (char*) &hostname_cache_metrics.inserts, SHOW_LONG_NOFLUSH},
+  {"Host_cache_misses",        (char*) &hostname_cache_metrics.misses, SHOW_LONG_NOFLUSH},
+  {"Host_cache_prunes",        (char*) &hostname_cache_metrics.prunes, SHOW_LONG_NOFLUSH},
+  {"Host_cache_used",          (char*) &hostname_cache_metrics.used, SHOW_LONG_NOFLUSH},
   {"Key_blocks_not_flushed",   (char*) offsetof(KEY_CACHE, global_blocks_changed), SHOW_KEY_CACHE_LONG},
   {"Key_blocks_unused",        (char*) offsetof(KEY_CACHE, blocks_unused), SHOW_KEY_CACHE_LONG},
   {"Key_blocks_used",          (char*) offsetof(KEY_CACHE, blocks_used), SHOW_KEY_CACHE_LONG},
@@ -7858,6 +7864,8 @@ void refresh_status(THD *thd)
   mysql_mutex_lock(&LOCK_thread_count);
   max_used_connections= thread_count-delayed_insert_threads;
   mysql_mutex_unlock(&LOCK_thread_count);
+
+  hostname_cache_metrics_reset();
 }
 
 

=== modified file 'sql/sql_acl.cc'
--- a/sql/sql_acl.cc	2010-12-17 11:28:59 +0000
+++ b/sql/sql_acl.cc	2010-12-22 17:31:51 +0000
@@ -8422,7 +8422,7 @@ static ulong parse_client_handshake_pack
     return packet_error;
 
   if (mpvio->connect_errors)
-    reset_host_errors(mpvio->ip);
+    hostname_cache_reset_host_errors(mpvio->ip);
 
   ulong client_capabilities= uint2korr(net->read_pos);
   if (client_capabilities & CLIENT_PROTOCOL_41)
@@ -8794,7 +8794,7 @@ static int server_mpvio_read_packet(MYSQ
 err:
   if (mpvio->status == MPVIO_EXT::FAILURE)
   {
-    inc_host_errors(mpvio->ip);
+    hostname_cache_inc_host_errors(mpvio->ip);
     my_error(ER_HANDSHAKE_ERROR, MYF(0), mpvio->auth_info.host_or_ip);
   }
   DBUG_RETURN(-1);
@@ -9391,7 +9391,7 @@ static int native_password_authenticate(
                 CR_ERROR : CR_OK);
   }
 
-  inc_host_errors(mpvio->ip);
+  hostname_cache_inc_host_errors(mpvio->ip);
   my_error(ER_HANDSHAKE_ERROR, MYF(0), mpvio->auth_info.host_or_ip);
   DBUG_RETURN(CR_ERROR);
 }
@@ -9445,7 +9445,7 @@ static int old_password_authenticate(MYS
                              CR_ERROR : CR_OK;
   }
 
-  inc_host_errors(mpvio->ip);
+  hostname_cache_inc_host_errors(mpvio->ip);
   my_error(ER_HANDSHAKE_ERROR, MYF(0), mpvio->auth_info.host_or_ip);
   return CR_ERROR;
 }

=== modified file 'sql/sql_show.cc'
--- a/sql/sql_show.cc	2010-12-17 11:28:59 +0000
+++ b/sql/sql_show.cc	2010-12-22 17:31:51 +0000
@@ -53,6 +53,7 @@
 #include "lock.h"                           // MYSQL_OPEN_IGNORE_FLUSH
 #include "debug_sync.h"
 #include "datadict.h"   // dd_frm_type()
+#include "hostname.h"
 
 #define STR_OR_NIL(S) ((S) ? (S) : "<nil>")
 
@@ -7418,6 +7419,15 @@ ST_FIELD_INFO tablespaces_fields_info[]=
 };
 
 
+ST_FIELD_INFO host_cache_info[]=
+{
+  {"IP_ADDRESS", 64, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE},
+  {"HOSTNAME", 64, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, NULL, SKIP_OPEN_TABLE},
+  {"ERRORS", 10, MYSQL_TYPE_LONG, 0, 0, NULL, SKIP_OPEN_TABLE},
+  {NULL, 0, MYSQL_TYPE_STRING, 0, 0, NULL, SKIP_OPEN_TABLE}
+};
+
+
 /*
   Description of ST_FIELD_INFO in table.h
 
@@ -7453,6 +7463,8 @@ ST_SCHEMA_TABLE schema_tables[]=
    fill_status, make_old_format, 0, 0, -1, 0, 0},
   {"GLOBAL_VARIABLES", variables_fields_info, create_schema_table,
    fill_variables, make_old_format, 0, 0, -1, 0, 0},
+  {"HOST_CACHE", host_cache_info, create_schema_table,
+   hostname_cache_fill_i_s, make_old_format, 0, -1, -1, 0, 0},
   {"KEY_COLUMN_USAGE", key_column_usage_fields_info, create_schema_table,
    get_all_tables, 0, get_schema_key_column_usage_record, 4, 5, 0,
    OPTIMIZE_I_S_TABLE|OPEN_TABLE_ONLY},

=== modified file 'sql/sys_vars.cc'
--- a/sql/sys_vars.cc	2010-12-17 11:28:59 +0000
+++ b/sql/sys_vars.cc	2010-12-22 17:31:51 +0000
@@ -45,6 +45,7 @@
                      // mysql_user_table_is_in_short_password_format
 #include "derror.h"  // read_texts
 #include "sql_base.h"                           // close_cached_tables
+#include "hostname.h"                           // host_cache_size, hostname_cache_resize
 
 #ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
 #include "../storage/perfschema/pfs_server.h"
@@ -3224,3 +3225,19 @@ static Sys_var_tz Sys_time_zone(
        SESSION_VAR(time_zone), NO_CMD_LINE,
        DEFAULT(&default_tz), NO_MUTEX_GUARD, IN_BINLOG);
 
+
+static bool fix_host_cache_size(sys_var *self, THD *thd, enum_var_type type)
+{
+  hostname_cache_resize((uint) host_cache_size);
+  return false;
+}
+
+
+static Sys_var_ulong Sys_host_cache_size(
+       "host_cache_size",
+       "How many hostnames should be cached to avoid resolving.",
+       GLOBAL_VAR(host_cache_size),
+       CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 2048),
+       DEFAULT(HOST_CACHE_SIZE),
+       BLOCK_SIZE(1),
+       NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL), ON_UPDATE(fix_host_cache_size));


Attachment: [text/bzr-bundle] bzr/georgi.kodinov@oracle.com-20101222173151-ezl6g8r1wrx4fivr.bundle
Thread
bzr commit into mysql-trunk-wl5259 branch (Georgi.Kodinov:3446) WL#5259Georgi Kodinov22 Dec