2526. [func] New named option "attach-cache" that allows multiple

views to share a single cache to save memory and
			improve lookup efficiency. [RT 18905]
This commit is contained in:
Tatuya JINMEI 神明達哉 2009-01-09 22:24:37 +00:00
parent 2cc6eb92f9
commit 7781f25078
11 changed files with 664 additions and 122 deletions

View file

@ -1,3 +1,7 @@
2526. [func] New named option "attach-cache" that allows multiple
views to share a single cache to save memory and
improve lookup efficiency. [RT 18905]
2525. [func] New logging category "query-errors" to provide detailed
internal information about query failures, especially
about server failures. [RT #19027]

View file

@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: server.h,v 1.93 2008/04/03 05:55:51 marka Exp $ */
/* $Id: server.h,v 1.94 2009/01/09 22:24:36 jinmei Exp $ */
#ifndef NAMED_SERVER_H
#define NAMED_SERVER_H 1
@ -91,6 +91,7 @@ struct ns_server {
isc_boolean_t flushonshutdown;
isc_boolean_t log_queries; /*%< For BIND 8 compatibility */
ns_cachelist_t cachelist; /*%< Possibly shared caches */
dns_stats_t * nsstats; /*%< Server statistics */
dns_stats_t * rcvquerystats; /*% Incoming query statistics */
dns_stats_t * opcodestats; /*%< Incoming message statistics */

View file

@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: types.h,v 1.29 2008/01/17 23:46:59 tbox Exp $ */
/* $Id: types.h,v 1.30 2009/01/09 22:24:36 jinmei Exp $ */
#ifndef NAMED_TYPES_H
#define NAMED_TYPES_H 1
@ -24,6 +24,8 @@
#include <dns/types.h>
typedef struct ns_cache ns_cache_t;
typedef ISC_LIST(ns_cache_t) ns_cachelist_t;
typedef struct ns_client ns_client_t;
typedef struct ns_clientmgr ns_clientmgr_t;
typedef struct ns_query ns_query_t;

View file

@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: server.c,v 1.522 2008/12/25 02:02:39 jinmei Exp $ */
/* $Id: server.c,v 1.523 2009/01/09 22:24:36 jinmei Exp $ */
/*! \file */
@ -142,6 +142,14 @@
fatal(msg, result); \
} while (0) \
/*%
* Maximum ADB size for views that share a cache. Use this limit to suppress
* the total of memory footprint, which should be the main reason for sharing
* a cache. Only effective when a finite max-cache-size is specified.
* This is currently defined to be 8MB.
*/
#define MAX_ADB_SIZE_FOR_CACHESHARE 8388608
struct ns_dispatch {
isc_sockaddr_t addr;
unsigned int dispatchgen;
@ -149,6 +157,14 @@ struct ns_dispatch {
ISC_LINK(struct ns_dispatch) link;
};
struct ns_cache {
dns_cache_t *cache;
dns_view_t *primaryview;
isc_boolean_t needflush;
isc_boolean_t adbsizeadjusted;
ISC_LINK(ns_cache_t) link;
};
struct dumpcontext {
isc_mem_t *mctx;
isc_boolean_t dumpcache;
@ -973,6 +989,63 @@ setquerystats(dns_zone_t *zone, isc_mem_t *mctx, isc_boolean_t on) {
return (ISC_R_SUCCESS);
}
static ns_cache_t *
cachelist_find(ns_cachelist_t *cachelist, const char *cachename) {
ns_cache_t *nsc;
for (nsc = ISC_LIST_HEAD(*cachelist);
nsc != NULL;
nsc = ISC_LIST_NEXT(nsc, link)) {
if (strcmp(dns_cache_getname(nsc->cache), cachename) == 0)
return (nsc);
}
return (NULL);
}
static isc_boolean_t
cache_reusable(dns_view_t *originview, dns_view_t *view,
isc_boolean_t new_zero_no_soattl)
{
if (originview->checknames != view->checknames ||
dns_resolver_getzeronosoattl(originview->resolver) !=
new_zero_no_soattl ||
originview->acceptexpired != view->acceptexpired ||
originview->enablevalidation != view->enablevalidation ||
originview->maxcachettl != view->maxcachettl ||
originview->maxncachettl != view->maxncachettl) {
return (ISC_FALSE);
}
return (ISC_TRUE);
}
static isc_boolean_t
cache_sharable(dns_view_t *originview, dns_view_t *view,
isc_boolean_t new_zero_no_soattl,
unsigned int new_cleaning_interval,
isc_uint32_t new_max_cache_size)
{
/*
* If the cache cannot even reused for the same view, it cannot be
* shared with other views.
*/
if (!cache_reusable(originview, view, new_zero_no_soattl))
return (ISC_FALSE);
/*
* Check other cache related parameters that must be consistent among
* the sharing views.
*/
if (dns_cache_getcleaninginterval(originview->cache) !=
new_cleaning_interval ||
dns_cache_getcachesize(originview->cache) != new_max_cache_size) {
return (ISC_FALSE);
}
return (ISC_TRUE);
}
/*
* Configure 'view' according to 'vconfig', taking defaults from 'config'
* where values are missing in 'vconfig'.
@ -982,8 +1055,9 @@ setquerystats(dns_zone_t *zone, isc_mem_t *mctx, isc_boolean_t on) {
*/
static isc_result_t
configure_view(dns_view_t *view, const cfg_obj_t *config,
const cfg_obj_t *vconfig, isc_mem_t *mctx,
cfg_aclconfctx_t *actx, isc_boolean_t need_hints)
const cfg_obj_t *vconfig, ns_cachelist_t *cachelist,
isc_mem_t *mctx, cfg_aclconfctx_t *actx,
isc_boolean_t need_hints)
{
const cfg_obj_t *maps[4];
const cfg_obj_t *cfgmaps[3];
@ -1005,6 +1079,7 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
dns_cache_t *cache = NULL;
isc_result_t result;
isc_uint32_t max_adb_size;
unsigned int cleaning_interval;
isc_uint32_t max_cache_size;
isc_uint32_t max_acache_size;
isc_uint32_t lame_ttl;
@ -1014,8 +1089,10 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
dns_dispatch_t *dispatch4 = NULL;
dns_dispatch_t *dispatch6 = NULL;
isc_boolean_t reused_cache = ISC_FALSE;
isc_boolean_t shared_cache = ISC_FALSE;
int i;
const char *str;
const char *cachename = NULL;
dns_order_t *order = NULL;
isc_uint32_t udpsize;
unsigned int resopts = 0;
@ -1029,6 +1106,8 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
const cfg_obj_t *disablelist = NULL;
dns_stats_t *resstats = NULL;
dns_stats_t *resquerystats = NULL;
ns_cache_t *nsc;
isc_boolean_t zero_no_soattl;
REQUIRE(DNS_VIEW_VALID(view));
@ -1170,59 +1249,13 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
#endif
/*
* Configure the view's cache. Try to reuse an existing
* cache if possible, otherwise create a new cache.
* Note that the ADB is not preserved in either case.
* When a matching view is found, the associated statistics are
* also retrieved and reused.
*
* XXX Determining when it is safe to reuse a cache is
* tricky. When the view's configuration changes, the cached
* data may become invalid because it reflects our old
* view of the world. As more view attributes become
* configurable, we will have to add code here to check
* whether they have changed in ways that could
* invalidate the cache.
* Obtain configuration parameters that affect the decision of whether
* we can reuse/share an existing cache.
*/
result = dns_viewlist_find(&ns_g_server->viewlist,
view->name, view->rdclass,
&pview);
if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
goto cleanup;
if (pview != NULL) {
INSIST(pview->cache != NULL);
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(3),
"reusing existing cache");
reused_cache = ISC_TRUE;
dns_cache_attach(pview->cache, &cache);
dns_view_getresstats(pview, &resstats);
dns_view_getresquerystats(pview, &resquerystats);
dns_view_detach(&pview);
} else {
CHECK(isc_mem_create(0, 0, &cmctx));
CHECK(dns_cache_create(cmctx, ns_g_taskmgr, ns_g_timermgr,
view->rdclass, "rbt", 0, NULL, &cache));
isc_mem_setname(cmctx, "cache", NULL);
}
dns_view_setcache(view, cache);
/*
* cache-file cannot be inherited if views are present, but this
* should be caught by the configuration checking stage.
*/
obj = NULL;
result = ns_config_get(maps, "cache-file", &obj);
if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") != 0) {
CHECK(dns_cache_setfilename(cache, cfg_obj_asstring(obj)));
if (!reused_cache)
CHECK(dns_cache_load(cache));
}
obj = NULL;
result = ns_config_get(maps, "cleaning-interval", &obj);
INSIST(result == ISC_R_SUCCESS);
dns_cache_setcleaninginterval(cache, cfg_obj_asuint32(obj) * 60);
cleaning_interval = cfg_obj_asuint32(obj) * 60;
obj = NULL;
result = ns_config_get(maps, "max-cache-size", &obj);
@ -1244,13 +1277,8 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
}
max_cache_size = (isc_uint32_t)value;
}
dns_cache_setcachesize(cache, max_cache_size);
dns_cache_detach(&cache);
/*
* Check-names.
*/
/* Check-names. */
obj = NULL;
result = ns_checknames_get(maps, "response", &obj);
INSIST(result == ISC_R_SUCCESS);
@ -1268,6 +1296,159 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
} else
INSIST(0);
obj = NULL;
result = ns_config_get(maps, "zero-no-soa-ttl-cache", &obj);
INSIST(result == ISC_R_SUCCESS);
zero_no_soattl = cfg_obj_asboolean(obj);
obj = NULL;
result = ns_config_get(maps, "dnssec-accept-expired", &obj);
INSIST(result == ISC_R_SUCCESS);
view->acceptexpired = cfg_obj_asboolean(obj);
obj = NULL;
result = ns_config_get(maps, "dnssec-validation", &obj);
INSIST(result == ISC_R_SUCCESS);
view->enablevalidation = cfg_obj_asboolean(obj);
obj = NULL;
result = ns_config_get(maps, "max-cache-ttl", &obj);
INSIST(result == ISC_R_SUCCESS);
view->maxcachettl = cfg_obj_asuint32(obj);
obj = NULL;
result = ns_config_get(maps, "max-ncache-ttl", &obj);
INSIST(result == ISC_R_SUCCESS);
view->maxncachettl = cfg_obj_asuint32(obj);
if (view->maxncachettl > 7 * 24 * 3600)
view->maxncachettl = 7 * 24 * 3600;
/*
* Configure the view's cache.
*
* First, check to see if there are any attach-cache options. If yes,
* attempt to lookup an existing cache at attach it to the view. If
* there is not one, then try to reuse an existing cache if possible;
* otherwise create a new cache.
*
* Note that the ADB is not preserved or shared in either case.
*
* When a matching view is found, the associated statistics are also
* retrieved and reused.
*
* XXX Determining when it is safe to reuse or share a cache is tricky.
* When the view's configuration changes, the cached data may become
* invalid because it reflects our old view of the world. We check
* some of the configuration parameters that could invalidate the cache
* or otherwise make it unsharable, but there are other configuration
* options that should be checked. For example, if a view uses a
* forwarder, changes in the forwarder configuration may invalidate
* the cache. At the moment, it's the administrator's responsibility to
* ensure these configuration options don't invalidate reusing/sharing.
*/
obj = NULL;
result = ns_config_get(maps, "attach-cache", &obj);
if (result == ISC_R_SUCCESS)
cachename = cfg_obj_asstring(obj);
else
cachename = view->name;
cache = NULL;
nsc = cachelist_find(cachelist, cachename);
if (nsc != NULL) {
if (!cache_sharable(nsc->primaryview, view, zero_no_soattl,
cleaning_interval, max_cache_size)) {
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
"views %s and %s can't share the cache "
"due to configuration parameter mismatch",
nsc->primaryview->name, view->name);
result = ISC_R_FAILURE;
goto cleanup;
}
dns_cache_attach(nsc->cache, &cache);
shared_cache = ISC_TRUE;
} else {
if (strcmp(cachename, view->name) == 0) {
result = dns_viewlist_find(&ns_g_server->viewlist,
cachename, view->rdclass,
&pview);
if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
goto cleanup;
if (pview != NULL) {
if (cache_reusable(pview, view,
zero_no_soattl)) {
isc_log_write(ns_g_lctx,
NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER,
ISC_LOG_DEBUG(1),
"cache cannot be reused "
"for view %s due to "
"configuration parameter "
"mismatch", view->name);
} else {
INSIST(pview->cache != NULL);
isc_log_write(ns_g_lctx,
NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER,
ISC_LOG_DEBUG(3),
"reusing existing cache");
reused_cache = ISC_TRUE;
dns_cache_attach(pview->cache, &cache);
}
dns_view_getresstats(pview, &resstats);
dns_view_getresquerystats(pview,
&resquerystats);
dns_view_detach(&pview);
}
}
if (cache == NULL) {
/*
* Create a cache with the desired name. This normally
* equals the view name, but may also be a forward
* reference to a view that share the cache with this
* view but is not yet configured. If it is not the
* view name but not a forward reference either, then it
* is simply a named cache that is not shared.
*/
CHECK(isc_mem_create(0, 0, &cmctx));
isc_mem_setname(cmctx, "cache", NULL);
CHECK(dns_cache_create2(cmctx, ns_g_taskmgr,
ns_g_timermgr, view->rdclass,
cachename, "rbt", 0, NULL,
&cache));
}
nsc = isc_mem_get(mctx, sizeof(*nsc));
if (nsc == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
nsc->cache = NULL;
dns_cache_attach(cache, &nsc->cache);
nsc->primaryview = view;
nsc->needflush = ISC_FALSE;
nsc->adbsizeadjusted = ISC_FALSE;
ISC_LINK_INIT(nsc, link);
ISC_LIST_APPEND(*cachelist, nsc, link);
}
dns_view_setcache2(view, cache, shared_cache);
/*
* cache-file cannot be inherited if views are present, but this
* should be caught by the configuration checking stage.
*/
obj = NULL;
result = ns_config_get(maps, "cache-file", &obj);
if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") != 0) {
CHECK(dns_cache_setfilename(cache, cfg_obj_asstring(obj)));
if (!reused_cache && !shared_cache)
CHECK(dns_cache_load(cache));
}
dns_cache_setcleaninginterval(cache, cleaning_interval);
dns_cache_setcachesize(cache, max_cache_size);
dns_cache_detach(&cache);
/*
* Resolver.
*
@ -1301,13 +1482,23 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
dns_view_setresquerystats(view, resquerystats);
/*
* Set the ADB cache size to 1/8th of the max-cache-size.
* Set the ADB cache size to 1/8th of the max-cache-size or
* MAX_ADB_SIZE_FOR_CACHESHARE when the cache is shared.
*/
max_adb_size = 0;
if (max_cache_size != 0) {
max_adb_size = max_cache_size / 8;
if (max_adb_size == 0)
max_adb_size = 1; /* Force minimum. */
if (view != nsc->primaryview &&
max_adb_size > MAX_ADB_SIZE_FOR_CACHESHARE) {
max_adb_size = MAX_ADB_SIZE_FOR_CACHESHARE;
if (!nsc->adbsizeadjusted) {
dns_adb_setadbsize(nsc->primaryview->adb,
MAX_ADB_SIZE_FOR_CACHESHARE);
nsc->adbsizeadjusted = ISC_TRUE;
}
}
}
dns_adb_setadbsize(view->adb, max_adb_size);
@ -1322,10 +1513,8 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
lame_ttl = 1800;
dns_resolver_setlamettl(view->resolver, lame_ttl);
obj = NULL;
result = ns_config_get(maps, "zero-no-soa-ttl-cache", &obj);
INSIST(result == ISC_R_SUCCESS);
dns_resolver_setzeronosoattl(view->resolver, cfg_obj_asboolean(obj));
/* Specify whether to use 0-TTL for negative response for SOA query */
dns_resolver_setzeronosoattl(view->resolver, zero_no_soattl);
/*
* Set the resolver's EDNS UDP size.
@ -1661,16 +1850,6 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
INSIST(result == ISC_R_SUCCESS);
view->enablednssec = cfg_obj_asboolean(obj);
obj = NULL;
result = ns_config_get(maps, "dnssec-accept-expired", &obj);
INSIST(result == ISC_R_SUCCESS);
view->acceptexpired = cfg_obj_asboolean(obj);
obj = NULL;
result = ns_config_get(maps, "dnssec-validation", &obj);
INSIST(result == ISC_R_SUCCESS);
view->enablevalidation = cfg_obj_asboolean(obj);
obj = NULL;
result = ns_config_get(maps, "dnssec-lookaside", &obj);
if (result == ISC_R_SUCCESS) {
@ -1725,18 +1904,6 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
if (result == ISC_R_SUCCESS)
CHECK(mustbesecure(obj, view->resolver));
obj = NULL;
result = ns_config_get(maps, "max-cache-ttl", &obj);
INSIST(result == ISC_R_SUCCESS);
view->maxcachettl = cfg_obj_asuint32(obj);
obj = NULL;
result = ns_config_get(maps, "max-ncache-ttl", &obj);
INSIST(result == ISC_R_SUCCESS);
view->maxncachettl = cfg_obj_asuint32(obj);
if (view->maxncachettl > 7 * 24 * 3600)
view->maxncachettl = 7 * 24 * 3600;
obj = NULL;
result = ns_config_get(maps, "preferred-glue", &obj);
if (result == ISC_R_SUCCESS) {
@ -2893,10 +3060,13 @@ load_configuration(const char *filename, ns_server_t *server,
isc_uint32_t interface_interval;
isc_uint32_t reserved;
isc_uint32_t udpsize;
ns_cachelist_t cachelist, tmpcachelist;
unsigned int maxsocks;
ns_cache_t *nsc;
cfg_aclconfctx_init(&aclconfctx);
ISC_LIST_INIT(viewlist);
ISC_LIST_INIT(cachelist);
/* Ensure exclusive access to configuration data. */
result = isc_task_beginexclusive(server->task);
@ -3283,7 +3453,7 @@ load_configuration(const char *filename, ns_server_t *server,
CHECK(create_view(vconfig, &viewlist, &view));
INSIST(view != NULL);
CHECK(configure_view(view, config, vconfig,
CHECK(configure_view(view, config, vconfig, &cachelist,
ns_g_mctx, &aclconfctx, ISC_TRUE));
dns_view_freeze(view);
dns_view_detach(&view);
@ -3301,7 +3471,7 @@ load_configuration(const char *filename, ns_server_t *server,
* In either case, we need to configure and freeze it.
*/
CHECK(create_view(NULL, &viewlist, &view));
CHECK(configure_view(view, config, NULL, ns_g_mctx,
CHECK(configure_view(view, config, NULL, &cachelist, ns_g_mctx,
&aclconfctx, ISC_TRUE));
dns_view_freeze(view);
dns_view_detach(&view);
@ -3320,8 +3490,8 @@ load_configuration(const char *filename, ns_server_t *server,
{
const cfg_obj_t *vconfig = cfg_listelt_value(element);
CHECK(create_view(vconfig, &viewlist, &view));
CHECK(configure_view(view, config, vconfig, ns_g_mctx,
&aclconfctx, ISC_FALSE));
CHECK(configure_view(view, config, vconfig, &cachelist,
ns_g_mctx, &aclconfctx, ISC_FALSE));
dns_view_freeze(view);
dns_view_detach(&view);
view = NULL;
@ -3334,6 +3504,13 @@ load_configuration(const char *filename, ns_server_t *server,
server->viewlist = viewlist;
viewlist = tmpviewlist;
/*
* Swap our new cache list with the production one.
*/
tmpcachelist = server->cachelist;
server->cachelist = cachelist;
cachelist = tmpcachelist;
/*
* Load the TKEY information from the configuration.
*/
@ -3625,6 +3802,13 @@ load_configuration(const char *filename, ns_server_t *server,
dns_view_detach(&view);
}
/* Same cleanup for cache list. */
while ((nsc = ISC_LIST_HEAD(cachelist)) != NULL) {
ISC_LIST_UNLINK(cachelist, nsc, link);
dns_cache_detach(&nsc->cache);
isc_mem_put(server->mctx, nsc, sizeof(*nsc));
}
/*
* Adjust the listening interfaces in accordance with the source
* addresses specified in views and zones.
@ -3770,6 +3954,7 @@ shutdown_server(isc_task_t *task, isc_event_t *event) {
dns_view_t *view, *view_next;
ns_server_t *server = (ns_server_t *)event->ev_arg;
isc_boolean_t flush = server->flushonshutdown;
ns_cache_t *nsc;
UNUSED(task);
INSIST(task == server->task);
@ -3799,6 +3984,12 @@ shutdown_server(isc_task_t *task, isc_event_t *event) {
dns_view_detach(&view);
}
while ((nsc = ISC_LIST_HEAD(server->cachelist)) != NULL) {
ISC_LIST_UNLINK(server->cachelist, nsc, link);
dns_cache_detach(&nsc->cache);
isc_mem_put(server->mctx, nsc, sizeof(*nsc));
}
isc_timer_detach(&server->interface_timer);
isc_timer_detach(&server->heartbeat_timer);
isc_timer_detach(&server->pps_timer);
@ -3953,6 +4144,8 @@ ns_server_create(isc_mem_t *mctx, ns_server_t **serverp) {
ISC_LIST_INIT(server->statschannels);
ISC_LIST_INIT(server->cachelist);
server->magic = NS_SERVER_MAGIC;
*serverp = server;
}
@ -3991,6 +4184,7 @@ ns_server_destroy(ns_server_t **serverp) {
isc_event_free(&server->reload_event);
INSIST(ISC_LIST_EMPTY(server->viewlist));
INSIST(ISC_LIST_EMPTY(server->cachelist));
dns_aclenv_destroy(&server->aclenv);
@ -4652,15 +4846,23 @@ dumpdone(void *arg, isc_result_t result) {
nextview:
fprintf(dctx->fp, ";\n; Start view %s\n;\n", dctx->view->view->name);
resume:
if (dctx->zone == NULL && dctx->cache == NULL && dctx->dumpcache) {
if (dctx->dumpcache && dns_view_iscacheshared(dctx->view->view)) {
fprintf(dctx->fp,
";\n; Cache of view '%s' is shared as '%s'\n",
dctx->view->view->name,
dns_cache_getname(dctx->view->view->cache));
} else if (dctx->zone == NULL && dctx->cache == NULL &&
dctx->dumpcache)
{
style = &dns_master_style_cache;
/* start cache dump */
if (dctx->view->view->cachedb != NULL)
dns_db_attach(dctx->view->view->cachedb, &dctx->cache);
if (dctx->cache != NULL) {
fprintf(dctx->fp, ";\n; Cache dump of view '%s'\n;\n",
dctx->view->view->name);
fprintf(dctx->fp,
";\n; Cache dump of view '%s' (cache %s)\n;\n",
dctx->view->view->name,
dns_cache_getname(dctx->view->view->cache));
result = dns_master_dumptostreaminc(dctx->mctx,
dctx->cache, NULL,
style, dctx->fp,
@ -4937,6 +5139,7 @@ ns_server_flushcache(ns_server_t *server, char *args) {
isc_boolean_t flushed;
isc_boolean_t found;
isc_result_t result;
ns_cache_t *nsc;
/* Skip the command name. */
ptr = next_token(&args, " \t");
@ -4950,22 +5153,96 @@ ns_server_flushcache(ns_server_t *server, char *args) {
RUNTIME_CHECK(result == ISC_R_SUCCESS);
flushed = ISC_TRUE;
found = ISC_FALSE;
for (view = ISC_LIST_HEAD(server->viewlist);
view != NULL;
view = ISC_LIST_NEXT(view, link))
{
if (viewname != NULL && strcasecmp(viewname, view->name) != 0)
continue;
/*
* Flushing a cache is tricky when caches are shared by multiple views.
* We first identify which caches should be flushed in the local cache
* list, flush these caches, and then update other views that refer to
* the flushed cache DB.
*/
if (viewname != NULL) {
/*
* Mark caches that need to be flushed. This is an O(#view^2)
* operation in the very worst case, but should be normally
* much more lightweight because only a few (most typically just
* one) views will match.
*/
for (view = ISC_LIST_HEAD(server->viewlist);
view != NULL;
view = ISC_LIST_NEXT(view, link))
{
if (strcasecmp(viewname, view->name) != 0)
continue;
found = ISC_TRUE;
for (nsc = ISC_LIST_HEAD(server->cachelist);
nsc != NULL;
nsc = ISC_LIST_NEXT(nsc, link)) {
if (nsc->cache == view->cache)
break;
}
INSIST(nsc != NULL);
nsc->needflush = ISC_TRUE;
}
} else
found = ISC_TRUE;
result = dns_view_flushcache(view);
/* Perform flush */
for (nsc = ISC_LIST_HEAD(server->cachelist);
nsc != NULL;
nsc = ISC_LIST_NEXT(nsc, link)) {
if (viewname != NULL && !nsc->needflush)
continue;
nsc->needflush = ISC_TRUE;
result = dns_view_flushcache2(nsc->primaryview, ISC_FALSE);
if (result != ISC_R_SUCCESS) {
flushed = ISC_FALSE;
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
"flushing cache in view '%s' failed: %s",
view->name, isc_result_totext(result));
nsc->primaryview->name,
isc_result_totext(result));
}
}
/*
* Fix up views that share a flushed cache: let the views update the
* cache DB they're referring to. This could also be an expensive
* operation, but should typically be marginal: the inner loop is only
* necessary for views that share a cache, and if there are many such
* views the number of shared cache should normally be small.
* A worst case is that we have n views and n/2 caches, each shared by
* two views. Then this will be a O(n^2/4) operation.
*/
for (view = ISC_LIST_HEAD(server->viewlist);
view != NULL;
view = ISC_LIST_NEXT(view, link))
{
if (!dns_view_iscacheshared(view))
continue;
for (nsc = ISC_LIST_HEAD(server->cachelist);
nsc != NULL;
nsc = ISC_LIST_NEXT(nsc, link)) {
if (!nsc->needflush || nsc->cache != view->cache)
continue;
result = dns_view_flushcache2(view, ISC_TRUE);
if (result != ISC_R_SUCCESS) {
flushed = ISC_FALSE;
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
"fixing cache in view '%s' "
"failed: %s", view->name,
isc_result_totext(result));
}
}
}
/* Cleanup the cache list. */
for (nsc = ISC_LIST_HEAD(server->cachelist);
nsc != NULL;
nsc = ISC_LIST_NEXT(nsc, link)) {
nsc->needflush = ISC_FALSE;
}
if (flushed && found) {
if (viewname != NULL)
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
@ -5034,6 +5311,11 @@ ns_server_flushname(ns_server_t *server, char *args) {
if (viewname != NULL && strcasecmp(viewname, view->name) != 0)
continue;
found = ISC_TRUE;
/*
* It's a little inefficient to try flushing name for all views
* if some of the views share a single cache. But since the
* operation is lightweight we prefer simplicity here.
*/
result = dns_view_flushname(view, name);
if (result != ISC_R_SUCCESS) {
flushed = ISC_FALSE;

View file

@ -14,7 +14,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: statschannel.c,v 1.15 2008/12/18 02:23:27 marka Exp $ */
/* $Id: statschannel.c,v 1.16 2009/01/09 22:24:36 jinmei Exp $ */
/*! \file */
@ -28,6 +28,7 @@
#include <isc/socket.h>
#include <isc/task.h>
#include <dns/cache.h>
#include <dns/db.h>
#include <dns/opcode.h>
#include <dns/rdataclass.h>
@ -635,7 +636,7 @@ generatexml(ns_server_t *server, int *buflen, xmlChar **buf) {
TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "bind"));
TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "statistics"));
TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "version",
ISC_XMLCHAR "2.0"));
ISC_XMLCHAR "2.1"));
/* Set common fields for statistics dump */
dumparg.type = statsformat_xml;
@ -674,11 +675,15 @@ generatexml(ns_server_t *server, int *buflen, xmlChar **buf) {
cachestats = dns_db_getrrsetstats(view->cachedb);
if (cachestats != NULL) {
xmlTextWriterStartElement(writer,
ISC_XMLCHAR "cache");
TRY0(xmlTextWriterStartElement(writer,
ISC_XMLCHAR "cache"));
TRY0(xmlTextWriterWriteAttribute(writer,
ISC_XMLCHAR "name",
ISC_XMLCHAR
dns_cache_getname(view->cache)));
dns_rdatasetstats_dump(cachestats, rdatasetstats_dump,
&dumparg, 0);
xmlTextWriterEndElement(writer); /* cache */
TRY0(xmlTextWriterEndElement(writer)); /* cache */
}
xmlTextWriterEndElement(writer); /* view */
@ -1220,7 +1225,15 @@ ns_stats_dump(ns_server_t *server, FILE *fp) {
if (strcmp(view->name, "_default") == 0)
fprintf(fp, "[View: default]\n");
else
fprintf(fp, "[View: %s]\n", view->name);
fprintf(fp, "[View: %s (Cache: %s)]\n", view->name,
dns_cache_getname(view->cache));
if (dns_view_iscacheshared(view)) {
/*
* Avoid dumping redundant statistics when the cache is
* shared.
*/
continue;
}
dns_rdatasetstats_dump(cachestats, rdatasetstats_dump, &dumparg,
0);
}

View file

@ -18,7 +18,7 @@
- PERFORMANCE OF THIS SOFTWARE.
-->
<!-- File: $Id: Bv9ARM-book.xml,v 1.385 2009/01/08 14:19:05 jreed Exp $ -->
<!-- File: $Id: Bv9ARM-book.xml,v 1.386 2009/01/09 22:24:36 jinmei Exp $ -->
<book xmlns:xi="http://www.w3.org/2001/XInclude">
<title>BIND 9 Administrator Reference Manual</title>
@ -4706,6 +4706,7 @@ category notify { null; };
</para>
<programlisting><command>options</command> {
<optional> attach-cache <replaceable>cache_name</replaceable>; </optional>
<optional> version <replaceable>version_string</replaceable>; </optional>
<optional> hostname <replaceable>hostname_string</replaceable>; </optional>
<optional> server-id <replaceable>server_id_string</replaceable>; </optional>
@ -4888,6 +4889,102 @@ category notify { null; };
<variablelist>
<varlistentry>
<term><command>attach-cache</command></term>
<listitem>
<para>
Allows multiple views to share a single cache
database.
Each view has its own cache database by default, but
if multiple views have the same operational policy
for name resolution and caching, those views can
share a single cache to save memory and possibly
improve resolution efficiency by using this option.
</para>
<para>
The <command>attach-cache</command> option
may also be specified in <command>view</command>
statements, in which case it overrides the
global <command>attach-cache</command> option.
</para>
<para>
The <replaceable>cache_name</replaceable> specifies
the cache to be shared.
When the <command>named</command> server configures
views which are supposed to share a cache, it
creates a cache with the specified name for the
first view of these sharing views.
The rest of the views will simply refer to the
already created cache.
</para>
<para>
One common configuration to share a cache would be to
allow all views to share a single cache.
This can be done by specifying
the <command>attach-cache</command> as a global
option with an arbitrary name.
</para>
<para>
Another possible operation is to allow a subset of
all views to share a cache while the others to
retain their own caches.
For example, if there are three views A, B, and C,
and only A and B should share a cache, specify the
<command>attach-cache</command> option as a view A (or
B)'s option, referring to the other view name:
</para>
<programlisting>
view "A" {
// this view has its own cache
...
};
view "B" {
// this view refers to A's cache
attach-cache "A";
};
view "C" {
// this view has its own cache
...
};
</programlisting>
<para>
Views that share a cache must have the same policy
on configurable parameters that may affect caching.
The current implementation requires the following
configurable options be consistent among these
views:
<command>check-names</command>,
<command>cleaning-interval</command>,
<command>dnssec-accept-expired</command>,
<command>dnssec-validation</command>,
<command>max-cache-ttl</command>,
<command>max-ncache-ttl</command>,
<command>max-cache-size</command>, and
<command>zero-no-soa-ttl</command>.
</para>
<para>
Note that there may be other parameters that may
cause confusion if they are inconsistent for
different views that share a single cache.
For example, if these views define different sets of
forwarders that can return different answers for the
same question, sharing the answer does not make
sense or could even be harmful.
It is administrator's responsibility to ensure
configuration differences in different views do
not cause disruption with a shared cache.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>directory</command></term>
<listitem>

View file

@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: cache.c,v 1.80 2008/09/24 02:46:22 marka Exp $ */
/* $Id: cache.c,v 1.81 2009/01/09 22:24:36 jinmei Exp $ */
/*! \file */
@ -122,6 +122,7 @@ struct dns_cache {
isc_mutex_t lock;
isc_mutex_t filelock;
isc_mem_t *mctx;
char *name;
/* Locked by 'lock'. */
int references;
@ -132,6 +133,7 @@ struct dns_cache {
char *db_type;
int db_argc;
char **db_argv;
isc_uint32_t size;
/* Locked by 'filelock'. */
char *filename;
@ -170,6 +172,16 @@ dns_cache_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
const char *db_type, unsigned int db_argc, char **db_argv,
dns_cache_t **cachep)
{
return (dns_cache_create2(mctx, taskmgr, timermgr, rdclass, "",
db_type, db_argc, db_argv, cachep));
}
isc_result_t
dns_cache_create2(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
const char *cachename, const char *db_type,
unsigned int db_argc, char **db_argv, dns_cache_t **cachep)
{
isc_result_t result;
dns_cache_t *cache;
@ -178,6 +190,7 @@ dns_cache_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
REQUIRE(cachep != NULL);
REQUIRE(*cachep == NULL);
REQUIRE(mctx != NULL);
REQUIRE(cachename != NULL);
cache = isc_mem_get(mctx, sizeof(*cache));
if (cache == NULL)
@ -186,6 +199,15 @@ dns_cache_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
cache->mctx = NULL;
isc_mem_attach(mctx, &cache->mctx);
cache->name = NULL;
if (cachename != NULL) {
cache->name = isc_mem_strdup(mctx, cachename);
if (cache->name == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup_mem;
}
}
result = isc_mutex_init(&cache->lock);
if (result != ISC_R_SUCCESS)
goto cleanup_mem;
@ -266,6 +288,8 @@ dns_cache_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
cleanup_lock:
DESTROYLOCK(&cache->lock);
cleanup_mem:
if (cache->name != NULL)
isc_mem_free(mctx, cache->name);
isc_mem_put(mctx, cache, sizeof(*cache));
isc_mem_detach(&mctx);
return (result);
@ -314,6 +338,9 @@ cache_free(dns_cache_t *cache) {
if (cache->db_type != NULL)
isc_mem_free(cache->mctx, cache->db_type);
if (cache->name != NULL)
isc_mem_free(cache->mctx, cache->name);
DESTROYLOCK(&cache->lock);
DESTROYLOCK(&cache->filelock);
cache->magic = 0;
@ -484,6 +511,26 @@ dns_cache_setcleaninginterval(dns_cache_t *cache, unsigned int t) {
UNLOCK(&cache->lock);
}
unsigned int
dns_cache_getcleaninginterval(dns_cache_t *cache) {
unsigned int t;
REQUIRE(VALID_CACHE(cache));
LOCK(&cache->lock);
t = cache->cleaner.cleaning_interval;
UNLOCK(&cache->lock);
return (t);
}
const char *
dns_cache_getname(dns_cache_t *cache) {
REQUIRE(VALID_CACHE(cache));
return (cache->name);
}
/*
* Initialize the cache cleaner object at *cleaner.
* Space for the object must be allocated by the caller.
@ -510,6 +557,7 @@ cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
cleaner->cleaning_timer = NULL;
cleaner->resched_event = NULL;
cleaner->overmem_event = NULL;
cleaner->cleaning_interval = 0; /* Initially turned off. */
result = dns_db_createiterator(cleaner->cache->db, ISC_FALSE,
&cleaner->iterator);
@ -538,7 +586,6 @@ cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
goto cleanup;
}
cleaner->cleaning_interval = 0; /* Initially turned off. */
result = isc_timer_create(timermgr, isc_timertype_inactive,
NULL, NULL, cleaner->task,
cleaning_timer_action, cleaner,
@ -940,6 +987,10 @@ dns_cache_setcachesize(dns_cache_t *cache, isc_uint32_t size) {
if (size != 0 && size < DNS_CACHE_MINSIZE)
size = DNS_CACHE_MINSIZE;
LOCK(&cache->lock);
cache->size = size;
UNLOCK(&cache->lock);
hiwater = size - (size >> 3); /* Approximately 7/8ths. */
lowater = size - (size >> 2); /* Approximately 3/4ths. */
@ -963,6 +1014,19 @@ dns_cache_setcachesize(dns_cache_t *cache, isc_uint32_t size) {
isc_mem_setwater(cache->mctx, water, cache, hiwater, lowater);
}
isc_uint32_t
dns_cache_getcachesize(dns_cache_t *cache) {
isc_uint32_t size;
REQUIRE(VALID_CACHE(cache));
LOCK(&cache->lock);
size = cache->size;
UNLOCK(&cache->lock);
return (size);
}
/*
* The cleaner task is shutting down; do the necessary cleanup.
*/

View file

@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: cache.h,v 1.26 2007/06/19 23:47:16 tbox Exp $ */
/* $Id: cache.h,v 1.27 2009/01/09 22:24:37 jinmei Exp $ */
#ifndef DNS_CACHE_H
#define DNS_CACHE_H 1
@ -65,8 +65,15 @@ dns_cache_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
const char *db_type, unsigned int db_argc, char **db_argv,
dns_cache_t **cachep);
isc_result_t
dns_cache_create2(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
const char *cachename, const char *db_type,
unsigned int db_argc, char **db_argv, dns_cache_t **cachep);
/*%<
* Create a new DNS cache.
* Create a new DNS cache. dns_cache_create2() will create a named cache.
* dns_cache_create() is a backward compatible version that internally specifies
* an empty name.
*
* Requires:
*
@ -76,6 +83,8 @@ dns_cache_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
* manager, or both are NULL. If NULL, no periodic cleaning of the
* cache will take place.
*
*\li 'cachename' is a valid string. This must not be NULL.
*
*\li 'cachep' is a valid pointer, and *cachep == NULL
*
* Ensures:
@ -217,12 +226,36 @@ dns_cache_setcleaninginterval(dns_cache_t *cache, unsigned int interval);
* Set the periodic cache cleaning interval to 'interval' seconds.
*/
unsigned int
dns_cache_getcleaninginterval(dns_cache_t *cache);
/*%<
* Get the periodic cache cleaning interval to 'interval' seconds.
*/
isc_uint32_t
dns_cache_getcachesize(dns_cache_t *cache);
/*%<
* Get the maximum cache size.
*/
const char *
dns_cache_getname(dns_cache_t *cache);
/*%<
* Get the cache name.
*/
void
dns_cache_setcachesize(dns_cache_t *cache, isc_uint32_t size);
/*%<
* Set the maximum cache size. 0 means unlimited.
*/
isc_uint32_t
dns_cache_getcachesize(dns_cache_t *cache);
/*%<
* Get the maximum cache size.
*/
isc_result_t
dns_cache_flush(dns_cache_t *cache);
/*%<

View file

@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: view.h,v 1.113 2009/01/05 23:47:53 tbox Exp $ */
/* $Id: view.h,v 1.114 2009/01/09 22:24:37 jinmei Exp $ */
#ifndef DNS_VIEW_H
#define DNS_VIEW_H 1
@ -102,6 +102,7 @@ struct dns_view {
isc_event_t reqevent;
dns_stats_t * resstats;
dns_stats_t * resquerystats;
isc_boolean_t cacheshared;
/* Configurable data. */
dns_tsig_keyring_t * statickeys;
@ -308,8 +309,12 @@ dns_view_createresolver(dns_view_t *view,
void
dns_view_setcache(dns_view_t *view, dns_cache_t *cache);
void
dns_view_setcache2(dns_view_t *view, dns_cache_t *cache, isc_boolean_t shared);
/*%<
* Set the view's cache database.
* Set the view's cache database. If 'shared' is true, this means the cache
* is created by another view and is shared with that view. dns_view_setcache()
* is a backward compatible version equivalent to setcache2(..., ISC_FALSE).
*
* Requires:
*
@ -726,8 +731,14 @@ dns_view_dumpdbtostream(dns_view_t *view, FILE *fp);
isc_result_t
dns_view_flushcache(dns_view_t *view);
isc_result_t
dns_view_flushcache2(dns_view_t *view, isc_boolean_t fixuponly);
/*%<
* Flush the view's cache (and ADB).
* Flush the view's cache (and ADB). If 'fixuponly' is true, it only updates
* the internal reference to the cache DB with omitting actual flush operation.
* 'fixuponly' is intended to be used for a view that shares a cache with
* a different view. dns_view_flushcache() is a backward compatible version
* that always sets fixuponly to false.
*
* Requires:
* 'view' is valid.
@ -876,4 +887,17 @@ dns_view_getresquerystats(dns_view_t *view, dns_stats_t **statsp);
*\li 'statsp' != NULL && '*statsp' != NULL
*/
isc_boolean_t
dns_view_iscacheshared(dns_view_t *view);
/*%<
* Check if the view shares the cache created by another view.
*
* Requires:
* \li 'view' is valid.
*
* Returns:
*\li #ISC_TRUE if the cache is shared.
*\li #ISC_FALSE othewise.
*/
#endif /* DNS_VIEW_H */

View file

@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: view.c,v 1.150 2008/06/17 03:14:20 marka Exp $ */
/* $Id: view.c,v 1.151 2009/01/09 22:24:36 jinmei Exp $ */
/*! \file */
@ -154,6 +154,7 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
view->rootexclude = NULL;
view->resstats = NULL;
view->resquerystats = NULL;
view->cacheshared = ISC_FALSE;
/*
* Initialize configuration data with default values.
@ -626,9 +627,15 @@ dns_view_createresolver(dns_view_t *view,
void
dns_view_setcache(dns_view_t *view, dns_cache_t *cache) {
dns_view_setcache2(view, cache, ISC_FALSE);
}
void
dns_view_setcache2(dns_view_t *view, dns_cache_t *cache, isc_boolean_t shared) {
REQUIRE(DNS_VIEW_VALID(view));
REQUIRE(!view->frozen);
view->cacheshared = shared;
if (view->cache != NULL) {
if (view->acache != NULL)
dns_acache_putdb(view->acache, view->cachedb);
@ -643,6 +650,13 @@ dns_view_setcache(dns_view_t *view, dns_cache_t *cache) {
dns_acache_setdb(view->acache, view->cachedb);
}
isc_boolean_t
dns_view_iscacheshared(dns_view_t *view) {
REQUIRE(DNS_VIEW_VALID(view));
return (view->cacheshared);
}
void
dns_view_sethints(dns_view_t *view, dns_db_t *hints) {
REQUIRE(DNS_VIEW_VALID(view));
@ -1278,15 +1292,22 @@ dns_view_dumpdbtostream(dns_view_t *view, FILE *fp) {
isc_result_t
dns_view_flushcache(dns_view_t *view) {
return (dns_view_flushcache2(view, ISC_FALSE));
}
isc_result_t
dns_view_flushcache2(dns_view_t *view, isc_boolean_t fixuponly) {
isc_result_t result;
REQUIRE(DNS_VIEW_VALID(view));
if (view->cachedb == NULL)
return (ISC_R_SUCCESS);
result = dns_cache_flush(view->cache);
if (result != ISC_R_SUCCESS)
return (result);
if (!fixuponly) {
result = dns_cache_flush(view->cache);
if (result != ISC_R_SUCCESS)
return (result);
}
if (view->acache != NULL)
dns_acache_putdb(view->acache, view->cachedb);
dns_db_detach(&view->cachedb);

View file

@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: namedconf.c,v 1.92 2008/09/27 23:35:31 jinmei Exp $ */
/* $Id: namedconf.c,v 1.93 2009/01/09 22:24:37 jinmei Exp $ */
/*! \file */
@ -797,6 +797,7 @@ view_clauses[] = {
{ "allow-recursion-on", &cfg_type_bracketed_aml, 0 },
{ "allow-v6-synthesis", &cfg_type_bracketed_aml,
CFG_CLAUSEFLAG_OBSOLETE },
{ "attach-cache", &cfg_type_astring, 0 },
{ "auth-nxdomain", &cfg_type_boolean, CFG_CLAUSEFLAG_NEWDEFAULT },
{ "cache-file", &cfg_type_qstring, 0 },
{ "check-names", &cfg_type_checknames, CFG_CLAUSEFLAG_MULTI },