diff --git a/plugins/experimental/cache_promote/cache_promote.cc b/plugins/experimental/cache_promote/cache_promote.cc index 03ba17c4493..67e04b7957e 100644 --- a/plugins/experimental/cache_promote/cache_promote.cc +++ b/plugins/experimental/cache_promote/cache_promote.cc @@ -16,8 +16,6 @@ limitations under the License. */ -#include -#include #include #include #include @@ -27,16 +25,18 @@ #include #include +#include #include -// TODO: We should eliminate this when we have unordered_map on all supported platforms -#if HAVE_UNORDERED_MAP -#include -#else -#include -#endif +#include "ts/ts.h" +#include "ts/remap.h" +#include "ts/ink_config.h" + +#define DEFAULT_BUCKET_SIZE 10 static const char *PLUGIN_NAME = "cache_promote"; +TSCont gNocacheCont; + ////////////////////////////////////////////////////////////////////////////////////////////// // Note that all options for all policies has to go here. Not particularly pretty... @@ -127,7 +127,7 @@ class ChancePolicy : public PromotionPolicy void usage() const { - TSError(PLUGIN_NAME, "Usage: @plugin=%s.so @pparam=--policy=chance @pparam=--sample=%", PLUGIN_NAME); + TSError("[%s] Usage: @plugin=%s.so @pparam=--policy=chance @pparam=--sample=%%", PLUGIN_NAME, PLUGIN_NAME); } const char * @@ -139,8 +139,8 @@ class ChancePolicy : public PromotionPolicy ////////////////////////////////////////////////////////////////////////////////////////////// -// The LRU based policy keeps track of number of URLs, with a counter for each slot. -// Objects are not promoted unless the counter reaches before it gets evicted. An +// The LRU basedo policy keeps track of number of URLs, with a counter for each slot. +// Objects are not promted unless the counter reaches before it gets evicted. An // optional parameter can be used to sample hits, this can reduce contention and // churning in the LRU as well. // @@ -182,31 +182,14 @@ struct LRUHashHasher { typedef std::pair LRUEntry; typedef std::list LRUList; +typedef std::unordered_map LRUMap; static LRUEntry NULL_LRU_ENTRY; // Used to create an "empty" new LRUEntry -// TODO: We should eliminate this when we have unordered_map on all supported platforms. -#if HAVE_UNORDERED_MAP -#include -typedef std::unordered_map LRUMap; -#else -#include -typedef std::map LRUMap; -#endif - class LRUPolicy : public PromotionPolicy { public: - LRUPolicy() : PromotionPolicy(), _buckets(1000), _hits(10) - { - // This doesn't have to be perfect, since this is just chance sampling. - // coverity[dont_call] - srand48((long)time(NULL) ^ (long)getpid() ^ (long)getppid()); -#if HAVE_UNORDERED_MAP - _map.reserve(_buckets); -#endif - _lock = TSMutexCreate(); - } + LRUPolicy() : PromotionPolicy(), _buckets(1000), _hits(10), _lock(TSMutexCreate()) {} ~LRUPolicy() { @@ -218,8 +201,7 @@ class LRUPolicy : public PromotionPolicy _freelist.clear(); TSMutexUnlock(_lock); - - // ToDo: Destroy mutex ? TS-1432 + TSMutexDestroy(_lock); } bool @@ -228,6 +210,12 @@ class LRUPolicy : public PromotionPolicy switch (opt) { case 'b': _buckets = static_cast(strtol(optarg, NULL, 10)); + if (_buckets <= 0) { + // buckets size of 0 doesn't make sense and is not supported. Set to default value of 10. + TSDebug(PLUGIN_NAME, "buckets size of 0 is not allowed. Use buckets size >= 10"); + TSDebug(PLUGIN_NAME, "Setting to default bucket size of 10"); + _buckets = DEFAULT_BUCKET_SIZE; + } break; case 'h': _hits = static_cast(strtol(optarg, NULL, 10)); @@ -237,6 +225,10 @@ class LRUPolicy : public PromotionPolicy return false; } + // This doesn't have to be perfect, since this is just chance sampling. + // coverity[dont_call] + srand48((long)time(NULL) ^ (long)getpid() ^ (long)getppid()); + return true; } @@ -249,24 +241,37 @@ class LRUPolicy : public PromotionPolicy char *url = TSHttpTxnEffectiveUrlStringGet(txnp, &url_len); bool ret = false; + // Generally shouldn't happen ... + if (!url) { + return false; + } + TSDebug(PLUGIN_NAME, "LRUPolicy::doPromote(%.*s ...)", url_len > 30 ? 30 : url_len, url); hash.init(url, url_len); + TSfree(url); // We have to hold the lock across all list and hash access / updates TSMutexLock(_lock); map_it = _map.find(&hash); if (_map.end() != map_it) { - // We have an entry in the URL + // We have an entry in the LRU if (++(map_it->second->second) >= _hits) { // Promoted! Cleanup the LRU, and signal success. Save the promoted entry on the freelist. TSDebug(PLUGIN_NAME, "saving the LRUEntry to the freelist"); - _freelist.splice(_freelist.begin(), _list, map_it->second); + // Check if list is not empty + if (!_list.empty()) { + _freelist.splice(_freelist.begin(), _list, map_it->second); + } _map.erase(map_it->first); ret = true; } else { // It's still not promoted, make sure it's moved to the front of the list - _list.splice(_list.begin(), _list, map_it->second); + TSDebug(PLUGIN_NAME, "still not promoted, got %d hits so far", map_it->second->second); + // Check if list is not empty + if (!_list.empty()) { + _list.splice(_list.begin(), _list, map_it->second); + } } } else { // New LRU entry for the URL, try to repurpose the list entry as much as possible @@ -295,7 +300,8 @@ class LRUPolicy : public PromotionPolicy void usage() const { - TSError(PLUGIN_NAME, "Usage: @plugin=%s.so @pparam=--policy=lru @pparam=--buckets= --hits= --sample=", PLUGIN_NAME); + TSError("[%s] Usage: @plugin=%s.so @pparam=--policy=lru @pparam=--buckets= --hits= --sample=", PLUGIN_NAME, + PLUGIN_NAME); } const char * @@ -346,7 +352,7 @@ class PromotionConfig } else if (0 == strncasecmp(optarg, "lru", 3)) { _policy = new LRUPolicy(); } else { - TSError("Unknown policy --policy=%s", optarg); + TSError("[%s] Unknown policy --policy=%s", PLUGIN_NAME, optarg); return false; } if (_policy) { @@ -359,14 +365,14 @@ class PromotionConfig _policy->setSample(optarg); } else { if (!_policy->parseOption(opt, optarg)) { - TSError("The specified policy (%s) does not support the -%c option", _policy->policyName(), opt); + TSError("[%s] The specified policy (%s) does not support the -%c option", PLUGIN_NAME, _policy->policyName(), opt); delete _policy; _policy = NULL; return false; } } } else { - TSError("The --policy= parameter must come first on the remap configuration"); + TSError("[%s] The --policy= parameter must come first on the remap configuration", PLUGIN_NAME); return false; } } @@ -380,6 +386,20 @@ class PromotionConfig }; +////////////////////////////////////////////////////////////////////////////////////////////// +// Little helper continuation, to turn off writing to the cache. ToDo: when we have proper +// APIs to make requests / responses, we can remove this completely. +static int +cont_nocache_response(TSCont contp, TSEvent event, void *edata) +{ + TSHttpTxn txnp = static_cast(edata); + + TSHttpTxnServerRespNoStoreSet(txnp, 1); + // Reenable and continue with the state machine. + TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); + return 0; +} + ////////////////////////////////////////////////////////////////////////////////////////////// // Main "plugin", a TXN hook in the TS_HTTP_READ_CACHE_HDR_HOOK. Unless the policy allows // caching, we will turn off the cache from here on for the TXN. @@ -393,35 +413,35 @@ cont_handle_policy(TSCont contp, TSEvent event, void *edata) { TSHttpTxn txnp = static_cast(edata); PromotionConfig *config = static_cast(TSContDataGet(contp)); - int obj_status; switch (event) { // Main HOOK case TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE: - if (TS_ERROR != TSHttpTxnCacheLookupStatusGet(txnp, &obj_status)) { - switch (obj_status) { - case TS_CACHE_LOOKUP_MISS: - case TS_CACHE_LOOKUP_SKIPPED: - if (config->getPolicy()->doSample() && config->getPolicy()->doPromote(txnp)) { - TSDebug(PLUGIN_NAME, "cache-status is %d, and leaving cache on (promoted)", obj_status); - } else { - TSDebug(PLUGIN_NAME, "cache-status is %d, and turning off the cache (not promoted)", obj_status); - TSHttpTxnHookAdd(txnp, TS_HTTP_READ_RESPONSE_HDR_HOOK, contp); + if (TS_SUCCESS != TSHttpTxnIsInternal(txnp)) { + int obj_status; + + if (TS_ERROR != TSHttpTxnCacheLookupStatusGet(txnp, &obj_status)) { + switch (obj_status) { + case TS_CACHE_LOOKUP_MISS: + case TS_CACHE_LOOKUP_SKIPPED: + if (config->getPolicy()->doSample() && config->getPolicy()->doPromote(txnp)) { + TSDebug(PLUGIN_NAME, "cache-status is %d, and leaving cache on (promoted)", obj_status); + } else { + TSDebug(PLUGIN_NAME, "cache-status is %d, and turning off the cache (not promoted)", obj_status); + TSHttpTxnHookAdd(txnp, TS_HTTP_READ_RESPONSE_HDR_HOOK, gNocacheCont); + } + break; + default: + // Do nothing, just let it handle the lookup. + TSDebug(PLUGIN_NAME, "cache-status is %d (hit), nothing to do", obj_status); + break; } - break; - default: - // Do nothing, just let it handle the lookup. - TSDebug(PLUGIN_NAME, "cache-status is %d (hit), nothing to do", obj_status); - break; } + } else { + TSDebug(PLUGIN_NAME, "Request is an internal (plugin) request, implicitly promoted"); } break; - // Temporaray hack, to deal with the fact that we can turn off the cache earlier - case TS_EVENT_HTTP_READ_RESPONSE_HDR: - TSHttpTxnServerRespNoStoreSet(txnp, 1); - break; - // Should not happen default: TSDebug(PLUGIN_NAME, "Unhandled event %d", (int)event); @@ -451,6 +471,8 @@ TSRemapInit(TSRemapInterface *api_info, char *errbuf, int errbuf_size) return TS_ERROR; } + gNocacheCont = TSContCreate(cont_nocache_response, NULL); + TSDebug(PLUGIN_NAME, "remap plugin is successfully initialized"); return TS_SUCCESS; /* success */ } @@ -460,18 +482,20 @@ TSReturnCode TSRemapNewInstance(int argc, char *argv[], void **ih, char * /* errbuf */, int /* errbuf_size */) { PromotionConfig *config = new PromotionConfig; - TSCont contp = TSContCreate(cont_handle_policy, TSMutexCreate()); --argc; ++argv; if (config->factory(argc, argv)) { + TSCont contp = TSContCreate(cont_handle_policy, NULL); + TSContDataSet(contp, static_cast(config)); *ih = static_cast(contp); return TS_SUCCESS; + } else { + delete config; + return TS_ERROR; } - - return TS_ERROR; } void @@ -492,10 +516,11 @@ TSRemapStatus TSRemapDoRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo * /* ATS_UNUSED rri */) { if (NULL == ih) { - TSDebug(PLUGIN_NAME, "No ACLs configured, this is probably a plugin bug"); + TSDebug(PLUGIN_NAME, "No promotion rules configured, this is probably a plugin bug"); } else { TSCont contp = static_cast(ih); + TSDebug(PLUGIN_NAME, "scheduling a TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK hook"); TSHttpTxnHookAdd(rh, TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, contp); }