You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by da...@apache.org on 2017/01/13 18:42:25 UTC

[05/10] incubator-trafficcontrol git commit: Removed ATS patches.

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/0d874bf1/traffic_server/patches/trafficserver-5.3.2-089d585.diff
----------------------------------------------------------------------
diff --git a/traffic_server/patches/trafficserver-5.3.2-089d585.diff b/traffic_server/patches/trafficserver-5.3.2-089d585.diff
deleted file mode 100644
index ede76cc..0000000
--- a/traffic_server/patches/trafficserver-5.3.2-089d585.diff
+++ /dev/null
@@ -1,5157 +0,0 @@
-diff --git a/README b/README
-index 78cce71..769f85a 100644
---- a/README
-+++ b/README
-@@ -1,5 +1,6 @@
- Apache Traffic Server
- 
-+
- Traffic Server is a high-performance building block for cloud services.
- It's more than just a caching proxy server; it also has support for
- plugins to build large scale web applications.
-diff --git a/configure.ac b/configure.ac
-index fe2af20..45c47fe 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -1948,6 +1948,7 @@ AS_IF([test "x$enable_experimental_plugins" = xyes], [
-     plugins/experimental/background_fetch/Makefile
-     plugins/experimental/balancer/Makefile
-     plugins/experimental/buffer_upload/Makefile
-+    plugins/experimental/cache_range_requests/Makefile
-     plugins/experimental/channel_stats/Makefile
-     plugins/experimental/collapsed_connection/Makefile
-     plugins/experimental/custom_redirect/Makefile
-diff --git a/doc/reference/api/TSHttpOverridableConfig.en.rst b/doc/reference/api/TSHttpOverridableConfig.en.rst
-index 837df9f..e53d371 100644
---- a/doc/reference/api/TSHttpOverridableConfig.en.rst
-+++ b/doc/reference/api/TSHttpOverridableConfig.en.rst
-@@ -57,6 +57,7 @@ Configurations
- The following configurations (from ``records.config``) are overridable: ::
- 
-     proxy.config.url_remap.pristine_host_hdr
-+    proxy.config.url_remap.remap_required
-     proxy.config.http.chunking_enabled
-     proxy.config.http.negative_caching_enabled
-     proxy.config.http.negative_caching_lifetime
-@@ -100,6 +101,7 @@ The following configurations (from ``records.config``) are overridable: ::
-     proxy.config.http.keep_alive_no_activity_timeout_out
-     proxy.config.http.transaction_no_activity_timeout_in
-     proxy.config.http.transaction_no_activity_timeout_out
-+    proxy.config.http.transaction_active_timeout_in
-     proxy.config.http.transaction_active_timeout_out
-     proxy.config.http.origin_max_connections
-     proxy.config.http.connect_attempts_max_retries
-@@ -135,7 +137,12 @@ The following configurations (from ``records.config``) are overridable: ::
-     proxy.config.http.accept_encoding_filter_enabled
-     proxy.config.http.cache.range.write
-     proxy.config.http.global_user_agent_header
--
-+    proxy.config.http.parent_proxy.per_parent_connect_attempts
-+    proxy.config.http.parent_proxy.total_connect_attempts
-+    proxy.config.http.parent_origin.simple_retry_enabled
-+    proxy.config.http.parent_origin.simple_retry_response_codes
-+    proxy.config.http.parent_origin.dead_server_retry_enabled
-+    proxy.config.http.parent_origin.dead_server_retry_response_codes
- 
- Examples
- ========
-diff --git a/iocore/cache/Cache.cc b/iocore/cache/Cache.cc
-index 2879e82..fae560d 100644
---- a/iocore/cache/Cache.cc
-+++ b/iocore/cache/Cache.cc
-@@ -38,6 +38,8 @@
- #include "P_CacheBC.h"
- #endif
- 
-+#include "hugepages.h"
-+
- // Compilation Options
- #define USELESS_REENABLES // allow them for now
- // #define VERIFY_JTEST_DATA
-@@ -1295,7 +1297,13 @@ Vol::init(char *s, off_t blocks, off_t dir_skip, bool clear)
- 
-   Debug("cache_init", "allocating %zu directory bytes for a %lld byte volume (%lf%%)", vol_dirlen(this), (long long)this->len,
-         (double)vol_dirlen(this) / (double)this->len * 100.0);
--  raw_dir = (char *)ats_memalign(ats_pagesize(), vol_dirlen(this));
-+
-+  raw_dir = NULL;
-+  if (ats_hugepage_enabled())
-+    raw_dir = (char *)ats_alloc_hugepage(vol_dirlen(this));
-+  if (raw_dir == NULL)
-+    raw_dir = (char *)ats_memalign(ats_pagesize(), vol_dirlen(this));
-+
-   dir = (Dir *)(raw_dir + vol_headerlen(this));
-   header = (VolHeaderFooter *)raw_dir;
-   footer = (VolHeaderFooter *)(raw_dir + vol_dirlen(this) - ROUND_TO_STORE_BLOCK(sizeof(VolHeaderFooter)));
-@@ -2179,7 +2187,7 @@ CacheProcessor::mark_storage_offline(CacheDisk *d ///< Target disk
-     Warning("All storage devices offline, cache disabled");
-     CacheProcessor::cache_ready = 0;
-   } else { // check cache types specifically
--    if (theCache && !theCache->hosttable->gen_host_rec.vol_hash_table) {
-+    if (theCache && !theCache->getHosttable(__func__)->gen_host_rec.vol_hash_table) {
-       unsigned int caches_ready = 0;
-       caches_ready = caches_ready | (1 << CACHE_FRAG_TYPE_HTTP);
-       caches_ready = caches_ready | (1 << CACHE_FRAG_TYPE_NONE);
-@@ -2187,7 +2195,7 @@ CacheProcessor::mark_storage_offline(CacheDisk *d ///< Target disk
-       CacheProcessor::cache_ready &= caches_ready;
-       Warning("all volumes for http cache are corrupt, http cache disabled");
-     }
--    if (theStreamCache && !theStreamCache->hosttable->gen_host_rec.vol_hash_table) {
-+    if (theStreamCache && !theStreamCache->getHosttable(__func__)->gen_host_rec.vol_hash_table) {
-       unsigned int caches_ready = 0;
-       caches_ready = caches_ready | (1 << CACHE_FRAG_TYPE_RTSP);
-       caches_ready = ~caches_ready;
-@@ -3366,9 +3374,10 @@ create_volume(int volume_number, off_t size_in_blocks, int scheme, CacheVol *cp)
- void
- rebuild_host_table(Cache *cache)
- {
--  build_vol_hash_table(&cache->hosttable->gen_host_rec);
--  if (cache->hosttable->m_numEntries != 0) {
--    CacheHostMatcher *hm = cache->hosttable->getHostMatcher();
-+  CacheHostTable *hosttable = cache->getHosttable(__func__);
-+  build_vol_hash_table(&hosttable->gen_host_rec);
-+  if (hosttable->m_numEntries != 0) {
-+    CacheHostMatcher *hm = hosttable->getHostMatcher();
-     CacheHostRecord *h_rec = hm->getDataArray();
-     int h_rec_len = hm->getNumElements();
-     int i;
-@@ -3383,8 +3392,9 @@ Vol *
- Cache::key_to_vol(CacheKey *key, char const *hostname, int host_len)
- {
-   uint32_t h = (key->slice32(2) >> DIR_TAG_WIDTH) % VOL_HASH_TABLE_SIZE;
-+  CacheHostTable *hosttable = getHosttable(__func__);
-   unsigned short *hash_table = hosttable->gen_host_rec.vol_hash_table;
--  CacheHostRecord *host_rec = &hosttable->gen_host_rec;
-+  CacheHostRecord *host_rec = &(hosttable->gen_host_rec);
- 
-   if (hosttable->m_numEntries > 0 && host_len) {
-     CacheHostResult res;
-@@ -3392,21 +3402,13 @@ Cache::key_to_vol(CacheKey *key, char const *hostname, int host_len)
-     if (res.record) {
-       unsigned short *host_hash_table = res.record->vol_hash_table;
-       if (host_hash_table) {
--        if (is_debug_tag_set("cache_hosting")) {
--          char format_str[50];
--          snprintf(format_str, sizeof(format_str), "Volume: %%xd for host: %%.%ds", host_len);
--          Debug("cache_hosting", format_str, res.record, hostname);
--        }
-+        Debug("cache_hosting", "Volume: %p for host: %.*s", res.record, host_len, hostname);
-         return res.record->vols[host_hash_table[h]];
-       }
-     }
-   }
-   if (hash_table) {
--    if (is_debug_tag_set("cache_hosting")) {
--      char format_str[50];
--      snprintf(format_str, sizeof(format_str), "Generic volume: %%xd for host: %%.%ds", host_len);
--      Debug("cache_hosting", format_str, host_rec, hostname);
--    }
-+    Debug("cache_hosting", "Generic volume: %p for host: %.*s", host_rec, host_len, hostname);
-     return host_rec->vols[hash_table[h]];
-   } else
-     return host_rec->vols[0];
-diff --git a/iocore/cache/CacheDir.cc b/iocore/cache/CacheDir.cc
-index 779c16b..e54fbfb 100644
---- a/iocore/cache/CacheDir.cc
-+++ b/iocore/cache/CacheDir.cc
-@@ -24,6 +24,8 @@
- 
- #include "P_Cache.h"
- 
-+#include "hugepages.h"
-+
- // #define LOOP_CHECK_MODE 1
- #ifdef LOOP_CHECK_MODE
- #define DIR_LOOP_THRESHOLD 1000
-@@ -1011,6 +1013,7 @@ sync_cache_dir_on_shutdown(void)
-   Debug("cache_dir_sync", "sync started");
-   char *buf = NULL;
-   size_t buflen = 0;
-+  bool buf_huge = false;
- 
-   EThread *t = (EThread *)0xdeadbeef;
-   for (int i = 0; i < gnvol; i++) {
-@@ -1077,10 +1080,22 @@ sync_cache_dir_on_shutdown(void)
- #endif
- 
-     if (buflen < dirlen) {
--      if (buf)
--        ats_memalign_free(buf);
--      buf = (char *)ats_memalign(ats_pagesize(), dirlen);
-+      if (buf) {
-+        if (buf_huge)
-+          ats_free_hugepage(buf, buflen);
-+        else
-+          ats_memalign_free(buf);
-+        buf = NULL;
-+      }
-       buflen = dirlen;
-+      if (ats_hugepage_enabled()) {
-+        buf = (char *)ats_alloc_hugepage(buflen);
-+        buf_huge = true;
-+      }
-+      if (buf == NULL) {
-+        buf = (char *)ats_memalign(ats_pagesize(), buflen);
-+        buf_huge = false;
-+      }
-     }
- 
-     if (!d->dir_sync_in_progress) {
-@@ -1104,8 +1119,13 @@ sync_cache_dir_on_shutdown(void)
-     Debug("cache_dir_sync", "done syncing dir for vol %s", d->hash_text.get());
-   }
-   Debug("cache_dir_sync", "sync done");
--  if (buf)
--    ats_memalign_free(buf);
-+  if (buf) {
-+    if (buf_huge)
-+      ats_free_hugepage(buf, buflen);
-+    else
-+      ats_memalign_free(buf);
-+    buf = NULL;
-+  }
- }
- 
- 
-@@ -1120,11 +1140,6 @@ CacheSync::mainEvent(int event, Event *e)
- Lrestart:
-   if (vol_idx >= gnvol) {
-     vol_idx = 0;
--    if (buf) {
--      ats_memalign_free(buf);
--      buf = 0;
--      buflen = 0;
--    }
-     Debug("cache_dir_sync", "sync done");
-     if (event == EVENT_INTERVAL)
-       trigger = e->ethread->schedule_in(this, HRTIME_SECONDS(cache_config_dir_sync_frequency));
-@@ -1196,10 +1211,22 @@ Lrestart:
-       Debug("cache_dir_sync", "pos: %" PRIu64 " Dir %s dirty...syncing to disk", vol->header->write_pos, vol->hash_text.get());
-       vol->header->dirty = 0;
-       if (buflen < dirlen) {
--        if (buf)
--          ats_memalign_free(buf);
--        buf = (char *)ats_memalign(ats_pagesize(), dirlen);
-+        if (buf) {
-+          if (buf_huge)
-+            ats_free_hugepage(buf, buflen);
-+          else
-+            ats_memalign_free(buf);
-+          buf = NULL;
-+        }
-         buflen = dirlen;
-+        if (ats_hugepage_enabled()) {
-+          buf = (char *)ats_alloc_hugepage(buflen);
-+          buf_huge = true;
-+        }
-+        if (buf == NULL) {
-+          buf = (char *)ats_memalign(ats_pagesize(), buflen);
-+          buf_huge = false;
-+        }
-       }
-       vol->header->sync_serial++;
-       vol->footer->sync_serial = vol->header->sync_serial;
-diff --git a/iocore/cache/CacheVol.cc b/iocore/cache/CacheVol.cc
-index 47f5775..fb82742 100644
---- a/iocore/cache/CacheVol.cc
-+++ b/iocore/cache/CacheVol.cc
-@@ -57,10 +57,11 @@ CacheVC::scanVol(int /* event ATS_UNUSED */, Event * /* e ATS_UNUSED */)
-   Debug("cache_scan_truss", "inside %p:scanVol", this);
-   if (_action.cancelled)
-     return free_CacheVC(this);
--  CacheHostRecord *rec = &theCache->hosttable->gen_host_rec;
-+  CacheHostTable *hosttable = theCache->getHosttable(__func__);
-+  CacheHostRecord *rec = &hosttable->gen_host_rec;
-   if (host_len) {
-     CacheHostResult res;
--    theCache->hosttable->Match(hostname, host_len, &res);
-+    hosttable->Match(hostname, host_len, &res);
-     if (res.record)
-       rec = res.record;
-   }
-diff --git a/iocore/cache/P_CacheDir.h b/iocore/cache/P_CacheDir.h
-index 0a31c32..f5c83d5 100644
---- a/iocore/cache/P_CacheDir.h
-+++ b/iocore/cache/P_CacheDir.h
-@@ -293,6 +293,7 @@ struct CacheSync : public Continuation {
-   int vol_idx;
-   char *buf;
-   size_t buflen;
-+  bool buf_huge;
-   off_t writepos;
-   AIOCallbackInternal io;
-   Event *trigger;
-@@ -300,7 +301,8 @@ struct CacheSync : public Continuation {
-   int mainEvent(int event, Event *e);
-   void aio_write(int fd, char *b, int n, off_t o);
- 
--  CacheSync() : Continuation(new_ProxyMutex()), vol_idx(0), buf(0), buflen(0), writepos(0), trigger(0), start_time(0)
-+  CacheSync()
-+    : Continuation(new_ProxyMutex()), vol_idx(0), buf(0), buflen(0), buf_huge(false), writepos(0), trigger(0), start_time(0)
-   {
-     SET_HANDLER(&CacheSync::mainEvent);
-   }
-diff --git a/iocore/cache/P_CacheHosting.h b/iocore/cache/P_CacheHosting.h
-index 21d2b1a..c874f64 100644
---- a/iocore/cache/P_CacheHosting.h
-+++ b/iocore/cache/P_CacheHosting.h
-@@ -163,7 +163,8 @@ struct CacheHostTableConfig : public Continuation {
-     (void)e;
-     (void)event;
-     CacheHostTable *t = new CacheHostTable((*ppt)->cache, (*ppt)->type);
--    CacheHostTable *old = (CacheHostTable *)ink_atomic_swap(&t, *ppt);
-+    CacheHostTable *old = (CacheHostTable *)ink_atomic_swap(ppt, t);
-+    Debug("cache_hosting", "swapped: old=%p, new=%p", old, t);
-     new_Deleter(old, CACHE_MEM_FREE_TIMEOUT);
-     return EVENT_DONE;
-   }
-diff --git a/iocore/cache/P_CacheInternal.h b/iocore/cache/P_CacheInternal.h
-index d06c8d3..d27a752 100644
---- a/iocore/cache/P_CacheInternal.h
-+++ b/iocore/cache/P_CacheInternal.h
-@@ -1058,6 +1058,12 @@ struct Cache {
-       hosttable(NULL), total_initialized_vol(0), scheme(CACHE_NONE_TYPE)
-   {
-   }
-+  CacheHostTable *
-+  getHosttable(const char *callfunc)
-+  {
-+    Debug("cache_hosting", "getHosttable() from: %s", callfunc);
-+    return hosttable;
-+  }
- };
- 
- extern Cache *theCache;
-diff --git a/lib/records/RecCore.cc b/lib/records/RecCore.cc
-index 5babfad..9c75006 100644
---- a/lib/records/RecCore.cc
-+++ b/lib/records/RecCore.cc
-@@ -789,7 +789,7 @@ RecRegisterStat(RecT rec_type, const char *name, RecDataT data_type, RecData dat
-     // new default value.
-     if ((r->stat_meta.persist_type == RECP_NULL || r->stat_meta.persist_type == RECP_PERSISTENT) &&
-         persist_type == RECP_NON_PERSISTENT) {
--      RecDebug(DL_Debug, "resetting default value for formerly persisted stat '%s'", r->name);
-+      RecDebug(DL_Debug, "resetting default value for formerly persisted stat id:%d '%s'", r->rsb_id, r->name);
-       RecDataSet(r->data_type, &(r->data), &(data_default));
-     }
- 
-@@ -955,6 +955,8 @@ RecDumpRecords(RecT rec_type, RecDumpEntryCb callback, void *edata)
- {
-   int i, num_records;
- 
-+  ink_rwlock_rdlock(&g_records_rwlock);
-+
-   num_records = g_num_records;
-   for (i = 0; i < num_records; i++) {
-     RecRecord *r = &(g_records[i]);
-@@ -964,6 +966,8 @@ RecDumpRecords(RecT rec_type, RecDumpEntryCb callback, void *edata)
-       rec_mutex_release(&(r->lock));
-     }
-   }
-+
-+  ink_rwlock_unlock(&g_records_rwlock);
- }
- 
- void
-diff --git a/lib/records/RecProcess.cc b/lib/records/RecProcess.cc
-index f0c59bb..9a44ee6 100644
---- a/lib/records/RecProcess.cc
-+++ b/lib/records/RecProcess.cc
-@@ -173,12 +173,18 @@ raw_stat_sync_to_global(RecRawStatBlock *rsb, int id)
-     tlp = ((RecRawStat *)((char *)(eventProcessor.all_ethreads[i]) + rsb->ethr_stat_offset)) + id;
-     total.sum += tlp->sum;
-     total.count += tlp->count;
-+    // Debug("stats","raw_stat_sync_to_global(): ethread: %d, id: %d, total.sum: %" PRId64 ", total.count: %" PRId64 ", tlp->sum: %"
-+    // PRId64 ", tlp->count: %" PRId64,
-+    // i, id, total.sum, total.count, tlp->sum, tlp->count);
-   }
- 
-   for (i = 0; i < eventProcessor.n_dthreads; i++) {
-     tlp = ((RecRawStat *)((char *)(eventProcessor.all_dthreads[i]) + rsb->ethr_stat_offset)) + id;
-     total.sum += tlp->sum;
-     total.count += tlp->count;
-+    // Debug("stats","raw_stat_sync_to_global(): dthread: %d, id: %d, total.sum: %" PRId64 ", total.count: %" PRId64 ", tlp->sum: %"
-+    // PRId64 ", tlp->count: %" PRId64,
-+    // i, id, total.sum, total.count, tlp->sum, tlp->count);
-   }
- 
-   if (total.sum < 0) { // Assure that we stay positive
-@@ -194,9 +200,9 @@ raw_stat_sync_to_global(RecRawStatBlock *rsb, int id)
-   delta.count = total.count - rsb->global[id]->last_count;
- 
-   // This is too verbose now, so leaving it out / leif
--  // Debug("stats", "raw_stat_sync_to_global(): rsb pointer:%p id:%d delta:%" PRId64 " total:%" PRId64 " last:%" PRId64 " global:%"
--  // PRId64 "\n",
--  // rsb, id, delta.sum, total.sum, rsb->global[id]->last_sum, rsb->global[id]->sum);
-+  Debug("stats.verbose", "raw_stat_sync_to_global(): rsb pointer:%p rsb data pointer:%p id:%d delta:%" PRId64 " total:%" PRId64
-+                         " last:%" PRId64 " global:%" PRId64 "\n",
-+        rsb, rsb->global[id], id, delta.sum, total.sum, rsb->global[id]->last_sum, rsb->global[id]->sum);
- 
-   // increment the global values by the delta
-   ink_atomic_increment(&(rsb->global[id]->sum), delta.sum);
-@@ -540,7 +546,7 @@ int
- _RecRegisterRawStat(RecRawStatBlock *rsb, RecT rec_type, const char *name, RecDataT data_type, RecPersistT persist_type, int id,
-                     RecRawStatSyncCb sync_cb)
- {
--  Debug("stats", "RecRawStatSyncCb(%s): rsb pointer:%p id:%d\n", name, rsb, id);
-+  Debug("stats", "_RecRegisterRawStat(%s): rsb pointer:%p id:%d\n", name, rsb, id);
- 
-   // check to see if we're good to proceed
-   ink_assert(id < rsb->max_stats);
-@@ -556,6 +562,11 @@ _RecRegisterRawStat(RecRawStatBlock *rsb, RecT rec_type, const char *name, RecDa
-     err = REC_ERR_FAIL;
-     goto Ldone;
-   }
-+  if (r->rsb_id > 0 && r->rsb_id != id) {
-+    Warning("_RecRegisterRawStat(): Created and reusing a stat with id = %d for new stat named %s", r->rsb_id, name);
-+  } else {
-+    Warning("_RecRegisterStat(): Stat created, name: %s, id: %d", name, id);
-+  }
-   r->rsb_id = id; // This is the index within the RSB raw block for this stat, used for lookups by name.
-   if (i_am_the_record_owner(r->rec_type)) {
-     r->sync_required = r->sync_required | REC_PEER_SYNC_REQUIRED;
-@@ -570,6 +581,7 @@ _RecRegisterRawStat(RecRawStatBlock *rsb, RecT rec_type, const char *name, RecDa
- 
-   // setup the periodic sync callback
-   RecRegisterRawStatSyncCb(name, sync_cb, rsb, id);
-+  Warning("_RecRegisterRawStat(): Stat created, id:%d name:%s, data address:%p", id, name, &r->stat_meta.data_raw);
- 
- Ldone:
-   return err;
-diff --git a/lib/ts/ConsistentHash.cc b/lib/ts/ConsistentHash.cc
-index c983ccb..912bc74 100644
---- a/lib/ts/ConsistentHash.cc
-+++ b/lib/ts/ConsistentHash.cc
-@@ -67,13 +67,19 @@ ATSConsistentHash::insert(ATSConsistentHashNode *node, float weight, ATSHash64 *
- }
- 
- ATSConsistentHashNode *
--ATSConsistentHash::lookup(const char *url, ATSConsistentHashIter *i, bool *w, ATSHash64 *h)
-+ATSConsistentHash::lookup(const char *url, size_t url_len, ATSConsistentHashIter *i, bool *w, ATSHash64 *h)
- {
-   uint64_t url_hash;
-   ATSConsistentHashIter NodeMapIterUp, *iter;
-   ATSHash64 *thash;
-   bool *wptr, wrapped = false;
- 
-+  if (url_len <= 0 && url) {
-+    url_len = strlen(url);
-+  } else {
-+    url_len = 0;
-+  }
-+
-   if (h) {
-     thash = h;
-   } else if (hash) {
-@@ -95,7 +101,7 @@ ATSConsistentHash::lookup(const char *url, ATSConsistentHashIter *i, bool *w, AT
-   }
- 
-   if (url) {
--    thash->update(url, strlen(url));
-+    thash->update(url, url_len);
-     thash->final();
-     url_hash = thash->get();
-     thash->clear();
-@@ -124,13 +130,19 @@ ATSConsistentHash::lookup(const char *url, ATSConsistentHashIter *i, bool *w, AT
- }
- 
- ATSConsistentHashNode *
--ATSConsistentHash::lookup_available(const char *url, ATSConsistentHashIter *i, bool *w, ATSHash64 *h)
-+ATSConsistentHash::lookup_available(const char *url, size_t url_len, ATSConsistentHashIter *i, bool *w, ATSHash64 *h)
- {
-   uint64_t url_hash;
-   ATSConsistentHashIter NodeMapIterUp, *iter;
-   ATSHash64 *thash;
-   bool *wptr, wrapped = false;
- 
-+  if (url_len <= 0 && url) {
-+    url_len = strlen(url);
-+  } else {
-+    url_len = 0;
-+  }
-+
-   if (h) {
-     thash = h;
-   } else if (hash) {
-@@ -152,7 +164,7 @@ ATSConsistentHash::lookup_available(const char *url, ATSConsistentHashIter *i, b
-   }
- 
-   if (url) {
--    thash->update(url, strlen(url));
-+    thash->update(url, url_len);
-     thash->final();
-     url_hash = thash->get();
-     thash->clear();
-@@ -179,6 +191,34 @@ ATSConsistentHash::lookup_available(const char *url, ATSConsistentHashIter *i, b
-   return (*iter)->second;
- }
- 
-+ATSConsistentHashNode *
-+ATSConsistentHash::lookup_by_hashval(uint64_t hashval, ATSConsistentHashIter *i, bool *w)
-+{
-+  ATSConsistentHashIter NodeMapIterUp, *iter;
-+  bool *wptr, wrapped = false;
-+
-+  if (w) {
-+    wptr = w;
-+  } else {
-+    wptr = &wrapped;
-+  }
-+
-+  if (i) {
-+    iter = i;
-+  } else {
-+    iter = &NodeMapIterUp;
-+  }
-+
-+  *iter = NodeMap.lower_bound(hashval);
-+
-+  if (*iter == NodeMap.end()) {
-+    *wptr = true;
-+    *iter = NodeMap.begin();
-+  }
-+
-+  return (*iter)->second;
-+}
-+
- ATSConsistentHash::~ATSConsistentHash()
- {
-   if (hash) {
-diff --git a/lib/ts/ConsistentHash.h b/lib/ts/ConsistentHash.h
-index 6406a6c6..49822ad 100644
---- a/lib/ts/ConsistentHash.h
-+++ b/lib/ts/ConsistentHash.h
-@@ -49,9 +49,11 @@ typedef std::map<uint64_t, ATSConsistentHashNode *>::iterator ATSConsistentHashI
- struct ATSConsistentHash {
-   ATSConsistentHash(int r = 1024, ATSHash64 *h = NULL);
-   void insert(ATSConsistentHashNode *node, float weight = 1.0, ATSHash64 *h = NULL);
--  ATSConsistentHashNode *lookup(const char *url = NULL, ATSConsistentHashIter *i = NULL, bool *w = NULL, ATSHash64 *h = NULL);
--  ATSConsistentHashNode *lookup_available(const char *url = NULL, ATSConsistentHashIter *i = NULL, bool *w = NULL,
--                                          ATSHash64 *h = NULL);
-+  ATSConsistentHashNode *lookup(const char *url = NULL, size_t url_len = 0, ATSConsistentHashIter *i = NULL, bool *w = NULL,
-+                                ATSHash64 *h = NULL);
-+  ATSConsistentHashNode *lookup_available(const char *url = NULL, size_t url_len = 0, ATSConsistentHashIter *i = NULL,
-+                                          bool *w = NULL, ATSHash64 *h = NULL);
-+  ATSConsistentHashNode *lookup_by_hashval(uint64_t hashval, ATSConsistentHashIter *i = NULL, bool *w = NULL);
-   ~ATSConsistentHash();
- 
- private:
-diff --git a/lib/ts/Makefile.am b/lib/ts/Makefile.am
-index d5ca4ac..3f5eee5 100644
---- a/lib/ts/Makefile.am
-+++ b/lib/ts/Makefile.am
-@@ -107,6 +107,8 @@ libtsutil_la_SOURCES = \
-   defalloc.h \
-   fastlz.c \
-   fastlz.h \
-+  hugepages.cc \
-+  hugepages.h \
-   ink_aiocb.h \
-   ink_align.h \
-   ink_apidefs.h \
-diff --git a/lib/ts/apidefs.h.in b/lib/ts/apidefs.h.in
-index 04f4dd9..31cb807 100644
---- a/lib/ts/apidefs.h.in
-+++ b/lib/ts/apidefs.h.in
-@@ -758,6 +758,14 @@ extern "C"
-     TS_CONFIG_HTTP_POST_CHECK_CONTENT_LENGTH_ENABLED,
-     TS_CONFIG_HTTP_GLOBAL_USER_AGENT_HEADER,
-     TS_CONFIG_HTTP_AUTH_SERVER_SESSION_PRIVATE,
-+    TS_CONFIG_HTTP_TRANSACTION_ACTIVE_TIMEOUT_IN,
-+    TS_CONFIG_HTTP_PER_PARENT_CONNECT_ATTEMPTS,
-+    TS_CONFIG_HTTP_PARENT_TOTAL_CONNECT_ATTEMPTS,
-+    TS_CONFIG_HTTP_SIMPLE_RETRY_ENABLED,
-+    TS_CONFIG_HTTP_SIMPLE_RETRY_RESPONSE_CODES,
-+    TS_CONFIG_HTTP_DEAD_SERVER_RETRY_ENABLED,
-+    TS_CONFIG_HTTP_DEAD_SERVER_RETRY_RESPONSE_CODES,
-+    TS_CONFIG_HTTP_URL_REMAP_REQUIRED,
-     TS_CONFIG_LAST_ENTRY
-   } TSOverridableConfigKey;
- 
-diff --git a/lib/ts/hugepages.cc b/lib/ts/hugepages.cc
-new file mode 100644
-index 0000000..d7d94a4
---- /dev/null
-+++ b/lib/ts/hugepages.cc
-@@ -0,0 +1,147 @@
-+/** @file
-+
-+  @section license License
-+
-+  Licensed to the Apache Software Foundation (ASF) under one
-+  or more contributor license agreements.  See the NOTICE file
-+  distributed with this work for additional information
-+  regarding copyright ownership.  The ASF licenses this file
-+  to you under the Apache License, Version 2.0 (the
-+  "License"); you may not use this file except in compliance
-+  with the License.  You may obtain a copy of the License at
-+
-+      http://www.apache.org/licenses/LICENSE-2.0
-+
-+  Unless required by applicable law or agreed to in writing, software
-+  distributed under the License is distributed on an "AS IS" BASIS,
-+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-+  See the License for the specific language governing permissions and
-+  limitations under the License.
-+ */
-+
-+#include <cstdio>
-+#include <sys/mman.h>
-+#include "Diags.h"
-+#include "ink_align.h"
-+
-+#define DEBUG_TAG "hugepages"
-+
-+#ifdef MAP_HUGETLB
-+#define MEMINFO_PATH "/proc/meminfo"
-+#define LINE_SIZE 256
-+#define TOKEN "Hugepagesize:"
-+#define TOKEN_SIZE (strlen(TOKEN))
-+
-+static int hugepage_size = -1;
-+static bool hugepage_enabled;
-+#endif
-+
-+size_t
-+ats_hugepage_size(void)
-+{
-+#ifdef MAP_HUGETLB
-+  return hugepage_size;
-+#else
-+  Debug(DEBUG_TAG, "MAP_HUGETLB not defined");
-+  return 0;
-+#endif
-+}
-+
-+bool
-+ats_hugepage_enabled(void)
-+{
-+#ifdef MAP_HUGETLB
-+  return hugepage_enabled;
-+#else
-+  return false;
-+#endif
-+}
-+
-+void
-+ats_hugepage_init(int enabled)
-+{
-+#ifdef MAP_HUGETLB
-+  FILE *fp;
-+  char line[LINE_SIZE];
-+  char *p, *ep;
-+
-+  hugepage_size = 0;
-+
-+  if (!enabled) {
-+    Debug(DEBUG_TAG, "hugepages not enabled");
-+    return;
-+  }
-+
-+  fp = fopen(MEMINFO_PATH, "r");
-+
-+  if (fp == NULL) {
-+    Debug(DEBUG_TAG, "Cannot open file %s", MEMINFO_PATH);
-+    return;
-+  }
-+
-+  while (fgets(line, sizeof(line), fp)) {
-+    if (strncmp(line, TOKEN, TOKEN_SIZE) == 0) {
-+      p = line + TOKEN_SIZE;
-+      while (*p == ' ') {
-+        p++;
-+      }
-+      hugepage_size = strtol(p, &ep, 10);
-+      // What other values can this be?
-+      if (strncmp(ep, " kB", 4)) {
-+        hugepage_size *= 1024;
-+      }
-+      break;
-+    }
-+  }
-+
-+  fclose(fp);
-+
-+  if (hugepage_size) {
-+    hugepage_enabled = true;
-+  }
-+
-+  Debug(DEBUG_TAG, "Hugepage size = %d", hugepage_size);
-+#else
-+  Debug(DEBUG_TAG, "MAP_HUGETLB not defined");
-+#endif
-+}
-+
-+void *
-+ats_alloc_hugepage(size_t s)
-+{
-+#ifdef MAP_HUGETLB
-+  size_t size;
-+  void *mem;
-+
-+  size = INK_ALIGN(s, ats_hugepage_size());
-+
-+  mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);
-+
-+  if (mem == MAP_FAILED) {
-+    Debug(DEBUG_TAG, "Could not allocate hugepages size = %zu", size);
-+    return NULL;
-+  }
-+
-+  return mem;
-+#else
-+  (void)s;
-+  Debug(DEBUG_TAG, "MAP_HUGETLB not defined");
-+  return NULL;
-+#endif
-+}
-+
-+bool
-+ats_free_hugepage(void *ptr, size_t s)
-+{
-+#ifdef MAP_HUGETLB
-+  size_t size;
-+
-+  size = INK_ALIGN(s, ats_hugepage_size());
-+  return (munmap(ptr, size) == 0);
-+#else
-+  (void)ptr;
-+  (void)s;
-+  Debug(DEBUG_TAG, "MAP_HUGETLB not defined");
-+  return false;
-+#endif
-+}
-diff --git a/lib/ts/hugepages.h b/lib/ts/hugepages.h
-new file mode 100644
-index 0000000..812542b
---- /dev/null
-+++ b/lib/ts/hugepages.h
-@@ -0,0 +1,32 @@
-+/** @file
-+
-+  @section license License
-+
-+  Licensed to the Apache Software Foundation (ASF) under one
-+  or more contributor license agreements.  See the NOTICE file
-+  distributed with this work for additional information
-+  regarding copyright ownership.  The ASF licenses this file
-+  to you under the Apache License, Version 2.0 (the
-+  "License"); you may not use this file except in compliance
-+  with the License.  You may obtain a copy of the License at
-+
-+      http://www.apache.org/licenses/LICENSE-2.0
-+
-+  Unless required by applicable law or agreed to in writing, software
-+  distributed under the License is distributed on an "AS IS" BASIS,
-+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-+  See the License for the specific language governing permissions and
-+  limitations under the License.
-+ */
-+#ifndef _hugepages_h_
-+#define _hugepages_h_
-+
-+#include <cstring>
-+
-+size_t ats_hugepage_size(void);
-+bool ats_hugepage_enabled(void);
-+void ats_hugepage_init(int);
-+void *ats_alloc_hugepage(size_t);
-+bool ats_free_hugepage(void *, size_t);
-+
-+#endif
-diff --git a/lib/ts/ink_queue.cc b/lib/ts/ink_queue.cc
-index e718b3f..0f14b68 100644
---- a/lib/ts/ink_queue.cc
-+++ b/lib/ts/ink_queue.cc
-@@ -50,6 +50,7 @@
- #include "ink_assert.h"
- #include "ink_queue_ext.h"
- #include "ink_align.h"
-+#include "hugepages.h"
- 
- inkcoreapi volatile int64_t fastalloc_mem_in_use = 0;
- inkcoreapi volatile int64_t fastalloc_mem_total = 0;
-@@ -100,9 +101,13 @@ ink_freelist_init(InkFreeList **fl, const char *name, uint32_t type_size, uint32
-   /* quick test for power of 2 */
-   ink_assert(!(alignment & (alignment - 1)));
-   f->alignment = alignment;
--  f->chunk_size = chunk_size;
-   // Make sure we align *all* the objects in the allocation, not just the first one
-   f->type_size = INK_ALIGN(type_size, alignment);
-+  if (ats_hugepage_enabled()) {
-+    f->chunk_size = INK_ALIGN(chunk_size * f->type_size, ats_hugepage_size()) / f->type_size;
-+  } else {
-+    f->chunk_size = chunk_size;
-+  }
-   SET_FREELIST_POINTER_VERSION(f->head, FROM_PTR(0), 0);
- 
-   f->used = 0;
-@@ -171,12 +176,16 @@ ink_freelist_new(InkFreeList *f)
- #ifdef DEBUG
-       char *oldsbrk = (char *)sbrk(0), *newsbrk = NULL;
- #endif
--      if (f->alignment)
--        newp = ats_memalign(f->alignment, f->chunk_size * type_size);
--      else
--        newp = ats_malloc(f->chunk_size * type_size);
-+      if (ats_hugepage_enabled())
-+        newp = ats_alloc_hugepage(f->chunk_size * type_size);
-+
-+      if (newp == NULL) {
-+        if (f->alignment)
-+          newp = ats_memalign(f->alignment, f->chunk_size * type_size);
-+        else
-+          newp = ats_malloc(f->chunk_size * type_size);
-+      }
-       ats_madvise((caddr_t)newp, f->chunk_size * type_size, f->advice);
--
-       fl_memadd(f->chunk_size * type_size);
- #ifdef DEBUG
-       newsbrk = (char *)sbrk(0);
-diff --git a/lib/ts/libts.h b/lib/ts/libts.h
-index d244158..20f85b2 100644
---- a/lib/ts/libts.h
-+++ b/lib/ts/libts.h
-@@ -41,6 +41,7 @@
- #define std *** _FIXME_REMOVE_DEPENDENCY_ON_THE_STL_ ***
- */
- 
-+#include "hugepages.h"
- #include "ink_config.h"
- #include "ink_platform.h"
- #include "ink_align.h"
-diff --git a/mgmt/RecordsConfig.cc b/mgmt/RecordsConfig.cc
-index f520f4a..0e229c2 100644
---- a/mgmt/RecordsConfig.cc
-+++ b/mgmt/RecordsConfig.cc
-@@ -506,6 +506,17 @@ static const RecordElement RecordsConfig[] =
-   ,
- 
-   //        ###################################
-+  //        # parent origin configuration     #
-+  //        ###################################
-+  {RECT_CONFIG, "proxy.config.http.parent_origin.simple_retry_enabled", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-1]", RECA_NULL}
-+  ,
-+  {RECT_CONFIG, "proxy.config.http.parent_origin.simple_retry_response_codes", RECD_STRING, "404", RECU_DYNAMIC, RR_NULL, RECC_STR, "^([0-9]+,)$", RECA_NULL}
-+  ,
-+  {RECT_CONFIG, "proxy.config.http.parent_origin.dead_server_retry_enabled", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-1]", RECA_NULL}
-+  ,
-+  {RECT_CONFIG, "proxy.config.http.parent_origin.dead_server_retry_response_codes", RECD_STRING, "503", RECU_DYNAMIC, RR_NULL, RECC_STR, "^([0-9]+,)$", RECA_NULL}
-+  ,
-+  //        ###################################
-   //        # NO DNS DOC IN CACHE             #
-   //        ###################################
-   {RECT_CONFIG, "proxy.config.http.doc_in_cache_skip_dns", RECD_INT, "1", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-1]", RECA_NULL}
-@@ -647,6 +658,13 @@ static const RecordElement RecordsConfig[] =
-   ,
-   {RECT_CONFIG, "proxy.config.http.cache.max_open_write_retries", RECD_INT, "1", RECU_DYNAMIC, RR_NULL, RECC_NULL, NULL, RECA_NULL}
-   ,
-+  //       #  open_write_fail_action has 3 options:
-+  //       #
-+  //       #  0 - default. disable cache and goto origin
-+  //       #  1 - return error if cache miss
-+  //       #  2 - serve stale until proxy.config.http.cache.max_stale_age, then goto origin, if refresh_miss
-+  {RECT_CONFIG, "proxy.config.http.cache.open_write_fail_action", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_NULL, NULL, RECA_NULL}
-+  ,
-   //       #  when_to_revalidate has 4 options:
-   //       #
-   //       #  0 - default. use use cache directives or heuristic
-@@ -2067,6 +2085,8 @@ static const RecordElement RecordsConfig[] =
-   ,
-   {RECT_CONFIG, "proxy.config.allocator.debug_filter", RECD_INT, "0", RECU_NULL, RR_NULL, RECC_NULL, "[0-3]", RECA_NULL}
-   ,
-+  {RECT_CONFIG, "proxy.config.allocator.hugepages", RECD_INT, "0", RECU_RESTART_TS, RR_NULL, RECC_NULL, "[0-1]", RECA_NULL}
-+  ,
- 
-   //############
-   //#
-diff --git a/plugins/cacheurl/cacheurl.cc b/plugins/cacheurl/cacheurl.cc
-index 5f03598..52565fc 100644
---- a/plugins/cacheurl/cacheurl.cc
-+++ b/plugins/cacheurl/cacheurl.cc
-@@ -42,6 +42,7 @@
- #define TOKENCOUNT 10
- #define OVECOUNT 30
- #define PLUGIN_NAME "cacheurl"
-+#define DEFAULT_CONFIG "cacheurl.config"
- 
- struct regex_info {
-   pcre *re;          /* Compiled regular expression */
-@@ -213,10 +214,10 @@ load_config_file(const char *config_file)
-   regex_info *info = 0;
- 
-   if (config_file == NULL) {
--    /* Default config file of plugins/cacheurl.config */
--    path = TSPluginDirGet();
--    path += "/cacheurl.config";
--  } else if (*config_file != '/') {
-+    config_file = DEFAULT_CONFIG;
-+  }
-+
-+  if (*config_file != '/') {
-     // Relative paths are relative to the config directory
-     path = TSConfigDirGet();
-     path += "/";
-diff --git a/plugins/experimental/Makefile.am b/plugins/experimental/Makefile.am
-index 2ef296c..52fef44 100644
---- a/plugins/experimental/Makefile.am
-+++ b/plugins/experimental/Makefile.am
-@@ -19,6 +19,7 @@ SUBDIRS = \
-  background_fetch \
-  balancer \
-  buffer_upload \
-+ cache_range_requests \
-  channel_stats \
-  collapsed_connection \
-  custom_redirect \
-diff --git a/plugins/experimental/background_fetch/background_fetch.cc b/plugins/experimental/background_fetch/background_fetch.cc
-index 878183e..fd0af7a 100644
---- a/plugins/experimental/background_fetch/background_fetch.cc
-+++ b/plugins/experimental/background_fetch/background_fetch.cc
-@@ -84,8 +84,13 @@ read_config(char *config_file, BgFetchRuleMap *ri)
-     snprintf(file_path, sizeof(file_path), "%s/%s", TSInstallDirGet(), config_file);
-     file = TSfopen(file_path, "r");
-     if (file == NULL) {
--      TSError("%s: invalid config file", PLUGIN_NAME);
--      return false;
-+      TSDebug(PLUGIN_NAME, "Failed to open config file %s, trying config path", config_file);
-+      snprintf(file_path, sizeof(file_path), "%s/%s", TSConfigDirGet(), config_file);
-+      file = TSfopen(file_path, "r");
-+      if (file == NULL) {
-+        TSError("%s: invalid config file", PLUGIN_NAME);
-+        return false;
-+      }
-     }
-   }
- 
-diff --git a/plugins/experimental/cache_range_requests/Makefile.am b/plugins/experimental/cache_range_requests/Makefile.am
-new file mode 100644
-index 0000000..5a27cac
---- /dev/null
-+++ b/plugins/experimental/cache_range_requests/Makefile.am
-@@ -0,0 +1,21 @@
-+#  Licensed to the Apache Software Foundation (ASF) under one
-+#  or more contributor license agreements.  See the NOTICE file
-+#  distributed with this work for additional information
-+#  regarding copyright ownership.  The ASF licenses this file
-+#  to you under the Apache License, Version 2.0 (the
-+#  "License"); you may not use this file except in compliance
-+#  with the License.  You may obtain a copy of the License at
-+#
-+#      http://www.apache.org/licenses/LICENSE-2.0
-+#
-+#  Unless required by applicable law or agreed to in writing, software
-+#  distributed under the License is distributed on an "AS IS" BASIS,
-+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-+#  See the License for the specific language governing permissions and
-+#  limitations under the License.
-+
-+include $(top_srcdir)/build/plugins.mk
-+
-+pkglib_LTLIBRARIES = cache_range_requests.la
-+cache_range_requests_la_SOURCES = cache_range_requests.cc
-+cache_range_requests_la_LDFLAGS = $(TS_PLUGIN_LDFLAGS)
-diff --git a/plugins/experimental/cache_range_requests/README b/plugins/experimental/cache_range_requests/README
-new file mode 100644
-index 0000000..faddeb5
---- /dev/null
-+++ b/plugins/experimental/cache_range_requests/README
-@@ -0,0 +1,36 @@
-+
-+Thousands of range requests for a very large object in the traffic server cache
-+are likely to increase system load averages due to I/O wait as objects are stored
-+on a single stripe or disk drive.
-+
-+This plugin allows you to remap individual range requests so that they are stored
-+as individual objects in the ATS cache when subsequent range requests are likely
-+to use the same range.  This spreads range requests over multiple stripes thereby
-+reducing I/O wait and system load averages.
-+
-+This plugin reads the range request header byte range value and then creates a
-+new cache key url using the original request url with the range value appended
-+to it.  The range header is removed where appropriate from the requests and the
-+origin server response code is changed from a 206 to a 200 to insure that the
-+object is written to cache using the new cache key url.  The response code sent 
-+to the client will be changed back to a 206 and all requests to the origin server 
-+will contain the range header so that the correct response is received.
-+
-+Installation:
-+
-+    make
-+    sudo make install
-+
-+If you don't have the traffic server binaries in your path, then you will need
-+to specify the path to tsxs manually:
-+
-+    make TSXS=/opt/trafficserver/bin/tsxs
-+    sudo make TSXS=/opt/trafficserver/bin/tsxs install
-+
-+Configuration:
-+
-+    Add @plugin=cache_range_requests.so to your remap.config rules.
-+
-+    Or for a global plugin where all range requests are processed,
-+    Add cache_range_requests.so to the plugin.config
-+
-diff --git a/plugins/experimental/cache_range_requests/cache_range_requests.cc b/plugins/experimental/cache_range_requests/cache_range_requests.cc
-new file mode 100644
-index 0000000..8722279
---- /dev/null
-+++ b/plugins/experimental/cache_range_requests/cache_range_requests.cc
-@@ -0,0 +1,411 @@
-+/*
-+ * Licensed to the Apache Software Foundation (ASF) under one
-+ * or more contributor license agreements.  See the NOTICE file
-+ * distributed with this work for additional information
-+ * regarding copyright ownership.  The ASF licenses this file
-+ * to you under the Apache License, Version 2.0 (the "License");
-+ * you may not use this file except in compliance with the
-+ * License.  You may obtain a copy of the License at
-+ *
-+ *     http://www.apache.org/licenses/LICENSE-2.0
-+ *
-+ * Unless required by applicable law or agreed to in writing,
-+ * software distributed under the License is distributed on an
-+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-+ * KIND, either express or implied.  See the License for the
-+ * specific language governing permissions and limitations
-+ * under the License.
-+ */
-+
-+/*
-+ * This plugin looks for range requests and then creates a new
-+ * cache key url so that each individual range requests is written
-+ * to the cache as a individual object so that subsequent range
-+ * requests are read accross different disk drives reducing I/O
-+ * wait and load averages when there are large numbers of range
-+ * requests.
-+ */
-+
-+#include <stdio.h>
-+#include <string.h>
-+#include "ts/ts.h"
-+#include "ts/remap.h"
-+
-+#define PLUGIN_NAME "cache_range_requests"
-+#define DEBUG_LOG(fmt, ...) TSDebug(PLUGIN_NAME, "[%s:%d] %s(): " fmt, __FILE__, __LINE__, __func__, ##__VA_ARGS__)
-+#define ERROR_LOG(fmt, ...) TSError("[%s:%d] %s(): " fmt, __FILE__, __LINE__, __func__, ##__VA_ARGS__)
-+
-+struct txndata {
-+  char *range_value;
-+};
-+
-+static void handle_read_request_header(TSCont, TSEvent, void *);
-+static void range_header_check(TSHttpTxn txnp);
-+static void handle_send_origin_request(TSCont, TSHttpTxn, struct txndata *);
-+static void handle_client_send_response(TSHttpTxn, struct txndata *);
-+static void handle_server_read_response(TSHttpTxn, struct txndata *);
-+static int remove_header(TSMBuffer, TSMLoc, const char *, int);
-+static bool set_header(TSMBuffer, TSMLoc, const char *, int, const char *, int);
-+static void transaction_handler(TSCont, TSEvent, void *);
-+
-+/**
-+ * Entry point when used as a global plugin.
-+ *
-+ */
-+static void
-+handle_read_request_header(TSCont txn_contp, TSEvent event, void *edata)
-+{
-+  TSHttpTxn txnp = static_cast<TSHttpTxn>(edata);
-+
-+  range_header_check(txnp);
-+
-+  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
-+}
-+
-+/**
-+ * Reads the client request header and if this is a range request:
-+ *
-+ * 1. creates a new cache key url using the range request information.
-+ * 2. Saves the range request information and then removes the range
-+ *    header so that the response retrieved from the origin will
-+ *    be written to cache.
-+ * 3. Schedules TS_HTTP_SEND_REQUEST_HDR_HOOK, TS_HTTP_SEND_RESPONSE_HDR_HOOK,
-+ *    and TS_HTTP_TXN_CLOSE_HOOK for further processing.
-+ */
-+static void
-+range_header_check(TSHttpTxn txnp)
-+{
-+  char cache_key_url[8192] = {0};
-+  char *req_url;
-+  int length, url_length;
-+  struct txndata *txn_state;
-+  TSMBuffer hdr_bufp;
-+  TSMLoc req_hdrs = NULL;
-+  TSMLoc loc = NULL;
-+  TSCont txn_contp;
-+
-+  if (TS_SUCCESS == TSHttpTxnClientReqGet(txnp, &hdr_bufp, &req_hdrs)) {
-+    loc = TSMimeHdrFieldFind(hdr_bufp, req_hdrs, TS_MIME_FIELD_RANGE, TS_MIME_LEN_RANGE);
-+    if (TS_NULL_MLOC != loc) {
-+      const char *hdr_value = TSMimeHdrFieldValueStringGet(hdr_bufp, req_hdrs, loc, 0, &length);
-+      if (!hdr_value || length <= 0) {
-+        DEBUG_LOG("Not a range request.");
-+      } else {
-+        if (NULL == (txn_contp = TSContCreate((TSEventFunc)transaction_handler, NULL))) {
-+          ERROR_LOG("failed to create the transaction handler continuation.");
-+        } else {
-+          txn_state = (struct txndata *)TSmalloc(sizeof(struct txndata));
-+          txn_state->range_value = TSstrndup(hdr_value, length);
-+          DEBUG_LOG("length: %d, txn_state->range_value: %s", length, txn_state->range_value);
-+          txn_state->range_value[length] = '\0'; // workaround for bug in core
-+
-+          req_url = TSHttpTxnEffectiveUrlStringGet(txnp, &url_length);
-+          snprintf(cache_key_url, 8192, "%s-%s", req_url, txn_state->range_value);
-+          DEBUG_LOG("Rewriting cache URL for %s to %s", req_url, cache_key_url);
-+          if (req_url != NULL)
-+            TSfree(req_url);
-+
-+          // set the cache key.
-+          if (TS_SUCCESS != TSCacheUrlSet(txnp, cache_key_url, strlen(cache_key_url))) {
-+            DEBUG_LOG("failed to change the cache url to %s.", cache_key_url);
-+          }
-+          // remove the range request header.
-+          if (remove_header(hdr_bufp, req_hdrs, TS_MIME_FIELD_RANGE, TS_MIME_LEN_RANGE) > 0) {
-+            DEBUG_LOG("Removed the Range: header from the request.");
-+          }
-+
-+          TSContDataSet(txn_contp, txn_state);
-+          TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_REQUEST_HDR_HOOK, txn_contp);
-+          TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, txn_contp);
-+          TSHttpTxnHookAdd(txnp, TS_HTTP_TXN_CLOSE_HOOK, txn_contp);
-+          DEBUG_LOG("Added TS_HTTP_SEND_REQUEST_HDR_HOOK, TS_HTTP_SEND_RESPONSE_HDR_HOOK, and TS_HTTP_TXN_CLOSE_HOOK");
-+        }
-+      }
-+      TSHandleMLocRelease(hdr_bufp, req_hdrs, loc);
-+    } else {
-+      DEBUG_LOG("no range request header.");
-+    }
-+    TSHandleMLocRelease(hdr_bufp, req_hdrs, NULL);
-+  } else {
-+    DEBUG_LOG("failed to retrieve the server request");
-+  }
-+}
-+
-+/**
-+ * Restores the range request header if the request must be
-+ * satisfied from the origin and schedules the TS_READ_RESPONSE_HDR_HOOK.
-+ */
-+static void
-+handle_send_origin_request(TSCont contp, TSHttpTxn txnp, struct txndata *txn_state)
-+{
-+  TSMBuffer hdr_bufp;
-+  TSMLoc req_hdrs = NULL;
-+
-+  if (TS_SUCCESS == TSHttpTxnServerReqGet(txnp, &hdr_bufp, &req_hdrs) && txn_state->range_value != NULL) {
-+    if (set_header(hdr_bufp, req_hdrs, TS_MIME_FIELD_RANGE, TS_MIME_LEN_RANGE, txn_state->range_value,
-+                   strlen(txn_state->range_value))) {
-+      DEBUG_LOG("Added range header: %s", txn_state->range_value);
-+      TSHttpTxnHookAdd(txnp, TS_HTTP_READ_RESPONSE_HDR_HOOK, contp);
-+    }
-+  }
-+  TSHandleMLocRelease(hdr_bufp, req_hdrs, NULL);
-+}
-+
-+/**
-+ * Changes the response code back to a 206 Partial content before
-+ * replying to the client that requested a range.
-+ */
-+static void
-+handle_client_send_response(TSHttpTxn txnp, struct txndata *txn_state)
-+{
-+  bool partial_content_reason = false;
-+  char *p;
-+  int length;
-+  TSMBuffer response, hdr_bufp;
-+  TSMLoc resp_hdr, req_hdrs = NULL;
-+
-+  TSReturnCode result = TSHttpTxnClientRespGet(txnp, &response, &resp_hdr);
-+  DEBUG_LOG("result: %d", result);
-+  if (TS_SUCCESS == result) {
-+    TSHttpStatus status = TSHttpHdrStatusGet(response, resp_hdr);
-+    // a cached result will have a TS_HTTP_OK with a 'Partial Content' reason
-+    if ((p = (char *)TSHttpHdrReasonGet(response, resp_hdr, &length)) != NULL) {
-+      if ((length == 15) && (0 == strncasecmp(p, "Partial Content", length))) {
-+        partial_content_reason = true;
-+      }
-+    }
-+    DEBUG_LOG("%d %.*s", status, length, p);
-+    if (TS_HTTP_STATUS_OK == status && partial_content_reason) {
-+      DEBUG_LOG("Got TS_HTTP_STATUS_OK.");
-+      TSHttpHdrStatusSet(response, resp_hdr, TS_HTTP_STATUS_PARTIAL_CONTENT);
-+      DEBUG_LOG("Set response header to TS_HTTP_STATUS_PARTIAL_CONTENT.");
-+    }
-+  }
-+  // add the range request header back in so that range requests may be logged.
-+  if (TS_SUCCESS == TSHttpTxnClientReqGet(txnp, &hdr_bufp, &req_hdrs) && txn_state->range_value != NULL) {
-+    if (set_header(hdr_bufp, req_hdrs, TS_MIME_FIELD_RANGE, TS_MIME_LEN_RANGE, txn_state->range_value,
-+                   strlen(txn_state->range_value))) {
-+      DEBUG_LOG("added range header: %s", txn_state->range_value);
-+    } else {
-+      DEBUG_LOG("set_header() failed.");
-+    }
-+  } else {
-+    DEBUG_LOG("failed to get Request Headers");
-+  }
-+  TSHandleMLocRelease(response, resp_hdr, NULL);
-+  TSHandleMLocRelease(hdr_bufp, req_hdrs, NULL);
-+}
-+
-+/**
-+ * After receiving a range request response from the origin, change
-+ * the response code from a 206 Partial content to a 200 OK so that
-+ * the response will be written to cache.
-+ */
-+static void
-+handle_server_read_response(TSHttpTxn txnp, struct txndata *txn_state)
-+{
-+  TSMBuffer response;
-+  TSMLoc resp_hdr;
-+  TSHttpStatus status;
-+
-+  if (TS_SUCCESS == TSHttpTxnServerRespGet(txnp, &response, &resp_hdr)) {
-+    status = TSHttpHdrStatusGet(response, resp_hdr);
-+    if (TS_HTTP_STATUS_PARTIAL_CONTENT == status) {
-+      DEBUG_LOG("Got TS_HTTP_STATUS_PARTIAL_CONTENT.");
-+      TSHttpHdrStatusSet(response, resp_hdr, TS_HTTP_STATUS_OK);
-+      DEBUG_LOG("Set response header to TS_HTTP_STATUS_OK.");
-+      bool cacheable = TSHttpTxnIsCacheable(txnp, NULL, response);
-+      DEBUG_LOG("range is cacheable: %d", cacheable);
-+    } else if (TS_HTTP_STATUS_OK == status) {
-+      DEBUG_LOG("The origin does not support range requests, attempting to disable cache write.");
-+      if (TS_SUCCESS == TSHttpTxnServerRespNoStoreSet(txnp, 1)) {
-+        DEBUG_LOG("Cache write has been disabled for this transaction.");
-+      } else {
-+        DEBUG_LOG("Unable to disable cache write for this transaction.");
-+      }
-+    }
-+  }
-+  TSHandleMLocRelease(response, resp_hdr, NULL);
-+}
-+
-+/**
-+ * Remove a header (fully) from an TSMLoc / TSMBuffer. Return the number
-+ * of fields (header values) we removed.
-+ *
-+ * From background_fetch.cc
-+ */
-+static int
-+remove_header(TSMBuffer bufp, TSMLoc hdr_loc, const char *header, int len)
-+{
-+  TSMLoc field = TSMimeHdrFieldFind(bufp, hdr_loc, header, len);
-+  int cnt = 0;
-+
-+  while (field) {
-+    TSMLoc tmp = TSMimeHdrFieldNextDup(bufp, hdr_loc, field);
-+
-+    ++cnt;
-+    TSMimeHdrFieldDestroy(bufp, hdr_loc, field);
-+    TSHandleMLocRelease(bufp, hdr_loc, field);
-+    field = tmp;
-+  }
-+
-+  return cnt;
-+}
-+
-+/**
-+ * Set a header to a specific value. This will avoid going to through a
-+ * remove / add sequence in case of an existing header.
-+ * but clean.
-+ *
-+ * From background_fetch.cc
-+ */
-+static bool
-+set_header(TSMBuffer bufp, TSMLoc hdr_loc, const char *header, int len, const char *val, int val_len)
-+{
-+  if (!bufp || !hdr_loc || !header || len <= 0 || !val || val_len <= 0) {
-+    return false;
-+  }
-+
-+  DEBUG_LOG("header: %s, len: %d, val: %s, val_len: %d", header, len, val, val_len);
-+  bool ret = false;
-+  TSMLoc field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, header, len);
-+
-+  if (!field_loc) {
-+    // No existing header, so create one
-+    if (TS_SUCCESS == TSMimeHdrFieldCreateNamed(bufp, hdr_loc, header, len, &field_loc)) {
-+      if (TS_SUCCESS == TSMimeHdrFieldValueStringSet(bufp, hdr_loc, field_loc, -1, val, val_len)) {
-+        TSMimeHdrFieldAppend(bufp, hdr_loc, field_loc);
-+        ret = true;
-+      }
-+      TSHandleMLocRelease(bufp, hdr_loc, field_loc);
-+    }
-+  } else {
-+    TSMLoc tmp = NULL;
-+    bool first = true;
-+
-+    while (field_loc) {
-+      if (first) {
-+        first = false;
-+        if (TS_SUCCESS == TSMimeHdrFieldValueStringSet(bufp, hdr_loc, field_loc, -1, val, val_len)) {
-+          ret = true;
-+        }
-+      } else {
-+        TSMimeHdrFieldDestroy(bufp, hdr_loc, field_loc);
-+      }
-+      tmp = TSMimeHdrFieldNextDup(bufp, hdr_loc, field_loc);
-+      TSHandleMLocRelease(bufp, hdr_loc, field_loc);
-+      field_loc = tmp;
-+    }
-+  }
-+
-+  return ret;
-+}
-+
-+/**
-+ * Remap initialization.
-+ */
-+TSReturnCode
-+TSRemapInit(TSRemapInterface *api_info, char *errbuf, int errbuf_size)
-+{
-+  if (!api_info) {
-+    strncpy(errbuf, "[tsremap_init] - Invalid TSRemapInterface argument", errbuf_size - 1);
-+    return TS_ERROR;
-+  }
-+
-+  if (api_info->tsremap_version < TSREMAP_VERSION) {
-+    snprintf(errbuf, errbuf_size - 1, "[TSRemapInit] - Incorrect API version %ld.%ld", api_info->tsremap_version >> 16,
-+             (api_info->tsremap_version & 0xffff));
-+    return TS_ERROR;
-+  }
-+
-+  DEBUG_LOG("cache_range_requests remap is successfully initialized.");
-+  return TS_SUCCESS;
-+}
-+
-+/**
-+ * not used.
-+ */
-+TSReturnCode
-+TSRemapNewInstance(int argc, char *argv[], void **ih, char * /*errbuf */, int /* errbuf_size */)
-+{
-+  return TS_SUCCESS;
-+}
-+
-+/**
-+ * not used.
-+ */
-+void
-+TSRemapDeleteInstance(void *ih)
-+{
-+  DEBUG_LOG("no op");
-+}
-+
-+/**
-+ * Remap entry point.
-+ */
-+TSRemapStatus
-+TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo * /* rri */)
-+{
-+  range_header_check(txnp);
-+  return TSREMAP_NO_REMAP;
-+}
-+
-+/**
-+ * Global plugin initialization.
-+ */
-+void
-+TSPluginInit(int argc, const char *argv[])
-+{
-+  TSPluginRegistrationInfo info;
-+  TSCont txnp_cont;
-+
-+  info.plugin_name = (char *)PLUGIN_NAME;
-+  info.vendor_name = (char *)"Comcast";
-+  info.support_email = (char *)"John_Rushford@cable.comcast.com";
-+
-+  if (TSPluginRegister(TS_SDK_VERSION_3_0, &info) != TS_SUCCESS) {
-+    ERROR_LOG("Plugin registration failed.\n");
-+    ERROR_LOG("Unable to initialize plugin (disabled).");
-+    return;
-+  }
-+
-+  if (NULL == (txnp_cont = TSContCreate((TSEventFunc)handle_read_request_header, NULL))) {
-+    ERROR_LOG("failed to create the transaction continuation handler.");
-+    return;
-+  } else {
-+    TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, txnp_cont);
-+  }
-+}
-+
-+/**
-+ * Transaction event handler.
-+ */
-+static void
-+transaction_handler(TSCont contp, TSEvent event, void *edata)
-+{
-+  TSHttpTxn txnp = static_cast<TSHttpTxn>(edata);
-+  struct txndata *txn_state = (struct txndata *)TSContDataGet(contp);
-+
-+  switch (event) {
-+  case TS_EVENT_HTTP_READ_RESPONSE_HDR:
-+    handle_server_read_response(txnp, txn_state);
-+    break;
-+  case TS_EVENT_HTTP_SEND_REQUEST_HDR:
-+    handle_send_origin_request(contp, txnp, txn_state);
-+    break;
-+  case TS_EVENT_HTTP_SEND_RESPONSE_HDR:
-+    handle_client_send_response(txnp, txn_state);
-+    break;
-+  case TS_EVENT_HTTP_TXN_CLOSE:
-+    if (txn_state != NULL && txn_state->range_value != NULL)
-+      TSfree(txn_state->range_value);
-+    if (txn_state != NULL)
-+      TSfree(txn_state);
-+    TSContDestroy(contp);
-+    break;
-+  default:
-+    TSAssert(!"Unexpected event");
-+    break;
-+  }
-+  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
-+}
-diff --git a/plugins/experimental/regex_revalidate/regex_revalidate.c b/plugins/experimental/regex_revalidate/regex_revalidate.c
-index f25c36f..d261308 100644
---- a/plugins/experimental/regex_revalidate/regex_revalidate.c
-+++ b/plugins/experimental/regex_revalidate/regex_revalidate.c
-@@ -40,8 +40,35 @@
- #include <pcre.h>
- #endif
- 
--#define LOG_PREFIX "regex_revalidate"
--#define CONFIG_TMOUT 60000
-+typedef struct invalidate_t {
-+  const char *regex_text;
-+  pcre *regex;
-+  pcre_extra *regex_extra;
-+  time_t epoch;
-+  time_t expiry;
-+  struct invalidate_t *volatile next;
-+} invalidate_t;
-+
-+typedef invalidate_t config_t;
-+
-+typedef struct {
-+  char *config_path;
-+  volatile time_t last_load;
-+  config_t *config;
-+  TSTextLogObject log;
-+} config_holder_t;
-+
-+static int free_handler(TSCont cont, TSEvent event, void *edata);
-+static int config_handler(TSCont cont, TSEvent event, void *edata);
-+static config_t *get_config(TSCont cont);
-+static config_holder_t *new_config_holder();
-+static config_holder_t *init_config_holder(config_holder_t *config_holder, const char *path);
-+static void free_config_holder_t(config_holder_t *config_holder);
-+static void schedule_free_invalidate_t(invalidate_t *iptr);
-+
-+#define PLUGIN_TAG "regex_revalidate"
-+#define DEFAULT_CONFIG_NAME "regex_revalidate.config"
-+#define PRUNE_TMOUT 60000
- #define FREE_TMOUT 300000
- #define OVECTOR_SIZE 30
- #define LOG_ROLL_INTERVAL 86400
-@@ -59,22 +86,6 @@ ts_free(void *s)
-   return TSfree(s);
- }
- 
--typedef struct invalidate_t {
--  const char *regex_text;
--  pcre *regex;
--  pcre_extra *regex_extra;
--  time_t epoch;
--  time_t expiry;
--  struct invalidate_t *next;
--} invalidate_t;
--
--typedef struct {
--  invalidate_t *volatile invalidate_list;
--  char *config_file;
--  volatile time_t last_load;
--  TSTextLogObject log;
--} plugin_state_t;
--
- static invalidate_t *
- init_invalidate_t(invalidate_t *i)
- {
-@@ -111,65 +122,6 @@ free_invalidate_t_list(invalidate_t *i)
-   free_invalidate_t(i);
- }
- 
--static plugin_state_t *
--init_plugin_state_t(plugin_state_t *pstate)
--{
--  pstate->invalidate_list = NULL;
--  pstate->config_file = NULL;
--  pstate->last_load = 0;
--  pstate->log = NULL;
--  return pstate;
--}
--
--static void
--free_plugin_state_t(plugin_state_t *pstate)
--{
--  if (pstate->invalidate_list)
--    free_invalidate_t_list(pstate->invalidate_list);
--  if (pstate->config_file)
--    TSfree(pstate->config_file);
--  if (pstate->log)
--    TSTextLogObjectDestroy(pstate->log);
--  TSfree(pstate);
--}
--
--static invalidate_t *
--copy_invalidate_t(invalidate_t *i)
--{
--  invalidate_t *iptr;
--  const char *errptr;
--  int erroffset;
--
--  iptr = (invalidate_t *)TSmalloc(sizeof(invalidate_t));
--  iptr->regex_text = TSstrdup(i->regex_text);
--  iptr->regex = pcre_compile(iptr->regex_text, 0, &errptr, &erroffset, NULL); // There is no pcre_copy :-(
--  iptr->regex_extra = pcre_study(iptr->regex, 0, &errptr);                    // Assuming no errors since this worked before :-/
--  iptr->epoch = i->epoch;
--  iptr->expiry = i->expiry;
--  iptr->next = NULL;
--  return iptr;
--}
--
--static invalidate_t *
--copy_config(invalidate_t *old_list)
--{
--  invalidate_t *new_list = NULL;
--  invalidate_t *iptr_old, *iptr_new;
--
--  if (old_list) {
--    new_list = copy_invalidate_t(old_list);
--    iptr_old = old_list->next;
--    iptr_new = new_list;
--    while (iptr_old) {
--      iptr_new->next = copy_invalidate_t(iptr_old);
--      iptr_new = iptr_new->next;
--      iptr_old = iptr_old->next;
--    }
--  }
--
--  return new_list;
--}
--
- static bool
- prune_config(invalidate_t **i)
- {
-@@ -184,14 +136,20 @@ prune_config(invalidate_t **i)
-     ilast = NULL;
-     while (iptr) {
-       if (difftime(iptr->expiry, now) < 0) {
--        TSDebug(LOG_PREFIX, "Removing %s expiry: %d now: %d", iptr->regex_text, (int)iptr->expiry, (int)now);
-+        TSDebug(PLUGIN_TAG, "Removing %s expiry: %d now: %d", iptr->regex_text, (int)iptr->expiry, (int)now);
-+        TSError(PLUGIN_TAG " - Removing %s expiry: %d now: %d", iptr->regex_text, (int)iptr->expiry, (int)now);
-         if (ilast) {
-+          // jlaue: TODO is this right?
-+          //                    iptr = __sync_val_compare_and_swap(&(ilast->next), ilast->next, iptr->next);
-           ilast->next = iptr->next;
--          free_invalidate_t(iptr);
-+          //                    free_invalidate_t(iptr);
-+          schedule_free_invalidate_t(iptr);
-           iptr = ilast->next;
-+
-         } else {
-           *i = iptr->next;
--          free_invalidate_t(iptr);
-+          //                    free_invalidate_t(iptr);
-+          schedule_free_invalidate_t(iptr);
-           iptr = *i;
-         }
-         pruned = true;
-@@ -204,161 +162,44 @@ prune_config(invalidate_t **i)
-   return pruned;
- }
- 
--static bool
--load_config(plugin_state_t *pstate, invalidate_t **ilist)
--{
--  FILE *fs;
--  struct stat s;
--  size_t path_len;
--  char *path;
--  char line[LINE_MAX];
--  time_t now;
--  pcre *config_re;
--  const char *errptr;
--  int erroffset, ovector[OVECTOR_SIZE], rc;
--  int ln = 0;
--  invalidate_t *iptr, *i;
--
--  if (pstate->config_file[0] != '/') {
--    path_len = strlen(TSConfigDirGet()) + strlen(pstate->config_file) + 2;
--    path = alloca(path_len);
--    snprintf(path, path_len, "%s/%s", TSConfigDirGet(), pstate->config_file);
--  } else
--    path = pstate->config_file;
--  if (stat(path, &s) < 0) {
--    TSDebug(LOG_PREFIX, "Could not stat %s", path);
--    return false;
--  }
--  if (s.st_mtime > pstate->last_load) {
--    now = time(NULL);
--    if (!(fs = fopen(path, "r"))) {
--      TSDebug(LOG_PREFIX, "Could not open %s for reading", path);
--      return false;
--    }
--    config_re = pcre_compile("^([^#].+?)\\s+(\\d+)\\s*$", 0, &errptr, &erroffset, NULL);
--    while (fgets(line, LINE_MAX, fs) != NULL) {
--      ln++;
--      TSDebug(LOG_PREFIX, "Processing: %d %s", ln, line);
--      rc = pcre_exec(config_re, NULL, line, strlen(line), 0, 0, ovector, OVECTOR_SIZE);
--      if (rc == 3) {
--        i = (invalidate_t *)TSmalloc(sizeof(invalidate_t));
--        init_invalidate_t(i);
--        pcre_get_substring(line, ovector, rc, 1, &i->regex_text);
--        i->epoch = now;
--        i->expiry = atoi(line + ovector[4]);
--        i->regex = pcre_compile(i->regex_text, 0, &errptr, &erroffset, NULL);
--        if (i->expiry <= i->epoch) {
--          TSDebug(LOG_PREFIX, "Rule is already expired!");
--          free_invalidate_t(i);
--        } else if (i->regex == NULL) {
--          TSDebug(LOG_PREFIX, "%s did not compile", i->regex_text);
--          free_invalidate_t(i);
--        } else {
--          i->regex_extra = pcre_study(i->regex, 0, &errptr);
--          if (!*ilist) {
--            *ilist = i;
--            TSDebug(LOG_PREFIX, "Created new list and Loaded %s %d %d", i->regex_text, (int)i->epoch, (int)i->expiry);
--          } else {
--            iptr = *ilist;
--            while (1) {
--              if (strcmp(i->regex_text, iptr->regex_text) == 0) {
--                if (iptr->expiry != i->expiry) {
--                  TSDebug(LOG_PREFIX, "Updating duplicate %s", i->regex_text);
--                  iptr->epoch = i->epoch;
--                  iptr->expiry = i->expiry;
--                }
--                free_invalidate_t(i);
--                i = NULL;
--                break;
--              } else if (!iptr->next)
--                break;
--              else
--                iptr = iptr->next;
--            }
--            if (i) {
--              iptr->next = i;
--              TSDebug(LOG_PREFIX, "Loaded %s %d %d", i->regex_text, (int)i->epoch, (int)i->expiry);
--            }
--          }
--        }
--      } else
--        TSDebug(LOG_PREFIX, "Skipping line %d", ln);
--    }
--    pcre_free(config_re);
--    fclose(fs);
--    pstate->last_load = s.st_mtime;
--    return true;
--  } else
--    TSDebug(LOG_PREFIX, "File mod time is not newer: %d >= %d", (int)pstate->last_load, (int)s.st_mtime);
--  return false;
--}
- 
- static void
--list_config(plugin_state_t *pstate, invalidate_t *i)
-+list_config(config_holder_t *config_holder, invalidate_t *i)
- {
-   invalidate_t *iptr;
- 
--  TSDebug(LOG_PREFIX, "Current config:");
--  if (pstate->log)
--    TSTextLogObjectWrite(pstate->log, "Current config:");
-+  TSDebug(PLUGIN_TAG, "Current config:");
-+  if (config_holder->log)
-+    TSTextLogObjectWrite(config_holder->log, "Current config:");
-   if (i) {
-     iptr = i;
-     while (iptr) {
--      TSDebug(LOG_PREFIX, "%s epoch: %d expiry: %d", iptr->regex_text, (int)iptr->epoch, (int)iptr->expiry);
--      if (pstate->log)
--        TSTextLogObjectWrite(pstate->log, "%s epoch: %d expiry: %d", iptr->regex_text, (int)iptr->epoch, (int)iptr->expiry);
-+      TSDebug(PLUGIN_TAG, "%s epoch: %d expiry: %d", iptr->regex_text, (int)iptr->epoch, (int)iptr->expiry);
-+      if (config_holder->log)
-+        TSTextLogObjectWrite(config_holder->log, "%s epoch: %d expiry: %d", iptr->regex_text, (int)iptr->epoch, (int)iptr->expiry);
-       iptr = iptr->next;
-     }
-   } else {
--    TSDebug(LOG_PREFIX, "EMPTY");
--    if (pstate->log)
--      TSTextLogObjectWrite(pstate->log, "EMPTY");
-+    TSDebug(PLUGIN_TAG, "EMPTY");
-+    if (config_holder->log)
-+      TSTextLogObjectWrite(config_holder->log, "EMPTY");
-   }
- }
- 
- static int
--free_handler(TSCont cont, TSEvent event ATS_UNUSED, void *edata ATS_UNUSED)
-+config_pruner(TSCont cont, TSEvent event ATS_UNUSED, void *edata ATS_UNUSED)
- {
--  invalidate_t *iptr;
-+  invalidate_t *i;
- 
--  TSDebug(LOG_PREFIX, "Freeing old config");
--  iptr = (invalidate_t *)TSContDataGet(cont);
--  free_invalidate_t_list(iptr);
--  TSContDestroy(cont);
--  return 0;
--}
-+  TSDebug(PLUGIN_TAG, "config_pruner");
-+  config_holder_t *configh = (config_holder_t *)TSContDataGet(cont);
-+  i = configh->config;
- 
--static int
--config_handler(TSCont cont, TSEvent event ATS_UNUSED, void *edata ATS_UNUSED)
--{
--  plugin_state_t *pstate;
--  invalidate_t *i, *iptr;
--  TSCont free_cont;
--  bool updated;
-+  prune_config(&i);
- 
--  TSDebug(LOG_PREFIX, "In config Handler");
--  pstate = (plugin_state_t *)TSContDataGet(cont);
--  i = copy_config(pstate->invalidate_list);
-+  configh->config = i;
- 
--  updated = prune_config(&i);
--  updated = load_config(pstate, &i) || updated;
--
--  if (updated) {
--    list_config(pstate, i);
--    iptr = __sync_val_compare_and_swap(&(pstate->invalidate_list), pstate->invalidate_list, i);
--
--    if (iptr) {
--      free_cont = TSContCreate(free_handler, NULL);
--      TSContDataSet(free_cont, (void *)iptr);
--      TSContSchedule(free_cont, FREE_TMOUT, TS_THREAD_POOL_TASK);
--    }
--  } else {
--    TSDebug(LOG_PREFIX, "No Changes");
--    if (i)
--      free_invalidate_t_list(i);
--  }
--
--  TSContSchedule(cont, CONFIG_TMOUT, TS_THREAD_POOL_TASK);
-+  TSContSchedule(cont, PRUNE_TMOUT, TS_THREAD_POOL_TASK);
-   return 0;
- }
- 
-@@ -387,7 +228,6 @@ main_handler(TSCont cont, TSEvent event, void *edata)
-   TSHttpTxn txn = (TSHttpTxn)edata;
-   int status;
-   invalidate_t *iptr;
--  plugin_state_t *pstate;
- 
-   time_t date = 0, now = 0;
-   char *url = NULL;
-@@ -397,8 +237,7 @@ main_handler(TSCont cont, TSEvent event, void *edata)
-   case TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE:
-     if (TSHttpTxnCacheLookupStatusGet(txn, &status) == TS_SUCCESS) {
-       if (status == TS_CACHE_LOOKUP_HIT_FRESH) {
--        pstate = (plugin_state_t *)TSContDataGet(cont);
--        iptr = pstate->invalidate_list;
-+        iptr = get_config(cont);
-         while (iptr) {
-           if (!date) {
-             date = get_date_from_cached_hdr(txn);
-@@ -410,7 +249,7 @@ main_handler(TSCont cont, TSEvent event, void *edata)
-             if (pcre_exec(iptr->regex, iptr->regex_extra, url, url_len, 0, 0, NULL, 0) >= 0) {
-               TSHttpTxnCacheLookupStatusSet(txn, TS_CACHE_LOOKUP_HIT_STALE);
-               iptr = NULL;
--              TSDebug(LOG_PREFIX, "Forced revalidate - %.*s", url_len, url);
-+              TSDebug(PLUGIN_TAG, "Forced revalidate - %.*s", url_len, url);
-             }
-           }
-           if (iptr)
-@@ -457,13 +296,12 @@ TSPluginInit(int argc, const char *argv[])
- {
-   TSPluginRegistrationInfo info;
-   TSCont main_cont, config_cont;
--  plugin_state_t *pstate;
--  invalidate_t *iptr = NULL;
-+  config_holder_t *config_holder;
-+  char *path = NULL;
- 
--  TSDebug(LOG_PREFIX, "Starting plugin init.");
-+  TSDebug(PLUGIN_TAG, "Starting plugin init.");
- 
--  pstate = (plugin_state_t *)TSmalloc(sizeof(plugin_state_t));
--  init_plugin_state_t(pstate);
-+  config_holder = new_config_holder();
- 
-   int c;
-   optind = 1;
-@@ -473,46 +311,48 @@ TSPluginInit(int argc, const char *argv[])
-   while ((c = getopt_long(argc, (char *const *)argv, "c:l:", longopts, NULL)) != -1) {
-     switch (c) {
-     case 'c':
--      pstate->config_file = TSstrdup(optarg);
-+      path = TSstrdup(optarg);
-       break;
-     case 'l':
--      TSTextLogObjectCreate(optarg, TS_LOG_MODE_ADD_TIMESTAMP, &pstate->log);
--      TSTextLogObjectRollingEnabledSet(pstate->log, 1);
--      TSTextLogObjectRollingIntervalSecSet(pstate->log, LOG_ROLL_INTERVAL);
--      TSTextLogObjectRollingOffsetHrSet(pstate->log, LOG_ROLL_OFFSET);
-+      TSTextLogObjectCreate(optarg, TS_LOG_MODE_ADD_TIMESTAMP, &config_holder->log);
-+      TSTextLogObjectRollingEnabledSet(config_holder->log, 1);
-+      TSTextLogObjectRollingIntervalSecSet(config_holder->log, LOG_ROLL_INTERVAL);
-+      TSTextLogObjectRollingOffsetHrSet(config_holder->log, LOG_ROLL_OFFSET);
-       break;
-     default:
-       break;
-     }
-   }
-+  config_holder = init_config_holder(config_holder, path);
- 
--  if (!pstate->config_file) {
-+  if (!config_holder->config_path) {
-     TSError("Plugin requires a --config option along with a config file name.");
--    free_plugin_state_t(pstate);
-+    free_config_holder_t(config_holder);
-     return;
-   }
- 
--  if (!load_config(pstate, &iptr))
--    TSDebug(LOG_PREFIX, "Problem loading config from file %s", pstate->config_file);
-+  //    if (!load_config(free_config_holder_t, &iptr))
-+  if (config_holder->config)
-+    TSDebug(PLUGIN_TAG, "Problem loading config from file %s", config_holder->config_path);
-   else {
--    pstate->invalidate_list = iptr;
--    list_config(pstate, iptr);
-+    //        config_holder->config = iptr;
-+    list_config(config_holder, config_holder->config);
-   }
- 
--  info.plugin_name = LOG_PREFIX;
-+  info.plugin_name = PLUGIN_TAG;
-   info.vendor_name = "Apache Software Foundation";
-   info.support_email = "dev@trafficserver.apache.org";
- 
-   if (TSPluginRegister(TS_SDK_VERSION_3_0, &info) != TS_SUCCESS) {
-     TSError("Plugin registration failed.");
--    free_plugin_state_t(pstate);
-+    free_config_holder_t(config_holder);
-     return;
-   } else
--    TSDebug(LOG_PREFIX, "Plugin registration succeeded.");
-+    TSDebug(PLUGIN_TAG, "Plugin registration succeeded.");
- 
-   if (!check_ts_version()) {
-     TSError("Plugin requires Traffic Server %d.%d.%d", TS_VERSION_MAJOR, TS_VERSION_MINOR, TS_VERSION_MICRO);
--    free_plugin_state_t(pstate);
-+    free_config_holder_t(config_holder);
-     return;
-   }
- 
-@@ -520,12 +360,235 @@ TSPluginInit(int argc, const char *argv[])
-   pcre_free = &ts_free;
- 
-   main_cont = TSContCreate(main_handler, NULL);
--  TSContDataSet(main_cont, (void *)pstate);
-+  TSContDataSet(main_cont, (void *)config_holder);
-   TSHttpHookAdd(TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, main_cont);
- 
-+  config_cont = TSContCreate(config_pruner, TSMutexCreate());
-+  TSContDataSet(config_cont, (void *)config_holder);
-+  TSContSchedule(config_cont, PRUNE_TMOUT, TS_THREAD_POOL_TASK);
-+
-   config_cont = TSContCreate(config_handler, TSMutexCreate());
--  TSContDataSet(config_cont, (void *)pstate);
--  TSContSchedule(config_cont, CONFIG_TMOUT, TS_THREAD_POOL_TASK);
-+  TSContDataSet(config_cont, (void *)config_holder);
-+  TSMgmtUpdateRegister(config_cont, PLUGIN_TAG);
-+
-+  TSDebug(PLUGIN_TAG, "Plugin Init Complete.");
-+}
-+
-+static config_t *
-+new_config(TSFile fs)
-+{
-+  char line[LINE_MAX];
-+  time_t now;
-+  pcre *config_re;
-+  const char *errptr;
-+  int erroffset, ovector[OVECTOR_SIZE], rc;
-+  int ln = 0;
-+  invalidate_t *iptr, *i, *config = 0;
-+
-+  now = time(NULL);
-+
-+  config_re = pcre_compile("^([^#].+?)\\s+(\\d+)\\s*$", 0, &errptr, &erroffset, NULL);
-+  while (TSfgets(fs, line, LINE_MAX - 1) != NULL) {
-+    ln++;
-+    TSDebug(PLUGIN_TAG, "Processing: %d %s", ln, line);
-+    rc = pcre_exec(config_re, NULL, line, strlen(line), 0, 0, ovector, OVECTOR_SIZE);
-+    if (rc == 3) {
-+      i = (invalidate_t *)TSmalloc(sizeof(invalidate_t));
-+      init_invalidate_t(i);
-+      pcre_get_substring(line, ovector, rc, 1, &i->regex_text);
-+      i->epoch = now;
-+      i->expiry = atoi(line + ovector[4]);
-+      i->regex = pcre_compile(i->regex_text, 0, &errptr, &erroffset, NULL);
-+      if (i->expiry <= i->epoch) {
-+        TSDebug(PLUGIN_TAG, "NOT Loaded, already expired! %s %d %d", i->regex_text, (int)i->epoch, (int)i->expiry);
-+        TSError(PLUGIN_TAG " - NOT Loaded, already expired: %s %d %d", i->regex_text, (int)i->epoch, (int)i->expiry);
-+        free_invalidate_t(i);
-+      } else if (i->regex == NULL) {
-+        TSDebug(PLUGIN_TAG, "%s did not compile", i->regex_text);
-+        free_invalidate_t(i);
-+      } else {
-+        i->regex_extra = pcre_study(i->regex, 0, &errptr);
-+        if (!config) {
-+          config = i;
-+          TSDebug(PLUGIN_TAG, "Created new list and Loaded %s %d %d", i->regex_text, (int)i->epoch, (int)i->expiry);
-+          TSError(PLUGIN_TAG " - New Revalidate: %s %d %d", i->regex_text, (int)i->epoch, (int)i->expiry);
-+        } else {
-+          iptr = config;
-+          while (1) {
-+            if (strcmp(i->regex_text, iptr->regex_text) == 0) {
-+              if (iptr->expiry != i->expiry) {
-+                TSDebug(PLUGIN_TAG, "Updating duplicate %s", i->regex_text);
-+                iptr->epoch = i->epoch;
-+                iptr->expiry = i->expiry;
-+              }
-+              free_invalidate_t(i);
-+              i = NULL;
-+              break;
-+            } else if (!iptr->next)
-+              break;
-+            else
-+              iptr = iptr->next;
-+          }
-+          if (i) {
-+            iptr->next = i;
-+            TSDebug(PLUGIN_TAG, "Loaded %s %d %d", i->regex_text, (int)i->epoch, (int)i->expiry);
-+          }
-+        }
-+      }
-+    } else
-+      TSDebug(PLUGIN_TAG, "Skipping line %d", ln);
-+  }
-+  pcre_free(config_re);
-+
-+  return config;
-+}
-+
-+static void
-+delete_config(config_t *config)
-+{
-+  TSDebug(PLUGIN_TAG, "Freeing config");
-+  free_invalidate_t_list(config);
-+}
-+
-+static int
-+free_invalidate_handler(TSCont cont, TSEvent event ATS_UNUSED, void *edata ATS_UNUSED)
-+{
-+  invalidate_t *i = (invalidate_t *)TSContDataGet(cont);
-+  free_invalidate_t(i);
-+  TSContDestroy(cont);
-+  return 0;
-+}
-+
-+static void
-+schedule_free_invalidate_t(invalidate_t *iptr)
-+{
-+  TSCont free_cont;
-+  free_cont = TSContCreate(free_invalidate_handler, NULL);
-+  TSContDataSet(free_cont, (void *)iptr);
-+  TSContSchedule(free_cont, FREE_TMOUT, TS_THREAD_POOL_TASK);
-+  return;
-+}
- 
--  TSDebug(LOG_PREFIX, "Plugin Init Complete.");
-+static config_t *
-+get_config(TSCont cont)
-+{
-+  config_holder_t *configh = (config_holder_t *)TSContDataGet(cont);
-+  if (!configh) {
-+    return 0;
-+  }
-+  return configh->config;
-+}
-+
-+static void
-+load_config_file(config_holder_t *config_holder)
-+{
-+  TSFile fh;
-+  struct stat s;
-+
-+  config_t *newconfig, *oldconfig;
-+  TSCont free_cont;
-+
-+  // check date
-+  if (stat(config_holder->config_path, &s) < 0) {
-+    TSDebug(PLUGIN_TAG, "Could not stat %s", config_holder->config_path);
-+    if (config_holder->config) {
-+      return;
-+    }
-+  } else {
-+    TSDebug(PLUGIN_TAG, "s.st_mtime=%lu, last_load=%lu", s.st_mtime, config_holder->last_load);
-+    if (s.st_mtime < config_holder->last_load) {
-+      return;
-+    }
-+  }
-+
-+  TSDebug(PLUGIN_TAG, "Opening config file: %s", config_holder->config_path);
-+  fh = TSfopen(config_holder->config_path, "r");
-+  TSError(PLUGIN_TAG " - Reading config: %s", config_holder->config_path);
-+
-+  if (!fh) {
-+    TSError("[%s] Unable to open config: %s.\n", PLUGIN_TAG, config_holder->config_path);
-+    return;
-+  }
-+
-+  newconfig = 0;
-+  newconfig = new_config(fh);
-+  if (newconfig) {
-+    config_holder->last_load = time(NULL);
-+    config_t **confp = &(config_holder->config);
-+    oldconfig = __sync_lock_test_and_set(confp, newconfig);
-+    if (oldconfig) {
-+      TSDebug(PLUGIN_TAG, "scheduling free: %p (%p)", oldconfig, newconfig);
-+      free_cont = TSContCreate(free_handler, NULL);
-+      TSContDataSet(free_cont, (void *)oldconfig);
-+      TSContSchedule(free_cont, FREE_TMOUT, TS_THREAD_POOL_TASK);
-+    }
-+  }
-+  if (fh)
-+    TSfclose(fh);
-+  return;
-+}
-+
-+static config_holder_t *
-+new_config_holder(void)
-+{
-+  config_holder_t *config_holder = TSmalloc(sizeof(config_holder_t));
-+  return config_holder;
-+}
-+
-+static config_holder_t *
-+init_config_holder(config_holder_t *config_holder, const char *path)
-+{
-+  int path_len = 0;
-+  config_holder->config_path = 0;
-+  config_holder->config = 0;
-+  config_holder->last_load = 0;
-+  config_holder->log = 0;
-+
-+  if (!path)
-+    path = DEFAULT_CONFIG_NAME;
-+  if (path[0] != '/') {
-+    path_len = strlen(TSConfigDirGet()) + strlen(path) + 2;
-+    config_holder->config_path = ts_malloc(path_len);
-+    snprintf(config_holder->config_path, path_len, "%s/%s", TSConfigDirGet(), path);
-+    TSDebug(PLUGIN_TAG, "path: '%s' len=%d", config_holder->config_path, path_len);
-+  } else
-+    config_holder->config_path = TSstrdup(path);
-+
-+  load_config_file(config_holder);
-+  return config_holder;
-+}
-+
-+static void
-+free_config_holder_t(config_holder_t *config_holder)
-+{
-+  if (config_holder->config)
-+    free_invalidate_t_list(config_holder->config);
-+  if (config_holder->config_path)
-+    TSfree(config_holder->config_path);
-+  if (config_holder->log)
-+    TSTextLogObjectDestroy(config_holder->log);
-+  TSfree(config_holder);
-+}
-+
-+static int
-+free_handler(TSCont cont, TSEvent event ATS_UNUSED, void *edata ATS_UNUSED)
-+{
-+  config_t *config;
-+
-+  TSDebug(PLUGIN_TAG, "Freeing old config");
-+  config = (config_t *)TSContDataGet(cont);
-+  delete_config(config);
-+  TSContDestroy(cont);
-+  return 0;
-+}
-+
-+static int
-+config_handler(TSCont cont, TSEvent event ATS_UNUSED, void *edata ATS_UNUSED)
-+{
-+  config_holder_t *config_holder;
-+
-+  TSDebug(PLUGIN_TAG, "In config Handler");
-+  config_holder = (config_holder_t *)TSContDataGet(cont);
-+  load_config_file(config_holder);
-+  return 0;
- }
-diff --git a/plugins/experimental/remap_stats/remap_stats.c b/plugins/experimental/remap_stats/remap_stats.c
-index e231ec2..8d3f987 100644
---- a/plugins/experimental/remap_stats/remap_stats.c
-+++ b/plugins/experimental/remap_stats/remap_stats.c
-@@ -23,12 +23,14 @@
- #include "ink_defs.h"
- 
- #include "ts/ts.h"
-+
- #include <stdint.h>
- #include <stdbool.h>
- #include <string.h>
- #include <stdio.h>
- #include <getopt.h>
- #include <search.h>
-+#include <time.h>
- 
- #define PLUGIN_NAME "remap_stats"
- #define DEBUG_TAG PLUGIN_NAME
-@@ -36,18 +38,52 @@
- #define MAX_STAT_LENGTH (1 << 8)
- 
- typedef struct {
-+  time_t last_update;
-   bool post_remap_host;
--  int txn_slot;
-+  int txn_slot, schedule_delay;
-   TSStatPersistence persist_type;
-   TSMutex stat_creation_mutex;
- } config_t;
- 
-+typedef struct {
-+  int stat_id;
-+  time_t last_update;
-+} value_t;
-+
-+static int
-+lookup_stat(char *name, TSStatPersistence persist_type, TSMutex create_mutex)
-+{
-+  int stat_id = -1;
-+
-+  TSMutexLock(create_mutex);
-+  if (TS_ERROR == TSStatFindName((const char *)name, &stat_id)) {
-+    stat_id = TSStatCreate((const char *)name, TS_RECORDDATATYPE_INT, persist_type, TS_STAT_SYNC_SUM);
-+    if (stat_id == TS_ERROR)
-+      TSDebug(DEBUG_TAG, "Error creating stat_name: %s", name);
-+    else
-+      TSError("[%s:%d] Created stat_name: %s stat_id: %d", __FILE__, __LINE__, name, stat_id);
-+  }
-+  TSMutexUnlock(create_mutex);
-+
-+  return stat_id;
-+}
-+
-+static inline time_t
-+now()
-+{
-+  struct timespec ts;
-+
-+  clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
-+
-+  return ts.tv_sec;
-+}
- 
- static void
--stat_add(char *name, TSMgmtInt amount, TSStatPersistence persist_type, TSMutex create_mutex)
-+stat_add(char *name, TSMgmtInt amount, TSStatPersistence persist_type, TSMutex create_mutex, time_t config_last_update)
- {
-   int stat_id = -1;
-   ENTRY search, *result = NULL;
-+  value_t *val;
-   static __thread struct hsearch_data stat_cache;
-   static __thread bool hash_init = false;
- 
-@@ -65,32 +101,43 @@ stat_add(char *name, TSMgmtInt amount, TSStatPersistence persist_type, TSMutex c
-     // This is an unlikely path because we most likely have the stat cached
-     // so this mutex won't be much overhead and it fixes a race condition
-     // in the RecCore. Hopefully this can be removed in the future.
--    TSMutexLock(create_mutex);
--    if (TS_ERROR == TSStatFindName((const char *)name, &stat_id)) {
--      stat_id = TSStatCreate((const char *)name, TS_RECORDDATATYPE_INT, persist_type, TS_STAT_SYNC_SUM);
--      if (stat_id == TS_ERROR)
--        TSDebug(DEBUG_TAG, "Error creating stat_name: %s", name);
--      else
--        TSDebug(DEBUG_TAG, "Created stat_name: %s stat_id: %d", name, stat_id);
--    }
--    TSMutexUnlock(create_mutex);
-+    stat_id = lookup_stat(name, persist_type, create_mutex);
- 
-     if (stat_id >= 0) {
-       search.key = TSstrdup(name);
--      search.data = (void *)((intptr_t)stat_id);
-+      val = (value_t *)TSmalloc(sizeof(value_t));
-+      val->stat_id = stat_id;
-+      val->last_update = now();
-+      search.data = (void *)val;
-       hsearch_r(search, ENTER, &result, &stat_cache);
-       TSDebug(DEBUG_TAG, "Cached stat_name: %s stat_id: %d", name, stat_id);
-     }
--  } else
--    stat_id = (int)((intptr_t)result->data);
-+  } else {
-+    val = (value_t *)result->data;
-+
-+    // Stat expiry should be rare, so lets assume this is unlikely
-+    if (unlikely(val->last_update < config_last_update)) {
-+      stat_id = lookup_stat(name, persist_type, create_mutex);
-+      TSDebug(DEBUG_TAG, "stat_add(): running stat expiry check for stat_id:%d named: %s", stat_id, name);
-+      // Stat changes should not happen so lets assume this is unlikely
-+      if (unlikely(val->stat_id != stat_id)) {
-+        TSError("[%s:%d] Found difference stat_name: %s old stat_id: %d new stat_id: %d", __FILE__, __LINE__, name, val->stat_id,
-+                stat_id);
-+        val->stat_id = stat_id;
-+        val->last_update = now();
-+      }
-+    } else {
-+      stat_id = val->stat_id;
-+    }
-+  }
- 
--  if (likely(stat_id >= 0))
-+  if (likely(stat_id >= 0)) {
-+    TSDebug(DEBUG_TAG, "stat_add(): preparing to increment, name=%s, stat_id=%d, amount=%" PRId64, name, stat_id, amount);
-     TSStatIntIncrement(stat_id, amount);
--  else
-+  } else
-     TSDebug(DEBUG_TAG, "stat error! stat_name: %s stat_id: %d", name, stat_id);
- }
- 
--
- static char *
- get_effective_host(TSHttpTxn txn)
- {
-@@ -113,7 +160,6 @@ get_effective_host(TSHttpTxn txn)
-   return tmp;
- }
- 
--
- static int
- handle_read_req_hdr(TSCont cont, TSEvent event ATS_UNUSED, void *edata)
- {
-@@ -130,7 +176,6 @@ handle_read_req_hdr(TSCont cont, TSEvent event ATS_UNUSED, void *edata)
-   return 0;
- }
- 
--
- static int
- handle_post_remap(TSCont cont, TSEvent event ATS_UNUSED, void *edata)
- {
-@@ -148,14 +193,12 @@ handle_post_remap(TSCont cont, TSEvent event ATS_UNUSED, void *edata)
-   }
- 
-   TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE);
--  TSDebug(DEBUG_TAG, "Post Remap Handler Finished");
-+  TSDebug(DEBUG_TAG, "Post Remap Handler Finished.");
-   return 0;
- }
- 
--
- #define CREATE_STAT_NAME(s, h, b) snprintf(s, MAX_STAT_LENGTH, "plugin.%s.%s.%s", PLUGIN_NAME, h, b)
- 
--
- static int
- handle_txn_close(TSCont cont, TSEvent event ATS_UNUSED, void *edata)
- {
-@@ -175,6 +218,8 @@ handle_txn_close(TSCont cont, TSEvent event ATS_UNUSED, void *edata)
- 
-   hostname = (char *)((uintptr_t)txnd & (~((uintptr_t)0x01))); // Get hostname
- 
-+  TSDebug(DEBUG_TAG, "handle_txn_close(): hostname: %s", hostname);
-+
-   if (txnd) {
-     if ((uintptr_t)txnd & 0x01) // remap succeeded?
-     {
-@@ -190,13 +235,13 @@ handle_txn_close(TSCont cont, TSEvent event ATS_UNUSED, void *edata)
-       in_bytes += TSHttpTxnClientReqBodyBytesGet(txn);
- 
-       CREATE_STAT_NAME(stat_name, remap, "in_bytes");
--      stat_add(stat_name, (TSMgmtInt)in_bytes, config->persist_type, config->stat_creation_mutex);
-+      stat_add(stat_name, (TSMgmtInt)in_bytes, config->persist_type, config->stat_creation_mutex, config->last_update);
- 
-       out_bytes = TSHttpTxnClientRespHdrBytesGet(txn);
-       out_bytes += TSHttpTxnClientRespBodyBytesGet(txn);
- 
-       CREATE_STAT_NAME(stat_name, remap, "out_bytes");
--      stat_add(stat_name, (TSMgmtInt)out_bytes, config->persist_type, config->stat_creation_mutex);
-+      stat_add(stat_name, (TSMgmtInt)out_bytes, config->persist_type, config->stat_creation_mutex, config->last_update);
- 
-       if (TSHttpTxnClientRespGet(txn, &buf, &hdr_loc) == TS_SUCCESS) {
-         status_code = (int)TSHttpHdrStatusGet(buf, hdr_loc);
-@@ -215,10 +260,10 @@ handle_txn_close(TSCont cont, TSEvent event ATS_UNUSED, void *edata)
-         else
-           CREATE_STAT_NAME(stat_name, remap, "status_other");
- 
--        stat_add(stat_name, 1, config->persist_type, config->stat_creation_mutex);
-+        stat_add(stat_name, 1, config->persist_type, config->stat_creation_mutex, config->last_update);
-       } else {
-         CREATE_STAT_NAME(stat_name, remap, "status_unknown");
--        stat_add(stat_name, 1, config->persist_type, config->stat_creation_mutex);
-+        stat_add(stat_name, 1, config->persist_type, config->stat_creation_mutex, config->last_update);
-       }
- 
-       if (remap != unknown)
-@@ -232,11 +277,43 @@ handle_txn_close(TSCont cont, TSEvent event ATS_UNUSED, void *edata)
-   return 0;
- }
- 
-+static int
-+do_time_update(TSCont cont, TSEvent event ATS_UNUSED, void *edata ATS_UNUSED)
-+{
-+  config_t *config = (config_t *)TSContDataGet(cont);
-+  TSDebug(DEBUG_TAG, "do_time_update() called, updating config->last_update.");
-+
-+
-+  while (!__sync_bool_compare_and_swap(&(config->last_update), config->last_update, now()))
-+    ;
-+
-+  TSContDestroy(cont);
-+  return 0;
-+}
-+
-+static int
-+handle_config_update(TSCont cont, TSEvent event ATS_UNUSED, void *edata ATS_UNUSED)
-+{
-+  config_t *config = (config_t *)TSContDataGet(cont);
-+
-+  TSDebug(DEBUG_TAG, "handle_config_update() called due to management update.");
-+
-+  if (config->schedule_delay > 0) {
-+    TSCont do_update_cont = TSContCreate(do_time_update, NULL);
-+    TSContDataSet(do_update_cont, (void *)config);
-+    TSContSchedule(do_update_cont, config->schedule_delay * 1000, TS_THREAD_POOL_TASK);
-+  } else {
-+    while (!__sync_bool_compare_and_swap(&(config->last_update), config->last_update, now()))
-+      ;
-+  }
-+  return 0;
-+}
-+
- void
- TSPluginInit(int argc, const char *argv[])
- {
-   TSPluginRegistrationInfo info;
--  TSCont pre_remap_cont, post_remap_cont, global_cont;
-+  TSCont pre_remap_cont, post_remap_cont, global_cont, update_cont;
-   config_t *config;
- 
-   info.plugin_name = PLUGIN_NAME;
-@@ -253,14 +330,18 @@ TSPluginInit(int argc, const char *argv[])
-   config->post_remap_host = false;
-   config->persist_type = TS_STAT_NON_PERSISTENT;
-   config->stat_creation_mutex = TSMutexCreate();
-+  config->schedule_delay = 0;
-+  config->last_update = 0;
- 
-   if (argc > 1) {
-     int c;
-     optind = 1;
--    static const struct option longopts[] = {
--      {"post-remap-host", no_argument, NULL, 'P'}, {"persistent", no_argument, NULL, 'p'}, {NULL, 0, NULL, 0}};
-+    static const struct option longopts[] = {{"post-remap-host", no_argument, NULL, 'P'},
-+                                             {"persistent", no_argument, NULL, 'p'},
-+                                             {"delay", required_argument, NULL, 'd'},
-+                                             {NULL, 0, NULL, 0}};
- 
--    while ((c = getopt_long(argc, (char *const *)argv, "Pp", longopts, NULL)) != -1) {
-+    while ((c = getopt_long(argc, (char *const *)argv, "Ppd:", longopts, NULL)) != -1) {
-       switch (c) {
-       case 'P':
-         config->post_remap_host = true;
-@@ -270,6 +351,10 @@ TSPluginInit(int argc, const char *argv[])
-         config->persist_type = TS_STAT_PERSISTENT;
-         TSDebug(DEBUG_TAG, "Using persistent stats");
-         break;
-+      case 'd':
-+        config->schedule_delay = atoi(optarg);
-+        TSDebug(DEBUG_TAG, "Setting scheduling delay to %d", config->schedule_delay);
-+        break;
-       default:
-         break;
-       }
-@@ -292,5 +377,9 @@ TSPluginInit(int argc, const char *argv[])
-   TSContDataSet(global_cont, (void *)config);
-   TSHttpHookAdd(TS_HTTP_TXN_CLOSE_HOOK, global_cont);
- 
-+  update_cont = TSContCreate(handle_config_update, NULL);
-+  TSContDataSet(update_cont, (void *)config);
-+  TSMgmtUpdateRegister(update_cont, PLUGIN_NAME);
-+
-   TSDebug(DEBUG_TAG, "Init complete");
- }
-diff --git a/plugins/experimental/stale_while_revalidate/stale_while_revalidate.c b/plugins/experimental/stale_while_revalidate/stale_while_revalidate.c
-index c3cd9bc..dd45572 100644
---- a/plugins/experimental/stale_while_revalidate/stale_while_revalidate.c
-+++ b/plugins/experimental/stale_while_revalidate/stale_while_revalidate.c
-@@ -268,6 +268,7 @@ consume_resource(TSCont cont, TSEvent event ATS_UNUSED, void *edata ATS_UNUSED)
-   case TS_EVENT_VCONN_WRITE_READY:
-     // We shouldn't get here because we specify the exact size of the buffer.
-     TSDebug(PLUGIN_NAME, "Write Ready");
-+    break;
-   case TS_EVENT_VCONN_WRITE_COMPLETE:
-     TSDebug(PLUGIN_NAME, "Write Complete");
-     // TSDebug(PLUGIN_NAME, "TSVConnShutdown()");
-diff --git a/plugins/experimental/ts_lua/ts_lua_http_config.c b/plugins/experimental/ts_lua/ts_lua_http_config.c
-index f840625..be81bd6 100644
---- a/plugins/experimental/ts_lua/ts_lua_http_config.c
-+++ b/plugins/experimental/ts_lua/ts_lua_http_config.c
-@@ -81,6 +81,13 @@ typedef enum {
-   TS_LUA_CONFIG_HTTP_CACHE_FUZZ_PROBABILITY = TS_CONFIG_HTTP_CACHE_FUZZ_PROBABILITY,
-   TS_LUA_CONFIG_NET_SOCK_PACKET_MARK_OUT = TS_CONFIG_NET_SOCK_PACKET_MARK_OUT,
-   TS_LUA_CONFIG_NET_SOCK_PACKET_TOS_OUT = TS_CONFIG_NET_SOCK_PACKET_TOS_OUT,
-+  TS_LUA_CONFIG_HTTP_PER_PARENT_CONNECT_ATTEMPTS = TS_CONFIG_HTTP_PER_PARENT_CONNECT_ATTEMPTS,
-+  TS_LUA_CONFIG_HTTP_PARENT_TOTAL_CONNECT_ATTEMPTS = TS_CONFIG_HTTP_PARENT_TOTAL_CONNECT_ATTEMPTS,
-+  TS_LUA_CONFIG_HTTP_SIMPLE_RETRY_ENABLED = TS_CONFIG_HTTP_SIMPLE_RETRY_ENABLED,
-+  TS_LUA_CONFIG_HTTP_SIMPLE_RETRY_RESPONSE_CODES = TS_CONFIG_HTTP_SIMPLE_RETRY_RESPONSE_CODES,
-+  TS_LUA_CONFIG_HTTP_DEAD_SERVER_RETRY_ENABLED = TS_CONFIG_HTTP_DEAD_SERVER_RETRY_ENABLED,
-+  TS_LUA_CONFIG_HTTP_DEAD_SERVER_RETRY_RESPONSE_CODES = TS_CONFIG_HTTP_SIMPLE_RETRY_RESPONSE_CODES,
-+  TS_LUA_CONFIG_HTTP_URL_REMAP_REQUIRED = TS_CONFIG_HTTP_URL_REMAP_REQUIRED,
-   TS_LUA_CONFIG_LAST_ENTRY = TS_CONFIG_LAST_ENTRY
- } TSLuaOverridableConfigKey;
- 
-diff --git a/plugins/experimental/url_sig/url_sig.c b/plugins/experimental/url_sig/url_sig.c
-index 672f2c5..2418f21 100644
---- a/plugins/experimental/url_sig/url_sig.c
-+++ b/plugins/experimental/url_sig/url_sig.c
-@@ -32,6 +32,12 @@
- #include <limits.h>
- #include <ctype.h>
- 
-+#ifdef HAVE_PCRE_PCRE_H
-+#include <pcre/pcre.h>
-+#else
-+#include <pcre.h>
-+#endif
-+
- #include <ts/ts.h>
- #include <ts/remap.h>
- 
-@@ -41,13 +47,26 @@ struct config {
-   TSHttpStatus err_status;
-   char *err_url;
-   char keys[MAX_KEY_NUM][MAX_KEY_LEN];
-+  pcre *regex;
-+  pcre_extra *regex_extra;
- };
- 
--void
-+static void
- free_cfg(struct config *cfg)
- {
-   TSError("Cleaning up...");
-   TSfree(cfg->err_url);
-+
-+  if (cfg->regex_extra)
-+#ifndef PCRE_STUDY_JIT_COMPILE
-+    pcre_free(cfg->regex_extra);
-+#else
-+    pcre_free_study(cfg->regex_extra);
-+#endif
-+
-+  if (cfg->regex)
-+    pcre_free(cfg->regex);
-+
-   TSfree(cfg);
- }
- 
-@@ -150,16 +169,30 @@ TSRemapNewInstance(int argc, char *argv[], void **ih, char *errbuf, int errbuf_s
-       value += 3;
-    

<TRUNCATED>