mod_mem_cache.c revision de0fed4fa6741aea09dbe28f03aa4c20e822ed5d
213N/A/* Licensed to the Apache Software Foundation (ASF) under one or more 213N/A * contributor license agreements. See the NOTICE file distributed with 213N/A * this work for additional information regarding copyright ownership. 213N/A * The ASF licenses this file to You under the Apache License, Version 2.0 213N/A * (the "License"); you may not use this file except in compliance with 213N/A * the License. You may obtain a copy of the License at 213N/A * Unless required by applicable law or agreed to in writing, software 213N/A * distributed under the License is distributed on an "AS IS" BASIS, 213N/A * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 213N/A * See the License for the specific language governing permissions and 213N/A * limitations under the License. 213N/A * Rules for managing obj->refcount: 213N/A * refcount should be incremented when an object is placed in the cache. Insertion 213N/A * of an object into the cache and the refcount increment should happen under 213N/A * protection of the sconf->lock. 213N/A * refcount should be decremented when the object is removed from the cache. 213N/A * Object should be removed from the cache and the refcount decremented while 213N/A * under protection of the sconf->lock. 213N/A * refcount should be incremented when an object is retrieved from the cache 213N/A * should occur under protection of the sconf->lock 213N/A * refcount can be atomically decremented w/o protection of the sconf->lock 213N/A * of 0 means the object is not in the cache and no worker threads are accessing long priority;
/**< the priority of this entry */ long total_refs;
/**< total number of references this entry has had */ /* Fields set by config directives */ /* maximum amount of data to buffer on a streamed response where * we haven't yet seen EOS */ /* Forward declarations */ /** callback to get the key of a item */ * memcache_cache_free is a callback that is only invoked by a thread * running in cache_insert(). cache_insert() runs under protection * of sconf->lock. By the time this function has been entered, the cache_object * has been ejected from the cache. decrement the refcount and if the refcount drops * to 0, cleanup the cache object. /* Decrement the refcount to account for the object being ejected * from the cache. If the refcount is 0, free the object. * functions return a 'negative' score since priority queues * dequeue the object with the highest value first * a 'proper' LRU function would just be * mobj->priority = mobj->total_refs; /* Cleanup the mem_cache_object_t */ /* If obj->complete is not set, the cache update failed and the * object needs to be removed from the cache then cleaned up. * The garbage collector may have ejected the object from the * cache already, so make sure it is really still in the cache * before attempting to remove it. /* If the refcount drops to 0, cleanup the cache object */ /* Iterate over the cache and clean up each unreferenced entry */ /* Cache is empty, free the cache table */ * TODO: enable directives to be overridden in various containers /* Number of objects in the cache */ /* Size of the cache in bytes */ /* Caching a streaming response. Assume the response is * less than or equal to max_streaming_buffer_size. We will * correct all the cache size counters in store_body once * we know exactly know how much we are caching. /* Note: cache_insert() will automatically garbage collect * objects from the cache if the max_cache_size threshold is * exceeded. This means mod_mem_cache does not need to implement "mem_cache: URL %s failed the size check and will not be cached.",
/* CACHE_TYPE_FILE is only valid for local content handled by the * default handler. Need a better way to check if the file is "mem_cache: Failed to create memory pool.");
/* Allocate and initialize cache_object_t */ /* Allocate and init mem_cache_object_t */ /* Finish initing the cache object */ /* Safe cast: We tested < sconf->max_cache_object_size above */ /* Place the cache_object_t into the hash table. * Note: Perhaps we should wait to put the object in the * hash table when the object is complete? I add the object here to * avoid multiple threads attempting to cache the same content only * to discover at the very end that only one of them will succeed. * Furthermore, adding the cache object to the table at the end could * open up a subtle but easy to exploit DoS hole: someone could request * a very large file with multiple requests. Better to detect this here * rather than after the cache object has been completely built and * XXX Need a way to insert into the cache w/o such coarse grained locking /* Add a refcount to account for the reference by the * hashtable in the cache. Refcount should be 2 now, one * for this thread, and one for the cache. /* This thread collided with another thread loading the same object * into the cache at the same time. Defer to the other thread which /* Populate the cache handle */ /* Look up entity keyed to 'url' */ /* cache is worried about overall counts, not 'open' ones */ /* If this is a subrequest, register the cleanup against * the main request. This will prevent the cache object * from being cleaned up from under the request after the * subrequest is destroyed. /* Initialize the cache_handle */ * refcount should be at least 1 upon entry to this function to account * for this thread's reference to the object. If the refcount is 1, then * object has been removed from the cache by another thread and this thread * is the last thread accessing the object. /* If the entity is still in the cache, remove it and decrement the * refcount. If the entity is not in the cache, do nothing. In both cases * decrement_refcount called by the last thread referencing the object will /* Define request processing hook handlers */ /* For performance, cleanup cache object after releasing the lock */ * The cache needs to keep track of the following information: * - Date, LastMod, Version, ReqTime, RespTime, ContentLength * - The original request headers (for Vary) * - The original response headers (for returning with a cached response) * - The body of the message /* Precompute how much storage we need to hold the headers */ /* If not set in headers_out, set Content-Type */ /* Init the info struct */ /* We can cache an open file descriptor if: * - the brigade contains one and only one file_bucket && * - the brigade is complete && * - the file_bucket is the last data bucket in the brigade /* Open a new XTHREAD handle to the file */ "mem_cache: Failed to open file '%s' while attempting to cache the file descriptor.",
name);
"mem_cache: Cached file: %s with key: %s",
name,
obj->
key);
/* Content not suitable for fd caching. Cache in-memory instead. */ * FD cacheing is not enabled or the content was not * suitable for fd caching. /* we didn't have space to cache it, fall back gracefully */ "mem_cache: Could not store body - not enough memory.");
/* Iterate accross the brigade and populate the cache storage */ /* Caching a streamed response. Reallocate a buffer of the * correct size and copy the streamed response into that /* we didn't have space to cache it, fall back gracefully */ "mem_cache: Could not store next bit of body - not enough memory.");
/* Now comes the crufty part... there is no way to tell the * cache that the size of the object has changed. We need * to remove the object, update the size and re-add the * object, all under protection of the lock. /* Has the object been ejected from the cache? /* Object is still in the cache, remove it, update the len field then * replace it under protection of sconf->lock. /* For illustration, cache no longer has reference to the object * so decrement the refcount * apr_atomic_dec32(&obj->refcount); /* For illustration, cache now has reference to the object, so * apr_atomic_inc32(&obj->refcount); /* Different object with the same key found in the cache. Doing nothing * here will cause the object refcount to drop to 0 in decrement_refcount * and the object will be cleaned up. /* Object has been ejected from the cache, add it back to the cache */ "mem_cache: Cached url: %s",
obj->
key);
/* not being able to read the bucket is fatal, * return this up the filter stack /* Check for buffer overflow */ /* we didn't have space to cache it, fall back gracefully */ "mem_cache: Could not store body - buffer overflow.");
/* This should not fail, but if it does, we are in BIG trouble * cause we just stomped all over the heap. * Configuration and start-up /* Sanity check the cache configuration */ "MCacheMaxObjectSize must be greater than MCacheMinObjectSize");
"MCacheSize must be greater than MCacheMaxObjectSize");
/* Issue a notice only if something other than the default config "MCacheMaxStreamingBuffer must be less than or equal to MCacheMaxObjectSize. " "Resetting MCacheMaxStreamingBuffer to MCacheMaxObjectSize.");
"MCacheMaxStreamingBuffer must be greater than or equal to MCacheMinObjectSize. " "Resetting MCacheMaxStreamingBuffer to MCacheMinObjectSize.");
return "MCacheSize argument must be an integer representing the max cache size in KBytes.";
return "MCacheMinObjectSize value must be an positive integer (bytes)";
return "MCacheMinObjectSize value must be an positive integer (bytes)";
return "MCacheMaxObjectSize value must be an integer (bytes)";
return "MCacheMaxObjectCount value must be an integer";
return "currently implemented algorithms are LRU and GDSF";
return "MCacheMaxStreamingBuffer value must be a number";
"The maximum amount of memory used by the cache in KBytes"),
"The maximum number of objects allowed to be placed in the cache"),
"The minimum size (in bytes) of an object to be placed in the cache"),
"The maximum size (in bytes) of an object to be placed in the cache"),
"The algorithm used to remove entries from the cache (default: GDSF)"),
"Maximum number of bytes of content to buffer for a streamed response"),
/* cache_hook_init(cache_mem_init, NULL, NULL, APR_HOOK_MIDDLE); */ cache_hook_create_entity(create_entity, NULL, NULL, APR_HOOK_MIDDLE); cache_hook_open_entity(open_entity, NULL, NULL, APR_HOOK_MIDDLE); cache_hook_remove_url(remove_url, NULL, NULL, APR_HOOK_MIDDLE); NULL,
/* create per-directory config structure */ NULL,
/* merge per-directory config structures */ NULL,
/* merge per-server config structures */