RE: [PECL-DEV] APC TTL Patch
| From: | Shannon Werb | Date: | Sat, 07 Aug 2004 12:39:00 +0000 |
| Subject: | RE: [PECL-DEV] APC TTL Patch | ||
| References: | 1 | Groups: | php.pecl.dev |
| Request: | Send a blank email to pecl-dev+get-1090@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.
>
Rasmus, thanks, I think this is an excellent enhancement. In our situtation
we have hosts with very large number of files, sometimes 10's of thousands.
In any 24 hour period we can easily cache 1500 files and consume 50MB of
shared memory. We do an apache graceful each night to roll logs which
clears the entire cache, but what I was hoping was that APC could look for
scripts which have not been used in a certain ttl and throw them away when
out of memory. This should prevent scenerio's where we have a heavier day
and run out of apc memory and a large number of the scripts in the cache
have only been accessed a few times the entire day and really don't need to
be there.
> 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.
Great, so it checks ttl on every new script insert? Not just when out of
memory?
Can I get at this data (access_time) and output it on the APC GUI you
provided?
>
> 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.
Understood, but I think this will help a lot in shared host solutions like
we manage.
When do you intend to commit to CVS?
Regards,
Shannon
>
> 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);
> }
>
> --
> PECL development discussion Mailing List
> (http://pecl.php.net/) To unsubscribe, visit:
> http://www.php.net/unsub.php
>
>