APC TTL Patch

From: Date: Fri, 06 Aug 2004 21:29:19 +0000
Subject: APC TTL Patch
Groups: php.pecl.dev 
Request: Send a blank email to pecl-dev+get-1089@lists.php.net to get a copy of this message
This is a rather large patch that adds a TTL mechanism to APC. The reason this is needed is because once a script is cached, it will never be removed from the cache unless another script with the same device+inode comes along and bumps it. Given this, there are a couple of ways APC can easily become very ineffective without a TTL mechanism. A simple scenario would be a server with a lot of scripts that are constantly modified in such a manner that the modified scripts receive a new inode. Most text editors and atomic update programs like rsync will give a file a new inode when replacing the old with a new. Another way is simply adding more and more scripts to a server. Once the cache is full all new scripts will not be cached even though some of the older scripts in the cache may not have been accessed for days or weeks. A periodic server restart or just an apc_clear_cache() API call will of course reset the entire cache and clean up such situations, but that seems too drastic to me. This patch attempts a more elegant solution. This patch adds an access_time field to each cache entry without adding a system call. In fact I got rid of a system call in make_slot(). It does this by reusing the time_t that Apache automatically grabs at the start of a request. On a cache insert it checks for other cache entries that hashed to the same slot and removes any that were accessed more than TTL seconds ago. I have only done nominal testing of this patch so far, but it seems to do the job. There are of course still ways you can end up filling your cache with lots of stale entries if your hash table slots (num_files_hint) is way out of whack with your number of files and total shared memory size, but for a properly configured setup, this patch should be quite effective. Index: INSTALL =================================================================== RCS file: /repository/pecl/apc/INSTALL,v retrieving revision 3.34 diff -u -r3.34 INSTALL --- INSTALL 28 Jun 2004 08:54:15 -0000 3.34 +++ INSTALL 6 Aug 2004 21:07:17 -0000 @@ -222,6 +222,14 @@ many thousands of source files. (Default: 1000) + apc.ttl The number of seconds a cache entry is allowed to + idle in a slot in case this cache entry slot is + needed by another entry. Leaving this at zero + means that your cache could potentially fill up + with stale entries while newer entries won't be + cached. + (Default: 0) + apc.gc_ttl The number of seconds that a cache entry may remain on the garbage-collection list. This value provides a failsafe in the event that a server Index: apc_cache.c =================================================================== RCS file: /repository/pecl/apc/apc_cache.c,v retrieving revision 3.55 diff -u -r3.55 apc_cache.c --- apc_cache.c 29 Mar 2004 17:18:11 -0000 3.55 +++ apc_cache.c 6 Aug 2004 21:07:17 -0000 @@ -20,6 +20,10 @@ #include "apc_lock.h" #include "apc_sma.h" #include "SAPI.h" +#if HAVE_APACHE +#include "httpd.h" +#endif + /* TODO: rehash when load factor exceeds threshold */ @@ -39,6 +43,7 @@ int num_hits; /* number of hits to this bucket */ time_t creation_time; /* time slot was initialized */ time_t deletion_time; /* time slot was removed from cache */ + time_t access_time; /* time slot was last accessed */ }; /* }}} */ @@ -59,6 +64,7 @@ slot_t** slots; /* array of cache slots (stored in SHM) */ int num_slots; /* number of slots in cache */ int gc_ttl; /* maximum time on GC list for a slot */ + int ttl; /* if slot is needed and entry's mtime is older than this ttl, remove it */ int lock; /* global semaphore lock */ }; /* }}} */ @@ -77,13 +83,22 @@ /* {{{ make_slot */ slot_t* make_slot(apc_cache_key_t key, apc_cache_entry_t* value, slot_t* next) { + time_t t; slot_t* p = apc_sma_malloc(sizeof(slot_t)); if (!p) return NULL; +#if HAVE_APACHE + /* Save a syscall here under Apache. This should actually be a SAPI call instead + * but we don't have that yet. Once that is introduced in PHP this can be cleaned up -Rasmus */ + t = ((request_rec *)SG(server_context))->request_time; +#else + t = time(0); +#endif p->key = key; p->value = value; p->next = next; p->num_hits = 0; - p->creation_time = time(0); + p->creation_time = t; + p->access_time = t; p->deletion_time = 0; return p; } @@ -180,7 +195,7 @@ /* }}} */ /* {{{ apc_cache_create */ -apc_cache_t* apc_cache_create(int size_hint, int gc_ttl) +apc_cache_t* apc_cache_create(int size_hint, int gc_ttl, int ttl) { apc_cache_t* cache; int cache_size; @@ -203,6 +218,7 @@ cache->slots = (slot_t**) (((char*) cache->shmaddr) + sizeof(header_t)); cache->num_slots = num_slots; cache->gc_ttl = gc_ttl; + cache->ttl = ttl; cache->lock = CREATE_LOCK; for (i = 0; i < num_slots; i++) { @@ -249,6 +265,7 @@ apc_cache_entry_t* value) { slot_t** slot; + time_t t; if (!value) { return 0; @@ -258,7 +275,18 @@ process_pending_removals(cache); slot = &cache->slots[hash(key) % cache->num_slots]; + apc_log(APC_WARNING,"Inserting inode %d TTL is %d\n",key.inode,cache->ttl); + if(*slot) { +#if HAVE_APACHE + /* Save a syscall here under Apache. This should actually be a SAPI call instead + * but we don't have that yet. Once that is introduced in PHP this can be cleaned up -Rasmus */ + t = ((request_rec *)SG(server_context))->request_time; +#else + t = time(0); +#endif + } while (*slot) { + apc_log(APC_WARNING,"Checking inode %d\n",(*slot)->key.inode); if (key_equals((*slot)->key, key)) { if ((*slot)->key.mtime < key.mtime) { remove_slot(cache, slot); @@ -266,6 +294,10 @@ } UNLOCK(cache); return 0; + } else if(cache->ttl && (*slot)->access_time < (t - cache->ttl)) { + apc_log(APC_WARNING, "Removing inode %d\n",(*slot)->key.inode); + remove_slot(cache, slot); + continue; } slot = &(*slot)->next; } @@ -284,10 +316,20 @@ apc_cache_entry_t* apc_cache_find(apc_cache_t* cache, apc_cache_key_t key) { slot_t** slot; + time_t t; LOCK(cache); slot = &cache->slots[hash(key) % cache->num_slots]; + if(*slot) { +#if HAVE_APACHE + /* Save a syscall here under Apache. This should actually be a SAPI call instead + * but we don't have that yet. Once that is introduced this can be cleaned up -Rasmus */ + t = ((request_rec *)SG(server_context))->request_time; +#else + t = time(0); +#endif + } while (*slot) { if (key_equals((*slot)->key, key)) { if ((*slot)->key.mtime < key.mtime) { @@ -297,6 +339,7 @@ (*slot)->num_hits++; (*slot)->value->ref_count++; + (*slot)->access_time = t; prevent_garbage_collection((*slot)->value); cache->header->num_hits++; @@ -414,6 +457,7 @@ link->mtime = p->key.mtime; link->creation_time = p->creation_time; link->deletion_time = p->deletion_time; + link->access_time = p->access_time; link->ref_count = p->value->ref_count; link->next = info->list; info->list = link; @@ -432,6 +476,7 @@ link->mtime = p->key.mtime; link->creation_time = p->creation_time; link->deletion_time = p->deletion_time; + link->access_time = p->access_time; link->ref_count = p->value->ref_count; link->next = info->deleted_list; info->deleted_list = link; Index: apc_cache.h =================================================================== RCS file: /repository/pecl/apc/apc_cache.h,v retrieving revision 3.14 diff -u -r3.14 apc_cache.h --- apc_cache.h 20 Jul 2003 00:07:02 -0000 3.14 +++ apc_cache.h 6 Aug 2004 21:07:17 -0000 @@ -63,8 +63,12 @@ * gc_ttl is the maximum time a cache entry may speed on the garbage * collection list. This is basically a work around for the inherent * unreliability of our reference counting mechanism (see apc_cache_release). + * + * ttl is the maximum time a cache entry can idle in a slot in case the slot + * is needed. This helps in cleaning up the cache and ensuring that entries + * hit frequently stay cached and ones not hit very often eventually disappear. */ -extern T apc_cache_create(int size_hint, int gc_ttl); +extern T apc_cache_create(int size_hint, int gc_ttl, int ttl); /* * apc_cache_destroy releases any OS resources associated with a cache object. @@ -156,6 +160,7 @@ time_t mtime; time_t creation_time; time_t deletion_time; + time_t access_time; int ref_count; apc_cache_link_t* next; }; Index: apc_globals.h =================================================================== RCS file: /repository/pecl/apc/apc_globals.h,v retrieving revision 3.5 diff -u -r3.5 apc_globals.h --- apc_globals.h 28 Jun 2004 08:54:15 -0000 3.5 +++ apc_globals.h 6 Aug 2004 21:07:17 -0000 @@ -31,6 +31,7 @@ int optimization; /* optimizer level (higher = more aggressive) */ int num_files_hint; /* parameter to apc_cache_create */ int gc_ttl; /* parameter to apc_cache_create */ + int ttl; /* parameter to apc_cache_create */ #if APC_MMAP char *mmap_file_mask; /* mktemp-style file-mask to pass to mmap */ #endif Index: apc_main.c =================================================================== RCS file: /repository/pecl/apc/apc_main.c,v retrieving revision 3.12 diff -u -r3.12 apc_main.c --- apc_main.c 3 Aug 2004 20:54:26 -0000 3.12 +++ apc_main.c 6 Aug 2004 21:07:17 -0000 @@ -237,7 +237,7 @@ #else apc_sma_init(APCG(shm_segments), APCG(shm_size)*1024*1024); #endif - APCG(cache) = apc_cache_create(APCG(num_files_hint), APCG(gc_ttl)); + APCG(cache) = apc_cache_create(APCG(num_files_hint), APCG(gc_ttl), APCG(ttl)); APCG(cache_stack) = apc_stack_create(0); APCG(compiled_filters) = apc_regex_compile_array(APCG(filters)); Index: php_apc.c =================================================================== RCS file: /repository/pecl/apc/php_apc.c,v retrieving revision 3.28 diff -u -r3.28 php_apc.c --- php_apc.c 28 Jun 2004 08:54:15 -0000 3.28 +++ php_apc.c 6 Aug 2004 21:07:17 -0000 @@ -86,6 +86,7 @@ STD_PHP_INI_ENTRY("apc.optimization", "0", PHP_INI_SYSTEM, OnUpdateInt, optimization, zend_apc_globals, apc_globals) STD_PHP_INI_ENTRY("apc.num_files_hint", "1000", PHP_INI_SYSTEM, OnUpdateInt, num_files_hint, zend_apc_globals, apc_globals) STD_PHP_INI_ENTRY("apc.gc_ttl", "3600", PHP_INI_SYSTEM, OnUpdateInt, gc_ttl, zend_apc_globals, apc_globals) +STD_PHP_INI_ENTRY("apc.ttl", "0", PHP_INI_SYSTEM, OnUpdateInt, ttl, zend_apc_globals, apc_globals) #if APC_MMAP STD_PHP_INI_ENTRY("apc.mmap_file_mask", NULL, PHP_INI_SYSTEM, OnUpdateString, mmap_file_mask, zend_apc_globals, apc_globals) #endif @@ -191,6 +192,7 @@ add_assoc_long(link, "mtime", p->mtime); add_assoc_long(link, "creation_time", p->creation_time); add_assoc_long(link, "deletion_time", p->deletion_time); + add_assoc_long(link, "access_time", p->access_time); add_assoc_long(link, "ref_count", p->ref_count); add_next_index_zval(list, link); } @@ -212,6 +214,7 @@ add_assoc_long(link, "mtime", p->mtime); add_assoc_long(link, "creation_time", p->creation_time); add_assoc_long(link, "deletion_time", p->deletion_time); + add_assoc_long(link, "access_time", p->access_time); add_assoc_long(link, "ref_count", p->ref_count); add_next_index_zval(list, link); }

« previous php.pecl.dev (#1089) next »