Cache handler

From Net-SNMP Wiki
Revision as of 17:00, 14 July 2011 by Wes (Talk | contribs) (Using the Cache Data)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
Net-SNMP MIB Helper
Cache Handler
Documentation: doxygen API
Code: agent/helpers/cache_helper.c
Other Helpers: Agent Helpers

Overview

The cache_helper is designed to do one thing: help you manage a cache of data for a given mib implementation. It simply calls your load and free hooks based on how long the cache should remain valid. You define the data structures to hold the information and use your cache to respond to requests. The cache handler simply takes care of the timing about when to load it.

It is typically injected during the mib module's initialization routine.

Usage

Using the cache handler involves a few steps:

  1. inject a cache handler into the handler chain for your module
  2. implement the loading routine
  3. implement the free routine
  4. use the cache to load your data

Initialization

In your init_XXX() routine, do something like this:

 #define MY_CACHE_TIMEOUT 30
 #define MAX_CACHE_SIZE   100

 NetsnmpCacheLoad my_cache_load;
 NetsnmpCacheFree my_cache_free;

 struct my_cache {
   u_long  index;
   char   *value;
 };

 static struct my_cache *theCache;
 static size_t theCacheSize;

 void init_XXX() {
   netsnmp_mib_handler *cache_handler;

   theCache = 0;
   theCacheSize = 0;

   /* create and register your "object".  Example using the ... */
   netsnmp_register_table_iterator(reginfo, iterator_info);

   /* now create our cache handler: */
   cache_handler = netsnmp_get_cache_handler(MY_CACHE_TIMEOUT,               /* how long a cache is valid for */
                                             my_cache_load,                 /* a pointer to the cache loading function */
                                             my_cahe_free,                   /* a pointer to the cache freeing function */
                                             my_oid, OID_LENGTH(my_oid)));   /* the OID of the registration point */

   /* Inject the handler into the handler chain for our registration: */
   netsnmp_inject_handler(reginfo, cache_handler);
 }

Loading the Cache

To load the cache, simply do whatever it is your need to do in order to load all the data ahead of time. IE, here is where you should put the slowest of your loading code to cache the results into a memory structure.

static int
my_cache_load(netsnmp_cache *cache, void *magic) {
  int i = 0;

  theCache = calloc(MAX_CACHE_SIZE, sizeof(struct my_cache));
  theCacheSize = MAX_CACHE_SIZE;

  while ( /* ... loop over your data source */ && i < theCacheSize) {
     theCache[i].index = i;
     theCache[i].value = strdup(/* get a value */);
     /* store lots of values? */

     i++;
  }
  return SNMP_ERR_SUCCESS;
}

Freeing the Cache

To free the cache, we need to free the data:

void
my_cache_free(netsnmp_cache *cache, void *magic)
{
  int i;

  netsnmp_assert_or_return(cache != NULL, );
  netsnmp_assert_or_return(theCache != NULL, );

  for(i = 0; i < theCacheSize; i ++) {
    free(theCache[i].value);
  }
  free(theCache);
}

Using the Cache Data

Once the cache has been defined, the above load and free routines will be called automatically by the handler. All you need is to use the loaded data in your handler's routines. For example, in an iterator based table implementation:

netsnmp_variable_list *
my_first_entry(void **loop_context,
               void **data_context,
               netsnmp_variable_list *index,
               netsnmp_iterator_info *data)
{
    netsnmp_assert_or_return(theCacheSize > 0, );

    /*
     * Point to the first entry, and use the
     * 'next_entry' hook to retrieve this row
     */
    *loop_context = 0;
    return my_next_entry( loop_context, data_context, index, data );
}

netsnmp_variable_list *
my_next_entry( void **loop_context,
               void **data_context,
               netsnmp_variable_list *index,
               netsnmp_iterator_info *data)
{
    u_long i = (int)*loop_context;
    long port;

    if (theCacheSize < i)
        return NULL;

    /*
     * Set up the indexing for the specified row...
     */
    snmp_set_var_value(index, (u_char *)&i, sizeof(i));

    /*
     * ... return the data structure for this row,
     * and update the loop context ready for the next one.
     */
    *data_context = (void *) &theCache[i];
    *loop_context = (void *)++i;

    return index;
}

Final Notes

Make sure to look at the flags (read the documentation) that are available for controlling the caching. There is a fair amount of flexibility in how things are loaded, freed, timed, etc.