• Main Page
  • Related Pages
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

P_HostDBProcessor.h

Go to the documentation of this file.
00001 /** @file
00002 
00003   A brief file description
00004 
00005   @section license License
00006 
00007   Licensed to the Apache Software Foundation (ASF) under one
00008   or more contributor license agreements.  See the NOTICE file
00009   distributed with this work for additional information
00010   regarding copyright ownership.  The ASF licenses this file
00011   to you under the Apache License, Version 2.0 (the
00012   "License"); you may not use this file except in compliance
00013   with the License.  You may obtain a copy of the License at
00014 
00015       http://www.apache.org/licenses/LICENSE-2.0
00016 
00017   Unless required by applicable law or agreed to in writing, software
00018   distributed under the License is distributed on an "AS IS" BASIS,
00019   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00020   See the License for the specific language governing permissions and
00021   limitations under the License.
00022  */
00023 
00024 /****************************************************************************
00025   P_HostDBProcessor.h
00026  ****************************************************************************/
00027 
00028 #ifndef _P_HostDBProcessor_h_
00029 #define _P_HostDBProcessor_h_
00030 
00031 #include "I_HostDBProcessor.h"
00032 
00033 //
00034 // Data
00035 //
00036 
00037 extern int hostdb_enable;
00038 extern int hostdb_migrate_on_demand;
00039 extern int hostdb_cluster;
00040 extern int hostdb_cluster_round_robin;
00041 extern int hostdb_lookup_timeout;
00042 extern int hostdb_insert_timeout;
00043 extern int hostdb_re_dns_on_reload;
00044 
00045 // 0 = obey, 1 = ignore, 2 = min(X,ttl), 3 = max(X,ttl)
00046 enum
00047   { TTL_OBEY, TTL_IGNORE, TTL_MIN, TTL_MAX };
00048 extern int hostdb_ttl_mode;
00049 
00050 extern unsigned int hostdb_current_interval;
00051 extern unsigned int hostdb_ip_stale_interval;
00052 extern unsigned int hostdb_ip_timeout_interval;
00053 extern unsigned int hostdb_ip_fail_timeout_interval;
00054 extern int hostdb_size;
00055 extern int hostdb_srv_enabled;
00056 extern char hostdb_filename[PATH_NAME_MAX + 1];
00057 
00058 //extern int hostdb_timestamp;
00059 extern int hostdb_sync_frequency;
00060 extern int hostdb_disable_reverse_lookup;
00061 
00062 // Static configuration information
00063 extern HostDBCache hostDB;
00064 
00065 /** Host DB record mark.
00066 
00067     The records in the host DB are de facto segregated by roughly the
00068     DNS query type. We use an intermediate type to provide a little flexibility
00069     although the type is presumed to be a single byte.
00070  */
00071 enum HostDBMark {
00072   HOSTDB_MARK_GENERIC, ///< Anything that's not one of the other types.
00073   HOSTDB_MARK_IPV4, ///< IPv4 / T_A
00074   HOSTDB_MARK_IPV6, ///< IPv6 / T_AAAA
00075   HOSTDB_MARK_SRV, ///< Service / T_SRV
00076 };
00077 /** Convert a HostDB @a mark to a string.
00078     @return A static string.
00079  */
00080 extern char const* string_for(HostDBMark mark);
00081 
00082 inline unsigned int HOSTDB_CLIENT_IP_HASH(
00083   sockaddr const* lhs,
00084   sockaddr const* rhs
00085 ) {
00086   unsigned int zret = ~static_cast<unsigned int>(0);
00087   if (ats_ip_are_compatible(lhs,rhs)) {
00088     if (ats_is_ip4(lhs)) {
00089       in_addr_t ip1 = ats_ip4_addr_cast(lhs);
00090       in_addr_t ip2 = ats_ip4_addr_cast(rhs);
00091       zret = (ip1 >> 16) ^ ip1 ^ ip2 ^ (ip2 >> 16);
00092     } else if (ats_is_ip6(lhs)) {
00093       uint32_t const* ip1 = ats_ip_addr32_cast(lhs);
00094       uint32_t const* ip2 = ats_ip_addr32_cast(rhs);
00095       for ( int i = 0 ; i < 4 ; ++i, ++ip1, ++ip2 ) {
00096         zret ^= (*ip1 >> 16) ^ *ip1 ^ *ip2 ^ (*ip2 >> 16);
00097       }
00098     }
00099   }
00100   return zret & 0xFFFF;
00101 }
00102 
00103 //
00104 // Constants
00105 //
00106 
00107 #define HOST_DB_HITS_BITS           3
00108 #define HOST_DB_TAG_BITS            56
00109 
00110 #define CONFIGURATION_HISTORY_PROBE_DEPTH   1
00111 
00112 // Bump this any time hostdb format is changed
00113 #define HOST_DB_CACHE_MAJOR_VERSION         3
00114 #define HOST_DB_CACHE_MINOR_VERSION         0
00115 // 2.2: IP family split 2.1 : IPv6
00116 
00117 #define DEFAULT_HOST_DB_FILENAME             "host.db"
00118 #define DEFAULT_HOST_DB_SIZE                 (1<<14)
00119 // Resolution of timeouts
00120 #define HOST_DB_TIMEOUT_INTERVAL             HRTIME_SECOND
00121 // Timeout DNS every 24 hours by default if ttl_mode is enabled
00122 #define HOST_DB_IP_TIMEOUT                   (24*60*60)
00123 // DNS entries should be revalidated every 12 hours
00124 #define HOST_DB_IP_STALE                     (12*60*60)
00125 // DNS entries which failed lookup, should be revalidated every hour
00126 #define HOST_DB_IP_FAIL_TIMEOUT              (60*60)
00127 
00128 //#define HOST_DB_MAX_INTERVAL                 (0x7FFFFFFF)
00129 #define HOST_DB_MAX_TTL                      (0x1FFFFF) //24 days
00130 
00131 //
00132 // Constants
00133 //
00134 
00135 // period to wait for a remote probe...
00136 #define HOST_DB_CLUSTER_TIMEOUT  HRTIME_MSECONDS(5000)
00137 #define HOST_DB_RETRY_PERIOD     HRTIME_MSECONDS(20)
00138 
00139 //#define TEST(_x) _x
00140 #define TEST(_x)
00141 
00142 
00143 #ifdef _HOSTDB_CC_
00144 template struct MultiCache <HostDBInfo >;
00145 #endif /* _HOSTDB_CC_ */
00146 
00147 struct ClusterMachine;
00148 struct HostEnt;
00149 struct ClusterConfiguration;
00150 
00151 // Stats
00152 enum HostDB_Stats
00153 {
00154   hostdb_total_entries_stat,
00155   hostdb_total_lookups_stat,
00156   hostdb_total_hits_stat,       // D == total hits
00157   hostdb_ttl_stat,              // D average TTL
00158   hostdb_ttl_expires_stat,      // D == TTL Expires
00159   hostdb_re_dns_on_reload_stat,
00160   hostdb_bytes_stat,
00161   HostDB_Stat_Count
00162 };
00163 
00164 
00165 struct RecRawStatBlock;
00166 extern RecRawStatBlock *hostdb_rsb;
00167 
00168 // Stat Macros
00169 
00170 #define HOSTDB_DEBUG_COUNT_DYN_STAT(_x, _y) \
00171 RecIncrRawStatCount(hostdb_rsb, mutex->thread_holding, (int)_x, _y)
00172 
00173 #define HOSTDB_INCREMENT_DYN_STAT(_x)  \
00174 RecIncrRawStatSum(hostdb_rsb, mutex->thread_holding, (int)_x, 1)
00175 
00176 #define HOSTDB_DECREMENT_DYN_STAT(_x) \
00177 RecIncrRawStatSum(hostdb_rsb, mutex->thread_holding, (int)_x, -1)
00178 
00179 #define HOSTDB_SUM_DYN_STAT(_x, _r) \
00180 RecIncrRawStatSum(hostdb_rsb, mutex->thread_holding, (int)_x, _r)
00181 
00182 #define HOSTDB_READ_DYN_STAT(_x, _count, _sum) do {\
00183 RecGetRawStatSum(hostdb_rsb, (int)_x, &_sum);          \
00184 RecGetRawStatCount(hostdb_rsb, (int)_x, &_count);         \
00185 } while (0)
00186 
00187 #define HOSTDB_SET_DYN_COUNT(_x, _count) \
00188 RecSetRawStatCount(hostdb_rsb, _x, _count);
00189 
00190 #define HOSTDB_INCREMENT_THREAD_DYN_STAT(_s, _t) \
00191   RecIncrRawStatSum(hostdb_rsb, _t, (int) _s, 1);
00192 
00193 #define HOSTDB_DECREMENT_THREAD_DYN_STAT(_s, _t) \
00194   RecIncrRawStatSum(hostdb_rsb, _t, (int) _s, -1);
00195 
00196 
00197 //
00198 // HostDBCache (Private)
00199 //
00200 struct HostDBCache: public MultiCache<HostDBInfo>
00201 {
00202   int rebuild_callout(HostDBInfo * e, RebuildMC & r);
00203   int start(int flags = 0);
00204   MultiCacheBase *dup()
00205   {
00206     return new HostDBCache;
00207   }
00208 
00209   // This accounts for an average of 2 HostDBInfo per DNS cache (for round-robin etc.)
00210   // In addition, we can do a padding for additional SRV records storage.
00211   virtual size_t estimated_heap_bytes_per_entry() const { return sizeof(HostDBInfo) * 2 + 512 * hostdb_srv_enabled; }
00212 
00213   Queue<HostDBContinuation, Continuation::Link_link> pending_dns[MULTI_CACHE_PARTITIONS];
00214   Queue<HostDBContinuation, Continuation::Link_link> &pending_dns_for_hash(INK_MD5 & md5);
00215   HostDBCache();
00216 };
00217 
00218 inline int
00219 HostDBRoundRobin::index_of(sockaddr const* ip) {
00220   bool bad = (rrcount <= 0 || rrcount > HOST_DB_MAX_ROUND_ROBIN_INFO || good <= 0 || good > HOST_DB_MAX_ROUND_ROBIN_INFO);
00221   if (bad) {
00222     ink_assert(!"bad round robin size");
00223     return -1;
00224   }
00225 
00226   for (int i = 0; i < good; i++) {
00227     if (ats_ip_addr_eq(ip, info[i].ip())) {
00228       return i;
00229     }
00230   }
00231 
00232   return -1;
00233 }
00234 
00235 inline HostDBInfo*
00236 HostDBRoundRobin::find_ip(sockaddr const* ip) {
00237   int idx = this->index_of(ip);
00238   return idx < 0 ? NULL : &info[idx];
00239 }
00240 
00241 inline HostDBInfo*
00242 HostDBRoundRobin::select_next(sockaddr const* ip) {
00243   HostDBInfo* zret = 0;
00244   if (good > 1) {
00245     int idx = this->index_of(ip);
00246     if (idx >= 0) {
00247       idx = (idx+1)%good;
00248       zret = &info[idx];
00249     }
00250   }
00251   return zret;
00252 }
00253 
00254 inline HostDBInfo *
00255 HostDBRoundRobin::find_target(const char *target) {
00256   bool bad = (rrcount <= 0 || rrcount > HOST_DB_MAX_ROUND_ROBIN_INFO || good <= 0 || good > HOST_DB_MAX_ROUND_ROBIN_INFO);
00257   if (bad) {
00258     ink_assert(!"bad round robin size");
00259     return NULL;
00260   }
00261 
00262   uint32_t key = makeHostHash(target);
00263   for (int i = 0; i < good; i++) {
00264     if (info[i].data.srv.key == key && !strcmp(target, info[i].srvname(this)))
00265       return &info[i];
00266   }
00267   return NULL;
00268 }
00269 
00270 inline HostDBInfo *
00271 HostDBRoundRobin::select_best_http(sockaddr const* client_ip, ink_time_t now, int32_t fail_window)
00272 {
00273   bool bad = (rrcount <= 0 || rrcount > HOST_DB_MAX_ROUND_ROBIN_INFO || good <= 0 || good > HOST_DB_MAX_ROUND_ROBIN_INFO);
00274 
00275   if (bad) {
00276     ink_assert(!"bad round robin size");
00277     return NULL;
00278   }
00279 
00280   int best_any = 0;
00281   int best_up = -1;
00282 
00283   if (HostDBProcessor::hostdb_strict_round_robin) {
00284     Debug("hostdb", "Using strict round robin");
00285     best_up = current++ % good;
00286   } else if (HostDBProcessor::hostdb_timed_round_robin > 0) {
00287     Debug("hostdb", "Using timed round-robin for HTTP");
00288     if ((now - timed_rr_ctime) > HostDBProcessor::hostdb_timed_round_robin) {
00289       Debug("hostdb", "Timed interval expired.. rotating");
00290       ++current;
00291       timed_rr_ctime = now;
00292     }
00293     best_up = current % good;
00294     Debug("hostdb", "Using %d for best_up", best_up);
00295   } else {
00296     Debug("hostdb", "Using default round robin");
00297     unsigned int best_hash_any = 0;
00298     unsigned int best_hash_up = 0;
00299     sockaddr const* ip;
00300     for (int i = 0; i < good; i++) {
00301       ip = info[i].ip();
00302       unsigned int h = HOSTDB_CLIENT_IP_HASH(client_ip, ip);
00303       if (best_hash_any <= h) {
00304         best_any = i;
00305         best_hash_any = h;
00306       }
00307       if (info[i].app.http_data.last_failure == 0 ||
00308           (unsigned int) (now - fail_window) > info[i].app.http_data.last_failure) {
00309         // Entry is marked up
00310         if (best_hash_up <= h) {
00311           best_up = i;
00312           best_hash_up = h;
00313         }
00314       } else {
00315         // Entry is marked down.  Make sure some nasty clock skew
00316         //  did not occur.  Use the retry time to set an upper bound
00317         //  as to how far in the future we should tolerate bogus last
00318         //  failure times.  This sets the upper bound that we would ever
00319         //  consider a server down to 2*down_server_timeout
00320         if (now + fail_window < (int32_t) (info[i].app.http_data.last_failure)) {
00321 #ifdef DEBUG
00322           // because this region is mmaped, I cann't get anything
00323           //   useful from the structure in core files,  therefore
00324           //   copy the revelvant info to the stack so it will
00325           //   be readble in the core
00326           HostDBInfo current_info;
00327           HostDBRoundRobin current_rr;
00328           memcpy(&current_info, &info[i], sizeof(HostDBInfo));
00329           memcpy(&current_rr, this, sizeof(HostDBRoundRobin));
00330 #endif
00331           ink_assert(!"extreme clock skew");
00332           info[i].app.http_data.last_failure = 0;
00333         }
00334       }
00335     }
00336   }
00337 
00338   if (best_up != -1) {
00339     ink_assert(best_up >= 0 && best_up < good);
00340     return &info[best_up];
00341   } else {
00342     ink_assert(best_any >= 0 && best_any < good);
00343     return &info[best_any];
00344   }
00345 }
00346 
00347 inline HostDBInfo *
00348 HostDBRoundRobin::select_best_srv(char *target, InkRand *rand, ink_time_t now, int32_t fail_window)
00349 {
00350   bool bad = (rrcount <= 0 || rrcount > HOST_DB_MAX_ROUND_ROBIN_INFO || good <= 0 || good > HOST_DB_MAX_ROUND_ROBIN_INFO);
00351 
00352   if (bad) {
00353     ink_assert(!"bad round robin size");
00354     return NULL;
00355   }
00356 
00357 #ifdef DEBUG
00358   for (int i = 1; i < good; ++i) {
00359     ink_assert(info[i].data.srv.srv_priority >= info[i-1].data.srv.srv_priority);
00360   }
00361 #endif
00362 
00363   int i = 0, len = 0;
00364   uint32_t weight = 0, p = INT32_MAX;
00365   HostDBInfo *result = NULL;
00366   HostDBInfo *infos[HOST_DB_MAX_ROUND_ROBIN_INFO];
00367 
00368   do {
00369     if (info[i].app.http_data.last_failure != 0 &&
00370         (uint32_t) (now - fail_window) < info[i].app.http_data.last_failure) {
00371       continue;
00372     }
00373 
00374     if (info[i].app.http_data.last_failure)
00375       info[i].app.http_data.last_failure = 0;
00376 
00377     if (info[i].data.srv.srv_priority <= p) {
00378       p = info[i].data.srv.srv_priority;
00379       weight += info[i].data.srv.srv_weight;
00380       infos[len++] = &info[i];
00381     } else
00382       break;
00383   } while (++i < good);
00384 
00385   if (len == 0) { // all failed
00386     result = &info[current++ % good];
00387   } else if (weight == 0) { // srv weight is 0
00388     result = &info[current++ % len];
00389   } else {
00390     uint32_t xx = rand->random() % weight;
00391     for (i = 0; i < len && xx >= infos[i]->data.srv.srv_weight; ++i)
00392       xx -= infos[i]->data.srv.srv_weight;
00393 
00394     result = infos[i];
00395   }
00396 
00397   if (result) {
00398     strcpy(target, result->srvname(this));
00399     return result;
00400   }
00401   return NULL;
00402 }
00403 
00404 //
00405 // Types
00406 //
00407 
00408 /** Container for an MD5 hash and its dependent data.
00409     This handles both the host name and raw address cases.
00410 */
00411 struct HostDBMD5 {
00412   typedef HostDBMD5 self; ///< Self reference type.
00413 
00414   INK_MD5 hash; ///< The hash value.
00415 
00416   char const* host_name; ///< Host name.
00417   int host_len; ///< Length of @a _host_name
00418   IpAddr ip; ///< IP address.
00419   int port; ///< IP port (host order).
00420   /// DNS server. Not strictly part of the MD5 data but
00421   /// it's both used by @c HostDBContinuation and provides access to
00422   /// MD5 data. It's just handier to store it here for both uses.
00423   DNSServer* dns_server;
00424   HostDBMark db_mark; ///< Mark / type of record.
00425 
00426   /// Default constructor.
00427   HostDBMD5();
00428   /// Recompute and update the MD5 hash.
00429   void refresh();
00430 };
00431 
00432 //
00433 // Handles a HostDB lookup request
00434 //
00435 struct HostDBContinuation;
00436 typedef int (HostDBContinuation::*HostDBContHandler) (int, void *);
00437 
00438 struct HostDBContinuation: public Continuation
00439 {
00440   Action action;
00441   HostDBMD5 md5;
00442   //  IpEndpoint ip;
00443   unsigned int ttl;
00444   //  HostDBMark db_mark; ///< Target type.
00445   /// Original IP address family style. Note this will disagree with
00446   /// @a md5.db_mark when doing a retry on an alternate family. The retry
00447   /// logic depends on it to avoid looping.
00448   HostResStyle host_res_style; ///< Address family priority.
00449   int dns_lookup_timeout;
00450   //  INK_MD5 md5;
00451   Event *timeout;
00452   ClusterMachine *from;
00453   Continuation *from_cont;
00454   HostDBApplicationInfo app;
00455   int probe_depth;
00456   ClusterMachine *past_probes[CONFIGURATION_HISTORY_PROBE_DEPTH];
00457   //  char name[MAXDNAME];
00458   //  int namelen;
00459   char md5_host_name_store[MAXDNAME+1]; // used as backing store for @a md5
00460   char srv_target_name[MAXDNAME];
00461   void *m_pDS;
00462   Action *pending_action;
00463 
00464   unsigned int missing:1;
00465   unsigned int force_dns:1;
00466   unsigned int round_robin:1;
00467 
00468   int probeEvent(int event, Event * e);
00469   int clusterEvent(int event, Event * e);
00470   int clusterResponseEvent(int event, Event * e);
00471   int dnsEvent(int event, HostEnt * e);
00472   int dnsPendingEvent(int event, Event * e);
00473   int backgroundEvent(int event, Event * e);
00474   int retryEvent(int event, Event * e);
00475   int removeEvent(int event, Event * e);
00476   int setbyEvent(int event, Event * e);
00477 
00478   /// Recompute the MD5 and update ancillary values.
00479   void refresh_MD5();
00480   void do_dns();
00481   bool is_byname()
00482   {
00483     return md5.db_mark == HOSTDB_MARK_IPV4 || md5.db_mark == HOSTDB_MARK_IPV6;
00484   }
00485   bool is_srv()
00486   {
00487     return md5.db_mark == HOSTDB_MARK_SRV;
00488   }
00489   HostDBInfo *lookup_done(IpAddr const& ip, char const* aname, bool round_robin, unsigned int attl, SRVHosts * s = NULL);
00490   bool do_get_response(Event * e);
00491   void do_put_response(ClusterMachine * m, HostDBInfo * r, Continuation * cont);
00492   int failed_cluster_request(Event * e);
00493   int key_partition();
00494   void remove_trigger_pending_dns();
00495   int set_check_pending_dns();
00496 
00497   ClusterMachine *master_machine(ClusterConfiguration * cc);
00498 
00499   HostDBInfo *insert(unsigned int attl);
00500 
00501   /** Optional values for @c init.
00502    */
00503   struct Options {
00504     typedef Options self; ///< Self reference type.
00505 
00506     int timeout; ///< Timeout value. Default 0
00507     HostResStyle host_res_style; ///< IP address family fallback. Default @c HOST_RES_NONE
00508     bool force_dns; ///< Force DNS lookup. Default @c false
00509     Continuation* cont; ///< Continuation / action. Default @c NULL (none)
00510 
00511     Options()
00512       : timeout(0), host_res_style(HOST_RES_NONE), force_dns(false), cont(0)
00513     { }
00514   };
00515   static const Options DEFAULT_OPTIONS; ///< Default defaults.
00516   void init(HostDBMD5 const& md5,
00517             Options const& opt = DEFAULT_OPTIONS);
00518   int make_get_message(char *buf, int len);
00519   int make_put_message(HostDBInfo * r, Continuation * c, char *buf, int len);
00520 
00521 HostDBContinuation():
00522   Continuation(NULL), ttl(0),
00523     host_res_style(DEFAULT_OPTIONS.host_res_style),
00524     dns_lookup_timeout(DEFAULT_OPTIONS.timeout),
00525     timeout(0), from(0),
00526     from_cont(0), probe_depth(0), missing(false),
00527     force_dns(DEFAULT_OPTIONS.force_dns), round_robin(false) {
00528     ink_zero(md5_host_name_store);
00529     ink_zero(md5.hash);
00530     SET_HANDLER((HostDBContHandler) & HostDBContinuation::probeEvent);
00531   }
00532 };
00533 
00534 //extern Queue<HostDBContinuation>  remoteHostDBQueue[MULTI_CACHE_PARTITIONS];
00535 
00536 inline unsigned int
00537 master_hash(INK_MD5 const& md5)
00538 {
00539   return static_cast<int>(md5[1] >> 32);
00540 }
00541 
00542 inline bool
00543 is_dotted_form_hostname(const char *c)
00544 {
00545   return -1 != (int) ink_inet_addr(c);
00546 }
00547 
00548 inline Queue<HostDBContinuation> &
00549 HostDBCache::pending_dns_for_hash(INK_MD5 & md5)
00550 {
00551   return pending_dns[partition_of_bucket((int) (fold_md5(md5) % hostDB.buckets))];
00552 }
00553 
00554 inline int
00555 HostDBContinuation::key_partition()
00556 {
00557   return hostDB.partition_of_bucket(fold_md5(md5.hash) % hostDB.buckets);
00558 }
00559 
00560 #endif /* _P_HostDBProcessor_h_ */

Generated by  doxygen 1.7.1