diff --git a/include/ts/apidefs.h.in b/include/ts/apidefs.h.in index 27e1522dd91..a933e8838e4 100644 --- a/include/ts/apidefs.h.in +++ b/include/ts/apidefs.h.in @@ -1308,6 +1308,42 @@ extern tsapi const char *const TS_PROTO_TAG_IPV6; */ #define TS_NULL_MLOC ((TSMLoc)0) +/* -------------------------------------------------------------------------- + HostStatus types */ + +typedef enum { + TS_HOST_STATUS_INIT, + TS_HOST_STATUS_DOWN, + TS_HOST_STATUS_UP, +} TSHostStatus; + +/* MUST match proxy/HostStatus.h Reason. + * If a value is added here, it MUST be added there with the same value. + */ +typedef enum { + TS_HOST_STATUS_ACTIVE = 0x1, + TS_HOST_STATUS_LOCAL = 0x2, + TS_HOST_STATUS_MANUAL = 0x4, + TS_HOST_STATUS_SELF_DETECT = 0x8, + TS_HOST_STATUS_ALL = 0xf, +} TSHostStatusReason; + +/* -------------------------------------------------------------------------- + ParentResult API types */ + +// used in ParentSelection to to set the number of ATSConsistentHashIter's +// used in NextHopSelectionStrategy to limit the host group +// size as well, group size is one to one with the number of rings +#define TS_MAX_GROUP_RINGS 5 + +typedef enum { + TS_PARENT_UNDEFINED, + TS_PARENT_DIRECT, + TS_PARENT_SPECIFIED, + TS_PARENT_AGENT, + TS_PARENT_FAIL, +} TSParentResultType; + /* -------------------------------------------------------------------------- Interface for the UUID APIs. https://www.ietf.org/rfc/rfc4122.txt. */ typedef enum { diff --git a/include/ts/ts.h b/include/ts/ts.h index 4c4565e683f..80be588cee6 100644 --- a/include/ts/ts.h +++ b/include/ts/ts.h @@ -2609,6 +2609,39 @@ tsapi TSReturnCode TSHttpTxnClientStreamIdGet(TSHttpTxn txnp, uint64_t *stream_i */ tsapi TSReturnCode TSHttpTxnClientStreamPriorityGet(TSHttpTxn txnp, TSHttpPriority *priority); +/* + * Returns TS_SUCCESS if hostname is this machine, as used for parent and remap self-detection. + * Returns TS_ERROR if hostname is not this machine. + */ +tsapi TSReturnCode TSHostnameIsSelf(const char *hostname); + +/* + * Gets the status of hostname in the outparam status, and the status reason in the outparam reason. + * The reason is a logical-or combination of the reasons in TSHostStatusReason. + * If either outparam is null, it will not be set and no error will be returned. + * Returns TS_SUCCESS if the hostname was a parent and existed in the HostStatus, else TS_ERROR. + */ +tsapi TSReturnCode TSHostStatusGet(const char *hostname, const size_t hostname_len, TSHostStatus *status, unsigned int *reason); + +/* + * Sets the status of hostname in status, down_time, and reason. + * The reason is a logical-or combination of the reasons in TSHostStatusReason. + */ +tsapi void TSHostStatusSet(const char *hostname, const size_t hostname_len, TSHostStatus status, const unsigned int down_time, + const unsigned int reason); + +struct TSParentResult; + +/* + * Gets the Transaction Parent Result pointer. + */ +tsapi void TSHttpTxnParentResultGet(TSHttpTxn txnp, struct TSParentResult *result); + +/* + * Sets the Transaction Parent Result pointer. + */ +tsapi void TSHttpTxnParentResultSet(TSHttpTxn txnp, struct TSParentResult *result); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/include/ts/nexthop.h b/include/tscpp/api/nexthop.h similarity index 54% rename from include/ts/nexthop.h rename to include/tscpp/api/nexthop.h index ed623805649..66c5c49c80d 100644 --- a/include/ts/nexthop.h +++ b/include/tscpp/api/nexthop.h @@ -30,20 +30,20 @@ #include -// plugin callback commands. enum NHCmd { NH_MARK_UP, NH_MARK_DOWN }; -struct NHHealthStatus { - virtual bool isNextHopAvailable(TSHttpTxn txn, const char *hostname, const int port, void *ih = nullptr) = 0; - virtual void markNextHop(TSHttpTxn txn, const char *hostname, const int port, const NHCmd status, void *ih = nullptr, - const time_t now = 0) = 0; - virtual ~NHHealthStatus() {} -}; +class TSNextHopSelectionStrategy +{ +public: + TSNextHopSelectionStrategy(){}; + virtual ~TSNextHopSelectionStrategy(){}; -struct NHPluginStrategy { - virtual void findNextHop(TSHttpTxn txnp, void *ih = nullptr) = 0; - virtual bool nextHopExists(TSHttpTxn txnp, void *ih = nullptr) = 0; - virtual ~NHPluginStrategy() {} + virtual void findNextHop(TSHttpTxn txnp, time_t now = 0) = 0; + virtual void markNextHop(TSHttpTxn txnp, const char *hostname, const int port, const NHCmd status, const time_t now = 0) = 0; + virtual bool nextHopExists(TSHttpTxn txnp) = 0; + virtual bool responseIsRetryable(unsigned int current_retry_attempts, TSHttpStatus response_code) = 0; + virtual bool onFailureMarkParentDown(TSHttpStatus response_code) = 0; - NHHealthStatus *healthStatus; + virtual bool goDirect() = 0; + virtual bool parentIsProxy() = 0; }; diff --git a/include/tscpp/api/parentresult.h b/include/tscpp/api/parentresult.h new file mode 100644 index 00000000000..7b63539a8c9 --- /dev/null +++ b/include/tscpp/api/parentresult.h @@ -0,0 +1,45 @@ +/** @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. + */ + +#pragma once + +#include "ts/apidefs.h" +#include "tscore/ConsistentHash.h" +#include + +extern const char *ParentResultStr[]; + +struct TSParentResult { + const char *hostname; + in_port_t port; + bool retry; + TSParentResultType result; + bool chash_init[TS_MAX_GROUP_RINGS] = {false}; + TSHostStatus first_choice_status = TSHostStatus::TS_HOST_STATUS_INIT; + int line_number; + uint32_t last_parent; + uint32_t start_parent; + uint32_t last_group; + bool wrap_around; + bool mapWrapped[2]; + int last_lookup; + ATSConsistentHashIter chashIter[TS_MAX_GROUP_RINGS]; +}; diff --git a/iocore/cache/test/stub.cc b/iocore/cache/test/stub.cc index 65f81e79c05..dcc232c1a2c 100644 --- a/iocore/cache/test/stub.cc +++ b/iocore/cache/test/stub.cc @@ -157,7 +157,7 @@ ts::svtoi(TextView src, TextView *out, int base) } void -HostStatus::setHostStatus(const char *name, HostStatus_t status, const unsigned int down_time, const unsigned int reason) +HostStatus::setHostStatus(const char *name, TSHostStatus status, const unsigned int down_time, const unsigned int reason) { } diff --git a/iocore/net/Socks.cc b/iocore/net/Socks.cc index dbc2a02835a..12bda59f049 100644 --- a/iocore/net/Socks.cc +++ b/iocore/net/Socks.cc @@ -90,7 +90,7 @@ SocksEntry::findServer() #ifdef SOCKS_WITH_TS if (nattempts == 1) { - ink_assert(server_result.result == PARENT_UNDEFINED); + ink_assert(server_result.result == TS_PARENT_UNDEFINED); server_params->findParent(&req_data, &server_result, fail_threshold, retry_time); } else { socks_conf_struct *conf = netProcessor.socks_conf_stuff; @@ -101,14 +101,14 @@ SocksEntry::findServer() server_params->markParentDown(&server_result, fail_threshold, retry_time); if (nattempts > conf->connection_attempts) { - server_result.result = PARENT_FAIL; + server_result.result = TS_PARENT_FAIL; } else { server_params->nextParent(&req_data, &server_result, fail_threshold, retry_time); } } switch (server_result.result) { - case PARENT_SPECIFIED: + case TS_PARENT_SPECIFIED: // Original was inet_addr, but should hostnames work? // ats_ip_pton only supports numeric (because other clients // explicitly want to avoid hostname lookups). @@ -122,8 +122,8 @@ SocksEntry::findServer() default: ink_assert(!"Unexpected event"); // fallthrough - case PARENT_DIRECT: - case PARENT_FAIL: + case TS_PARENT_DIRECT: + case TS_PARENT_FAIL: memset(&server_addr, 0, sizeof(server_addr)); } #else diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 52ce5d8bfdc..d4512727d6f 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -63,6 +63,7 @@ include experimental/cert_reporting_tool/Makefile.inc include experimental/collapsed_forwarding/Makefile.inc include experimental/cookie_remap/Makefile.inc include experimental/custom_redirect/Makefile.inc +include experimental/nexthop_strategy_consistenthash/Makefile.inc include experimental/fq_pacing/Makefile.inc include experimental/geoip_acl/Makefile.inc include experimental/header_freq/Makefile.inc diff --git a/plugins/experimental/nexthop_strategy_consistenthash/Makefile.inc b/plugins/experimental/nexthop_strategy_consistenthash/Makefile.inc new file mode 100644 index 00000000000..c75eb8e7d76 --- /dev/null +++ b/plugins/experimental/nexthop_strategy_consistenthash/Makefile.inc @@ -0,0 +1,29 @@ +# 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. + +pkglib_LTLIBRARIES += experimental/nexthop_strategy_consistenthash/nexthop_strategy_consistenthash.la + +experimental_nexthop_strategy_consistenthash_nexthop_strategy_consistenthash_la_SOURCES = \ + experimental/nexthop_strategy_consistenthash/strategy.cc \ + experimental/nexthop_strategy_consistenthash/healthstatus.cc \ + experimental/nexthop_strategy_consistenthash/consistenthash.cc \ + experimental/nexthop_strategy_consistenthash/consistenthash_config.cc \ + experimental/nexthop_strategy_consistenthash/plugin_consistenthash.cc + +experimental_nexthop_strategy_consistenthash_nexthop_strategy_consistenthash_la_LDFLAGS = \ + $(AM_LDFLAGS) + +AM_CPPFLAGS += @YAMLCPP_INCLUDES@ $(TS_INCLUDES) diff --git a/plugins/experimental/nexthop_strategy_consistenthash/consistenthash.cc b/plugins/experimental/nexthop_strategy_consistenthash/consistenthash.cc new file mode 100644 index 00000000000..c8c10332651 --- /dev/null +++ b/plugins/experimental/nexthop_strategy_consistenthash/consistenthash.cc @@ -0,0 +1,486 @@ +/* + * 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 "strategy.h" +#include "consistenthash.h" +#include "util.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "tscore/HashSip.h" +#include "tscore/ConsistentHash.h" +#include "tscore/ink_assert.h" +#include "ts/ts.h" +#include "ts/remap.h" +#include "tscpp/api/nexthop.h" +#include "tscpp/api/parentresult.h" + +// hash_key strings. +constexpr std::string_view hash_key_url = "url"; +constexpr std::string_view hash_key_hostname = "hostname"; +constexpr std::string_view hash_key_path = "path"; +constexpr std::string_view hash_key_path_query = "path+query"; +constexpr std::string_view hash_key_path_fragment = "path+fragment"; +constexpr std::string_view hash_key_cache = "cache_key"; + +static HostRecord * +chash_lookup(std::shared_ptr ring, uint64_t hash_key, ATSConsistentHashIter *iter, bool *wrapped, + ATSHash64Sip24 *hash, bool *hash_init, bool *mapWrapped, uint64_t sm_id) +{ + HostRecord *host_rec = nullptr; + + if (*hash_init == false) { + host_rec = static_cast(ring->lookup_by_hashval(hash_key, iter, wrapped)); + *hash_init = true; + } else { + host_rec = static_cast(ring->lookup(nullptr, iter, wrapped, hash)); + } + bool wrap_around = *wrapped; + *wrapped = *mapWrapped && *wrapped; + if (!*mapWrapped && wrap_around) { + *mapWrapped = true; + } + + return host_rec; +} + +NextHopConsistentHash::NextHopConsistentHash(const std::string_view name) : NextHopSelectionStrategy(name) {} + +NextHopConsistentHash::~NextHopConsistentHash() +{ + NH_Debug(NH_DEBUG_TAG, "destructor called for strategy named: %s", strategy_name.c_str()); +} + +bool +NextHopConsistentHash::Init(const YAML::Node &n) +{ + ATSHash64Sip24 hash; + + try { + if (n["hash_key"]) { + auto hash_key_val = n["hash_key"].Scalar(); + if (hash_key_val == hash_key_url) { + hash_key = NH_URL_HASH_KEY; + } else if (hash_key_val == hash_key_hostname) { + hash_key = NH_HOSTNAME_HASH_KEY; + } else if (hash_key_val == hash_key_path) { + hash_key = NH_PATH_HASH_KEY; + } else if (hash_key_val == hash_key_path_query) { + hash_key = NH_PATH_QUERY_HASH_KEY; + } else if (hash_key_val == hash_key_path_fragment) { + hash_key = NH_PATH_FRAGMENT_HASH_KEY; + } else if (hash_key_val == hash_key_cache) { + hash_key = NH_CACHE_HASH_KEY; + } else { + hash_key = NH_PATH_HASH_KEY; + NH_Note("Invalid 'hash_key' value, '%s', for the strategy named '%s', using default '%s'.", hash_key_val.c_str(), + strategy_name.c_str(), hash_key_path.data()); + } + } + } catch (std::exception &ex) { + NH_Note("Error parsing the strategy named '%s' due to '%s', this strategy will be ignored.", strategy_name.c_str(), ex.what()); + return false; + } + + bool result = NextHopSelectionStrategy::Init(n); + if (!result) { + return false; + } + + // load up the hash rings. + for (uint32_t i = 0; i < groups; i++) { + std::shared_ptr hash_ring = std::make_shared(); + for (uint32_t j = 0; j < host_groups[i].size(); j++) { + // ATSConsistentHash needs the raw pointer. + HostRecord *p = host_groups[i][j].get(); + // need to copy the 'hash_string' or 'hostname' cstring to 'name' for insertion into ATSConsistentHash. + if (!p->hash_string.empty()) { + p->name = const_cast(p->hash_string.c_str()); + } else { + p->name = const_cast(p->hostname.c_str()); + } + p->group_index = host_groups[i][j]->group_index; + p->host_index = host_groups[i][j]->host_index; + hash_ring->insert(p, p->weight, &hash); + NH_Debug(NH_DEBUG_TAG, "Loading hash rings - ring: %d, host record: %d, name: %s, hostname: %s, stategy: %s", i, j, p->name, + p->hostname.c_str(), strategy_name.c_str()); + } + hash.clear(); + rings.push_back(hash_ring); + } + return true; +} + +// returns a hash key calculated from the request and 'hash_key' configuration +// parameter. +uint64_t +NextHopConsistentHash::getHashKey(uint64_t sm_id, TSMBuffer reqp, TSMLoc url, TSMLoc parent_selection_url, ATSHash64 *h) +{ + int len = 0; + const char *url_string_ref = nullptr; + + // calculate a hash using the selected config. + switch (hash_key) { + case NH_URL_HASH_KEY: + url_string_ref = TSUrlStringGet(reqp, url, &len); + if (url_string_ref && len > 0) { + h->update(url_string_ref, len); + NH_Debug(NH_DEBUG_TAG, "[%" PRIu64 "] url hash string: %s", sm_id, url_string_ref); + } + break; + // hostname hash + case NH_HOSTNAME_HASH_KEY: + url_string_ref = TSUrlHostGet(reqp, url, &len); + if (url_string_ref && len > 0) { + h->update(url_string_ref, len); + } + break; + // path + query string + case NH_PATH_QUERY_HASH_KEY: + url_string_ref = TSUrlPathGet(reqp, url, &len); + h->update("/", 1); + if (url_string_ref && len > 0) { + h->update(url_string_ref, len); + } + url_string_ref = TSUrlHttpQueryGet(reqp, url, &len); + if (url_string_ref && len > 0) { + h->update("?", 1); + h->update(url_string_ref, len); + } + break; + // path + fragment hash + case NH_PATH_FRAGMENT_HASH_KEY: + url_string_ref = TSUrlPathGet(reqp, url, &len); + h->update("/", 1); + if (url_string_ref && len > 0) { + h->update(url_string_ref, len); + } + url_string_ref = TSUrlHttpFragmentGet(reqp, url, &len); + if (url_string_ref && len > 0) { + h->update("?", 1); + h->update(url_string_ref, len); + } + break; + // use the cache key created by the cache-key plugin. + case NH_CACHE_HASH_KEY: + if (parent_selection_url != TS_NULL_MLOC) { + url_string_ref = TSUrlStringGet(reqp, parent_selection_url, &len); + if (url_string_ref && len > 0) { + NH_Debug(NH_DEBUG_TAG, "[%" PRIu64 "] using parent selection over-ride string:'%.*s'.", sm_id, len, url_string_ref); + h->update(url_string_ref, len); + } + } else { + url_string_ref = TSUrlPathGet(reqp, url, &len); + h->update("/", 1); + if (url_string_ref && len > 0) { + NH_Debug(NH_DEBUG_TAG, "[%" PRIu64 "] the parent selection over-ride url is not set, using default path: %s.", sm_id, + url_string_ref); + h->update(url_string_ref, len); + } + } + break; + // use the path as the hash, default. + case NH_PATH_HASH_KEY: + default: + url_string_ref = TSUrlPathGet(reqp, url, &len); + h->update("/", 1); + if (url_string_ref && len > 0) { + h->update(url_string_ref, len); + } + break; + } + + h->final(); + return h->get(); +} + +static void +setParentResultErr(TSHttpTxn txnp, TSParentResult *result) +{ + result->hostname = nullptr; + result->port = 0; + result->retry = false; + TSHttpTxnParentResultSet(txnp, result); +} + +void +NextHopConsistentHash::findNextHop(TSHttpTxn txnp, time_t now) +{ + NH_Debug(NH_DEBUG_TAG, "NH plugin findNexthop calling"); + + TSParentResult result_obj; + TSParentResult *result = &result_obj; + TSHttpTxnParentResultGet(txnp, result); + + int64_t sm_id = TSHttpTxnIdGet(txnp); + + TSMBuffer reqp; // TODO verify doesn't need freed + + TSMLoc hdr; + ScopedFreeMLoc hdr_cleanup(&reqp, TS_NULL_MLOC, &hdr); + if (TSHttpTxnClientReqGet(txnp, &reqp, &hdr) == TS_ERROR) { + setParentResultErr(txnp, result); + return; + } + + TSMLoc parent_selection_url = TS_NULL_MLOC; + ScopedFreeMLoc parent_selection_url_cleanup(&reqp, TS_NULL_MLOC, &parent_selection_url); + if (TSUrlCreate(reqp, &parent_selection_url) != TS_SUCCESS) { + NH_Error("nexthop failed to create url for parent_selection_url"); + setParentResultErr(txnp, result); + return; + } + if (TSHttpTxnParentSelectionUrlGet(txnp, reqp, parent_selection_url) != TS_SUCCESS) { + parent_selection_url = TS_NULL_MLOC; + } + + TSMLoc url; + ScopedFreeMLoc url_cleanup(&reqp, hdr, &url); + if (TSHttpHdrUrlGet(reqp, hdr, &url) != TS_SUCCESS) { + NH_Error("failed to get header url, cannot find next hop"); + setParentResultErr(txnp, result); + return; + } + + // TODO is it really worth getting the string out to debug print here? + NH_Debug(NH_DEBUG_TAG, "NH plugin findNexthop got url 'x'"); + + int64_t retry_time = 0; // = sm->t_state.txn_conf->parent_retry_time; + if (TSHttpTxnConfigIntGet(txnp, TS_CONFIG_HTTP_PARENT_PROXY_RETRY_TIME, &retry_time) != TS_SUCCESS) { + // TODO get and cache on init, to prevent potential runtime failure? + NH_Error("failed to get parent retry time, cannot find next hop"); + setParentResultErr(txnp, result); + return; + } + + time_t _now = now; + bool firstcall = false; + bool nextHopRetry = false; + bool wrapped = false; + std::vector wrap_around(groups, false); + uint32_t cur_ring = 0; // there is a hash ring for each host group + uint64_t hash_key = 0; + uint32_t lookups = 0; + ATSHash64Sip24 hash; + HostRecord *hostRec = nullptr; + std::shared_ptr pRec = nullptr; + TSHostStatus host_stat = TSHostStatus::TS_HOST_STATUS_INIT; + + if (result->line_number == -1 && result->result == TS_PARENT_UNDEFINED) { + firstcall = true; + } + + if (firstcall) { + NH_Debug(NH_DEBUG_TAG, "[%" PRIu64 "] firstcall, line_number: %d, result: %s", sm_id, result->line_number, + ParentResultStr[result->result]); + result->line_number = NextHopConsistentHash::LineNumberPlaceholder; + cur_ring = 0; + for (uint32_t i = 0; i < groups; i++) { + result->chash_init[i] = false; + wrap_around[i] = false; + } + } else { + NH_Debug(NH_DEBUG_TAG, "[%" PRIu64 "] not firstcall, line_number: %d, result: %s", sm_id, result->line_number, + ParentResultStr[result->result]); + switch (ring_mode) { + case NH_ALTERNATE_RING: + if (groups > 1) { + cur_ring = (result->last_group + 1) % groups; + } else { + cur_ring = result->last_group; + } + break; + case NH_EXHAUST_RING: + default: + if (!wrapped) { + cur_ring = result->last_group; + } else if (groups > 1) { + cur_ring = (result->last_group + 1) % groups; + } + break; + } + } + + // Do the initial parent look-up. + hash_key = getHashKey(sm_id, reqp, url, parent_selection_url, &hash); + + do { // search until we've selected a different parent if !firstcall + std::shared_ptr r = rings[cur_ring]; + hostRec = chash_lookup(r, hash_key, &result->chashIter[cur_ring], &wrapped, &hash, &result->chash_init[cur_ring], + &result->mapWrapped[cur_ring], sm_id); + wrap_around[cur_ring] = wrapped; + lookups++; + // the 'available' flag is maintained in 'host_groups' and not the hash ring. + if (hostRec) { + pRec = host_groups[hostRec->group_index][hostRec->host_index]; + if (firstcall) { + TSHostStatus hostStatus; + const bool hostExists = + pRec ? (TSHostStatusGet(pRec->hostname.c_str(), pRec->hostname.size(), &hostStatus, nullptr) == TS_SUCCESS) : false; + result->first_choice_status = hostExists ? hostStatus : TSHostStatus::TS_HOST_STATUS_UP; + break; + } + } else { + pRec = nullptr; + } + } while (pRec && result->hostname && strcmp(pRec->hostname.c_str(), result->hostname) == 0); + + NH_Debug(NH_DEBUG_TAG, "[%" PRIu64 "] Initial parent lookups: %d", sm_id, lookups); + + // ---------------------------------------------------------------------------------------------------- + // Validate initial parent look-up and perform additional look-ups if required. + // ---------------------------------------------------------------------------------------------------- + + TSHostStatus hostStatus; + unsigned int hostReasons; + const bool hostExists = + pRec ? (TSHostStatusGet(pRec->hostname.c_str(), pRec->hostname.size(), &hostStatus, &hostReasons) == TS_SUCCESS) : false; + host_stat = hostExists ? hostStatus : TSHostStatus::TS_HOST_STATUS_UP; + // if the config ignore_self_detect is set to true and the host is down due to SELF_DETECT reason + // ignore the down status and mark it as avaialble + if ((pRec && ignore_self_detect) && (hostExists && hostStatus == TS_HOST_STATUS_DOWN)) { + if (hostReasons & TS_HOST_STATUS_SELF_DETECT) { + host_stat = TS_HOST_STATUS_UP; + } + } + if (!pRec || (pRec && !pRec->available) || host_stat == TS_HOST_STATUS_DOWN) { + do { + // check if an unavailable server is now retryable, use it if it is. + if (pRec && !pRec->available && host_stat == TS_HOST_STATUS_UP) { + _now == 0 ? _now = time(nullptr) : _now = now; + // check if the host is retryable. It's retryable if the retry window has elapsed + if ((pRec->failedAt + retry_time) < static_cast(_now)) { + nextHopRetry = true; + result->last_parent = pRec->host_index; + result->last_lookup = pRec->group_index; + result->retry = nextHopRetry; + result->result = TS_PARENT_SPECIFIED; + NH_Debug(NH_DEBUG_TAG, "[%" PRIu64 "] next hop %s is now retryable, marked it available.", sm_id, pRec->hostname.c_str()); + break; + } + } + switch (ring_mode) { + case NH_ALTERNATE_RING: + if (groups > 0) { + cur_ring = (pRec->group_index + 1) % groups; + } + break; + case NH_EXHAUST_RING: + default: + if (wrap_around[cur_ring] && groups > 1) { + cur_ring = (cur_ring + 1) % groups; + } + break; + } + std::shared_ptr r = rings[cur_ring]; + hostRec = chash_lookup(r, hash_key, &result->chashIter[cur_ring], &wrapped, &hash, &result->chash_init[cur_ring], + &result->mapWrapped[cur_ring], sm_id); + wrap_around[cur_ring] = wrapped; + lookups++; + if (hostRec) { + pRec = host_groups[hostRec->group_index][hostRec->host_index]; + + TSHostStatus hostStatus; + unsigned int hostReasons; + const bool hostExists = + pRec ? (TSHostStatusGet(pRec->hostname.c_str(), pRec->hostname.size(), &hostStatus, &hostReasons) == TS_SUCCESS) : false; + host_stat = hostExists ? hostStatus : TSHostStatus::TS_HOST_STATUS_UP; + + // if the config ignore_self_detect is set to true and the host is down due to SELF_DETECT reason + // ignore the down status and mark it as avaialble + if ((pRec && ignore_self_detect) && (hostExists && hostStatus == TS_HOST_STATUS_DOWN)) { + if (hostReasons & TS_HOST_STATUS_SELF_DETECT) { + host_stat = TS_HOST_STATUS_UP; + } + } + NH_Debug(NH_DEBUG_TAG, "[%" PRIu64 "] Selected a new parent: %s, available: %s, wrapped: %s, lookups: %d.", sm_id, + pRec->hostname.c_str(), (pRec->available) ? "true" : "false", (wrapped) ? "true" : "false", lookups); + // use available host. + if (pRec->available && host_stat == TS_HOST_STATUS_UP) { + break; + } + } else { + pRec = nullptr; + } + bool all_wrapped = true; + for (uint32_t c = 0; c < groups; c++) { + if (wrap_around[c] == false) { + all_wrapped = false; + } + } + if (all_wrapped) { + NH_Debug(NH_DEBUG_TAG, "[%" PRIu64 "] No available parents.", sm_id); + if (pRec) { + pRec = nullptr; + } + break; + } + } while (!pRec || (pRec && !pRec->available) || host_stat == TS_HOST_STATUS_DOWN); + } + + // ---------------------------------------------------------------------------------------------------- + // Validate and return the final result. + // ---------------------------------------------------------------------------------------------------- + + if (pRec && host_stat == TS_HOST_STATUS_UP && (pRec->available || result->retry)) { + result->result = TS_PARENT_SPECIFIED; + result->hostname = pRec->hostname.c_str(); + result->last_parent = pRec->host_index; + result->last_lookup = result->last_group = cur_ring; + switch (scheme) { + case NH_SCHEME_NONE: + case NH_SCHEME_HTTP: + result->port = pRec->getPort(scheme); + break; + case NH_SCHEME_HTTPS: + result->port = pRec->getPort(scheme); + break; + } + result->retry = nextHopRetry; + ink_assert(result->hostname != nullptr); + ink_assert(result->port != 0); + NH_Debug(NH_DEBUG_TAG, "[%" PRIu64 "] result->result: %s Chosen parent: %s:%d", sm_id, ParentResultStr[result->result], + result->hostname, result->port); + } else { + if (go_direct == true) { + result->result = TS_PARENT_DIRECT; + } else { + result->result = TS_PARENT_FAIL; + } + result->hostname = nullptr; + result->port = 0; + result->retry = false; + NH_Debug(NH_DEBUG_TAG, "[%" PRIu64 "] result->result: %s set hostname null port 0 retry false", sm_id, + ParentResultStr[result->result]); + } + + TSHttpTxnParentResultSet(txnp, result); +} diff --git a/plugins/experimental/nexthop_strategy_consistenthash/consistenthash.h b/plugins/experimental/nexthop_strategy_consistenthash/consistenthash.h new file mode 100644 index 00000000000..668b0b22204 --- /dev/null +++ b/plugins/experimental/nexthop_strategy_consistenthash/consistenthash.h @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#pragma once + +#include "strategy.h" + +class NextHopSelectionStrategy; + +enum NHHashKeyType { + NH_URL_HASH_KEY = 0, + NH_HOSTNAME_HASH_KEY, + NH_PATH_HASH_KEY, // default, consistent hash uses the request url path + NH_PATH_QUERY_HASH_KEY, + NH_PATH_FRAGMENT_HASH_KEY, + NH_CACHE_HASH_KEY +}; + +class NextHopConsistentHash : public NextHopSelectionStrategy +{ + std::vector> rings; + uint64_t getHashKey(uint64_t sm_id, TSMBuffer reqp, TSMLoc url, TSMLoc parent_selection_url, ATSHash64 *h); + +public: + const uint32_t LineNumberPlaceholder = 99999; + + NHHashKeyType hash_key = NH_PATH_HASH_KEY; + + NextHopConsistentHash() = delete; + NextHopConsistentHash(const std::string_view name); + ~NextHopConsistentHash(); + bool Init(const YAML::Node &n); + void findNextHop(TSHttpTxn txnp, time_t now = 0) override; +}; diff --git a/plugins/experimental/nexthop_strategy_consistenthash/consistenthash_config.cc b/plugins/experimental/nexthop_strategy_consistenthash/consistenthash_config.cc new file mode 100644 index 00000000000..0a0818e0340 --- /dev/null +++ b/plugins/experimental/nexthop_strategy_consistenthash/consistenthash_config.cc @@ -0,0 +1,207 @@ +/* + * 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 "strategy.h" +#include "consistenthash.h" +#include "util.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "tscore/HashSip.h" +#include "tscore/ConsistentHash.h" +#include "tscore/ink_assert.h" +#include "ts/ts.h" +#include "ts/remap.h" +#include "tscpp/api/nexthop.h" +#include "tscpp/api/parentresult.h" + +void loadConfigFile(const std::string fileName, std::stringstream &doc, std::unordered_set &include_once); + +// createStrategy creates and initializes a Consistent Hash strategy from the given YAML node. +// Caller takes ownership of the returned pointer, and must call delete on it. +TSNextHopSelectionStrategy * +createStrategy(const std::string &name, const YAML::Node &node) +{ + NextHopConsistentHash *st = new NextHopConsistentHash(name); + if (!st->Init(node)) { + return nullptr; + } + return st; +} + +// createStrategyFromFile creates a Consistent Hash strategy from the given config file. +// Caller takes ownership of the returned pointer, and must call delete on it. +TSNextHopSelectionStrategy * +createStrategyFromFile(const char *file, const char *strategyName) +{ + NH_Debug(NH_DEBUG_TAG, "plugin createStrategyFromFile file '%s' strategy '%s'", file, strategyName); + + YAML::Node config; + YAML::Node strategies; + std::stringstream doc; + std::unordered_set include_once; + std::string_view fn = file; + + // strategy policy + constexpr std::string_view consistent_hash = "consistent_hash"; + + const char *basename = fn.substr(fn.find_last_of('/') + 1).data(); + + try { + NH_Note("%s loading ...", basename); + loadConfigFile(fn.data(), doc, include_once); + + config = YAML::Load(doc); + if (config.IsNull()) { + NH_Note("No NextHop strategy configs were loaded."); + return nullptr; + } + + strategies = config["strategies"]; + if (strategies.Type() != YAML::NodeType::Sequence) { + NH_Error("malformed %s file, expected a 'strategies' sequence", basename); + return nullptr; + } + + for (unsigned int i = 0; i < strategies.size(); ++i) { + YAML::Node strategy = strategies[i]; + auto name = strategy["strategy"].as(); + if (name != strategyName) { + continue; + } + auto policy = strategy["policy"]; + if (!policy) { + NH_Error("No policy is defined for the strategy named '%s', this strategy will be ignored.", name.c_str()); + continue; + } + auto policy_value = policy.Scalar(); + if (policy_value != consistent_hash) { + NH_Error("Strategy named '%s' has unsupported policy '%s', this strategy will be ignored.", strategyName, + policy_value.c_str()); + return nullptr; + } + return createStrategy(name, strategy); + } + NH_Error("no strategy named '%s' found", strategyName); + } catch (std::exception &ex) { + NH_Note("%s", ex.what()); + } + return nullptr; +} + +/* + * loads the contents of a file into a std::stringstream document. If the file has a '#include file' + * directive, that 'file' is read into the document beginning at the the point where the + * '#include' was found. This allows the 'strategy' and 'hosts' yaml files to be separate. The + * 'strategy' yaml file would then normally have the '#include hosts.yml' in it's begining. + */ +void +loadConfigFile(const std::string fileName, std::stringstream &doc, std::unordered_set &include_once) +{ + const char *sep = " \t"; + char *tok, *last; + struct stat buf; + std::string line; + + if (stat(fileName.c_str(), &buf) == -1) { + std::string err_msg = strerror(errno); + throw std::invalid_argument("Unable to stat '" + fileName + "': " + err_msg); + } + + // if fileName is a directory, concatenate all '.yaml' files alphanumerically + // into a single document stream. No #include is supported. + if (S_ISDIR(buf.st_mode)) { + DIR *dir = nullptr; + struct dirent *dir_ent = nullptr; + std::vector files; + + NH_Note("loading strategy YAML files from the directory %s", fileName.c_str()); + if ((dir = opendir(fileName.c_str())) == nullptr) { + std::string err_msg = strerror(errno); + throw std::invalid_argument("Unable to open the directory '" + fileName + "': " + err_msg); + } else { + while ((dir_ent = readdir(dir)) != nullptr) { + // filename should be greater that 6 characters to have a '.yaml' suffix. + if (strlen(dir_ent->d_name) < 6) { + continue; + } + std::string_view sv = dir_ent->d_name; + if (sv.find(".yaml", sv.size() - 5) == sv.size() - 5) { + files.push_back(sv); + } + } + // sort the files alphanumerically + std::sort(files.begin(), files.end(), + [](const std::string_view lhs, const std::string_view rhs) { return lhs.compare(rhs) < 0; }); + + for (uint32_t i = 0; i < files.size(); i++) { + std::ifstream file(fileName + "/" + files[i].data()); + if (file.is_open()) { + while (std::getline(file, line)) { + if (line[0] == '#') { + // continue; + } + doc << line << "\n"; + } + file.close(); + } else { + throw std::invalid_argument("Unable to open and read '" + fileName + "/" + files[i].data() + "'"); + } + } + } + } else { + std::ifstream file(fileName); + if (file.is_open()) { + while (std::getline(file, line)) { + if (line[0] == '#') { + tok = strtok_r(const_cast(line.c_str()), sep, &last); + if (tok != nullptr && strcmp(tok, "#include") == 0) { + std::string f = strtok_r(nullptr, sep, &last); + if (include_once.find(f) == include_once.end()) { + include_once.insert(f); + // try to load included file. + try { + loadConfigFile(f, doc, include_once); + } catch (std::exception &ex) { + throw std::invalid_argument("Unable to open included file '" + f + "' from '" + fileName + "'"); + } + } + } + } else { + doc << line << "\n"; + } + } + file.close(); + } else { + throw std::invalid_argument("Unable to open and read '" + fileName + "'"); + } + } +} diff --git a/plugins/experimental/nexthop_strategy_consistenthash/consistenthash_config.h b/plugins/experimental/nexthop_strategy_consistenthash/consistenthash_config.h new file mode 100644 index 00000000000..953112fe142 --- /dev/null +++ b/plugins/experimental/nexthop_strategy_consistenthash/consistenthash_config.h @@ -0,0 +1,26 @@ +/* + * 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. + */ + +#pragma once + +#include +#include +#include "tscpp/api/nexthop.h" + +TSNextHopSelectionStrategy *createStrategyFromFile(const char *file, const char *strategyName); +TSNextHopSelectionStrategy *createStrategy(const std::string &name, const YAML::Node &node); diff --git a/plugins/experimental/nexthop_strategy_consistenthash/healthstatus.cc b/plugins/experimental/nexthop_strategy_consistenthash/healthstatus.cc new file mode 100644 index 00000000000..2c56386d380 --- /dev/null +++ b/plugins/experimental/nexthop_strategy_consistenthash/healthstatus.cc @@ -0,0 +1,148 @@ +/* + * 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 "strategy.h" +#include "consistenthash.h" +#include "util.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "tscore/HashSip.h" +#include "tscore/ConsistentHash.h" +#include "tscore/ink_assert.h" +#include "ts/ts.h" +#include "ts/remap.h" +#include "tscpp/api/nexthop.h" +#include "tscpp/api/parentresult.h" + +void +NextHopHealthStatus::insert(std::vector> &hosts) +{ + for (uint32_t ii = 0; ii < hosts.size(); ii++) { + std::shared_ptr h = hosts[ii]; + for (auto protocol = h->protocols.begin(); protocol != h->protocols.end(); ++protocol) { + const std::string host_port = h->getHostPort((*protocol)->port); + host_map.emplace(std::make_pair(host_port, h)); + NH_Debug(NH_DEBUG_TAG, "inserting %s into host_map", host_port.c_str()); + } + } +} + +void +NextHopHealthStatus::markNextHop(TSHttpTxn txnp, const char *hostname, const int port, const NHCmd status, const time_t now) +{ + const time_t _now = now == 0 ? time(nullptr) : now; + TSParentResult result_obj; + TSParentResult *result = &result_obj; + TSHttpTxnParentResultGet(txnp, result); + const int64_t sm_id = TSHttpTxnIdGet(txnp); + + int64_t fail_threshold; // = sm->t_state.txn_conf->parent_fail_threshold; + if (TSHttpTxnConfigIntGet(txnp, TS_CONFIG_HTTP_PARENT_PROXY_FAIL_THRESHOLD, &fail_threshold) != TS_SUCCESS) { + NH_Error("markNextHop failed to get parent_fail_threshold, cannot mark next hop"); + return; + } + + int64_t retry_time; // = sm->t_state.txn_conf->parent_retry_time; + if (TSHttpTxnConfigIntGet(txnp, TS_CONFIG_HTTP_PARENT_PROXY_RETRY_TIME, &retry_time) != TS_SUCCESS) { + NH_Error("markNextHop failed to get parent_retry_time, cannot mark next hop"); + return; + } + + uint32_t new_fail_count = 0; + + // make sure we're called back with a result structure for a parent + // that is being retried. + if (status == NH_MARK_UP) { + ink_assert(result->retry == true); + } + if (result->result != TS_PARENT_SPECIFIED) { + return; + } + + const std::string host_port = HostRecord::makeHostPort(hostname, port); + auto iter = host_map.find(host_port); + if (iter == host_map.end()) { + NH_Debug(NH_DEBUG_TAG, "[%" PRId64 "] no host named %s found in host_map", sm_id, host_port.c_str()); + return; + } + + std::shared_ptr h = iter->second; + + switch (status) { + // Mark the host up. + case NH_MARK_UP: + if (!h->available) { + h->set_available(); + NH_Note("[%" PRId64 "] http parent proxy %s restored", sm_id, hostname); + } + break; + // Mark the host down. + case NH_MARK_DOWN: + if (h->failedAt == 0 || result->retry == true) { + { // lock guard + std::lock_guard guard(h->_mutex); + if (h->failedAt == 0) { + h->failedAt = _now; + if (result->retry == false) { + new_fail_count = h->failCount = 1; + } + } else if (result->retry == true) { + h->failedAt = _now; + } + } // end lock guard + NH_Note("[%" PRId64 "] NextHop %s marked as down %s", sm_id, (result->retry) ? "retry" : "initially", h->hostname.c_str()); + } else { + int old_count = 0; + // if the last failure was outside the retry window, set the failcount to 1 and failedAt to now. + { // lock guard + std::lock_guard lock(h->_mutex); + if ((h->failedAt + retry_time) < static_cast(_now)) { + h->failCount = 1; + h->failedAt = _now; + } else { + old_count = h->failCount = 1; + } + new_fail_count = old_count + 1; + } // end of lock_guard + NH_Debug(NH_DEBUG_TAG, "[%" PRId64 "] Parent fail count increased to %d for %s", sm_id, new_fail_count, h->hostname.c_str()); + } + + if (new_fail_count >= fail_threshold) { + h->set_unavailable(); + NH_Note("[%" PRId64 "] Failure threshold met failcount:%d >= threshold:%" PRId64 ", http parent proxy %s marked down", sm_id, + new_fail_count, fail_threshold, h->hostname.c_str()); + NH_Debug(NH_DEBUG_TAG, "[%" PRId64 "] NextHop %s marked unavailable, h->available=%s", sm_id, h->hostname.c_str(), + (h->available) ? "true" : "false"); + } + break; + } +} diff --git a/plugins/experimental/nexthop_strategy_consistenthash/healthstatus.h b/plugins/experimental/nexthop_strategy_consistenthash/healthstatus.h new file mode 100644 index 00000000000..eb221ec63d7 --- /dev/null +++ b/plugins/experimental/nexthop_strategy_consistenthash/healthstatus.h @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include "tscpp/api/nexthop.h" + +struct HostRecord; + +class NextHopHealthStatus +{ +public: + void insert(std::vector> &hosts); + void markNextHop(TSHttpTxn txn, const char *hostname, const int port, const NHCmd status, const time_t now = 0); + NextHopHealthStatus(){}; + +private: + std::unordered_map> host_map; +}; diff --git a/plugins/experimental/nexthop_strategy_consistenthash/plugin_consistenthash.cc b/plugins/experimental/nexthop_strategy_consistenthash/plugin_consistenthash.cc new file mode 100644 index 00000000000..581d5c7ab00 --- /dev/null +++ b/plugins/experimental/nexthop_strategy_consistenthash/plugin_consistenthash.cc @@ -0,0 +1,133 @@ +/* + * 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 "strategy.h" +#include "consistenthash.h" +#include "consistenthash_config.h" +#include "util.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "tscore/HashSip.h" +#include "tscore/ConsistentHash.h" +#include "tscore/ink_assert.h" +#include "ts/ts.h" +#include "ts/remap.h" +#include "tscpp/api/nexthop.h" +#include "tscpp/api/parentresult.h" + +void +TSRemapDeleteInstance(void *ih) +{ + std::string *config_file_path = static_cast(ih); + delete config_file_path; +} + +TSRemapStatus +TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri) +{ + return TSREMAP_NO_REMAP; +} + +// TODO figure out why TSRemapInitStrategy needs this, but nothing else does. +#ifdef __cplusplus +extern "C" { +#endif + +TSReturnCode +TSRemapInitStrategy(TSNextHopSelectionStrategy *&strategy, void *ih, char *errbuf, int errbuf_size) +{ + NH_Debug(NH_DEBUG_TAG, "%s TSRemapInitStrategy called.", PLUGIN_NAME); + if (ih == nullptr) { + NH_Debug(NH_DEBUG_TAG, "%s TSRemapInitStrategy called with nullptr, returning null strategy.", PLUGIN_NAME); + strategy = nullptr; + return TS_SUCCESS; // TODO determine if we should return TS_ERROR here? + } + strategy = static_cast(ih); + NH_Debug(NH_DEBUG_TAG, "%s is successfully initialized.", PLUGIN_NAME); + return TS_SUCCESS; +} + +#ifdef __cplusplus +} +#endif + +TSReturnCode +TSRemapInit(TSRemapInterface *api_info, char *errbuf, int errbuf_size) +{ + // TODO add ATS API Version check here, to bail if ATS doesn't support the version necessary for strategy plugins + + if (!api_info) { + strncpy(errbuf, "[tsstrategy_init] - Invalid TSRemapInterface argument", errbuf_size - 1); + return TS_ERROR; + } + + if (api_info->tsremap_version < TSREMAP_VERSION) { + snprintf(errbuf, errbuf_size, "[TSStrategyInit] - Incorrect API version %ld.%ld", api_info->tsremap_version >> 16, + (api_info->tsremap_version & 0xffff)); + return TS_ERROR; + } + + NH_Debug(NH_DEBUG_TAG, "%s is successfully initialized.", PLUGIN_NAME); + return TS_SUCCESS; +} + +TSReturnCode +TSRemapNewInstance(int argc, char *argv[], void **ih, char *errbuff, int errbuff_size) +{ + NH_Debug(NH_DEBUG_TAG, "%s TSRemapNewInstance called.", PLUGIN_NAME); + for (int i = 0; i < argc; ++i) { + NH_Debug(NH_DEBUG_TAG, "%s TSRemapNewInstance arg %d '%s'", PLUGIN_NAME, i, argv[i]); + } + + if (argc < 3) { + NH_Error("insufficient number of arguments, %d, no config file argument.", argc); + return TS_ERROR; + } + + if (argc < 3) { + NH_Error("too many arguments, %d, only expected config file argument. Ignoring the rest!", argc); + } + + const char *config_file_path = argv[2]; + + NH_Debug(NH_DEBUG_TAG, "%s TSRemapInitStrategy called with path '%s'", PLUGIN_NAME, config_file_path); + + TSNextHopSelectionStrategy *strategy = createStrategyFromFile(config_file_path, PLUGIN_NAME); + if (strategy == nullptr) { + NH_Debug(NH_DEBUG_TAG, "%s failed to create strategy.", PLUGIN_NAME); + *ih = nullptr; + return TS_ERROR; + } + NH_Debug(NH_DEBUG_TAG, "%s successfully created strategy.", PLUGIN_NAME); + *ih = static_cast(strategy); + return TS_SUCCESS; +} diff --git a/plugins/experimental/nexthop_strategy_consistenthash/strategy.cc b/plugins/experimental/nexthop_strategy_consistenthash/strategy.cc new file mode 100644 index 00000000000..897e6c4029b --- /dev/null +++ b/plugins/experimental/nexthop_strategy_consistenthash/strategy.cc @@ -0,0 +1,346 @@ +/* + * 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 "strategy.h" +#include "consistenthash.h" +#include "util.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "tscore/HashSip.h" +#include "tscore/ConsistentHash.h" +#include "tscore/ink_assert.h" +#include "ts/ts.h" +#include "ts/remap.h" +#include "tscpp/api/nexthop.h" +#include "tscpp/api/parentresult.h" + +// +// NextHopSelectionStrategy.cc +// + +// ring mode strings +constexpr std::string_view alternate_rings = "alternate_ring"; +constexpr std::string_view exhaust_rings = "exhaust_ring"; + +// health check strings +constexpr std::string_view active_health_check = "active"; +constexpr std::string_view passive_health_check = "passive"; + +NextHopSelectionStrategy::NextHopSelectionStrategy(const std::string_view &name) +{ + strategy_name = name; +} + +// +// parse out the data for this strategy. +// +bool +NextHopSelectionStrategy::Init(const YAML::Node &n) +{ + NH_Debug(NH_DEBUG_TAG, "calling Init()"); + + try { + if (n["scheme"]) { + auto scheme_val = n["scheme"].Scalar(); + if (scheme_val == "http") { + scheme = NH_SCHEME_HTTP; + } else if (scheme_val == "https") { + scheme = NH_SCHEME_HTTPS; + } else { + scheme = NH_SCHEME_NONE; + NH_Note("Invalid 'scheme' value, '%s', for the strategy named '%s', setting to NH_SCHEME_NONE", scheme_val.c_str(), + strategy_name.c_str()); + } + } + + // go_direct config. + if (n["go_direct"]) { + go_direct = n["go_direct"].as(); + } + + // parent_is_proxy config. + if (n["parent_is_proxy"]) { + parent_is_proxy = n["parent_is_proxy"].as(); + } + + // ignore_self_detect + if (n["ignore_self_detect"]) { + ignore_self_detect = n["ignore_self_detect"].as(); + } + + // failover node. + YAML::Node failover_node; + if (n["failover"]) { + failover_node = n["failover"]; + if (failover_node["ring_mode"]) { + auto ring_mode_val = failover_node["ring_mode"].Scalar(); + if (ring_mode_val == alternate_rings) { + ring_mode = NH_ALTERNATE_RING; + } else if (ring_mode_val == exhaust_rings) { + ring_mode = NH_EXHAUST_RING; + } else { + ring_mode = NH_ALTERNATE_RING; + NH_Note("Invalid 'ring_mode' value, '%s', for the strategy named '%s', using default '%s'.", ring_mode_val.c_str(), + strategy_name.c_str(), alternate_rings.data()); + } + } + if (failover_node["max_simple_retries"]) { + max_simple_retries = failover_node["max_simple_retries"].as(); + } + + YAML::Node resp_codes_node; + if (failover_node["response_codes"]) { + resp_codes_node = failover_node["response_codes"]; + if (resp_codes_node.Type() != YAML::NodeType::Sequence) { + NH_Error("Error in the response_codes definition for the strategy named '%s', skipping response_codes.", + strategy_name.c_str()); + } else { + for (unsigned int k = 0; k < resp_codes_node.size(); ++k) { + auto code = resp_codes_node[k].as(); + if (code > 300 && code < 599) { + resp_codes.add(code); + } else { + NH_Note("Skipping invalid response code '%d' for the strategy named '%s'.", code, strategy_name.c_str()); + } + } + resp_codes.sort(); + } + } + YAML::Node health_check_node; + if (failover_node["health_check"]) { + health_check_node = failover_node["health_check"]; + if (health_check_node.Type() != YAML::NodeType::Sequence) { + NH_Error("Error in the health_check definition for the strategy named '%s', skipping health_checks.", + strategy_name.c_str()); + } else { + for (auto it = health_check_node.begin(); it != health_check_node.end(); ++it) { + auto health_check = it->as(); + if (health_check.compare(active_health_check) == 0) { + health_checks.active = true; + } + if (health_check.compare(passive_health_check) == 0) { + health_checks.passive = true; + } + } + } + } + } + + // parse and load the host data + YAML::Node groups_node; + if (n["groups"]) { + groups_node = n["groups"]; + // a groups list is required. + if (groups_node.Type() != YAML::NodeType::Sequence) { + throw std::invalid_argument("Invalid groups definition, expected a sequence, '" + strategy_name + "' cannot be loaded."); + } else { + uint32_t grp_size = groups_node.size(); + if (grp_size > TS_MAX_GROUP_RINGS) { + NH_Note("the groups list exceeds the maximum of %d for the strategy '%s'. Only the first %d groups will be configured.", + TS_MAX_GROUP_RINGS, strategy_name.c_str(), TS_MAX_GROUP_RINGS); + groups = TS_MAX_GROUP_RINGS; + } else { + groups = groups_node.size(); + } + // resize the hosts vector. + host_groups.reserve(groups); + // loop through the groups + for (unsigned int grp = 0; grp < groups; ++grp) { + YAML::Node hosts_list = groups_node[grp]; + + // a list of hosts is required. + if (hosts_list.Type() != YAML::NodeType::Sequence) { + throw std::invalid_argument("Invalid hosts definition, expected a sequence, '" + strategy_name + "' cannot be loaded."); + } else { + // loop through the hosts list. + std::vector> hosts_inner; + + for (unsigned int hst = 0; hst < hosts_list.size(); ++hst) { + std::shared_ptr host_rec = std::make_shared(hosts_list[hst].as()); + host_rec->group_index = grp; + host_rec->host_index = hst; + if (TSHostnameIsSelf(host_rec->hostname.c_str()) == TS_SUCCESS) { + TSHostStatusSet(host_rec->hostname.c_str(), host_rec->hostname.size(), TSHostStatus::TS_HOST_STATUS_DOWN, 0, + (unsigned int)TS_HOST_STATUS_SELF_DETECT); + } + hosts_inner.push_back(std::move(host_rec)); + num_parents++; + } + passive_health.insert(hosts_inner); + host_groups.push_back(std::move(hosts_inner)); + } + } + } + } + } catch (std::exception &ex) { + NH_Note("Error parsing the strategy named '%s' due to '%s', this strategy will be ignored.", strategy_name.c_str(), ex.what()); + return false; + } + + return true; +} + +void +NextHopSelectionStrategy::markNextHop(TSHttpTxn txnp, const char *hostname, const int port, const NHCmd status, const time_t now) +{ + NH_Debug(NH_DEBUG_TAG, "nhplugin markNextHop calling"); + return passive_health.markNextHop(txnp, hostname, port, status, now); +} + +bool +NextHopSelectionStrategy::nextHopExists(TSHttpTxn txnp) +{ + NH_Debug(NH_DEBUG_TAG, "nhplugin nextHopExists calling"); + + const int64_t sm_id = TSHttpTxnIdGet(txnp); + + for (uint32_t gg = 0; gg < groups; gg++) { + for (uint32_t hh = 0; hh < host_groups[gg].size(); hh++) { + HostRecord *p = host_groups[gg][hh].get(); + if (p->available) { + NH_Debug(NH_DEBUG_TAG, "[%" PRIu64 "] found available next hop %s", sm_id, p->hostname.c_str()); + return true; + } + } + } + return false; +} + +bool +NextHopSelectionStrategy::responseIsRetryable(unsigned int current_retry_attempts, TSHttpStatus response_code) +{ + return this->resp_codes.contains(response_code) && current_retry_attempts < this->max_simple_retries && + current_retry_attempts < this->num_parents; +} + +bool +NextHopSelectionStrategy::onFailureMarkParentDown(TSHttpStatus response_code) +{ + return static_cast(response_code) >= 500 && static_cast(response_code) <= 599; +} + +bool +NextHopSelectionStrategy::goDirect() +{ + NH_Debug(NH_DEBUG_TAG, "nhplugin goDirect calling"); + return this->go_direct; +} + +bool +NextHopSelectionStrategy::parentIsProxy() +{ + NH_Debug(NH_DEBUG_TAG, "nhplugin parentIsProxy calling"); + return this->parent_is_proxy; +} + +namespace YAML +{ +template <> struct convert { + static bool + decode(const Node &node, HostRecord &nh) + { + YAML::Node nd; + bool merge_tag_used = false; + + // check for YAML merge tag. + if (node["<<"]) { + nd = node["<<"]; + merge_tag_used = true; + } else { + nd = node; + } + + // lookup the hostname + if (nd["host"]) { + nh.hostname = nd["host"].Scalar(); + } else { + throw std::invalid_argument("Invalid host definition, missing host name."); + } + + // lookup the port numbers supported by this host. + YAML::Node proto = nd["protocol"]; + + if (proto.Type() != YAML::NodeType::Sequence) { + throw std::invalid_argument("Invalid host protocol definition, expected a sequence."); + } else { + for (unsigned int ii = 0; ii < proto.size(); ii++) { + YAML::Node protocol_node = proto[ii]; + std::shared_ptr pr = std::make_shared(protocol_node.as()); + nh.protocols.push_back(std::move(pr)); + } + } + + // get the host's weight + YAML::Node weight; + if (merge_tag_used) { + weight = node["weight"]; + nh.weight = weight.as(); + } else if ((weight = nd["weight"])) { + nh.weight = weight.as(); + } else { + NH_Note("No weight is defined for the host '%s', using default 1.0", nh.hostname.data()); + nh.weight = 1.0; + } + + // get the host's optional hash_string + YAML::Node hash; + if ((hash = nd["hash_string"])) { + nh.hash_string = hash.Scalar(); + } + + return true; + } +}; + +template <> struct convert { + static bool + decode(const Node &node, NHProtocol &nh) + { + if (node["scheme"]) { + if (node["scheme"].Scalar() == "http") { + nh.scheme = NH_SCHEME_HTTP; + } else if (node["scheme"].Scalar() == "https") { + nh.scheme = NH_SCHEME_HTTPS; + } else { + nh.scheme = NH_SCHEME_NONE; + } + } + if (node["port"]) { + nh.port = node["port"].as(); + } + if (node["health_check_url"]) { + nh.health_check_url = node["health_check_url"].Scalar(); + } + return true; + } +}; +}; // namespace YAML diff --git a/plugins/experimental/nexthop_strategy_consistenthash/strategy.h b/plugins/experimental/nexthop_strategy_consistenthash/strategy.h new file mode 100644 index 00000000000..c572212dbc7 --- /dev/null +++ b/plugins/experimental/nexthop_strategy_consistenthash/strategy.h @@ -0,0 +1,234 @@ +/* + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tscore/ConsistentHash.h" +#include "ts/ts.h" +#include "tscpp/api/nexthop.h" +#include "ts/remap.h" +#include "healthstatus.h" + +// TODO rename, move to respective sub-plugins +#define PLUGIN_NAME "nexthop_strategy_consistenthash.so" + +constexpr const char *NH_DEBUG_TAG = "plugin_nexthop"; + +#define NH_Debug(tag, fmt, ...) TSDebug(tag, "[%s:%d]: " fmt, __FILE__, __LINE__, ##__VA_ARGS__) +#define NH_Error(fmt, ...) TSError("(%s) [%s:%d]: " fmt, PLUGIN_NAME, __FILE__, __LINE__, ##__VA_ARGS__) +#define NH_Note(fmt, ...) TSDebug(NH_DEBUG_TAG, "[%s:%d]: " fmt, __FILE__, __LINE__, ##__VA_ARGS__) + +constexpr const char *policy_strings[] = {"NH_UNDEFINED", "NH_FIRST_LIVE", "NH_RR_STRICT", + "NH_RR_IP", "NH_RR_LATCHED", "NH_CONSISTENT_HASH"}; + +enum NHPolicyType { + NH_UNDEFINED = 0, + NH_FIRST_LIVE, // first available nexthop + NH_RR_STRICT, // strict round robin + NH_RR_IP, // round robin by client ip. + NH_RR_LATCHED, // latched to available next hop. + NH_CONSISTENT_HASH, // consistent hashing strategy. + NH_PLUGIN, // hashing strategy is a plugin +}; + +enum NHSchemeType { NH_SCHEME_NONE = 0, NH_SCHEME_HTTP, NH_SCHEME_HTTPS }; + +enum NHRingMode { NH_ALTERNATE_RING = 0, NH_EXHAUST_RING }; + +// response codes container +struct ResponseCodes { + ResponseCodes(){}; + std::vector codes; + void + add(short code) + { + codes.push_back(code); + } + bool + contains(short code) + { + return std::binary_search(codes.begin(), codes.end(), code); + } + void + sort() + { + std::sort(codes.begin(), codes.end()); + } +}; + +struct HealthChecks { + bool active = false; + bool passive = false; +}; + +struct NHProtocol { + NHSchemeType scheme; + uint32_t port; + std::string health_check_url; +}; + +struct HostRecord : ATSConsistentHashNode { + std::mutex _mutex; + std::string hostname; + time_t failedAt; + uint32_t failCount; + time_t upAt; + float weight; + std::string hash_string; + int host_index; + int group_index; + std::vector> protocols; + + // construct without locking the _mutex. + HostRecord() + { + hostname = ""; + failedAt = 0; + failCount = 0; + upAt = 0; + weight = 0; + hash_string = ""; + host_index = -1; + group_index = -1; + available = true; + } + + // copy constructor to avoid copying the _mutex. + HostRecord(const HostRecord &o) + { + hostname = o.hostname; + failedAt = o.failedAt; + failCount = o.failCount; + upAt = o.upAt; + weight = o.weight; + hash_string = o.hash_string; + host_index = -1; + group_index = -1; + available = true; + protocols = o.protocols; + } + + // assign without copying the _mutex. + HostRecord & + operator=(const HostRecord &o) + { + hostname = o.hostname; + failedAt = o.failedAt; + upAt = o.upAt; + weight = o.weight; + hash_string = o.hash_string; + host_index = o.host_index; + group_index = o.group_index; + available = o.available; + protocols = o.protocols; + return *this; + } + + // locks the record when marking this host down. + void + set_unavailable() + { + if (available) { + std::lock_guard lock(_mutex); + failedAt = time(nullptr); + available = false; + } + } + + // locks the record when marking this host up. + void + set_available() + { + if (!available) { + std::lock_guard lock(_mutex); + failedAt = 0; + failCount = 0; + upAt = time(nullptr); + available = true; + } + } + + int + getPort(NHSchemeType scheme) const + { + int port = 0; + for (uint32_t i = 0; i < protocols.size(); i++) { + if (protocols[i]->scheme == scheme) { + port = protocols[i]->port; + break; + } + } + return port; + } + + static std::string + makeHostPort(const std::string &hostname, const int port) + { + return hostname + ":" + std::to_string(port); + } + + std::string + getHostPort(const int port) const + { + return makeHostPort(this->hostname, port); + } +}; + +class NextHopSelectionStrategy : public TSNextHopSelectionStrategy +{ +public: + NextHopSelectionStrategy(); + NextHopSelectionStrategy(const std::string_view &name); + virtual ~NextHopSelectionStrategy(){}; + bool Init(const YAML::Node &n); + + virtual void findNextHop(TSHttpTxn txnp, time_t now = 0) = 0; + virtual void markNextHop(TSHttpTxn txnp, const char *hostname, const int port, const NHCmd status, const time_t now = 0); + virtual bool nextHopExists(TSHttpTxn txnp); + virtual bool responseIsRetryable(unsigned int current_retry_attempts, TSHttpStatus response_code); + virtual bool onFailureMarkParentDown(TSHttpStatus response_code); + virtual bool goDirect(); + virtual bool parentIsProxy(); + +protected: + std::string strategy_name; + bool go_direct = true; + bool parent_is_proxy = true; + bool ignore_self_detect = false; + NHSchemeType scheme = NH_SCHEME_NONE; + NHRingMode ring_mode = NH_ALTERNATE_RING; + ResponseCodes resp_codes; + HealthChecks health_checks; + NextHopHealthStatus passive_health; + std::vector>> host_groups; + uint32_t max_simple_retries = 1; + uint32_t groups = 0; + uint32_t grp_index = 0; + uint32_t hst_index = 0; + uint32_t num_parents = 0; + uint32_t distance = 0; // index into the strategies list. +}; diff --git a/plugins/experimental/nexthop_strategy_consistenthash/util.h b/plugins/experimental/nexthop_strategy_consistenthash/util.h new file mode 100644 index 00000000000..fa050b009bc --- /dev/null +++ b/plugins/experimental/nexthop_strategy_consistenthash/util.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#pragma once + +// ScopedFreeMLoc frees the given TSMLoc with TSHandleMLocRelease(buf, parent, *m) when it goes out of scope. +// The lifetime of buf and parent must exceed this. +// The parent must be allocated before this (if it exists). +// The parent may be TS_NULL_MLOC. +// If mloc is set to TS_NULL_MLOC or never allocated, it will not be freed. +struct ScopedFreeMLoc { + ScopedFreeMLoc(TSMBuffer *_buf, TSMLoc _parent, TSMLoc *_mloc) : mloc(_mloc), parent(_parent), buf(_buf){}; + ~ScopedFreeMLoc() + { + if (*mloc != TS_NULL_MLOC) { + TSHandleMLocRelease(*buf, parent, *mloc); + } + }; + +private: + TSMLoc *mloc; + TSMLoc parent; + TSMBuffer *buf; +}; + +// StrVal is a string as returned by TSUrlStringGet and other TS API functions. +// Zeroes on initialization. +struct StrVal { + StrVal() : ptr(nullptr), len(0){}; + char *ptr; + int len; +}; + +// ScopedFreeStrVal frees the ptr in the given Strval when it goes out of scope. +struct ScopedFreeStrVal { + ScopedFreeStrVal(StrVal *_strval) : strval(_strval){}; + ~ScopedFreeStrVal() { TSfree(strval->ptr); }; + +private: + StrVal *strval; +}; diff --git a/proxy/HostStatus.h b/proxy/HostStatus.h index 2cc18130a7c..6ed571f8f92 100644 --- a/proxy/HostStatus.h +++ b/proxy/HostStatus.h @@ -41,15 +41,12 @@ // host_status stats prefix. static const std::string stat_prefix = "proxy.process.host_status."; -enum HostStatus_t { - HOST_STATUS_INIT, - HOST_STATUS_DOWN, - HOST_STATUS_UP, -}; - static const constexpr char *HostStatusNames[3] = {"HOST_STATUS_INIT", "HOST_STATUS_DOWN", "HOST_STATUS_UP"}; static const constexpr char *ReasonStatus[2] = {"UP", "DOWN"}; +/* MUST match apidefs.h.in TSHostStatusReason + * If a value is added here, it MUST be added there with the same value. + */ struct Reason { static constexpr const unsigned int ACTIVE = 0x1; static constexpr const unsigned int LOCAL = 0x2; @@ -97,7 +94,7 @@ struct Reason { // host status POD struct HostStatRec { - HostStatus_t status; + TSHostStatus status; unsigned int reasons; // time the host was marked down for a given reason. time_t active_marked_down; @@ -186,12 +183,13 @@ struct HostStatus { static HostStatus instance; return instance; } - void setHostStatus(const char *name, const HostStatus_t status, const unsigned int down_time, const unsigned int reason); - HostStatRec *getHostStatus(const char *name); - void createHostStat(const char *name, const char *data = nullptr); + void setHostStatus(const std::string_view name, const TSHostStatus status, const unsigned int down_time, + const unsigned int reason); + HostStatRec *getHostStatus(const std::string_view name); + void createHostStat(const std::string_view name, const char *data = nullptr); void loadHostStatusFromStats(); - void loadRecord(std::string &name, HostStatRec &h); - RecErrT getHostStat(std::string &stat_name, char *buf, unsigned int buf_len); + void loadRecord(const std::string_view name, HostStatRec &h); + RecErrT getHostStat(const std::string &stat_name, char *buf, unsigned int buf_len); private: HostStatus(); @@ -199,7 +197,7 @@ struct HostStatus { HostStatus &operator=(HostStatus const &) = delete; // next hop status, key is hostname or ip string, data is HostStatRec - std::unordered_map hosts_statuses; + std::map> hosts_statuses; ink_rwlock host_status_rwlock; }; diff --git a/proxy/ParentConsistentHash.cc b/proxy/ParentConsistentHash.cc index 568fba97d92..580f9067d78 100644 --- a/proxy/ParentConsistentHash.cc +++ b/proxy/ParentConsistentHash.cc @@ -142,7 +142,7 @@ ParentConsistentHash::selectParent(bool first_call, ParentResult *result, Reques uint32_t last_lookup; pRecord *prtmp = nullptr, *pRec = nullptr; HostStatus &pStatus = HostStatus::instance(); - HostStatus_t host_stat = HostStatus_t::HOST_STATUS_INIT; + TSHostStatus host_stat = TSHostStatus::TS_HOST_STATUS_INIT; Debug("parent_select", "ParentConsistentHash::%s(): Using a consistent hash parent selection strategy.", __func__); ink_assert(numParents(result) > 0 || result->rec->go_direct == true); @@ -150,9 +150,9 @@ ParentConsistentHash::selectParent(bool first_call, ParentResult *result, Reques // Should only get into this state if we are supposed to go direct. if (parents[PRIMARY] == nullptr && parents[SECONDARY] == nullptr) { if (result->rec->go_direct == true && result->rec->parent_is_proxy == true) { - result->result = PARENT_DIRECT; + result->result = TS_PARENT_DIRECT; } else { - result->result = PARENT_FAIL; + result->result = TS_PARENT_FAIL; } result->hostname = nullptr; result->port = 0; @@ -173,7 +173,7 @@ ParentConsistentHash::selectParent(bool first_call, ParentResult *result, Reques last_lookup = PRIMARY; break; case 3: - if (result->first_choice_status == HOST_STATUS_DOWN && chash[SECONDARY] != nullptr) { + if (result->first_choice_status == TS_HOST_STATUS_DOWN && chash[SECONDARY] != nullptr) { last_lookup = SECONDARY; } else { last_lookup = PRIMARY; @@ -203,7 +203,7 @@ ParentConsistentHash::selectParent(bool first_call, ParentResult *result, Reques } if (firstCall) { HostStatRec *hst = (pRec) ? pStatus.getHostStatus(pRec->hostname) : nullptr; - result->first_choice_status = (hst) ? hst->status : HostStatus_t::HOST_STATUS_UP; + result->first_choice_status = (hst) ? hst->status : TSHostStatus::TS_HOST_STATUS_UP; break; } } while (pRec && !firstCall && last_lookup == PRIMARY && strcmp(pRec->hostname, result->hostname) == 0); @@ -216,19 +216,19 @@ ParentConsistentHash::selectParent(bool first_call, ParentResult *result, Reques // didn't find a parent or the parent is marked unavailable or the parent is marked down HostStatRec *hst = (pRec) ? pStatus.getHostStatus(pRec->hostname) : nullptr; - host_stat = (hst) ? hst->status : HostStatus_t::HOST_STATUS_UP; + host_stat = (hst) ? hst->status : TSHostStatus::TS_HOST_STATUS_UP; // if the config ignore_self_detect is set to true and the host is down due to SELF_DETECT reason // ignore the down status and mark it as avaialble - if ((pRec && result->rec->ignore_self_detect) && (hst && hst->status == HOST_STATUS_DOWN)) { + if ((pRec && result->rec->ignore_self_detect) && (hst && hst->status == TS_HOST_STATUS_DOWN)) { if (hst->reasons == Reason::SELF_DETECT) { - host_stat = HOST_STATUS_UP; + host_stat = TS_HOST_STATUS_UP; } } - if (!pRec || (pRec && !pRec->available) || host_stat == HOST_STATUS_DOWN) { + if (!pRec || (pRec && !pRec->available) || host_stat == TS_HOST_STATUS_DOWN) { do { // check if the host is retryable. It's retryable if the retry window has elapsed - // and the global host status is HOST_STATUS_UP - if (pRec && !pRec->available && host_stat == HOST_STATUS_UP) { + // and the global host status is TS_HOST_STATUS_UP + if (pRec && !pRec->available && host_stat == TS_HOST_STATUS_UP) { Debug("parent_select", "Parent.failedAt = %u, retry = %u, xact_start = %u", (unsigned int)pRec->failedAt, (unsigned int)retry_time, (unsigned int)request_info->xact_start); if ((pRec->failedAt + retry_time) < request_info->xact_start) { @@ -237,7 +237,7 @@ ParentConsistentHash::selectParent(bool first_call, ParentResult *result, Reques result->last_parent = pRec->idx; result->last_lookup = last_lookup; result->retry = parentRetry; - result->result = PARENT_SPECIFIED; + result->result = TS_PARENT_SPECIFIED; Debug("parent_select", "Down parent %s is now retryable, marked it available.", pRec->hostname); break; } @@ -254,7 +254,7 @@ ParentConsistentHash::selectParent(bool first_call, ParentResult *result, Reques } break; case 3: - if (result->first_choice_status == HOST_STATUS_DOWN) { + if (result->first_choice_status == TS_HOST_STATUS_DOWN) { if (chash[SECONDARY] != nullptr && !wrap_around[SECONDARY]) { last_lookup = SECONDARY; } else if (!wrap_around[PRIMARY]) { @@ -296,15 +296,15 @@ ParentConsistentHash::selectParent(bool first_call, ParentResult *result, Reques break; } hst = (pRec) ? pStatus.getHostStatus(pRec->hostname) : nullptr; - host_stat = (hst) ? hst->status : HostStatus_t::HOST_STATUS_UP; + host_stat = (hst) ? hst->status : TSHostStatus::TS_HOST_STATUS_UP; // if the config ignore_self_detect is set to true and the host is down due to SELF_DETECT reason // ignore the down status and mark it as avaialble - if ((pRec && result->rec->ignore_self_detect) && (hst && hst->status == HOST_STATUS_DOWN)) { + if ((pRec && result->rec->ignore_self_detect) && (hst && hst->status == TS_HOST_STATUS_DOWN)) { if (hst->reasons == Reason::SELF_DETECT) { - host_stat = HOST_STATUS_UP; + host_stat = TS_HOST_STATUS_UP; } } - } while (!pRec || !pRec->available || host_stat == HOST_STATUS_DOWN); + } while (!pRec || !pRec->available || host_stat == TS_HOST_STATUS_DOWN); } Debug("parent_select", "Additional parent lookups: %d", lookups); @@ -315,16 +315,16 @@ ParentConsistentHash::selectParent(bool first_call, ParentResult *result, Reques // use the available or marked for retry parent. hst = (pRec) ? pStatus.getHostStatus(pRec->hostname) : nullptr; - host_stat = (hst) ? hst->status : HostStatus_t::HOST_STATUS_UP; + host_stat = (hst) ? hst->status : TSHostStatus::TS_HOST_STATUS_UP; // if the config ignore_self_detect is set to true and the host is down due to SELF_DETECT reason // ignore the down status and mark it as avaialble - if ((pRec && result->rec->ignore_self_detect) && (hst && hst->status == HOST_STATUS_DOWN)) { + if ((pRec && result->rec->ignore_self_detect) && (hst && hst->status == TS_HOST_STATUS_DOWN)) { if (hst->reasons == Reason::SELF_DETECT) { - host_stat = HOST_STATUS_UP; + host_stat = TS_HOST_STATUS_UP; } } - if (pRec && host_stat == HOST_STATUS_UP && (pRec->available || result->retry)) { - result->result = PARENT_SPECIFIED; + if (pRec && host_stat == TS_HOST_STATUS_UP && (pRec->available || result->retry)) { + result->result = TS_PARENT_SPECIFIED; result->hostname = pRec->hostname; result->port = pRec->port; result->last_parent = pRec->idx; @@ -335,9 +335,9 @@ ParentConsistentHash::selectParent(bool first_call, ParentResult *result, Reques Debug("parent_select", "Chosen parent: %s.%d", result->hostname, result->port); } else { if (result->rec->go_direct == true && result->rec->parent_is_proxy == true) { - result->result = PARENT_DIRECT; + result->result = TS_PARENT_DIRECT; } else { - result->result = PARENT_FAIL; + result->result = TS_PARENT_FAIL; } result->hostname = nullptr; result->port = 0; @@ -372,8 +372,8 @@ ParentConsistentHash::markParentUp(ParentResult *result) // Make sure that we are being called back with with a // result structure with a parent that is being retried ink_release_assert(result->retry == true); - ink_assert(result->result == PARENT_SPECIFIED); - if (result->result != PARENT_SPECIFIED) { + ink_assert(result->result == TS_PARENT_SPECIFIED); + if (result->result != TS_PARENT_SPECIFIED) { return; } // If we were set through the API we currently have not failover diff --git a/proxy/ParentRoundRobin.cc b/proxy/ParentRoundRobin.cc index 5c955bb2415..731f644443b 100644 --- a/proxy/ParentRoundRobin.cc +++ b/proxy/ParentRoundRobin.cc @@ -63,7 +63,7 @@ ParentRoundRobin::selectParent(bool first_call, ParentResult *result, RequestDat bool parentUp = false; bool parentRetry = false; HostStatus &pStatus = HostStatus::instance(); - HostStatus_t host_stat = HostStatus_t::HOST_STATUS_UP; + TSHostStatus host_stat = TSHostStatus::TS_HOST_STATUS_UP; HttpRequestData *request_info = static_cast(rdata); @@ -76,9 +76,9 @@ ParentRoundRobin::selectParent(bool first_call, ParentResult *result, RequestDat ink_assert(result->rec->go_direct == true); // Could not find a parent if (result->rec->go_direct == true && result->rec->parent_is_proxy == true) { - result->result = PARENT_DIRECT; + result->result = TS_PARENT_DIRECT; } else { - result->result = PARENT_FAIL; + result->result = TS_PARENT_FAIL; } result->hostname = nullptr; @@ -122,9 +122,9 @@ ParentRoundRobin::selectParent(bool first_call, ParentResult *result, RequestDat if (result->rec->go_direct == true) { // Could not find a parent if (result->rec->parent_is_proxy == true) { - result->result = PARENT_DIRECT; + result->result = TS_PARENT_DIRECT; } else { - result->result = PARENT_FAIL; + result->result = TS_PARENT_FAIL; } result->hostname = nullptr; result->port = 0; @@ -137,18 +137,18 @@ ParentRoundRobin::selectParent(bool first_call, ParentResult *result, RequestDat // should be retried do { HostStatRec *hst = pStatus.getHostStatus(parents[cur_index].hostname); - host_stat = (hst) ? hst->status : HostStatus_t::HOST_STATUS_UP; + host_stat = (hst) ? hst->status : TSHostStatus::TS_HOST_STATUS_UP; // if the config ignore_self_detect is set to true and the host is down due to SELF_DETECT reason // ignore the down status and mark it as avaialble - if (result->rec->ignore_self_detect && (hst && hst->status == HOST_STATUS_DOWN)) { + if (result->rec->ignore_self_detect && (hst && hst->status == TS_HOST_STATUS_DOWN)) { if (hst->reasons == Reason::SELF_DETECT) { - host_stat = HOST_STATUS_UP; + host_stat = TS_HOST_STATUS_UP; } } Debug("parent_select", "cur_index: %d, result->start_parent: %d", cur_index, result->start_parent); // DNS ParentOnly inhibits bypassing the parent so always return that t if ((parents[cur_index].failedAt == 0) || (parents[cur_index].failCount < static_cast(fail_threshold))) { - if (host_stat == HOST_STATUS_UP) { + if (host_stat == TS_HOST_STATUS_UP) { Debug("parent_select", "FailThreshold = %d", fail_threshold); Debug("parent_select", "Selecting a parent due to little failCount (faileAt: %u failCount: %d)", (unsigned)parents[cur_index].failedAt, parents[cur_index].failCount); @@ -156,7 +156,7 @@ ParentRoundRobin::selectParent(bool first_call, ParentResult *result, RequestDat } } else { if ((result->wrap_around) || - ((parents[cur_index].failedAt + retry_time) < request_info->xact_start && host_stat == HOST_STATUS_UP)) { + ((parents[cur_index].failedAt + retry_time) < request_info->xact_start && host_stat == TS_HOST_STATUS_UP)) { Debug("parent_select", "Parent[%d].failedAt = %u, retry = %u,xact_start = %" PRId64 " but wrap = %d", cur_index, (unsigned)parents[cur_index].failedAt, retry_time, (int64_t)request_info->xact_start, result->wrap_around); // Reuse the parent @@ -168,9 +168,9 @@ ParentRoundRobin::selectParent(bool first_call, ParentResult *result, RequestDat } } - if (parentUp == true && host_stat != HOST_STATUS_DOWN) { + if (parentUp == true && host_stat != TS_HOST_STATUS_DOWN) { Debug("parent_select", "status for %s: %d", parents[cur_index].hostname, host_stat); - result->result = PARENT_SPECIFIED; + result->result = TS_PARENT_SPECIFIED; result->hostname = parents[cur_index].hostname; result->port = parents[cur_index].port; result->last_parent = cur_index; @@ -184,9 +184,9 @@ ParentRoundRobin::selectParent(bool first_call, ParentResult *result, RequestDat } while (static_cast(cur_index) != result->start_parent); if (result->rec->go_direct == true && result->rec->parent_is_proxy == true) { - result->result = PARENT_DIRECT; + result->result = TS_PARENT_DIRECT; } else { - result->result = PARENT_FAIL; + result->result = TS_PARENT_FAIL; } result->hostname = nullptr; diff --git a/proxy/ParentSelection.cc b/proxy/ParentSelection.cc index 08254a453d8..ce6a0573c8a 100644 --- a/proxy/ParentSelection.cc +++ b/proxy/ParentSelection.cc @@ -48,6 +48,8 @@ static const char *default_var = "proxy.config.http.parent_proxies"; static const char *retry_var = "proxy.config.http.parent_proxy.retry_time"; static const char *threshold_var = "proxy.config.http.parent_proxy.fail_threshold"; +const char *ParentResultStr[] = {"PARENT_UNDEFINED", "PARENT_DIRECT", "PARENT_SPECIFIED", "PARENT_AGENT", "PARENT_FAIL"}; + // // Config Callback Prototypes // @@ -109,7 +111,7 @@ ParentConfigParams::findParent(HttpRequestData *rdata, ParentResult *result, uns // Check to see if the parent was set through the // api if (apiParentExists(rdata)) { - result->result = PARENT_SPECIFIED; + result->result = TS_PARENT_SPECIFIED; result->hostname = rdata->api_info->parent_proxy_name; result->port = rdata->api_info->parent_proxy_port; result->rec = extApiRecord; @@ -133,8 +135,8 @@ ParentConfigParams::findParent(HttpRequestData *rdata, ParentResult *result, uns if (defaultPtr != nullptr) { rec = result->rec = defaultPtr; } else { - result->result = PARENT_DIRECT; - Debug("parent_select", "Returning PARENT_DIRECT (no parents were found)"); + result->result = TS_PARENT_DIRECT; + Debug("parent_select", "Returning TS_PARENT_DIRECT (no parents were found)"); return; } } @@ -146,19 +148,19 @@ ParentConfigParams::findParent(HttpRequestData *rdata, ParentResult *result, uns const char *host = rdata->get_host(); switch (result->result) { - case PARENT_UNDEFINED: - Debug("parent_select", "PARENT_UNDEFINED"); + case TS_PARENT_UNDEFINED: + Debug("parent_select", "TS_PARENT_UNDEFINED"); Debug("parent_select", "Result for %s was %s", host, ParentResultStr[result->result]); break; - case PARENT_FAIL: + case TS_PARENT_FAIL: Debug("parent_select", "PARENT_FAIL"); break; - case PARENT_DIRECT: + case TS_PARENT_DIRECT: Debug("parent_select", "PARENT_DIRECT"); Debug("parent_select", "Result for %s was %s", host, ParentResultStr[result->result]); break; - case PARENT_SPECIFIED: - Debug("parent_select", "PARENT_SPECIFIED"); + case TS_PARENT_SPECIFIED: + Debug("parent_select", "TS_PARENT_SPECIFIED"); Debug("parent_select", "Result for %s was parent %s:%d", host, result->hostname, result->port); break; default: @@ -177,16 +179,16 @@ ParentConfigParams::nextParent(HttpRequestData *rdata, ParentResult *result, uns // Make sure that we are being called back with a // result structure with a parent - ink_assert(result->result == PARENT_SPECIFIED); - if (result->result != PARENT_SPECIFIED) { - result->result = PARENT_FAIL; + ink_assert(result->result == TS_PARENT_SPECIFIED); + if (result->result != TS_PARENT_SPECIFIED) { + result->result = TS_PARENT_FAIL; return; } // If we were set through the API we currently have not failover // so just return fail if (result->is_api_result()) { Debug("parent_select", "Retry result for %s was %s", rdata->get_host(), ParentResultStr[result->result]); - result->result = PARENT_FAIL; + result->result = TS_PARENT_FAIL; return; } Debug("parent_select", "ParentConfigParams::nextParent(): result->r: %d, tablePtr: %p", result->result, tablePtr); @@ -198,19 +200,19 @@ ParentConfigParams::nextParent(HttpRequestData *rdata, ParentResult *result, uns const char *host = rdata->get_host(); switch (result->result) { - case PARENT_UNDEFINED: - Debug("parent_select", "PARENT_UNDEFINED"); + case TS_PARENT_UNDEFINED: + Debug("parent_select", "TS_PARENT_UNDEFINED"); Debug("parent_select", "Retry result for %s was %s", host, ParentResultStr[result->result]); break; - case PARENT_FAIL: + case TS_PARENT_FAIL: Debug("parent_select", "PARENT_FAIL"); Debug("parent_select", "Retry result for %s was %s", host, ParentResultStr[result->result]); break; - case PARENT_DIRECT: + case TS_PARENT_DIRECT: Debug("parent_select", "PARENT_DIRECT"); Debug("parent_select", "Retry result for %s was %s", host, ParentResultStr[result->result]); break; - case PARENT_SPECIFIED: + case TS_PARENT_SPECIFIED: Debug("parent_select", "Retry result for %s was parent %s:%d", host, result->hostname, result->port); break; default: @@ -397,7 +399,7 @@ ParentRecord::PreProcessParents(const char *val, const int line_num, char *buf, continue; } else { Debug("parent_select", "token: %s, matches this machine. Marking down self from parent list at line %d", fqdn, line_num); - hs.setHostStatus(fqdn, HostStatus_t::HOST_STATUS_DOWN, 0, Reason::SELF_DETECT); + hs.setHostStatus(fqdn, TSHostStatus::TS_HOST_STATUS_DOWN, 0, Reason::SELF_DETECT); } } } else { @@ -409,7 +411,7 @@ ParentRecord::PreProcessParents(const char *val, const int line_num, char *buf, } else { Debug("parent_select", "token: %s, matches this machine. Marking down self from parent list at line %d", token, line_num); - hs.setHostStatus(token, HostStatus_t::HOST_STATUS_DOWN, 0, Reason::SELF_DETECT); + hs.setHostStatus(token, TSHostStatus::TS_HOST_STATUS_DOWN, 0, Reason::SELF_DETECT); } } } @@ -814,7 +816,7 @@ ParentRecord::Init(matcher_line *line_info) // is not set in parent.config. Therefore ParentRoundRobin is the default // strategy. If setting go_direct to true, there should be no parent list // in parent.config and ParentRoundRobin::lookup will set parent_result->r - // to PARENT_DIRECT. + // to TS_PARENT_DIRECT. case P_NO_ROUND_ROBIN: case P_STRICT_ROUND_ROBIN: case P_HASH_ROUND_ROBIN: @@ -1106,11 +1108,11 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, // from records.snap as DOWN due to previous testing. // HostStatus &_st = HostStatus::instance(); - _st.setHostStatus("furry", HOST_STATUS_UP, 0, Reason::MANUAL); - _st.setHostStatus("fluffy", HOST_STATUS_UP, 0, Reason::MANUAL); - _st.setHostStatus("frisky", HOST_STATUS_UP, 0, Reason::MANUAL); - _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0, Reason::MANUAL); - _st.setHostStatus("curly", HOST_STATUS_UP, 0, Reason::MANUAL); + _st.setHostStatus("furry", TS_HOST_STATUS_UP, 0, Reason::MANUAL); + _st.setHostStatus("fluffy", TS_HOST_STATUS_UP, 0, Reason::MANUAL); + _st.setHostStatus("frisky", TS_HOST_STATUS_UP, 0, Reason::MANUAL); + _st.setHostStatus("fuzzy", TS_HOST_STATUS_UP, 0, Reason::MANUAL); + _st.setHostStatus("curly", TS_HOST_STATUS_UP, 0, Reason::MANUAL); // Test 1 tbl[0] = '\0'; @@ -1122,9 +1124,9 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, REINIT; br(request, "fruit_basket.net"); FP; - red += verify(result, PARENT_SPECIFIED, "red", 37412); - orange += verify(result, PARENT_SPECIFIED, "orange", 37412); - yellow += verify(result, PARENT_SPECIFIED, "yellow", 37412); + red += verify(result, TS_PARENT_SPECIFIED, "red", 37412); + orange += verify(result, TS_PARENT_SPECIFIED, "orange", 37412); + yellow += verify(result, TS_PARENT_SPECIFIED, "yellow", 37412); } RE(((red == 7) && (orange == 7) && (yellow == 7)), 1); // Test 2 @@ -1137,10 +1139,10 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, REINIT; br(request, "fruit_basket.net"); FP; - g += verify(result, PARENT_SPECIFIED, "green", 4325); - b += verify(result, PARENT_SPECIFIED, "blue", 4325); - i += verify(result, PARENT_SPECIFIED, "indigo", 4325); - v += verify(result, PARENT_SPECIFIED, "violet", 4325); + g += verify(result, TS_PARENT_SPECIFIED, "green", 4325); + b += verify(result, TS_PARENT_SPECIFIED, "blue", 4325); + i += verify(result, TS_PARENT_SPECIFIED, "indigo", 4325); + v += verify(result, TS_PARENT_SPECIFIED, "violet", 4325); } RE((((g == 17) && !b && !i && !v) || (!g && (b == 17) && !i && !v) || (!g && !b && (i == 17) && !v) || (!g && !b && !i && (v == 17))), @@ -1174,19 +1176,19 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, REINIT; br(request, "numeric_host", &ip.sa); FP; - RE(verify(result, PARENT_SPECIFIED, "cat", 37) + verify(result, PARENT_SPECIFIED, "dog", 24), 3); + RE(verify(result, TS_PARENT_SPECIFIED, "cat", 37) + verify(result, TS_PARENT_SPECIFIED, "dog", 24), 3); ats_ip_pton(TEST_IP6_ADDR, &ip.sa); ST(4); REINIT; br(request, "numeric_host", &ip.sa); FP; - RE(verify(result, PARENT_SPECIFIED, "zwoop", 37) + verify(result, PARENT_SPECIFIED, "jMCg", 24), 4); + RE(verify(result, TS_PARENT_SPECIFIED, "zwoop", 37) + verify(result, TS_PARENT_SPECIFIED, "jMCg", 24), 4); // Test 5 ST(5); REINIT; br(request, "www.pilot.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "pilot_net", 80), 5); + RE(verify(result, TS_PARENT_SPECIFIED, "pilot_net", 80), 5); // Test 6 ST(6); REINIT; @@ -1194,21 +1196,21 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, const char *snoopy_dog = "http://www.snoopy.com/"; request->hdr->url_set(snoopy_dog, strlen(snoopy_dog)); FP; - RE(verify(result, PARENT_SPECIFIED, "odie", 80) + verify(result, PARENT_SPECIFIED, "garfield", 80), 5); + RE(verify(result, TS_PARENT_SPECIFIED, "odie", 80) + verify(result, TS_PARENT_SPECIFIED, "garfield", 80), 5); // Test 7 ST(7); REINIT; br(request, "a.rabbit.i.am"); FP; - RE(verify(result, PARENT_SPECIFIED, "amy", 80) + verify(result, PARENT_SPECIFIED, "katie", 80) + - verify(result, PARENT_SPECIFIED, "carissa", 771), + RE(verify(result, TS_PARENT_SPECIFIED, "amy", 80) + verify(result, TS_PARENT_SPECIFIED, "katie", 80) + + verify(result, TS_PARENT_SPECIFIED, "carissa", 771), 6); // Test 6+ BUGBUG needs to be fixed // ST(7); REINIT; // br(request, "www.microsoft.net"); - // FP; RE( verify(result,PARENT_SPECIFIED,"zoo.net",341) + - // verify(result,PARENT_SPECIFIED,"zoo.net",347) + - // verify(result,PARENT_SPECIFIED,"zoo.edu",111) ,7); + // FP; RE( verify(result,TS_PARENT_SPECIFIED,"zoo.net",341) + + // verify(result,TS_PARENT_SPECIFIED,"zoo.net",347) + + // verify(result,TS_PARENT_SPECIFIED,"zoo.edu",111) ,7); // Test 6++ BUGBUG needs to be fixed // ST(7); REINIT; // br(request, "snow.imac.net:2020"); @@ -1224,7 +1226,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, // request->hdr->url_set(u); // ink_assert(request->hdr->url_get()->port_get() == 819); // printf("url: %s\n",request->hdr->url_get()->string_get(0)); - // FP; RE(verify(result,PARENT_SPECIFIED,"genie",80),8); + // FP; RE(verify(result,TS_PARENT_SPECIFIED,"genie",80),8); // Test 7 - N Parent Table tbl[0] = '\0'; T("dest_domain=rabbit.net parent=fuzzy:80,fluffy:80,furry:80,frisky:80 round_robin=strict go_direct=true\n"); @@ -1234,7 +1236,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, REINIT; br(request, "i.am.rabbit.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 7); + RE(verify(result, TS_PARENT_SPECIFIED, "fuzzy", 80), 7); params->markParentDown(result, fail_threshold, retry_time); // Test 9 @@ -1242,44 +1244,44 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, REINIT; br(request, "i.am.rabbit.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 8); + RE(verify(result, TS_PARENT_SPECIFIED, "fluffy", 80), 8); // Test 10 ST(10); REINIT; br(request, "i.am.rabbit.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "furry", 80), 9); + RE(verify(result, TS_PARENT_SPECIFIED, "furry", 80), 9); // Test 11 ST(11); REINIT; br(request, "i.am.rabbit.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "frisky", 80), 10); + RE(verify(result, TS_PARENT_SPECIFIED, "frisky", 80), 10); // restart the loop // Test 12 ST(12); REINIT; br(request, "i.am.rabbit.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 11); + RE(verify(result, TS_PARENT_SPECIFIED, "fluffy", 80), 11); // Test 13 ST(13); REINIT; br(request, "i.am.rabbit.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 12); + RE(verify(result, TS_PARENT_SPECIFIED, "fluffy", 80), 12); // Test 14 ST(14); REINIT; br(request, "i.am.rabbit.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "furry", 80), 13); + RE(verify(result, TS_PARENT_SPECIFIED, "furry", 80), 13); // Test 15 ST(15); REINIT; br(request, "i.am.rabbit.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "frisky", 80), 14); + RE(verify(result, TS_PARENT_SPECIFIED, "frisky", 80), 14); params->markParentDown(result, fail_threshold, retry_time); // restart the loop @@ -1289,44 +1291,44 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, REINIT; br(request, "i.am.rabbit.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 15); + RE(verify(result, TS_PARENT_SPECIFIED, "fluffy", 80), 15); // Test 17 ST(17); REINIT; br(request, "i.am.rabbit.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 16); + RE(verify(result, TS_PARENT_SPECIFIED, "fluffy", 80), 16); // Test 18 ST(18); REINIT; br(request, "i.am.rabbit.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "furry", 80), 17); + RE(verify(result, TS_PARENT_SPECIFIED, "furry", 80), 17); // Test 19 ST(19); REINIT; br(request, "i.am.rabbit.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 18); + RE(verify(result, TS_PARENT_SPECIFIED, "fluffy", 80), 18); // restart the loop // Test 20 ST(20); REINIT; br(request, "i.am.rabbit.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 19); + RE(verify(result, TS_PARENT_SPECIFIED, "fluffy", 80), 19); // Test 21 ST(21); REINIT; br(request, "i.am.rabbit.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 20); + RE(verify(result, TS_PARENT_SPECIFIED, "fluffy", 80), 20); // Test 22 ST(22); REINIT; br(request, "i.am.rabbit.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "furry", 80), 21); + RE(verify(result, TS_PARENT_SPECIFIED, "furry", 80), 21); params->markParentDown(result, fail_threshold, retry_time); // Test 23 - 32 @@ -1335,7 +1337,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, REINIT; br(request, "i.am.rabbit.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), i); + RE(verify(result, TS_PARENT_SPECIFIED, "fluffy", 80), i); } params->markParentDown(result, 1, 5); // now they're all down @@ -1346,7 +1348,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, REINIT; br(request, "i.am.rabbit.net"); FP; - RE(verify(result, PARENT_DIRECT, nullptr, 0), i); + RE(verify(result, TS_PARENT_DIRECT, nullptr, 0), i); } // sleep(5); // parents should come back up; they don't @@ -1364,16 +1366,16 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, sleep(1); switch (i % 4) { case 0: - RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), i); + RE(verify(result, TS_PARENT_SPECIFIED, "fuzzy", 80), i); break; case 1: - RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), i); + RE(verify(result, TS_PARENT_SPECIFIED, "fluffy", 80), i); break; case 2: - RE(verify(result, PARENT_SPECIFIED, "furry", 80), i); + RE(verify(result, TS_PARENT_SPECIFIED, "furry", 80), i); break; case 3: - RE(verify(result, PARENT_SPECIFIED, "frisky", 80), i); + RE(verify(result, TS_PARENT_SPECIFIED, "frisky", 80), i); break; default: ink_assert(0); @@ -1390,7 +1392,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 173); + RE(verify(result, TS_PARENT_SPECIFIED, "fuzzy", 80), 173); params->markParentDown(result, fail_threshold, retry_time); // fuzzy is down. // Test 174 @@ -1399,7 +1401,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "frisky", 80), 174); + RE(verify(result, TS_PARENT_SPECIFIED, "frisky", 80), 174); params->markParentDown(result, fail_threshold, retry_time); // frisky is down. @@ -1409,7 +1411,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "furry", 80), 175); + RE(verify(result, TS_PARENT_SPECIFIED, "furry", 80), 175); params->markParentDown(result, fail_threshold, retry_time); // frisky is down. @@ -1419,7 +1421,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 176); + RE(verify(result, TS_PARENT_SPECIFIED, "fluffy", 80), 176); params->markParentDown(result, fail_threshold, retry_time); // all are down now. @@ -1429,7 +1431,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_FAIL, nullptr, 80), 177); + RE(verify(result, TS_PARENT_FAIL, nullptr, 80), 177); // Test 178 tbl[0] = '\0'; @@ -1441,7 +1443,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 178); + RE(verify(result, TS_PARENT_SPECIFIED, "fuzzy", 80), 178); params->markParentDown(result, fail_threshold, retry_time); // fuzzy is down @@ -1451,7 +1453,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 179); + RE(verify(result, TS_PARENT_SPECIFIED, "fluffy", 80), 179); params->markParentDown(result, fail_threshold, retry_time); // fluffy is down @@ -1461,7 +1463,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "furry", 80), 180); + RE(verify(result, TS_PARENT_SPECIFIED, "furry", 80), 180); params->markParentDown(result, fail_threshold, retry_time); // furry is down @@ -1471,7 +1473,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "frisky", 80), 181); + RE(verify(result, TS_PARENT_SPECIFIED, "frisky", 80), 181); params->markParentDown(result, fail_threshold, retry_time); // frisky is down and we should be back on fuzzy. @@ -1481,7 +1483,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_FAIL, nullptr, 80), 182); + RE(verify(result, TS_PARENT_FAIL, nullptr, 80), 182); // wait long enough so that fuzzy is retryable. sleep(params->policy.ParentRetryTime - 2); @@ -1492,42 +1494,42 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 183); + RE(verify(result, TS_PARENT_SPECIFIED, "fuzzy", 80), 183); // Test 184 // mark fuzzy down with HostStatus API. - _st.setHostStatus("fuzzy", HOST_STATUS_DOWN, 0, Reason::MANUAL); + _st.setHostStatus("fuzzy", TS_HOST_STATUS_DOWN, 0, Reason::MANUAL); ST(184); REINIT; br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 184); + RE(verify(result, TS_PARENT_SPECIFIED, "fluffy", 80), 184); // Test 185 // mark fluffy down and expect furry to be chosen - _st.setHostStatus("fluffy", HOST_STATUS_DOWN, 0, Reason::MANUAL); + _st.setHostStatus("fluffy", TS_HOST_STATUS_DOWN, 0, Reason::MANUAL); ST(185); REINIT; br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "furry", 80), 185); + RE(verify(result, TS_PARENT_SPECIFIED, "furry", 80), 185); // Test 186 // mark furry and frisky down, fuzzy up and expect fuzzy to be chosen - _st.setHostStatus("furry", HOST_STATUS_DOWN, 0, Reason::MANUAL); - _st.setHostStatus("frisky", HOST_STATUS_DOWN, 0, Reason::MANUAL); - _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0, Reason::MANUAL); + _st.setHostStatus("furry", TS_HOST_STATUS_DOWN, 0, Reason::MANUAL); + _st.setHostStatus("frisky", TS_HOST_STATUS_DOWN, 0, Reason::MANUAL); + _st.setHostStatus("fuzzy", TS_HOST_STATUS_UP, 0, Reason::MANUAL); ST(186); REINIT; br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 186); + RE(verify(result, TS_PARENT_SPECIFIED, "fuzzy", 80), 186); // Test 187 // test the HostStatus API with ParentConsistent Hash. @@ -1538,38 +1540,38 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, REBUILD; // mark all up. - _st.setHostStatus("furry", HOST_STATUS_UP, 0, Reason::MANUAL); - _st.setHostStatus("fluffy", HOST_STATUS_UP, 0, Reason::MANUAL); - _st.setHostStatus("frisky", HOST_STATUS_UP, 0, Reason::MANUAL); - _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0, Reason::MANUAL); + _st.setHostStatus("furry", TS_HOST_STATUS_UP, 0, Reason::MANUAL); + _st.setHostStatus("fluffy", TS_HOST_STATUS_UP, 0, Reason::MANUAL); + _st.setHostStatus("frisky", TS_HOST_STATUS_UP, 0, Reason::MANUAL); + _st.setHostStatus("fuzzy", TS_HOST_STATUS_UP, 0, Reason::MANUAL); REINIT; br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 187); + RE(verify(result, TS_PARENT_SPECIFIED, "fuzzy", 80), 187); // Test 188 // mark fuzzy down and expect fluffy. - _st.setHostStatus("fuzzy", HOST_STATUS_DOWN, 0, Reason::MANUAL); + _st.setHostStatus("fuzzy", TS_HOST_STATUS_DOWN, 0, Reason::MANUAL); ST(188); REINIT; br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "frisky", 80), 188); + RE(verify(result, TS_PARENT_SPECIFIED, "frisky", 80), 188); // Test 189 // mark fuzzy back up and expect fuzzy. - _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0, Reason::MANUAL); + _st.setHostStatus("fuzzy", TS_HOST_STATUS_UP, 0, Reason::MANUAL); ST(189); REINIT; br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 189); + RE(verify(result, TS_PARENT_SPECIFIED, "fuzzy", 80), 189); // Test 190 // mark fuzzy back down and set the host status down @@ -1578,23 +1580,23 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, // because the host status is set to down. params->markParentDown(result, fail_threshold, retry_time); // set host status down - _st.setHostStatus("fuzzy", HOST_STATUS_DOWN, 0, Reason::MANUAL); + _st.setHostStatus("fuzzy", TS_HOST_STATUS_DOWN, 0, Reason::MANUAL); // sleep long enough so that fuzzy is retryable sleep(params->policy.ParentRetryTime + 1); ST(190); REINIT; br(request, "i.am.rabbit.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "frisky", 80), 190); + RE(verify(result, TS_PARENT_SPECIFIED, "frisky", 80), 190); // now set the host status on fuzzy to up and it should now // be retried. - _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0, Reason::MANUAL); + _st.setHostStatus("fuzzy", TS_HOST_STATUS_UP, 0, Reason::MANUAL); ST(191); REINIT; br(request, "i.am.rabbit.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 191); + RE(verify(result, TS_PARENT_SPECIFIED, "fuzzy", 80), 191); // Test 192 tbl[0] = '\0'; @@ -1602,16 +1604,16 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, T("dest_domain=rabbit.net parent=fuzzy:80,fluffy:80,furry:80,frisky:80 round_robin=false go_direct=true\n"); REBUILD; // mark all up. - _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0, Reason::MANUAL); - _st.setHostStatus("fluffy", HOST_STATUS_UP, 0, Reason::MANUAL); - _st.setHostStatus("furry", HOST_STATUS_UP, 0, Reason::MANUAL); - _st.setHostStatus("frisky", HOST_STATUS_UP, 0, Reason::MANUAL); + _st.setHostStatus("fuzzy", TS_HOST_STATUS_UP, 0, Reason::MANUAL); + _st.setHostStatus("fluffy", TS_HOST_STATUS_UP, 0, Reason::MANUAL); + _st.setHostStatus("furry", TS_HOST_STATUS_UP, 0, Reason::MANUAL); + _st.setHostStatus("frisky", TS_HOST_STATUS_UP, 0, Reason::MANUAL); // fuzzy should be chosen. sleep(1); REINIT; br(request, "i.am.rabbit.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 192); + RE(verify(result, TS_PARENT_SPECIFIED, "fuzzy", 80), 192); // Test 193 // mark fuzzy down and wait for it to become retryable @@ -1620,21 +1622,21 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, sleep(params->policy.ParentRetryTime + 1); // since the host status is down even though fuzzy is // retryable, fluffy should be chosen - _st.setHostStatus("fuzzy", HOST_STATUS_DOWN, 0, Reason::MANUAL); + _st.setHostStatus("fuzzy", TS_HOST_STATUS_DOWN, 0, Reason::MANUAL); REINIT; br(request, "i.am.rabbit.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 193); + RE(verify(result, TS_PARENT_SPECIFIED, "fluffy", 80), 193); // Test 194 // set the host status for fuzzy back up and since its // retryable fuzzy should be chosen ST(194); - _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0, Reason::MANUAL); + _st.setHostStatus("fuzzy", TS_HOST_STATUS_UP, 0, Reason::MANUAL); REINIT; br(request, "i.am.rabbit.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 194); + RE(verify(result, TS_PARENT_SPECIFIED, "fuzzy", 80), 194); // Test 195 // secondary_mode=1 (default) is covered by tests cases 173-177 above @@ -1649,7 +1651,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 195); + RE(verify(result, TS_PARENT_SPECIFIED, "fuzzy", 80), 195); params->markParentDown(result, fail_threshold, retry_time); // fuzzy is down. // Test 196 @@ -1658,7 +1660,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 196); + RE(verify(result, TS_PARENT_SPECIFIED, "fluffy", 80), 196); params->markParentDown(result, fail_threshold, retry_time); // fluffy is down. @@ -1668,7 +1670,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "frisky", 80), 197); + RE(verify(result, TS_PARENT_SPECIFIED, "frisky", 80), 197); params->markParentDown(result, fail_threshold, retry_time); // frisky is down. @@ -1678,7 +1680,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "furry", 80), 198); + RE(verify(result, TS_PARENT_SPECIFIED, "furry", 80), 198); params->markParentDown(result, fail_threshold, retry_time); // all are down now. @@ -1688,7 +1690,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_FAIL, nullptr, 80), 199); + RE(verify(result, TS_PARENT_FAIL, nullptr, 80), 199); // Test 200 // secondary_mode=3 is tested here first-choice NOT marked down @@ -1702,7 +1704,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "fuzzy", 80), 200); + RE(verify(result, TS_PARENT_SPECIFIED, "fuzzy", 80), 200); params->markParentDown(result, fail_threshold, retry_time); // fuzzy is down. // Test 201 @@ -1711,7 +1713,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 201); + RE(verify(result, TS_PARENT_SPECIFIED, "fluffy", 80), 201); params->markParentDown(result, fail_threshold, retry_time); // fluffy is down. @@ -1721,7 +1723,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "frisky", 80), 202); + RE(verify(result, TS_PARENT_SPECIFIED, "frisky", 80), 202); params->markParentDown(result, fail_threshold, retry_time); // frisky is down. @@ -1731,7 +1733,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "furry", 80), 203); + RE(verify(result, TS_PARENT_SPECIFIED, "furry", 80), 203); params->markParentDown(result, fail_threshold, retry_time); // all are down now. @@ -1741,7 +1743,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_FAIL, nullptr, 80), 204); + RE(verify(result, TS_PARENT_FAIL, nullptr, 80), 204); // Test 205 // secondary_mode=3 is tested here first-choice marked down @@ -1751,12 +1753,12 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, T("dest_domain=rabbit.net parent=fuzzy:80|1.0;fluffy:80|1.0 secondary_parent=furry:80|1.0;frisky:80|1.0 " "round_robin=consistent_hash go_direct=false secondary_mode=3\n"); REBUILD; - _st.setHostStatus("fuzzy", HOST_STATUS_DOWN, 0, Reason::MANUAL); + _st.setHostStatus("fuzzy", TS_HOST_STATUS_DOWN, 0, Reason::MANUAL); REINIT; br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "frisky", 80), 205); + RE(verify(result, TS_PARENT_SPECIFIED, "frisky", 80), 205); params->markParentDown(result, fail_threshold, retry_time); // frisky is down. // Test 206 @@ -1765,7 +1767,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "furry", 80), 206); + RE(verify(result, TS_PARENT_SPECIFIED, "furry", 80), 206); params->markParentDown(result, fail_threshold, retry_time); // furry is down. @@ -1775,7 +1777,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_SPECIFIED, "fluffy", 80), 207); + RE(verify(result, TS_PARENT_SPECIFIED, "fluffy", 80), 207); params->markParentDown(result, fail_threshold, retry_time); // all are down now. @@ -1785,7 +1787,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, br(request, "i.am.rabbit.net"); FP; sleep(1); - RE(verify(result, PARENT_FAIL, nullptr, 80), 208); + RE(verify(result, TS_PARENT_FAIL, nullptr, 80), 208); // Tests 209 through 211 test that host selection is based upon the hash_string @@ -1799,7 +1801,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, REINIT; br(request, "i.am.stooges.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "larry", 80), 209); + RE(verify(result, TS_PARENT_SPECIFIED, "larry", 80), 209); // Test 210 // fuzzy { curly larry, moe } fluffy @@ -1811,7 +1813,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, REINIT; br(request, "i.am.stooges.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "curly", 80), 210); + RE(verify(result, TS_PARENT_SPECIFIED, "curly", 80), 210); // Test 211 // fuzzy { curly larry, moe } fluffy @@ -1822,10 +1824,10 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, "round_robin=consistent_hash go_direct=false\n"); REBUILD; REINIT; - _st.setHostStatus("curly", HOST_STATUS_DOWN, 0, Reason::MANUAL); + _st.setHostStatus("curly", TS_HOST_STATUS_DOWN, 0, Reason::MANUAL); br(request, "i.am.stooges.net"); FP; - RE(verify(result, PARENT_SPECIFIED, "carol", 80), 211); + RE(verify(result, TS_PARENT_SPECIFIED, "carol", 80), 211); delete request; delete result; @@ -1837,12 +1839,12 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */, // verify returns 1 iff the test passes int -verify(ParentResult *r, ParentResultType e, const char *h, int p) +verify(ParentResult *r, TSParentResultType e, const char *h, int p) { if (is_debug_tag_set("parent_select")) { show_result(r); } - return (r->result != e) ? 0 : ((e != PARENT_SPECIFIED) ? 1 : (strcmp(r->hostname, h) ? 0 : ((r->port == p) ? 1 : 0))); + return (r->result != e) ? 0 : ((e != TS_PARENT_SPECIFIED) ? 1 : (strcmp(r->hostname, h) ? 0 : ((r->port == p) ? 1 : 0))); } // br creates an HttpRequestData object @@ -1865,19 +1867,19 @@ void show_result(ParentResult *p) { switch (p->result) { - case PARENT_UNDEFINED: - printf("result is PARENT_UNDEFINED\n"); + case TS_PARENT_UNDEFINED: + printf("result is TS_PARENT_UNDEFINED\n"); break; - case PARENT_DIRECT: - printf("result is PARENT_DIRECT\n"); + case TS_PARENT_DIRECT: + printf("result is TS_PARENT_DIRECT\n"); break; - case PARENT_SPECIFIED: - printf("result is PARENT_SPECIFIED\n"); + case TS_PARENT_SPECIFIED: + printf("result is TS_PARENT_SPECIFIED\n"); printf("hostname is %s\n", p->hostname); printf("port is %d\n", p->port); break; - case PARENT_FAIL: - printf("result is PARENT_FAIL\n"); + case TS_PARENT_FAIL: + printf("result is TS_PARENT_FAIL\n"); break; default: // Handled here: @@ -1885,3 +1887,45 @@ show_result(ParentResult *p) break; } } + +void +ParentResult::copyFrom(TSParentResult *r) +{ + this->hostname = r->hostname; + this->port = r->port; + this->retry = r->retry; + this->result = r->result; + memcpy(this->chash_init, r->chash_init, TS_MAX_GROUP_RINGS * sizeof(bool)); + this->first_choice_status = r->first_choice_status; + this->line_number = r->line_number; + this->last_parent = r->last_parent; + this->start_parent = r->start_parent; + this->last_group = r->last_group; + this->wrap_around = r->wrap_around; + memcpy(this->mapWrapped, r->mapWrapped, 2 * sizeof(bool)); + this->last_lookup = r->last_lookup; + for (int i = 0; i < TS_MAX_GROUP_RINGS; ++i) { + this->chashIter[i] = r->chashIter[i]; + } +} + +void +ParentResult::copyTo(TSParentResult *r) +{ + r->hostname = this->hostname; + r->port = this->port; + r->retry = this->retry; + r->result = this->result; + memcpy(r->chash_init, this->chash_init, TS_MAX_GROUP_RINGS * sizeof(bool)); + r->first_choice_status = this->first_choice_status; + r->line_number = this->line_number; + r->last_parent = this->last_parent; + r->start_parent = this->start_parent; + r->last_group = this->last_group; + r->wrap_around = this->wrap_around; + memcpy(r->mapWrapped, this->mapWrapped, 2 * sizeof(bool)); + r->last_lookup = this->last_lookup; + for (int i = 0; i < TS_MAX_GROUP_RINGS; ++i) { + r->chashIter[i] = this->chashIter[i]; + } +} diff --git a/proxy/ParentSelection.h b/proxy/ParentSelection.h index 010fcba509a..a0d12da3cbb 100644 --- a/proxy/ParentSelection.h +++ b/proxy/ParentSelection.h @@ -37,6 +37,7 @@ #include "tscore/ConsistentHash.h" #include "tscore/Tokenizer.h" #include "tscore/ink_apidefs.h" +#include "tscpp/api/parentresult.h" #include "HostStatus.h" #include @@ -51,16 +52,6 @@ struct OverridableHttpConfigParams; class ParentRecord; class ParentSelectionStrategy; -enum ParentResultType { - PARENT_UNDEFINED, - PARENT_DIRECT, - PARENT_SPECIFIED, - PARENT_AGENT, - PARENT_FAIL, -}; - -static const char *ParentResultStr[] = {"PARENT_UNDEFINED", "PARENT_DIRECT", "PARENT_SPECIFIED", "PARENT_AGENT", "PARENT_FAIL"}; - enum ParentRR_t { P_NO_ROUND_ROBIN = 0, P_STRICT_ROUND_ROBIN, @@ -174,27 +165,22 @@ class ParentRecord : public ControlBase // between HttpTransact & the parent selection code. The following ParentRecord *const extApiRecord = (ParentRecord *)0xeeeeffff; -// used here to to set the number of ATSConsistentHashIter's -// used in NextHopSelectionStrategy to limit the host group -// size as well, group size is one to one with the number of rings -constexpr const uint32_t MAX_GROUP_RINGS = 5; - struct ParentResult { ParentResult() { reset(); } // For outside consumption - ParentResultType result; + TSParentResultType result; const char *hostname; int port; bool retry; - bool chash_init[MAX_GROUP_RINGS] = {false}; - HostStatus_t first_choice_status = HostStatus_t::HOST_STATUS_INIT; + bool chash_init[TS_MAX_GROUP_RINGS] = {false}; + TSHostStatus first_choice_status = TSHostStatus::TS_HOST_STATUS_INIT; void reset() { ink_zero(*this); line_number = -1; - result = PARENT_UNDEFINED; + result = TS_PARENT_UNDEFINED; mapWrapped[0] = false; mapWrapped[1] = false; } @@ -213,7 +199,7 @@ struct ParentResult { // If we don't have a result, we either haven't done a parent // lookup yet (PARENT_UNDEFINED), or the lookup didn't match // anything (PARENT_DIRECT). - ink_assert(result == PARENT_UNDEFINED || result == PARENT_DIRECT); + ink_assert(result == TS_PARENT_UNDEFINED || result == TS_PARENT_DIRECT); return false; } @@ -281,7 +267,7 @@ struct ParentResult { return false; } else { // Caller should check for a valid result beforehand. - ink_assert(result != PARENT_UNDEFINED); + ink_assert(result != TS_PARENT_UNDEFINED); ink_assert(is_some()); return rec->bypass_ok(); } @@ -296,6 +282,9 @@ struct ParentResult { last_lookup, ParentResultStr[result]); } + void copyFrom(TSParentResult *r); + void copyTo(TSParentResult *r); + private: // Internal use only // Not to be modified by HTTP @@ -308,7 +297,7 @@ struct ParentResult { bool mapWrapped[2]; // state for consistent hash. int last_lookup; - ATSConsistentHashIter chashIter[MAX_GROUP_RINGS]; + ATSConsistentHashIter chashIter[TS_MAX_GROUP_RINGS]; friend class NextHopSelectionStrategy; friend class NextHopRoundRobin; @@ -440,7 +429,7 @@ ParentRecord *createDefaultParent(char *val); // Unit Test Functions void show_result(ParentResult *aParentResult); void br(HttpRequestData *h, const char *os_hostname, sockaddr const *dest_ip = nullptr); // short for build request -int verify(ParentResult *r, ParentResultType e, const char *h, int p); +int verify(ParentResult *r, TSParentResultType e, const char *h, int p); /* For supporting multiple Socks servers, we essentially use the diff --git a/proxy/ParentSelectionStrategy.cc b/proxy/ParentSelectionStrategy.cc index cf067b41ea3..67a2d102a9e 100644 --- a/proxy/ParentSelectionStrategy.cc +++ b/proxy/ParentSelectionStrategy.cc @@ -32,8 +32,8 @@ ParentSelectionStrategy::markParentDown(ParentResult *result, unsigned int fail_ // Make sure that we are being called back with with a // result structure with a parent - ink_assert(result->result == PARENT_SPECIFIED); - if (result->result != PARENT_SPECIFIED) { + ink_assert(result->result == TS_PARENT_SPECIFIED); + if (result->result != TS_PARENT_SPECIFIED) { return; } // If we were set through the API we currently have not failover @@ -104,8 +104,8 @@ ParentSelectionStrategy::markParentUp(ParentResult *result) // Make sure that we are being called back with with a // result structure with a parent that is being retried ink_release_assert(result->retry == true); - ink_assert(result->result == PARENT_SPECIFIED); - if (result->result != PARENT_SPECIFIED) { + ink_assert(result->result == TS_PARENT_SPECIFIED); + if (result->result != TS_PARENT_SPECIFIED) { return; } // If we were set through the API we currently have not failover diff --git a/proxy/http/HttpSM.cc b/proxy/http/HttpSM.cc index 6991b95c8ef..a50a80e083c 100644 --- a/proxy/http/HttpSM.cc +++ b/proxy/http/HttpSM.cc @@ -7411,7 +7411,7 @@ HttpSM::set_next_state() call_transact_and_set_next_state(nullptr); break; } else if (t_state.http_config_param->use_client_target_addr == 2 && !t_state.url_remap_success && - t_state.parent_result.result != PARENT_SPECIFIED && t_state.client_info.is_transparent && + t_state.parent_result.result != TS_PARENT_SPECIFIED && t_state.client_info.is_transparent && t_state.dns_info.os_addr_style == HttpTransact::DNSLookupInfo::OS_Addr::OS_ADDR_TRY_DEFAULT && ats_is_ip(addr = ua_txn->get_netvc()->get_local_addr())) { /* If the connection is client side transparent and the URL @@ -7440,14 +7440,14 @@ HttpSM::set_next_state() t_state.dns_info.os_addr_style = HttpTransact::DNSLookupInfo::OS_Addr::OS_ADDR_TRY_CLIENT; call_transact_and_set_next_state(nullptr); break; - } else if (t_state.parent_result.result == PARENT_UNDEFINED && t_state.dns_info.lookup_success) { + } else if (t_state.parent_result.result == TS_PARENT_UNDEFINED && t_state.dns_info.lookup_success) { // Already set, and we don't have a parent proxy to lookup ink_assert(ats_is_ip(t_state.host_db_info.ip())); SMDebug("dns", "[HttpTransact::HandleRequest] Skipping DNS lookup, provided by plugin"); call_transact_and_set_next_state(nullptr); break; } else if (t_state.dns_info.looking_up == HttpTransact::ORIGIN_SERVER && t_state.http_config_param->no_dns_forward_to_parent && - t_state.parent_result.result != PARENT_UNDEFINED) { + t_state.parent_result.result != TS_PARENT_UNDEFINED) { t_state.dns_info.lookup_success = true; call_transact_and_set_next_state(nullptr); break; diff --git a/proxy/http/HttpTransact.cc b/proxy/http/HttpTransact.cc index ebec4d33c82..a21e731cf71 100644 --- a/proxy/http/HttpTransact.cc +++ b/proxy/http/HttpTransact.cc @@ -21,7 +21,7 @@ limitations under the License. */ -#include "ts/nexthop.h" +#include "tscpp/api/nexthop.h" #include "tscore/ink_platform.h" #include @@ -99,7 +99,7 @@ bypass_ok(HttpTransact::State *s) if (mp && mp->strategy) { // remap strategies do not support the TSHttpTxnParentProxySet API. - r = mp->strategy->go_direct; + r = mp->strategy->goDirect(); } else if (s->parent_params) { r = s->parent_result.bypass_ok(); } @@ -154,7 +154,7 @@ parent_is_proxy(HttpTransact::State *s) url_mapping *mp = s->url_map.getMapping(); if (mp && mp->strategy) { - r = mp->strategy->parent_is_proxy; + r = mp->strategy->parentIsProxy(); } else if (s->parent_params) { r = s->parent_result.parent_is_proxy(); } @@ -343,8 +343,8 @@ response_is_retryable(HttpTransact::State *s, HTTPStatus response_code) const url_mapping *mp = s->url_map.getMapping(); if (mp && mp->strategy) { - if (mp->strategy->responseIsRetryable(s->current.simple_retry_attempts, response_code)) { - if (mp->strategy->onFailureMarkParentDown(response_code)) { + if (mp->strategy->responseIsRetryable(s->current.simple_retry_attempts, static_cast(response_code))) { + if (mp->strategy->onFailureMarkParentDown(static_cast(response_code))) { return PARENT_RETRY_UNAVAILABLE_SERVER; } else { return PARENT_RETRY_SIMPLE; @@ -535,16 +535,16 @@ find_server_and_update_current_info(HttpTransact::State *s) // I just wanted to do this for cop heartbeats, someone else // wanted it for all requests to local_host. TxnDebug("http_trans", "request is from localhost, so bypass parent"); - s->parent_result.result = PARENT_DIRECT; + s->parent_result.result = TS_PARENT_DIRECT; } else if (s->method == HTTP_WKSIDX_CONNECT && s->http_config_param->disable_ssl_parenting) { - if (s->parent_result.result == PARENT_SPECIFIED) { + if (s->parent_result.result == TS_PARENT_SPECIFIED) { nextParent(s); } else { findParent(s); } if (!s->parent_result.is_some() || is_api_result(s) || parent_is_proxy(s)) { TxnDebug("http_trans", "request not cacheable, so bypass parent"); - s->parent_result.result = PARENT_DIRECT; + s->parent_result.result = TS_PARENT_DIRECT; } } else if (s->txn_conf->uncacheable_requests_bypass_parent && s->http_config_param->no_dns_forward_to_parent == 0 && !HttpTransact::is_request_cache_lookupable(s)) { @@ -554,33 +554,33 @@ find_server_and_update_current_info(HttpTransact::State *s) // we are assuming both child and parent have similar configuration // with respect to whether a request is cacheable or not. // For example, the cache_urls_that_look_dynamic variable. - if (s->parent_result.result == PARENT_SPECIFIED) { + if (s->parent_result.result == TS_PARENT_SPECIFIED) { nextParent(s); } else { findParent(s); } if (!s->parent_result.is_some() || is_api_result(s) || parent_is_proxy(s)) { TxnDebug("http_trans", "request not cacheable, so bypass parent"); - s->parent_result.result = PARENT_DIRECT; + s->parent_result.result = TS_PARENT_DIRECT; } } else { switch (s->parent_result.result) { - case PARENT_UNDEFINED: + case TS_PARENT_UNDEFINED: findParent(s); break; - case PARENT_SPECIFIED: + case TS_PARENT_SPECIFIED: nextParent(s); // Hack! // We already have a parent that failed, if we are now told // to go the origin server, we can only obey this if we // dns'ed the origin server - if (s->parent_result.result == PARENT_DIRECT && s->http_config_param->no_dns_forward_to_parent != 0) { + if (s->parent_result.result == TS_PARENT_DIRECT && s->http_config_param->no_dns_forward_to_parent != 0) { ink_assert(!s->server_info.dst_addr.isValid()); - s->parent_result.result = PARENT_FAIL; + s->parent_result.result = TS_PARENT_FAIL; } break; - case PARENT_FAIL: + case TS_PARENT_FAIL: // Check to see if should bypass the parent and go direct // We can only do this if // 1) the config permitted us to dns the origin server @@ -588,13 +588,13 @@ find_server_and_update_current_info(HttpTransact::State *s) // 3) the parent was not set from API if (s->http_config_param->no_dns_forward_to_parent == 0 && bypass_ok(s) && parent_is_proxy(s) && !s->parent_params->apiParentExists(&s->request_data)) { - s->parent_result.result = PARENT_DIRECT; + s->parent_result.result = TS_PARENT_DIRECT; } break; default: ink_assert(0); // FALL THROUGH - case PARENT_DIRECT: + case TS_PARENT_DIRECT: // // if we have already decided to go direct // // dont bother calling nextParent. // // do nothing here, guy. @@ -603,7 +603,7 @@ find_server_and_update_current_info(HttpTransact::State *s) } switch (s->parent_result.result) { - case PARENT_SPECIFIED: + case TS_PARENT_SPECIFIED: s->parent_info.name = s->arena.str_store(s->parent_result.hostname, strlen(s->parent_result.hostname)); update_current_info(&s->current, &s->parent_info, HttpTransact::PARENT_PROXY, (s->current.attempts)++); update_dns_info(&s->dns_info, &s->current); @@ -611,17 +611,17 @@ find_server_and_update_current_info(HttpTransact::State *s) s->next_hop_scheme = URL_WKSIDX_HTTP; return HttpTransact::PARENT_PROXY; - case PARENT_FAIL: + case TS_PARENT_FAIL: // No more parents - need to return an error message s->current.request_to = HttpTransact::HOST_NONE; return HttpTransact::HOST_NONE; - case PARENT_DIRECT: + case TS_PARENT_DIRECT: // if the configuration does not allow the origin to be dns'd // we're unable to go direct to the origin. if (s->http_config_param->no_dns_forward_to_parent) { Warning("no available parents and the config proxy.config.http.no_dns_just_forward_to_parent, prevents origin lookups."); - s->parent_result.result = PARENT_FAIL; + s->parent_result.result = TS_PARENT_FAIL; return HttpTransact::HOST_NONE; } /* fall through */ @@ -761,7 +761,7 @@ how_to_open_connection(HttpTransact::State *s) // Setting up a direct CONNECT tunnel enters OriginServerRawOpen. We always do that if we // are not forwarding CONNECT and are not going to a parent proxy. if (s->method == HTTP_WKSIDX_CONNECT) { - if (s->txn_conf->forward_connect_method != 1 && s->parent_result.result != PARENT_SPECIFIED) { + if (s->txn_conf->forward_connect_method != 1 && s->parent_result.result != TS_PARENT_SPECIFIED) { connect_next_action = HttpTransact::SM_ACTION_ORIGIN_SERVER_RAW_OPEN; } } @@ -1733,7 +1733,7 @@ HttpTransact::PPDNSLookup(State *s) if (!s->current.server->dst_addr.isValid()) { if (s->current.request_to == PARENT_PROXY) { TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, PPDNSLookup); - } else if (s->parent_result.result == PARENT_DIRECT && s->http_config_param->no_dns_forward_to_parent != 1) { + } else if (s->parent_result.result == TS_PARENT_DIRECT && s->http_config_param->no_dns_forward_to_parent != 1) { // We ran out of parents but parent configuration allows us to go to Origin Server directly return CallOSDNSLookup(s); } else { @@ -2149,7 +2149,7 @@ HttpTransact::LookupSkipOpenServer(State *s) if (s->current.request_to == PARENT_PROXY) { TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, PPDNSLookup); - } else if (s->parent_result.result == PARENT_FAIL) { + } else if (s->parent_result.result == TS_PARENT_FAIL) { handle_parent_died(s); return; } @@ -2591,7 +2591,7 @@ HttpTransact::CallOSDNSLookup(State *s) TxnDebug("http", "[HttpTransact::callos] %s ", s->server_info.name); HostStatus &pstatus = HostStatus::instance(); HostStatRec *hst = pstatus.getHostStatus(s->server_info.name); - if (hst && hst->status == HostStatus_t::HOST_STATUS_DOWN) { + if (hst && hst->status == TSHostStatus::TS_HOST_STATUS_DOWN) { TxnDebug("http", "[HttpTransact::callos] %d ", s->cache_lookup_result); s->current.state = OUTBOUND_CONGESTION; if (s->cache_lookup_result == CACHE_LOOKUP_HIT_STALE || s->cache_lookup_result == CACHE_LOOKUP_HIT_WARNING || @@ -2831,9 +2831,9 @@ HttpTransact::HandleCacheOpenReadHit(State *s) update_current_info(&s->current, nullptr, UNDEFINED_LOOKUP, 0); TxnDebug("http_trans", "CacheOpenReadHit - server_down, returning stale document"); } - // a parent lookup could come back as PARENT_FAIL if in parent.config, go_direct == false and + // a parent lookup could come back as TS_PARENT_FAIL if in parent.config, go_direct == false and // there are no available parents (all down). - else if (s->current.request_to == HOST_NONE && s->parent_result.result == PARENT_FAIL) { + else if (s->current.request_to == HOST_NONE && s->parent_result.result == TS_PARENT_FAIL) { if (is_server_negative_cached(s) && response_returnable == true && is_stale_cache_response_returnable(s) == true) { server_up = false; update_current_info(&s->current, nullptr, UNDEFINED_LOOKUP, 0); @@ -3273,16 +3273,16 @@ HttpTransact::HandleCacheOpenReadMiss(State *s) get_ka_info_from_config(s, &s->server_info); } find_server_and_update_current_info(s); - // a parent lookup could come back as PARENT_FAIL if in parent.config go_direct == false and + // a parent lookup could come back as TS_PARENT_FAIL if in parent.config go_direct == false and // there are no available parents (all down). - if (s->parent_result.result == PARENT_FAIL) { + if (s->parent_result.result == TS_PARENT_FAIL) { handle_parent_died(s); return; } if (!s->current.server->dst_addr.isValid()) { - ink_release_assert(s->parent_result.result == PARENT_DIRECT || s->current.request_to == PARENT_PROXY || + ink_release_assert(s->parent_result.result == TS_PARENT_DIRECT || s->current.request_to == PARENT_PROXY || s->http_config_param->no_dns_forward_to_parent != 0); - if (s->parent_result.result == PARENT_DIRECT && s->http_config_param->no_dns_forward_to_parent != 1) { + if (s->parent_result.result == TS_PARENT_DIRECT && s->http_config_param->no_dns_forward_to_parent != 1) { return CallOSDNSLookup(s); } if (s->current.request_to == PARENT_PROXY) { @@ -3587,7 +3587,7 @@ HttpTransact::handle_response_from_parent(State *s) if (s->current.state != OUTBOUND_CONGESTION) { markParentDown(s); } - s->parent_result.result = PARENT_FAIL; + s->parent_result.result = TS_PARENT_FAIL; handle_parent_died(s); return; } @@ -3625,7 +3625,7 @@ HttpTransact::handle_response_from_parent(State *s) if (s->current.state == CONNECTION_ERROR) { markParentDown(s); } - s->parent_result.result = PARENT_FAIL; + s->parent_result.result = TS_PARENT_FAIL; next_lookup = HOST_NONE; } break; @@ -7453,7 +7453,7 @@ HttpTransact::AuthenticationNeeded(const OverridableHttpConfigParams *p, HTTPHdr void HttpTransact::handle_parent_died(State *s) { - ink_assert(s->parent_result.result == PARENT_FAIL); + ink_assert(s->parent_result.result == TS_PARENT_FAIL); if (s->current.state == OUTBOUND_CONGESTION) { build_error_response(s, HTTP_STATUS_SERVICE_UNAVAILABLE, "Next Hop Congested", "congestion#retryAfter"); diff --git a/proxy/http/HttpTransact.h b/proxy/http/HttpTransact.h index 5bce8fb92a4..7679e86dc95 100644 --- a/proxy/http/HttpTransact.h +++ b/proxy/http/HttpTransact.h @@ -693,8 +693,8 @@ class HttpTransact TransactFunc_t pending_work = nullptr; HttpRequestData request_data; - ParentConfigParams *parent_params = nullptr; - std::shared_ptr next_hop_strategy = nullptr; + ParentConfigParams *parent_params = nullptr; + std::shared_ptr next_hop_strategy = nullptr; ParentResult parent_result; CacheControlResult cache_control; CacheLookupResult_t cache_lookup_result = CACHE_LOOKUP_NONE; diff --git a/proxy/http/remap/NextHopConsistentHash.cc b/proxy/http/remap/NextHopConsistentHash.cc index c14c971bf9a..444425c02f7 100644 --- a/proxy/http/remap/NextHopConsistentHash.cc +++ b/proxy/http/remap/NextHopConsistentHash.cc @@ -56,6 +56,8 @@ chash_lookup(const std::shared_ptr &ring, uint64_t hash_key, return host_rec; } +NextHopConsistentHash::NextHopConsistentHash(const std::string_view name) : NextHopSelectionStrategy(name) {} + NextHopConsistentHash::~NextHopConsistentHash() { NH_Debug(NH_DEBUG_TAG, "destructor called for strategy named: %s", strategy_name.c_str()); @@ -208,7 +210,7 @@ NextHopConsistentHash::getHashKey(uint64_t sm_id, HttpRequestData *hrdata, ATSHa } void -NextHopConsistentHash::findNextHop(TSHttpTxn txnp, void *ih, time_t now) +NextHopConsistentHash::findNextHop(TSHttpTxn txnp, time_t now) { HttpSM *sm = reinterpret_cast(txnp); ParentResult *result = &sm->t_state.parent_result; @@ -227,17 +229,17 @@ NextHopConsistentHash::findNextHop(TSHttpTxn txnp, void *ih, time_t now) HostRecord *hostRec = nullptr; std::shared_ptr pRec = nullptr; HostStatus &pStatus = HostStatus::instance(); - HostStatus_t host_stat = HostStatus_t::HOST_STATUS_INIT; + TSHostStatus host_stat = TSHostStatus::TS_HOST_STATUS_INIT; HostStatRec *hst = nullptr; - if (result->line_number == -1 && result->result == PARENT_UNDEFINED) { + if (result->line_number == -1 && result->result == TS_PARENT_UNDEFINED) { firstcall = true; } if (firstcall) { NH_Debug(NH_DEBUG_TAG, "[%" PRIu64 "] firstcall, line_number: %d, result: %s", sm_id, result->line_number, ParentResultStr[result->result]); - result->line_number = distance; + result->line_number = NextHopConsistentHash::LineNumberPlaceholder; cur_ring = 0; for (uint32_t i = 0; i < groups; i++) { result->chash_init[i] = false; @@ -279,7 +281,7 @@ NextHopConsistentHash::findNextHop(TSHttpTxn txnp, void *ih, time_t now) pRec = host_groups[hostRec->group_index][hostRec->host_index]; if (firstcall) { hst = (pRec) ? pStatus.getHostStatus(pRec->hostname.c_str()) : nullptr; - result->first_choice_status = (hst) ? hst->status : HostStatus_t::HOST_STATUS_UP; + result->first_choice_status = (hst) ? hst->status : TSHostStatus::TS_HOST_STATUS_UP; break; } } else { @@ -294,18 +296,18 @@ NextHopConsistentHash::findNextHop(TSHttpTxn txnp, void *ih, time_t now) // ---------------------------------------------------------------------------------------------------- hst = (pRec) ? pStatus.getHostStatus(pRec->hostname.c_str()) : nullptr; - host_stat = (hst) ? hst->status : HostStatus_t::HOST_STATUS_UP; + host_stat = (hst) ? hst->status : TSHostStatus::TS_HOST_STATUS_UP; // if the config ignore_self_detect is set to true and the host is down due to SELF_DETECT reason // ignore the down status and mark it as avaialble - if ((pRec && ignore_self_detect) && (hst && hst->status == HOST_STATUS_DOWN)) { + if ((pRec && ignore_self_detect) && (hst && hst->status == TS_HOST_STATUS_DOWN)) { if (hst->reasons == Reason::SELF_DETECT) { - host_stat = HOST_STATUS_UP; + host_stat = TS_HOST_STATUS_UP; } } - if (!pRec || (pRec && !pRec->available) || host_stat == HOST_STATUS_DOWN) { + if (!pRec || (pRec && !pRec->available) || host_stat == TS_HOST_STATUS_DOWN) { do { // check if an unavailable server is now retryable, use it if it is. - if (pRec && !pRec->available && host_stat == HOST_STATUS_UP) { + if (pRec && !pRec->available && host_stat == TS_HOST_STATUS_UP) { _now == 0 ? _now = time(nullptr) : _now = now; // check if the host is retryable. It's retryable if the retry window has elapsed if ((pRec->failedAt + retry_time) < static_cast(_now)) { @@ -313,7 +315,7 @@ NextHopConsistentHash::findNextHop(TSHttpTxn txnp, void *ih, time_t now) result->last_parent = pRec->host_index; result->last_lookup = pRec->group_index; result->retry = nextHopRetry; - result->result = PARENT_SPECIFIED; + result->result = TS_PARENT_SPECIFIED; NH_Debug(NH_DEBUG_TAG, "[%" PRIu64 "] next hop %s is now retryable, marked it available.", sm_id, pRec->hostname.c_str()); break; } @@ -339,18 +341,18 @@ NextHopConsistentHash::findNextHop(TSHttpTxn txnp, void *ih, time_t now) if (hostRec) { pRec = host_groups[hostRec->group_index][hostRec->host_index]; hst = (pRec) ? pStatus.getHostStatus(pRec->hostname.c_str()) : nullptr; - host_stat = (hst) ? hst->status : HostStatus_t::HOST_STATUS_UP; + host_stat = (hst) ? hst->status : TSHostStatus::TS_HOST_STATUS_UP; // if the config ignore_self_detect is set to true and the host is down due to SELF_DETECT reason // ignore the down status and mark it as avaialble - if ((pRec && ignore_self_detect) && (hst && hst->status == HOST_STATUS_DOWN)) { + if ((pRec && ignore_self_detect) && (hst && hst->status == TS_HOST_STATUS_DOWN)) { if (hst->reasons == Reason::SELF_DETECT) { - host_stat = HOST_STATUS_UP; + host_stat = TS_HOST_STATUS_UP; } } NH_Debug(NH_DEBUG_TAG, "[%" PRIu64 "] Selected a new parent: %s, available: %s, wrapped: %s, lookups: %d.", sm_id, pRec->hostname.c_str(), (pRec->available) ? "true" : "false", (wrapped) ? "true" : "false", lookups); // use available host. - if (pRec->available && host_stat == HOST_STATUS_UP) { + if (pRec->available && host_stat == TS_HOST_STATUS_UP) { break; } } else { @@ -369,23 +371,21 @@ NextHopConsistentHash::findNextHop(TSHttpTxn txnp, void *ih, time_t now) } break; } - } while (!pRec || (pRec && !pRec->available) || host_stat == HOST_STATUS_DOWN); + } while (!pRec || (pRec && !pRec->available) || host_stat == TS_HOST_STATUS_DOWN); } // ---------------------------------------------------------------------------------------------------- // Validate and return the final result. // ---------------------------------------------------------------------------------------------------- - if (pRec && host_stat == HOST_STATUS_UP && (pRec->available || result->retry)) { - result->result = PARENT_SPECIFIED; + if (pRec && host_stat == TS_HOST_STATUS_UP && (pRec->available || result->retry)) { + result->result = TS_PARENT_SPECIFIED; result->hostname = pRec->hostname.c_str(); result->last_parent = pRec->host_index; result->last_lookup = result->last_group = cur_ring; switch (scheme) { case NH_SCHEME_NONE: case NH_SCHEME_HTTP: - result->port = pRec->getPort(scheme); - break; case NH_SCHEME_HTTPS: result->port = pRec->getPort(scheme); break; @@ -397,9 +397,9 @@ NextHopConsistentHash::findNextHop(TSHttpTxn txnp, void *ih, time_t now) result->hostname, result->port); } else { if (go_direct == true) { - result->result = PARENT_DIRECT; + result->result = TS_PARENT_DIRECT; } else { - result->result = PARENT_FAIL; + result->result = TS_PARENT_FAIL; } result->hostname = nullptr; result->port = 0; diff --git a/proxy/http/remap/NextHopConsistentHash.h b/proxy/http/remap/NextHopConsistentHash.h index 6ce8cffb877..74e37f9d30d 100644 --- a/proxy/http/remap/NextHopConsistentHash.h +++ b/proxy/http/remap/NextHopConsistentHash.h @@ -38,16 +38,17 @@ enum NHHashKeyType { class NextHopConsistentHash : public NextHopSelectionStrategy { - std::vector> rings; - - uint64_t getHashKey(uint64_t sm_id, HttpRequestData *hrdata, ATSHash64 *h); - public: + const uint32_t LineNumberPlaceholder = 99999; + NHHashKeyType hash_key = NH_PATH_HASH_KEY; NextHopConsistentHash() = delete; - NextHopConsistentHash(const std::string_view name, const NHPolicyType &policy) : NextHopSelectionStrategy(name, policy) {} + NextHopConsistentHash(const std::string_view name); ~NextHopConsistentHash(); bool Init(const YAML::Node &n); - void findNextHop(TSHttpTxn txnp, void *ih = nullptr, time_t now = 0) override; + void findNextHop(TSHttpTxn txnp, time_t now = 0) override; + // private: + std::vector> rings; + uint64_t getHashKey(uint64_t sm_id, HttpRequestData *hrdata, ATSHash64 *h); }; diff --git a/proxy/http/remap/NextHopHealthStatus.cc b/proxy/http/remap/NextHopHealthStatus.cc index afb43c7222c..75f02898e4b 100644 --- a/proxy/http/remap/NextHopHealthStatus.cc +++ b/proxy/http/remap/NextHopHealthStatus.cc @@ -39,33 +39,11 @@ NextHopHealthStatus::insert(std::vector> &hosts) } } -/** - * check that hostname is available for use. - */ -bool -NextHopHealthStatus::isNextHopAvailable(TSHttpTxn txn, const char *hostname, const int port, void *ih) -{ - HttpSM *sm = reinterpret_cast(txn); - int64_t sm_id = sm->sm_id; - - const std::string host_port = HostRecord::makeHostPort(hostname, port); - auto iter = host_map.find(host_port); - - if (iter == host_map.end()) { - NH_Debug(NH_DEBUG_TAG, "[%" PRId64 "] no host named %s found in host_map", sm_id, host_port.c_str()); - return false; - } - - std::shared_ptr p = iter->second; - return p->available; -} - /** * mark up or down the indicated host */ void -NextHopHealthStatus::markNextHop(TSHttpTxn txn, const char *hostname, const int port, const NHCmd status, void *ih, - const time_t now) +NextHopHealthStatus::markNextHop(TSHttpTxn txn, const char *hostname, const int port, const NHCmd status, const time_t now) { time_t _now; now == 0 ? _now = time(nullptr) : _now = now; @@ -82,7 +60,7 @@ NextHopHealthStatus::markNextHop(TSHttpTxn txn, const char *hostname, const int if (status == NH_MARK_UP) { ink_assert(result.retry == true); } - if (result.result != PARENT_SPECIFIED) { + if (result.result != TS_PARENT_SPECIFIED) { return; } diff --git a/proxy/http/remap/NextHopRoundRobin.cc b/proxy/http/remap/NextHopRoundRobin.cc index 0bbcc298726..35dca028417 100644 --- a/proxy/http/remap/NextHopRoundRobin.cc +++ b/proxy/http/remap/NextHopRoundRobin.cc @@ -27,13 +27,19 @@ #include "HttpSM.h" #include "NextHopRoundRobin.h" +NextHopRoundRobin::NextHopRoundRobin(const std::string_view &name, const NHPolicyType &policy) + : NextHopSelectionStrategy(name), policy_type(policy) +{ + NH_Debug(NH_DEBUG_TAG, "Using a selection strategy of type %s", policy_strings[policy]); +} + NextHopRoundRobin::~NextHopRoundRobin() { NH_Debug(NH_DEBUG_TAG, "destructor called for strategy named: %s", strategy_name.c_str()); } void -NextHopRoundRobin::findNextHop(TSHttpTxn txnp, void *ih, time_t now) +NextHopRoundRobin::findNextHop(TSHttpTxn txnp, time_t now) { HttpSM *sm = reinterpret_cast(txnp); ParentResult *result = &sm->t_state.parent_result; @@ -54,17 +60,15 @@ NextHopRoundRobin::findNextHop(TSHttpTxn txnp, void *ih, time_t now) uint32_t start_host = 0; std::shared_ptr cur_host; HostStatus &pStatus = HostStatus::instance(); - HostStatus_t host_stat = HostStatus_t::HOST_STATUS_UP; + TSHostStatus host_stat = TSHostStatus::TS_HOST_STATUS_UP; - if (result->line_number != -1 && result->result != PARENT_UNDEFINED) { + if (result->line_number != -1 && result->result != TS_PARENT_UNDEFINED) { firstcall = false; } if (firstcall) { - // distance is the index into the strategies map, this is the equivalent to the old line_number in parent.config. - result->line_number = distance; - NH_Debug(NH_DEBUG_TAG, "[%" PRIu64 "] first call , cur_grp_index: %d, cur_hst_index: %d, distance: %d", sm_id, cur_grp_index, - cur_hst_index, distance); + result->line_number = NextHopRoundRobin::LineNumberPlaceholder; + NH_Debug(NH_DEBUG_TAG, "[%" PRIu64 "] first call , cur_grp_index: %d, cur_hst_index: %d", sm_id, cur_grp_index, cur_hst_index); switch (policy_type) { case NH_FIRST_LIVE: result->start_parent = cur_hst_index = 0; @@ -95,8 +99,7 @@ NextHopRoundRobin::findNextHop(TSHttpTxn txnp, void *ih, time_t now) cur_host = host_groups[cur_grp_index][cur_hst_index]; NH_Debug(NH_DEBUG_TAG, "[%" PRIu64 "] first call, cur_grp_index: %d, cur_hst_index: %d", sm_id, cur_grp_index, cur_hst_index); } else { - NH_Debug(NH_DEBUG_TAG, "[%" PRIu64 "] next call, cur_grp_index: %d, cur_hst_index: %d, distance: %d", sm_id, cur_grp_index, - cur_hst_index, distance); + NH_Debug(NH_DEBUG_TAG, "[%" PRIu64 "] next call, cur_grp_index: %d, cur_hst_index: %d", sm_id, cur_grp_index, cur_hst_index); // Move to next parent due to failure latched_index = cur_hst_index = (result->last_parent + 1) % hst_size; cur_host = host_groups[cur_grp_index][cur_hst_index]; @@ -105,9 +108,9 @@ NextHopRoundRobin::findNextHop(TSHttpTxn txnp, void *ih, time_t now) if (static_cast(cur_hst_index) == result->start_parent) { // We've wrapped around so bypass if we can if (go_direct == true) { - result->result = PARENT_DIRECT; + result->result = TS_PARENT_DIRECT; } else { - result->result = PARENT_FAIL; + result->result = TS_PARENT_FAIL; } result->hostname = nullptr; result->port = 0; @@ -122,12 +125,12 @@ NextHopRoundRobin::findNextHop(TSHttpTxn txnp, void *ih, time_t now) // should be retried do { HostStatRec *hst = pStatus.getHostStatus(cur_host->hostname.c_str()); - host_stat = (hst) ? hst->status : HostStatus_t::HOST_STATUS_UP; + host_stat = (hst) ? hst->status : TSHostStatus::TS_HOST_STATUS_UP; // if the config ignore_self_detect is set to true and the host is down due to SELF_DETECT reason // ignore the down status and mark it as avaialble - if (ignore_self_detect && (hst && hst->status == HOST_STATUS_DOWN)) { + if (ignore_self_detect && (hst && hst->status == TS_HOST_STATUS_DOWN)) { if (hst->reasons == Reason::SELF_DETECT) { - host_stat = HOST_STATUS_UP; + host_stat = TS_HOST_STATUS_UP; } } @@ -138,7 +141,7 @@ NextHopRoundRobin::findNextHop(TSHttpTxn txnp, void *ih, time_t now) request_info.xact_start); // check if 'cur_host' is available, mark it up if it is. if ((cur_host->failedAt == 0) || (cur_host->failCount < fail_threshold)) { - if (host_stat == HOST_STATUS_UP) { + if (host_stat == TS_HOST_STATUS_UP) { NH_Debug(NH_DEBUG_TAG, "[%" PRIu64 "] Selecting a parent, %s, due to little failCount (faileAt: %d failCount: %d), FailThreshold: %" PRIu64, @@ -149,7 +152,7 @@ NextHopRoundRobin::findNextHop(TSHttpTxn txnp, void *ih, time_t now) // available. _now == 0 ? _now = time(nullptr) : _now = now; if (((result->wrap_around) || (cur_host->failedAt + retry_time) < static_cast(_now)) && - host_stat == HOST_STATUS_UP) { + host_stat == TS_HOST_STATUS_UP) { // Reuse the parent parentUp = true; parentRetry = true; @@ -163,9 +166,9 @@ NextHopRoundRobin::findNextHop(TSHttpTxn txnp, void *ih, time_t now) cur_host->hostname.c_str(), HostStatusNames[host_stat]); // The selected host is available or retryable, return the search result. - if (parentUp == true && host_stat != HOST_STATUS_DOWN) { + if (parentUp == true && host_stat != TS_HOST_STATUS_DOWN) { NH_Debug(NH_DEBUG_TAG, "[%" PRIu64 "] status for %s: %s", sm_id, cur_host->hostname.c_str(), HostStatusNames[host_stat]); - result->result = PARENT_SPECIFIED; + result->result = TS_PARENT_SPECIFIED; result->hostname = cur_host->hostname.c_str(); result->port = cur_host->getPort(scheme); result->last_parent = cur_hst_index; @@ -214,9 +217,9 @@ NextHopRoundRobin::findNextHop(TSHttpTxn txnp, void *ih, time_t now) } while (!wrapped); if (go_direct == true) { - result->result = PARENT_DIRECT; + result->result = TS_PARENT_DIRECT; } else { - result->result = PARENT_FAIL; + result->result = TS_PARENT_FAIL; } result->hostname = nullptr; diff --git a/proxy/http/remap/NextHopRoundRobin.h b/proxy/http/remap/NextHopRoundRobin.h index a7cb8233153..e3bfffaa317 100644 --- a/proxy/http/remap/NextHopRoundRobin.h +++ b/proxy/http/remap/NextHopRoundRobin.h @@ -28,17 +28,20 @@ class NextHopRoundRobin : public NextHopSelectionStrategy { - std::mutex _mutex; - uint32_t latched_index = 0; - public: + const uint32_t LineNumberPlaceholder = 99999; + NextHopRoundRobin() = delete; - NextHopRoundRobin(const std::string_view &name, const NHPolicyType &policy) : NextHopSelectionStrategy(name, policy) {} + NextHopRoundRobin(const std::string_view &name, const NHPolicyType &policy); ~NextHopRoundRobin(); bool Init(const YAML::Node &n) { return NextHopSelectionStrategy::Init(n); } - void findNextHop(TSHttpTxn txnp, void *ih = nullptr, time_t now = 0) override; + void findNextHop(TSHttpTxn txnp, time_t now = 0) override; + // private: + std::mutex _mutex; + uint32_t latched_index = 0; + NHPolicyType policy_type = NH_UNDEFINED; }; diff --git a/proxy/http/remap/NextHopSelectionStrategy.cc b/proxy/http/remap/NextHopSelectionStrategy.cc index e589c3ebc2f..e19f42d7258 100644 --- a/proxy/http/remap/NextHopSelectionStrategy.cc +++ b/proxy/http/remap/NextHopSelectionStrategy.cc @@ -34,14 +34,9 @@ constexpr std::string_view exhaust_rings = "exhaust_ring"; constexpr std::string_view active_health_check = "active"; constexpr std::string_view passive_health_check = "passive"; -constexpr const char *policy_strings[] = {"NH_UNDEFINED", "NH_FIRST_LIVE", "NH_RR_STRICT", - "NH_RR_IP", "NH_RR_LATCHED", "NH_CONSISTENT_HASH"}; - -NextHopSelectionStrategy::NextHopSelectionStrategy(const std::string_view &name, const NHPolicyType &policy) +NextHopSelectionStrategy::NextHopSelectionStrategy(const std::string_view &name) { strategy_name = name; - policy_type = policy; - NH_Debug(NH_DEBUG_TAG, "Using a selection strategy of type %s", policy_strings[policy]); } // @@ -150,10 +145,10 @@ NextHopSelectionStrategy::Init(const YAML::Node &n) Machine *mach = Machine::instance(); HostStatus &h_stat = HostStatus::instance(); uint32_t grp_size = groups_node.size(); - if (grp_size > MAX_GROUP_RINGS) { + if (grp_size > TS_MAX_GROUP_RINGS) { NH_Note("the groups list exceeds the maximum of %d for the strategy '%s'. Only the first %d groups will be configured.", - MAX_GROUP_RINGS, strategy_name.c_str(), MAX_GROUP_RINGS); - groups = MAX_GROUP_RINGS; + TS_MAX_GROUP_RINGS, strategy_name.c_str(), TS_MAX_GROUP_RINGS); + groups = TS_MAX_GROUP_RINGS; } else { groups = groups_node.size(); } @@ -175,7 +170,7 @@ NextHopSelectionStrategy::Init(const YAML::Node &n) host_rec->group_index = grp; host_rec->host_index = hst; if (mach->is_self(host_rec->hostname.c_str())) { - h_stat.setHostStatus(host_rec->hostname.c_str(), HostStatus_t::HOST_STATUS_DOWN, 0, Reason::SELF_DETECT); + h_stat.setHostStatus(host_rec->hostname.c_str(), TSHostStatus::TS_HOST_STATUS_DOWN, 0, Reason::SELF_DETECT); } hosts_inner.push_back(std::move(host_rec)); num_parents++; @@ -195,14 +190,13 @@ NextHopSelectionStrategy::Init(const YAML::Node &n) } void -NextHopSelectionStrategy::markNextHop(TSHttpTxn txnp, const char *hostname, const int port, const NHCmd status, void *ih, - const time_t now) +NextHopSelectionStrategy::markNextHop(TSHttpTxn txnp, const char *hostname, const int port, const NHCmd status, const time_t now) { - return passive_health.markNextHop(txnp, hostname, port, status, ih, now); + return passive_health.markNextHop(txnp, hostname, port, status, now); } bool -NextHopSelectionStrategy::nextHopExists(TSHttpTxn txnp, void *ih) +NextHopSelectionStrategy::nextHopExists(TSHttpTxn txnp) { HttpSM *sm = reinterpret_cast(txnp); int64_t sm_id = sm->sm_id; @@ -220,18 +214,30 @@ NextHopSelectionStrategy::nextHopExists(TSHttpTxn txnp, void *ih) } bool -NextHopSelectionStrategy::responseIsRetryable(unsigned int current_retry_attempts, HTTPStatus response_code) +NextHopSelectionStrategy::responseIsRetryable(unsigned int current_retry_attempts, TSHttpStatus response_code) { - return this->resp_codes.contains(response_code) && current_retry_attempts < this->max_simple_retries && + return this->resp_codes.contains(static_cast(response_code)) && current_retry_attempts < this->max_simple_retries && current_retry_attempts < this->num_parents; } bool -NextHopSelectionStrategy::onFailureMarkParentDown(HTTPStatus response_code) +NextHopSelectionStrategy::onFailureMarkParentDown(TSHttpStatus response_code) { return static_cast(response_code) >= 500 && static_cast(response_code) <= 599; } +bool +NextHopSelectionStrategy::goDirect() +{ + return this->go_direct; +} + +bool +NextHopSelectionStrategy::parentIsProxy() +{ + return this->parent_is_proxy; +} + namespace YAML { template <> struct convert { diff --git a/proxy/http/remap/NextHopSelectionStrategy.h b/proxy/http/remap/NextHopSelectionStrategy.h index 6076188f00c..9fd100af9f8 100644 --- a/proxy/http/remap/NextHopSelectionStrategy.h +++ b/proxy/http/remap/NextHopSelectionStrategy.h @@ -23,7 +23,7 @@ #pragma once -#include "ts/nexthop.h" +#include "tscpp/api/nexthop.h" #include "ParentSelection.h" #ifndef _NH_UNIT_TESTS_ @@ -37,6 +37,9 @@ constexpr const char *NH_DEBUG_TAG = "next_hop"; +constexpr const char *policy_strings[] = {"NH_UNDEFINED", "NH_FIRST_LIVE", "NH_RR_STRICT", + "NH_RR_IP", "NH_RR_LATCHED", "NH_CONSISTENT_HASH"}; + namespace YAML { class Node; @@ -44,11 +47,12 @@ class Node; enum NHPolicyType { NH_UNDEFINED = 0, - NH_FIRST_LIVE, // first available nexthop - NH_RR_STRICT, // strict round robin - NH_RR_IP, // round robin by client ip. - NH_RR_LATCHED, // latched to available next hop. - NH_CONSISTENT_HASH // consistent hashing strategy. + NH_FIRST_LIVE, // first available nexthop + NH_RR_STRICT, // strict round robin + NH_RR_IP, // round robin by client ip. + NH_RR_LATCHED, // latched to available next hop. + NH_CONSISTENT_HASH, // consistent hashing strategy. + NH_PLUGIN, // hashing strategy is a plugin }; enum NHSchemeType { NH_SCHEME_NONE = 0, NH_SCHEME_HTTP, NH_SCHEME_HTTPS }; @@ -196,41 +200,39 @@ struct HostRecord : ATSConsistentHashNode { } }; -class NextHopHealthStatus : public NHHealthStatus +class NextHopHealthStatus { public: void insert(std::vector> &hosts); - bool isNextHopAvailable(TSHttpTxn txn, const char *hostname, const int port, void *ih = nullptr) override; - void markNextHop(TSHttpTxn txn, const char *hostname, const int port, const NHCmd status, void *ih = nullptr, - const time_t now = 0) override; + void markNextHop(TSHttpTxn txn, const char *hostname, const int port, const NHCmd status, const time_t now = 0); NextHopHealthStatus(){}; private: std::unordered_map> host_map; }; -class NextHopSelectionStrategy +class NextHopSelectionStrategy : public TSNextHopSelectionStrategy { public: NextHopSelectionStrategy(); - NextHopSelectionStrategy(const std::string_view &name, const NHPolicyType &type); + NextHopSelectionStrategy(const std::string_view &name); virtual ~NextHopSelectionStrategy(){}; bool Init(const YAML::Node &n); - virtual void findNextHop(TSHttpTxn txnp, void *ih = nullptr, time_t now = 0) = 0; - void markNextHop(TSHttpTxn txnp, const char *hostname, const int port, const NHCmd status, void *ih = nullptr, - const time_t now = 0); - bool nextHopExists(TSHttpTxn txnp, void *ih = nullptr); - - virtual bool responseIsRetryable(unsigned int current_retry_attempts, HTTPStatus response_code); - virtual bool onFailureMarkParentDown(HTTPStatus response_code); + virtual void findNextHop(TSHttpTxn txnp, time_t now = 0) = 0; + virtual void markNextHop(TSHttpTxn txnp, const char *hostname, const int port, const NHCmd status, const time_t now = 0); + virtual bool nextHopExists(TSHttpTxn txnp); + virtual bool responseIsRetryable(unsigned int current_retry_attempts, TSHttpStatus response_code); + virtual bool onFailureMarkParentDown(TSHttpStatus response_code); + virtual bool goDirect(); + virtual bool parentIsProxy(); + // protected: std::string strategy_name; - bool go_direct = true; - bool parent_is_proxy = true; - bool ignore_self_detect = false; - NHPolicyType policy_type = NH_UNDEFINED; - NHSchemeType scheme = NH_SCHEME_NONE; - NHRingMode ring_mode = NH_ALTERNATE_RING; + bool go_direct = true; + bool parent_is_proxy = true; + bool ignore_self_detect = false; + NHSchemeType scheme = NH_SCHEME_NONE; + NHRingMode ring_mode = NH_ALTERNATE_RING; ResponseCodes resp_codes; HealthChecks health_checks; NextHopHealthStatus passive_health; diff --git a/proxy/http/remap/NextHopStrategyFactory.cc b/proxy/http/remap/NextHopStrategyFactory.cc index 4de0b8aa521..31739a8a0d0 100644 --- a/proxy/http/remap/NextHopStrategyFactory.cc +++ b/proxy/http/remap/NextHopStrategyFactory.cc @@ -111,7 +111,7 @@ NextHopStrategyFactory::~NextHopStrategyFactory() void NextHopStrategyFactory::createStrategy(const std::string &name, const NHPolicyType policy_type, const YAML::Node &node) { - std::shared_ptr strat; + std::shared_ptr strat; std::shared_ptr strat_rr; std::shared_ptr strat_chash; @@ -134,7 +134,7 @@ NextHopStrategyFactory::createStrategy(const std::string &name, const NHPolicyTy } break; case NH_CONSISTENT_HASH: - strat_chash = std::make_shared(name, policy_type); + strat_chash = std::make_shared(name); if (strat_chash->Init(node)) { _strategies.emplace(std::make_pair(std::string(name), strat_chash)); } else { @@ -146,26 +146,35 @@ NextHopStrategyFactory::createStrategy(const std::string &name, const NHPolicyTy }; } -std::shared_ptr +std::shared_ptr NextHopStrategyFactory::strategyInstance(const char *name) { - std::shared_ptr ps_strategy; - if (!strategies_loaded) { NH_Error("no strategy configurations were defined, see defintions in '%s' file", fn.c_str()); return nullptr; - } else { - auto it = _strategies.find(name); - if (it == _strategies.end()) { - // NH_Error("no strategy found for name: %s", name); - return nullptr; - } else { - ps_strategy = it->second; - ps_strategy->distance = std::distance(_strategies.begin(), it); - } } + auto strategy = _strategies.find(name); + if (strategy == _strategies.end()) { + // NH_Error("no strategy found for name: %s", name); + return nullptr; + } + return strategy->second; +} - return ps_strategy; +/* + * Adds a new strategy to the factory. + * Designed for plugins which want to dynamically add strategies. + */ +bool +NextHopStrategyFactory::addStrategy(const std::string_view name, std::shared_ptr strategy) +{ + if (_strategies.find(name) != _strategies.end()) { + NH_Error("failed to add strategy '%.*s' because a strategy with that name already exists", int(name.size()), name.data()); + return false; + } + _strategies.emplace(name, strategy); + strategies_loaded = true; + return true; } /* diff --git a/proxy/http/remap/NextHopStrategyFactory.h b/proxy/http/remap/NextHopStrategyFactory.h index ecb25a8943c..a7d15f37fa5 100644 --- a/proxy/http/remap/NextHopStrategyFactory.h +++ b/proxy/http/remap/NextHopStrategyFactory.h @@ -42,7 +42,8 @@ class NextHopStrategyFactory NextHopStrategyFactory() = delete; NextHopStrategyFactory(const char *file); ~NextHopStrategyFactory(); - std::shared_ptr strategyInstance(const char *name); + std::shared_ptr strategyInstance(const char *name); + bool addStrategy(const std::string_view name, std::shared_ptr strategy); bool strategies_loaded; @@ -50,5 +51,6 @@ class NextHopStrategyFactory std::string fn; void loadConfigFile(const std::string &file, std::stringstream &doc, std::unordered_set &include_once); void createStrategy(const std::string &name, const NHPolicyType policy_type, const YAML::Node &node); - std::unordered_map> _strategies; + void createStrategyPlugin(const std::string &name, const YAML::Node &node); + std::map, std::less<>> _strategies; }; diff --git a/proxy/http/remap/PluginDso.cc b/proxy/http/remap/PluginDso.cc index 76e302f215a..d002a09852f 100644 --- a/proxy/http/remap/PluginDso.cc +++ b/proxy/http/remap/PluginDso.cc @@ -284,6 +284,12 @@ PluginDso::instanceCount() return _instanceCount.refcount(); } +fs::path +PluginDso::configPath() const +{ + return _configPath; +} + bool PluginDso::isDynamicReloadEnabled() const { diff --git a/proxy/http/remap/PluginDso.h b/proxy/http/remap/PluginDso.h index 9e6df2d1a74..031e9fadb1e 100644 --- a/proxy/http/remap/PluginDso.h +++ b/proxy/http/remap/PluginDso.h @@ -94,6 +94,7 @@ class PluginDso : public PluginThreadContext void decInstanceCount(); int instanceCount(); bool isDynamicReloadEnabled() const; + virtual fs::path configPath() const; class LoadedPlugins : public RefCountObj { diff --git a/proxy/http/remap/PluginFactory.cc b/proxy/http/remap/PluginFactory.cc index 1837ddd7411..493fe430ac1 100644 --- a/proxy/http/remap/PluginFactory.cc +++ b/proxy/http/remap/PluginFactory.cc @@ -76,6 +76,18 @@ RemapPluginInst::doRemap(TSHttpTxn rh, TSRemapRequestInfo *rri) return _plugin.doRemap(_instance, rh, rri); } +std::shared_ptr +RemapPluginInst::initStrategy() +{ + return _plugin.initStrategy(this->_instance); +} + +std::string_view +RemapPluginInst::name() const +{ + return _plugin.configPath().view(); +} + void RemapPluginInst::osResponse(TSHttpTxn rh, int os_response_type) { diff --git a/proxy/http/remap/PluginFactory.h b/proxy/http/remap/PluginFactory.h index eb2ca5c1366..ba44863fd68 100644 --- a/proxy/http/remap/PluginFactory.h +++ b/proxy/http/remap/PluginFactory.h @@ -54,6 +54,12 @@ class RemapPluginInst TSRemapStatus doRemap(TSHttpTxn rh, TSRemapRequestInfo *rri); void osResponse(TSHttpTxn rh, int os_response_type); + /* Initialize the Strategy if the plugin has a TSRemapInitStrategy function. Returns a nullptr if not. */ + std::shared_ptr initStrategy(); + /* Gets the name of the plugin. + Lifetime: this. The returned string_view must not be used after the RemapPluginInst is deallocated. */ + std::string_view name() const; + /* List used by the plugin factory */ using self_type = RemapPluginInst; ///< Self reference type. self_type *_next = nullptr; diff --git a/proxy/http/remap/RemapConfig.cc b/proxy/http/remap/RemapConfig.cc index c9d06e9d4fb..5e52f6d429e 100644 --- a/proxy/http/remap/RemapConfig.cc +++ b/proxy/http/remap/RemapConfig.cc @@ -1277,25 +1277,6 @@ remap_parse_config_bti(const char *path, BUILD_TABLE_INFO *bti) } } - // check for a 'strategy' and if wire it up if one exists. - if ((bti->remap_optflg & REMAP_OPTFLG_STRATEGY) != 0 && - (maptype == FORWARD_MAP || maptype == FORWARD_MAP_REFERER || maptype == FORWARD_MAP_WITH_RECV_PORT)) { - const char *strategy = strchr(bti->argv[0], static_cast('=')); - if (strategy == nullptr) { - errStr = "missing 'strategy' name argument, unable to add mapping rule"; - goto MAP_ERROR; - } else { - strategy++; - new_mapping->strategy = bti->rewrite->strategyFactory->strategyInstance(strategy); - if (!new_mapping->strategy) { - snprintf(errStrBuf, sizeof(errStrBuf), "no strategy named '%s' is defined in the config", strategy); - errStr = errStrBuf; - goto MAP_ERROR; - } - Debug("url_rewrite_regex", "mapped the 'strategy' named %s", strategy); - } - } - // Check "remap" plugin options and load .so object if ((bti->remap_optflg & REMAP_OPTFLG_PLUGIN) != 0 && (maptype == FORWARD_MAP || maptype == FORWARD_MAP_REFERER || maptype == FORWARD_MAP_WITH_RECV_PORT)) { @@ -1323,6 +1304,40 @@ remap_parse_config_bti(const char *path, BUILD_TABLE_INFO *bti) } } + for (std::size_t pi = 0, pend = new_mapping->plugin_instance_count(); pi != pend; ++pi) { + RemapPluginInst *pluginInst = new_mapping->get_plugin_instance(pi); + if (pluginInst == nullptr) { + continue; // TODO error? + } + std::shared_ptr strategy = pluginInst->initStrategy(); + const std::string_view pluginName = pluginInst->name(); + if (strategy != nullptr) { + Debug("remap_plugin", "Remap plugin got strategy '%.*s'", int(pluginName.size()), pluginName.data()); + bti->rewrite->strategyFactory->addStrategy(pluginName, strategy); // will log an error if the name already exists + } else { + Debug("remap_plugin", "Remap plugin has no strategy in '%.*s'", int(pluginName.size()), pluginName.data()); + } + } + + // check for a 'strategy' and if wire it up if one exists. + if ((bti->remap_optflg & REMAP_OPTFLG_STRATEGY) != 0 && + (maptype == FORWARD_MAP || maptype == FORWARD_MAP_REFERER || maptype == FORWARD_MAP_WITH_RECV_PORT)) { + const char *strategy = strchr(bti->argv[0], static_cast('=')); + if (strategy == nullptr) { + errStr = "missing 'strategy' name argument, unable to add mapping rule"; + goto MAP_ERROR; + } else { + strategy++; + new_mapping->strategy = bti->rewrite->strategyFactory->strategyInstance(strategy); + if (!new_mapping->strategy) { + snprintf(errStrBuf, sizeof(errStrBuf), "no strategy named '%s' is defined in the config", strategy); + errStr = errStrBuf; + goto MAP_ERROR; + } + Debug("url_rewrite_regex", "mapped the 'strategy' named %s", strategy); + } + } + // Now add the mapping to appropriate container if (!bti->rewrite->InsertMapping(maptype, new_mapping, reg_map, fromHost_lower, is_cur_mapping_regex)) { errStr = "unable to add mapping rule to lookup table"; diff --git a/proxy/http/remap/RemapPluginInfo.cc b/proxy/http/remap/RemapPluginInfo.cc index 736fd4d6727..e30f289ef2e 100644 --- a/proxy/http/remap/RemapPluginInfo.cc +++ b/proxy/http/remap/RemapPluginInfo.cc @@ -30,6 +30,7 @@ #include "tscore/ink_apidefs.h" #include "RemapPluginInfo.h" +#include "NextHopSelectionStrategy.h" #ifdef PLUGIN_DSO_TESTS #include "unit-tests/plugin_testing_common.h" #else @@ -93,6 +94,7 @@ RemapPluginInfo::load(std::string &error) delete_instance_cb = getFunctionSymbol(TSREMAP_FUNCNAME_DELETE_INSTANCE); do_remap_cb = getFunctionSymbol(TSREMAP_FUNCNAME_DO_REMAP); os_response_cb = getFunctionSymbol(TSREMAP_FUNCNAME_OS_RESPONSE); + init_strategy_cb = getFunctionSymbol(TSREMAP_FUNCNAME_INIT_STRATEGY); /* Validate if the callback TSREMAP functions are specified correctly in the plugin. */ bool valid = true; @@ -271,6 +273,31 @@ RemapPluginInfo::indicatePostReload(TSRemapReloadStatus reloadStatus) resetPluginContext(); } +/* Initialize strategy (optional). */ +std::shared_ptr +RemapPluginInfo::initStrategy(void *ih) +{ + if (!init_strategy_cb) { + PluginDebug(_tag, "plugin '%s' has no init_strategy_cb, returning nullptr", _configPath.c_str()); + return std::shared_ptr(); + } + char tmpbuf[2048]; + ink_zero(tmpbuf); + TSNextHopSelectionStrategy *strategy_raw = nullptr; + if (init_strategy_cb(strategy_raw, ih, tmpbuf, sizeof(tmpbuf) - 1) != TS_SUCCESS) { + PluginDebug(_tag, "plugin '%s' has init_strategy_cb but not success, returning error", _configPath.c_str()); + std::string error; + error.assign("failed to initialize plugin ") + .append(_configPath.string()) + .append(": ") + .append(tmpbuf[0] ? tmpbuf : "Unknown plugin error"); + PluginError("plugin '%s' initializing strategy returned error: %s", _configPath.c_str(), error.c_str()); + return std::shared_ptr(); + } + PluginDebug(_tag, "plugin '%s' has init_strategy_cb, returning strategy", _configPath.c_str()); + return std::shared_ptr(strategy_raw); +} + inline void RemapPluginInfo::setPluginContext() { diff --git a/proxy/http/remap/RemapPluginInfo.h b/proxy/http/remap/RemapPluginInfo.h index 16156dc749c..1a774e736ad 100644 --- a/proxy/http/remap/RemapPluginInfo.h +++ b/proxy/http/remap/RemapPluginInfo.h @@ -26,6 +26,7 @@ #include #include +#include #include "tscore/ink_platform.h" #include "ts/apidefs.h" @@ -33,6 +34,7 @@ #include "PluginDso.h" class url_mapping; +class TSNextHopSelectionStrategy; extern thread_local PluginThreadContext *pluginThreadContext; @@ -44,6 +46,7 @@ static constexpr const char *const TSREMAP_FUNCNAME_NEW_INSTANCE = "TSRema static constexpr const char *const TSREMAP_FUNCNAME_DELETE_INSTANCE = "TSRemapDeleteInstance"; static constexpr const char *const TSREMAP_FUNCNAME_DO_REMAP = "TSRemapDoRemap"; static constexpr const char *const TSREMAP_FUNCNAME_OS_RESPONSE = "TSRemapOSResponse"; +static constexpr const char *const TSREMAP_FUNCNAME_INIT_STRATEGY = "TSRemapInitStrategy"; /** * Holds information for a remap plugin, remap specific callback entry points for plugin init/done and instance init/done, do_remap, @@ -68,6 +71,8 @@ class RemapPluginInfo : public PluginDso using Do_Remap_F = TSRemapStatus(void *ih, TSHttpTxn rh, TSRemapRequestInfo *rri); /// I have no idea what this is for. using OS_Response_F = void(void *ih, TSHttpTxn rh, int os_response_type); + /// Initialization function to create a new strategy, called on strategies load. + using Init_Strategy_F = TSReturnCode(TSNextHopSelectionStrategy *&strategy, void *ih, char *errbuf, int errbuf_size); void *dl_handle = nullptr; /* "handle" for the dynamic library */ Init_F *init_cb = nullptr; @@ -78,6 +83,7 @@ class RemapPluginInfo : public PluginDso Delete_Instance_F *delete_instance_cb = nullptr; Do_Remap_F *do_remap_cb = nullptr; OS_Response_F *os_response_cb = nullptr; + Init_Strategy_F *init_strategy_cb = nullptr; RemapPluginInfo(const fs::path &configPath, const fs::path &effectivePath, const fs::path &runtimePath); ~RemapPluginInfo(); @@ -101,6 +107,9 @@ class RemapPluginInfo : public PluginDso void indicatePreReload() override; void indicatePostReload(TSRemapReloadStatus reloadStatus) override; + /* Used by strategies on initialization, to add plugin-defined strategies for use by remaps */ + std::shared_ptr initStrategy(void *ih); + protected: /* Utility to be used only with unit testing */ std::string missingRequiredSymbolError(const std::string &pluginName, const char *required, const char *requiring = nullptr); diff --git a/proxy/http/remap/UrlMapping.h b/proxy/http/remap/UrlMapping.h index a0482c14c2f..1f0aa26ab6a 100644 --- a/proxy/http/remap/UrlMapping.h +++ b/proxy/http/remap/UrlMapping.h @@ -34,7 +34,7 @@ #include "tscore/Regex.h" #include "tscore/List.h" -class NextHopSelectionStrategy; +class TSNextHopSelectionStrategy; /** * Used to store http referer strings (and/or regexp) @@ -111,7 +111,7 @@ class url_mapping bool ip_allow_check_enabled_p = false; acl_filter_rule *filter = nullptr; // acl filtering (list of rules) LINK(url_mapping, link); // For use with the main Queue linked list holding all the mapping - std::shared_ptr strategy = nullptr; + std::shared_ptr strategy = nullptr; int getRank() const diff --git a/proxy/http/remap/unit-tests/nexthop_test_stubs.cc b/proxy/http/remap/unit-tests/nexthop_test_stubs.cc index 6eb2d7e7046..f98387fb9dd 100644 --- a/proxy/http/remap/unit-tests/nexthop_test_stubs.cc +++ b/proxy/http/remap/unit-tests/nexthop_test_stubs.cc @@ -30,6 +30,8 @@ #include "HttpSM.h" #include "nexthop_test_stubs.h" +const char *ParentResultStr[] = {"PARENT_UNDEFINED", "PARENT_DIRECT", "PARENT_SPECIFIED", "PARENT_AGENT", "PARENT_FAIL"}; + HttpSM::HttpSM() : Continuation(nullptr), vc_table(this) {} void HttpSM::cleanup() @@ -203,14 +205,14 @@ HostStatus::getHostStatus(const char *name) if (this->hosts_statuses[name] == nullptr) { // for unit tests only, always return a record with HOST_STATUS_UP, if it wasn't set with setHostStatus static HostStatRec rec; - rec.status = HostStatus_t::HOST_STATUS_UP; + rec.status = TSHostStatus::TS_HOST_STATUS_UP; return &rec; } return this->hosts_statuses[name]; } void -HostStatus::setHostStatus(char const *host, HostStatus_t status, unsigned int down_time, unsigned int reason) +HostStatus::setHostStatus(char const *host, TSHostStatus status, unsigned int down_time, unsigned int reason) { if (this->hosts_statuses[host] == nullptr) { this->hosts_statuses[host] = new (HostStatRec); diff --git a/proxy/http/remap/unit-tests/test_NextHopConsistentHash.cc b/proxy/http/remap/unit-tests/test_NextHopConsistentHash.cc index 47660d6cbee..977fc3e5135 100644 --- a/proxy/http/remap/unit-tests/test_NextHopConsistentHash.cc +++ b/proxy/http/remap/unit-tests/test_NextHopConsistentHash.cc @@ -51,9 +51,11 @@ SCENARIO("Testing NextHopConsistentHash class, using policy 'consistent_hash'", GIVEN("Loading the consistent-hash-tests.yaml config for 'consistent_hash' tests.") { // load the configuration strtegies. - std::shared_ptr strategy; + std::shared_ptr strategy; NextHopStrategyFactory nhf(TS_SRC_DIR "unit-tests/consistent-hash-tests.yaml"); - strategy = nhf.strategyInstance("consistent-hash-1"); + strategy = nhf.strategyInstance("consistent-hash-1"); + NextHopConsistentHash *ptr = static_cast(strategy.get()); + REQUIRE(ptr != nullptr); WHEN("the config is loaded.") { @@ -61,7 +63,7 @@ SCENARIO("Testing NextHopConsistentHash class, using policy 'consistent_hash'", { REQUIRE(nhf.strategies_loaded == true); REQUIRE(strategy != nullptr); - REQUIRE(strategy->groups == 3); + REQUIRE(ptr->groups == 3); } } @@ -94,7 +96,7 @@ SCENARIO("Testing NextHopConsistentHash class, using policy 'consistent_hash'", result->reset(); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "p1.foo.com") == 0); // mark down p1.foo.com. markNextHop looks at the 'result' @@ -107,7 +109,7 @@ SCENARIO("Testing NextHopConsistentHash class, using policy 'consistent_hash'", build_request(10002, &sm, nullptr, "rabbit.net", nullptr); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "p2.foo.com") == 0); // mark down p2.foo.com @@ -118,7 +120,7 @@ SCENARIO("Testing NextHopConsistentHash class, using policy 'consistent_hash'", build_request(10003, &sm, nullptr, "rabbit.net", nullptr); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "s2.bar.com") == 0); // mark down s2.bar.com @@ -129,7 +131,7 @@ SCENARIO("Testing NextHopConsistentHash class, using policy 'consistent_hash'", build_request(10004, &sm, nullptr, "rabbit.net", nullptr); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "s1.bar.com") == 0); // mark down s1.bar.com. @@ -140,7 +142,7 @@ SCENARIO("Testing NextHopConsistentHash class, using policy 'consistent_hash'", build_request(10005, &sm, nullptr, "rabbit.net", nullptr); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "q1.bar.com") == 0); // mark down q1.bar.com @@ -150,7 +152,7 @@ SCENARIO("Testing NextHopConsistentHash class, using policy 'consistent_hash'", build_request(10006, &sm, nullptr, "rabbit.net", nullptr); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "q2.bar.com") == 0); // mark down q2.bar.com @@ -160,7 +162,7 @@ SCENARIO("Testing NextHopConsistentHash class, using policy 'consistent_hash'", build_request(10007, &sm, nullptr, "rabbit.net", nullptr); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_DIRECT); + CHECK(result->result == TSParentResultType::TS_PARENT_DIRECT); CHECK(result->hostname == nullptr); // sleep and test that q2 is becomes retryable; @@ -169,8 +171,8 @@ SCENARIO("Testing NextHopConsistentHash class, using policy 'consistent_hash'", // eighth request - reusing the ParentResult from the last request // simulating a failure triggers a search for another parent, not firstcall. build_request(10008, &sm, nullptr, "rabbit.net", nullptr); - strategy->findNextHop(txnp, nullptr, now); - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + strategy->findNextHop(txnp, now); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "q2.bar.com") == 0); // free up request resources. @@ -190,9 +192,11 @@ SCENARIO("Testing NextHopConsistentHash class (all firstcalls), using policy 'co GIVEN("Loading the consistent-hash-tests.yaml config for 'consistent_hash' tests.") { - std::shared_ptr strategy; + std::shared_ptr strategy; NextHopStrategyFactory nhf(TS_SRC_DIR "unit-tests/consistent-hash-tests.yaml"); - strategy = nhf.strategyInstance("consistent-hash-1"); + strategy = nhf.strategyInstance("consistent-hash-1"); + NextHopConsistentHash *ptr = static_cast(strategy.get()); + REQUIRE(ptr != nullptr); WHEN("the config is loaded.") { @@ -200,7 +204,7 @@ SCENARIO("Testing NextHopConsistentHash class (all firstcalls), using policy 'co { REQUIRE(nhf.strategies_loaded == true); REQUIRE(strategy != nullptr); - REQUIRE(strategy->groups == 3); + REQUIRE(ptr->groups == 3); } } @@ -225,7 +229,7 @@ SCENARIO("Testing NextHopConsistentHash class (all firstcalls), using policy 'co build_request(20001, &sm, nullptr, "rabbit.net", nullptr); result->reset(); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "p1.foo.com") == 0); // mark down p1.foo.com @@ -234,7 +238,7 @@ SCENARIO("Testing NextHopConsistentHash class (all firstcalls), using policy 'co build_request(20002, &sm, nullptr, "rabbit.net", nullptr); result->reset(); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "p2.foo.com") == 0); // mark down p2.foo.com @@ -244,7 +248,7 @@ SCENARIO("Testing NextHopConsistentHash class (all firstcalls), using policy 'co result->reset(); build_request(20003, &sm, nullptr, "rabbit.net", nullptr); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "s2.bar.com") == 0); // mark down s2.bar.com @@ -254,7 +258,7 @@ SCENARIO("Testing NextHopConsistentHash class (all firstcalls), using policy 'co result->reset(); build_request(20004, &sm, nullptr, "rabbit.net", nullptr); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "s1.bar.com") == 0); // mark down s1.bar.com @@ -264,15 +268,15 @@ SCENARIO("Testing NextHopConsistentHash class (all firstcalls), using policy 'co result->reset(); build_request(20005, &sm, nullptr, "rabbit.net/asset1", nullptr); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "q1.bar.com") == 0); // sixth request - wait and p1 should now become available time_t now = time(nullptr) + 5; result->reset(); build_request(20006, &sm, nullptr, "rabbit.net", nullptr); - strategy->findNextHop(txnp, nullptr, now); - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + strategy->findNextHop(txnp, now); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "p1.foo.com") == 0); } // free up request resources. @@ -292,12 +296,14 @@ SCENARIO("Testing NextHop ignore_self_detect false", "[NextHopConsistentHash]") GIVEN("Loading the consistent-hash-tests.yaml config for 'consistent_hash' tests.") { // load the configuration strtegies. - std::shared_ptr strategy; + std::shared_ptr strategy; NextHopStrategyFactory nhf(TS_SRC_DIR "unit-tests/consistent-hash-tests.yaml"); - strategy = nhf.strategyInstance("ignore-self-detect-false"); + strategy = nhf.strategyInstance("ignore-self-detect-false"); + NextHopConsistentHash *ptr = static_cast(strategy.get()); + REQUIRE(ptr != nullptr); HostStatus &hs = HostStatus::instance(); - hs.setHostStatus("localhost", HostStatus_t::HOST_STATUS_DOWN, 0, Reason::SELF_DETECT); + hs.setHostStatus("localhost", TSHostStatus::TS_HOST_STATUS_DOWN, 0, Reason::SELF_DETECT); WHEN("the config is loaded.") { @@ -305,7 +311,7 @@ SCENARIO("Testing NextHop ignore_self_detect false", "[NextHopConsistentHash]") { REQUIRE(nhf.strategies_loaded == true); REQUIRE(strategy != nullptr); - REQUIRE(strategy->groups == 2); + REQUIRE(ptr->groups == 2); } } @@ -323,7 +329,7 @@ SCENARIO("Testing NextHop ignore_self_detect false", "[NextHopConsistentHash]") build_request(10001, &sm, nullptr, "rabbit.net", nullptr); result->reset(); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_DIRECT); + CHECK(result->result == TSParentResultType::TS_PARENT_DIRECT); CHECK(result->hostname == nullptr); br_destroy(sm); } @@ -342,12 +348,14 @@ SCENARIO("Testing NextHop ignore_self_detect true", "[NextHopConsistentHash]") GIVEN("Loading the consistent-hash-tests.yaml config for 'consistent_hash' tests.") { // load the configuration strtegies. - std::shared_ptr strategy; + std::shared_ptr strategy; NextHopStrategyFactory nhf(TS_SRC_DIR "unit-tests/consistent-hash-tests.yaml"); - strategy = nhf.strategyInstance("ignore-self-detect-true"); + strategy = nhf.strategyInstance("ignore-self-detect-true"); + NextHopConsistentHash *ptr = static_cast(strategy.get()); + REQUIRE(ptr != nullptr); HostStatus &hs = HostStatus::instance(); - hs.setHostStatus("localhost", HostStatus_t::HOST_STATUS_DOWN, 0, Reason::SELF_DETECT); + hs.setHostStatus("localhost", TSHostStatus::TS_HOST_STATUS_DOWN, 0, Reason::SELF_DETECT); WHEN("the config is loaded.") { @@ -355,7 +363,7 @@ SCENARIO("Testing NextHop ignore_self_detect true", "[NextHopConsistentHash]") { REQUIRE(nhf.strategies_loaded == true); REQUIRE(strategy != nullptr); - REQUIRE(strategy->groups == 2); + REQUIRE(ptr->groups == 2); } } @@ -372,7 +380,7 @@ SCENARIO("Testing NextHop ignore_self_detect true", "[NextHopConsistentHash]") build_request(10001, &sm, nullptr, "rabbit.net", nullptr); result->reset(); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "localhost") == 0); CHECK(result->port == 8000); br_destroy(sm); @@ -392,9 +400,11 @@ SCENARIO("Testing NextHopConsistentHash same host different port markdown", "[Ne GIVEN("Loading the consistent-hash-tests.yaml config for 'consistent_hash' tests.") { // load the configuration strtegies. - std::shared_ptr strategy; + std::shared_ptr strategy; NextHopStrategyFactory nhf(TS_SRC_DIR "unit-tests/consistent-hash-tests.yaml"); - strategy = nhf.strategyInstance("same-host-different-port"); + strategy = nhf.strategyInstance("same-host-different-port"); + NextHopConsistentHash *ptr = static_cast(strategy.get()); + REQUIRE(ptr != nullptr); WHEN("the config is loaded.") { @@ -402,7 +412,7 @@ SCENARIO("Testing NextHopConsistentHash same host different port markdown", "[Ne { REQUIRE(nhf.strategies_loaded == true); REQUIRE(strategy != nullptr); - REQUIRE(strategy->groups == 3); + REQUIRE(ptr->groups == 3); } } @@ -422,7 +432,7 @@ SCENARIO("Testing NextHopConsistentHash same host different port markdown", "[Ne result->reset(); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "localhost") == 0); CHECK(result->port == 8000); @@ -431,7 +441,7 @@ SCENARIO("Testing NextHopConsistentHash same host different port markdown", "[Ne build_request(10002, &sm, nullptr, "rabbit.net", nullptr); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "localhost") == 0); CHECK(result->port == 8002); @@ -440,7 +450,7 @@ SCENARIO("Testing NextHopConsistentHash same host different port markdown", "[Ne build_request(10003, &sm, nullptr, "rabbit.net", nullptr); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "localhost") == 0); CHECK(result->port == 8004); br_destroy(sm); @@ -460,9 +470,11 @@ SCENARIO("Testing NextHopConsistentHash hash_string override", "[NextHopConsiste GIVEN("Loading the consistent-hash-tests.yaml config for 'consistent_hash' tests.") { // load the configuration strtegies. - std::shared_ptr strategy; + std::shared_ptr strategy; NextHopStrategyFactory nhf(TS_SRC_DIR "unit-tests/consistent-hash-tests.yaml"); - strategy = nhf.strategyInstance("hash-string-override"); + strategy = nhf.strategyInstance("hash-string-override"); + NextHopConsistentHash *ptr = static_cast(strategy.get()); + REQUIRE(ptr != nullptr); WHEN("the config is loaded.") { @@ -470,7 +482,7 @@ SCENARIO("Testing NextHopConsistentHash hash_string override", "[NextHopConsiste { REQUIRE(nhf.strategies_loaded == true); REQUIRE(strategy != nullptr); - REQUIRE(strategy->groups == 2); + REQUIRE(ptr->groups == 2); } } @@ -492,7 +504,7 @@ SCENARIO("Testing NextHopConsistentHash hash_string override", "[NextHopConsiste // We happen to know that 'foo.test' will be first if the hostname is the hash // and foo.test will be first for the hash 'first' and the bar.test hash 'second'. // So, if the hash_string override isn't getting applied, this will fail. - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "bar.test") == 0); CHECK(result->port == 80); @@ -519,9 +531,11 @@ SCENARIO("Testing NextHopConsistentHash class (alternating rings), using policy GIVEN("Loading the consistent-hash-tests.yaml config for 'consistent_hash' tests.") { - std::shared_ptr strategy; + std::shared_ptr strategy; NextHopStrategyFactory nhf(TS_SRC_DIR "unit-tests/consistent-hash-tests.yaml"); - strategy = nhf.strategyInstance("consistent-hash-2"); + strategy = nhf.strategyInstance("consistent-hash-2"); + NextHopConsistentHash *ptr = static_cast(strategy.get()); + REQUIRE(ptr != nullptr); WHEN("the config is loaded.") { @@ -529,7 +543,7 @@ SCENARIO("Testing NextHopConsistentHash class (alternating rings), using policy { REQUIRE(nhf.strategies_loaded == true); REQUIRE(strategy != nullptr); - REQUIRE(strategy->groups == 3); + REQUIRE(ptr->groups == 3); } } @@ -550,7 +564,7 @@ SCENARIO("Testing NextHopConsistentHash class (alternating rings), using policy build_request(30001, &sm, nullptr, "bunny.net/asset1", nullptr); result->reset(); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "c2.foo.com") == 0); // simulated failure, mark c2 down and retry request @@ -559,7 +573,7 @@ SCENARIO("Testing NextHopConsistentHash class (alternating rings), using policy // second request build_request(30002, &sm, nullptr, "bunny.net.net/asset1", nullptr); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "c3.bar.com") == 0); // mark down c3.bar.com @@ -569,7 +583,7 @@ SCENARIO("Testing NextHopConsistentHash class (alternating rings), using policy build_request(30003, &sm, nullptr, "bunny.net/asset2", nullptr); result->reset(); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "c6.bar.com") == 0); // just mark it down and retry request @@ -577,7 +591,7 @@ SCENARIO("Testing NextHopConsistentHash class (alternating rings), using policy // fourth request build_request(30004, &sm, nullptr, "bunny.net/asset2", nullptr); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "c1.foo.com") == 0); // mark it down @@ -586,7 +600,7 @@ SCENARIO("Testing NextHopConsistentHash class (alternating rings), using policy build_request(30005, &sm, nullptr, "bunny.net/asset3", nullptr); result->reset(); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "c4.bar.com") == 0); // mark it down and retry @@ -595,7 +609,7 @@ SCENARIO("Testing NextHopConsistentHash class (alternating rings), using policy result->reset(); build_request(30006, &sm, nullptr, "bunny.net/asset3", nullptr); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "c5.bar.com") == 0); // mark it down @@ -604,15 +618,15 @@ SCENARIO("Testing NextHopConsistentHash class (alternating rings), using policy result->reset(); build_request(30007, &sm, nullptr, "bunny.net/asset4", nullptr); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_FAIL); + CHECK(result->result == TSParentResultType::TS_PARENT_FAIL); CHECK(result->hostname == nullptr); // eighth request - retry after waiting for the retry window to expire. time_t now = time(nullptr) + 5; result->reset(); build_request(30008, &sm, nullptr, "bunny.net/asset4", nullptr); - strategy->findNextHop(txnp, nullptr, now); - CHECK(result->result == ParentResultType::PARENT_SPECIFIED); + strategy->findNextHop(txnp, now); + CHECK(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "c2.foo.com") == 0); } // free up request resources. diff --git a/proxy/http/remap/unit-tests/test_NextHopRoundRobin.cc b/proxy/http/remap/unit-tests/test_NextHopRoundRobin.cc index 506d6470391..389ff2aa439 100644 --- a/proxy/http/remap/unit-tests/test_NextHopRoundRobin.cc +++ b/proxy/http/remap/unit-tests/test_NextHopRoundRobin.cc @@ -47,17 +47,20 @@ SCENARIO("Testing NextHopRoundRobin class, using policy 'rr-strict'", "[NextHopR GIVEN("Loading the round-robin-tests.yaml config for round robin 'rr-strict' tests.") { - std::shared_ptr strategy; + std::shared_ptr strategy; NextHopStrategyFactory nhf(TS_SRC_DIR "unit-tests/round-robin-tests.yaml"); strategy = nhf.strategyInstance("rr-strict-exhaust-ring"); + NextHopRoundRobin *ptr = static_cast(strategy.get()); + REQUIRE(ptr != nullptr); + WHEN("the config is loaded.") { THEN("the rr-strict strategy is ready for use.") { REQUIRE(nhf.strategies_loaded == true); REQUIRE(strategy != nullptr); - REQUIRE(strategy->policy_type == NH_RR_STRICT); + REQUIRE(ptr->policy_type == NH_RR_STRICT); } } @@ -135,7 +138,7 @@ SCENARIO("Testing NextHopRoundRobin class, using policy 'rr-strict'", "[NextHopR build_request(10009, &sm, nullptr, "rabbit.net", nullptr); result->reset(); strategy->findNextHop(txnp); - CHECK(result->result == ParentResultType::PARENT_DIRECT); + CHECK(result->result == TSParentResultType::TS_PARENT_DIRECT); // check that nextHopExists() returns false when all parents are down. CHECK(strategy->nextHopExists(txnp) == false); @@ -146,15 +149,15 @@ SCENARIO("Testing NextHopRoundRobin class, using policy 'rr-strict'", "[NextHopR // ninth request, p1 and p2 are still down, should get p2.foo.com as it will be retried build_request(10010, &sm, nullptr, "rabbit.net", nullptr); result->reset(); - strategy->findNextHop(txnp, nullptr, now); - REQUIRE(result->result == ParentResultType::PARENT_SPECIFIED); + strategy->findNextHop(txnp, now); + REQUIRE(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "p2.foo.com") == 0); // tenth request, p1 should now be retried. build_request(10011, &sm, nullptr, "rabbit.net", nullptr); result->reset(); - strategy->findNextHop(txnp, nullptr, now); - REQUIRE(result->result == ParentResultType::PARENT_SPECIFIED); + strategy->findNextHop(txnp, now); + REQUIRE(result->result == TSParentResultType::TS_PARENT_SPECIFIED); CHECK(strcmp(result->hostname, "p1.foo.com") == 0); } br_destroy(sm); @@ -172,17 +175,20 @@ SCENARIO("Testing NextHopRoundRobin class, using policy 'first-live'", "[NextHop GIVEN("Loading the round-robin-tests.yaml config for round robin 'first-live' tests.") { - std::shared_ptr strategy; + std::shared_ptr strategy; NextHopStrategyFactory nhf(TS_SRC_DIR "unit-tests/round-robin-tests.yaml"); strategy = nhf.strategyInstance("first-live"); + REQUIRE(strategy != nullptr); + + NextHopRoundRobin *ptr = static_cast(strategy.get()); + REQUIRE(ptr != nullptr); WHEN("the config is loaded.") { THEN("the 'first-live' strategy is available.") { REQUIRE(nhf.strategies_loaded == true); - REQUIRE(strategy != nullptr); - REQUIRE(strategy->policy_type == NH_FIRST_LIVE); + REQUIRE(ptr->policy_type == NH_FIRST_LIVE); } } @@ -223,7 +229,7 @@ SCENARIO("Testing NextHopRoundRobin class, using policy 'first-live'", "[NextHop // fourth request, p1 should be marked for retry build_request(10015, &sm, nullptr, "rabbit.net", nullptr); result->reset(); - strategy->findNextHop(txnp, nullptr, now); + strategy->findNextHop(txnp, now); CHECK(strcmp(result->hostname, "p1.foo.com") == 0); } br_destroy(sm); @@ -241,9 +247,13 @@ SCENARIO("Testing NextHopRoundRobin class, using policy 'rr-ip'", "[NextHopRound GIVEN("Loading the round-robin-tests.yaml config for round robin 'rr-ip' tests.") { - std::shared_ptr strategy; + std::shared_ptr strategy; NextHopStrategyFactory nhf(TS_SRC_DIR "unit-tests/round-robin-tests.yaml"); strategy = nhf.strategyInstance("rr-ip"); + REQUIRE(strategy != nullptr); + NextHopRoundRobin *ptr = static_cast(strategy.get()); + REQUIRE(ptr != nullptr); + sockaddr_in sa1, sa2; sa1.sin_port = 10000; sa1.sin_family = AF_INET; @@ -261,7 +271,7 @@ SCENARIO("Testing NextHopRoundRobin class, using policy 'rr-ip'", "[NextHopRound { REQUIRE(nhf.strategies_loaded == true); REQUIRE(strategy != nullptr); - REQUIRE(strategy->policy_type == NH_RR_IP); + REQUIRE(ptr->policy_type == NH_RR_IP); } } @@ -328,9 +338,12 @@ SCENARIO("Testing NextHopRoundRobin class, using policy 'latched'", "[NextHopRou GIVEN("Loading the round-robin-tests.yaml config for round robin 'latched' tests.") { - std::shared_ptr strategy; + std::shared_ptr strategy; NextHopStrategyFactory nhf(TS_SRC_DIR "unit-tests/round-robin-tests.yaml"); strategy = nhf.strategyInstance("latched"); + REQUIRE(strategy != nullptr); + NextHopRoundRobin *ptr = static_cast(strategy.get()); + REQUIRE(ptr != nullptr); WHEN("the config is loaded.") { @@ -338,7 +351,7 @@ SCENARIO("Testing NextHopRoundRobin class, using policy 'latched'", "[NextHopRou { REQUIRE(nhf.strategies_loaded == true); REQUIRE(strategy != nullptr); - REQUIRE(strategy->policy_type == NH_RR_LATCHED); + REQUIRE(ptr->policy_type == NH_RR_LATCHED); } } diff --git a/proxy/http/remap/unit-tests/test_NextHopStrategyFactory.cc b/proxy/http/remap/unit-tests/test_NextHopStrategyFactory.cc index de9a71f8aa8..e0fdb3604a9 100644 --- a/proxy/http/remap/unit-tests/test_NextHopStrategyFactory.cc +++ b/proxy/http/remap/unit-tests/test_NextHopStrategyFactory.cc @@ -63,11 +63,11 @@ SCENARIO("factory tests loading yaml configs", "[loadConfig]") { THEN("Expect that these results for 'strategy-1'") { - std::shared_ptr strategy = nhf.strategyInstance("strategy-1"); + std::shared_ptr strategy = nhf.strategyInstance("strategy-1"); REQUIRE(strategy != nullptr); - CHECK(strategy->parent_is_proxy == true); - CHECK(strategy->max_simple_retries == 1); - CHECK(strategy->policy_type == NH_CONSISTENT_HASH); + CHECK(strategy->parentIsProxy() == true); + CHECK(strategy->responseIsRetryable(0, static_cast(503))); + CHECK(!strategy->responseIsRetryable(1, static_cast(503))); // down cast here using the stored pointer so that I can verify the hash_key was set // properly. @@ -75,16 +75,16 @@ SCENARIO("factory tests loading yaml configs", "[loadConfig]") REQUIRE(ptr != nullptr); CHECK(ptr->hash_key == NH_CACHE_HASH_KEY); - CHECK(strategy->go_direct == false); - CHECK(strategy->scheme == NH_SCHEME_HTTP); - CHECK(strategy->ring_mode == NH_EXHAUST_RING); - CHECK(strategy->groups == 2); - std::shared_ptr h = strategy->host_groups[0][0]; + CHECK(strategy->goDirect() == false); + // CHECK(ptr->scheme == NH_SCHEME_HTTP); // TODO fix + CHECK(ptr->ring_mode == NH_EXHAUST_RING); + CHECK(ptr->groups == 2); + std::shared_ptr h = ptr->host_groups[0][0]; CHECK(h != nullptr); - for (unsigned int i = 0; i < strategy->groups; i++) { - CHECK(strategy->host_groups[i].size() == 2); - for (unsigned int j = 0; j < strategy->host_groups[i].size(); j++) { - h = strategy->host_groups[i][j]; + for (unsigned int i = 0; i < ptr->groups; i++) { + CHECK(ptr->host_groups[i].size() == 2); + for (unsigned int j = 0; j < ptr->host_groups[i].size(); j++) { + h = ptr->host_groups[i][j]; switch (i) { case 0: switch (j) { @@ -138,9 +138,9 @@ SCENARIO("factory tests loading yaml configs", "[loadConfig]") } } } - CHECK(strategy->resp_codes.contains(404)); - CHECK(strategy->resp_codes.contains(503)); - CHECK(!strategy->resp_codes.contains(604)); + CHECK(ptr->resp_codes.contains(404)); + CHECK(ptr->resp_codes.contains(503)); + CHECK(!ptr->resp_codes.contains(604)); } } @@ -148,19 +148,21 @@ SCENARIO("factory tests loading yaml configs", "[loadConfig]") { THEN("Expect that these results for 'strategy-2'") { - std::shared_ptr strategy = nhf.strategyInstance("strategy-2"); + std::shared_ptr strategy = nhf.strategyInstance("strategy-2"); REQUIRE(strategy != nullptr); - CHECK(strategy->policy_type == NH_RR_STRICT); - CHECK(strategy->go_direct == true); - CHECK(strategy->scheme == NH_SCHEME_HTTP); - CHECK(strategy->ring_mode == NH_EXHAUST_RING); - CHECK(strategy->groups == 2); - std::shared_ptr h = strategy->host_groups[0][0]; + NextHopRoundRobin *ptr = static_cast(strategy.get()); + REQUIRE(ptr != nullptr); + CHECK(ptr->policy_type == NH_RR_STRICT); + CHECK(ptr->go_direct == true); + CHECK(ptr->scheme == NH_SCHEME_HTTP); + CHECK(ptr->ring_mode == NH_EXHAUST_RING); + CHECK(ptr->groups == 2); + std::shared_ptr h = ptr->host_groups[0][0]; CHECK(h != nullptr); - for (unsigned int i = 0; i < strategy->groups; i++) { - CHECK(strategy->host_groups[i].size() == 2); - for (unsigned int j = 0; j < strategy->host_groups[i].size(); j++) { - h = strategy->host_groups[i][j]; + for (unsigned int i = 0; i < ptr->groups; i++) { + CHECK(ptr->host_groups[i].size() == 2); + for (unsigned int j = 0; j < ptr->host_groups[i].size(); j++) { + h = ptr->host_groups[i][j]; switch (i) { case 0: switch (j) { @@ -210,9 +212,9 @@ SCENARIO("factory tests loading yaml configs", "[loadConfig]") } } } - CHECK(strategy->resp_codes.contains(404)); - CHECK(strategy->resp_codes.contains(503)); - CHECK(!strategy->resp_codes.contains(604)); + CHECK(ptr->resp_codes.contains(404)); + CHECK(ptr->resp_codes.contains(503)); + CHECK(!ptr->resp_codes.contains(604)); } } } @@ -235,19 +237,21 @@ SCENARIO("factory tests loading yaml configs", "[loadConfig]") { THEN("Expect that these results for 'strategy-3'") { - std::shared_ptr strategy = nhf.strategyInstance("strategy-3"); + std::shared_ptr strategy = nhf.strategyInstance("strategy-3"); REQUIRE(strategy != nullptr); - CHECK(strategy->policy_type == NH_RR_IP); - CHECK(strategy->go_direct == true); - CHECK(strategy->scheme == NH_SCHEME_HTTPS); - CHECK(strategy->ring_mode == NH_EXHAUST_RING); - CHECK(strategy->groups == 2); - std::shared_ptr h = strategy->host_groups[0][0]; + NextHopRoundRobin *ptr = static_cast(strategy.get()); + REQUIRE(ptr != nullptr); + CHECK(ptr->policy_type == NH_RR_IP); + CHECK(ptr->go_direct == true); + CHECK(ptr->scheme == NH_SCHEME_HTTPS); + CHECK(ptr->ring_mode == NH_EXHAUST_RING); + CHECK(ptr->groups == 2); + std::shared_ptr h = ptr->host_groups[0][0]; CHECK(h != nullptr); - for (unsigned int i = 0; i < strategy->groups; i++) { - CHECK(strategy->host_groups[i].size() == 2); - for (unsigned int j = 0; j < strategy->host_groups[i].size(); j++) { - h = strategy->host_groups[i][j]; + for (unsigned int i = 0; i < ptr->groups; i++) { + CHECK(ptr->host_groups[i].size() == 2); + for (unsigned int j = 0; j < ptr->host_groups[i].size(); j++) { + h = ptr->host_groups[i][j]; switch (i) { case 0: switch (j) { @@ -300,9 +304,9 @@ SCENARIO("factory tests loading yaml configs", "[loadConfig]") } } } - CHECK(strategy->resp_codes.contains(404)); - CHECK(strategy->resp_codes.contains(503)); - CHECK(!strategy->resp_codes.contains(604)); + CHECK(ptr->resp_codes.contains(404)); + CHECK(ptr->resp_codes.contains(503)); + CHECK(!ptr->resp_codes.contains(604)); } } @@ -310,19 +314,21 @@ SCENARIO("factory tests loading yaml configs", "[loadConfig]") { THEN("Expect that these results for 'strategy-4'") { - std::shared_ptr strategy = nhf.strategyInstance("strategy-4"); + std::shared_ptr strategy = nhf.strategyInstance("strategy-4"); REQUIRE(strategy != nullptr); - CHECK(strategy->policy_type == NH_RR_LATCHED); - CHECK(strategy->go_direct == true); - CHECK(strategy->scheme == NH_SCHEME_HTTP); - CHECK(strategy->ring_mode == NH_ALTERNATE_RING); - CHECK(strategy->groups == 1); - std::shared_ptr h = strategy->host_groups[0][0]; + NextHopRoundRobin *ptr = static_cast(strategy.get()); + REQUIRE(ptr != nullptr); + CHECK(ptr->policy_type == NH_RR_LATCHED); + CHECK(ptr->go_direct == true); + CHECK(ptr->scheme == NH_SCHEME_HTTP); + CHECK(ptr->ring_mode == NH_ALTERNATE_RING); + CHECK(ptr->groups == 1); + std::shared_ptr h = ptr->host_groups[0][0]; CHECK(h != nullptr); - for (unsigned int i = 0; i < strategy->groups; i++) { - CHECK(strategy->host_groups[i].size() == 2); - for (unsigned int j = 0; j < strategy->host_groups[i].size(); j++) { - h = strategy->host_groups[i][j]; + for (unsigned int i = 0; i < ptr->groups; i++) { + CHECK(ptr->host_groups[i].size() == 2); + for (unsigned int j = 0; j < ptr->host_groups[i].size(); j++) { + h = ptr->host_groups[i][j]; switch (i) { case 0: switch (j) { @@ -351,9 +357,9 @@ SCENARIO("factory tests loading yaml configs", "[loadConfig]") } } } - CHECK(strategy->resp_codes.contains(404)); - CHECK(strategy->resp_codes.contains(503)); - CHECK(!strategy->resp_codes.contains(604)); + CHECK(ptr->resp_codes.contains(404)); + CHECK(ptr->resp_codes.contains(503)); + CHECK(!ptr->resp_codes.contains(604)); } } } @@ -376,26 +382,28 @@ SCENARIO("factory tests loading yaml configs", "[loadConfig]") { THEN("expect the following details.") { - std::shared_ptr strategy = nhf.strategyInstance("mid-tier-north"); + std::shared_ptr strategy = nhf.strategyInstance("mid-tier-north"); REQUIRE(strategy != nullptr); - CHECK(strategy->parent_is_proxy == false); - CHECK(strategy->max_simple_retries == 2); - CHECK(strategy->policy_type == NH_RR_IP); - CHECK(strategy->go_direct == true); - CHECK(strategy->scheme == NH_SCHEME_HTTP); - CHECK(strategy->ring_mode == NH_EXHAUST_RING); - CHECK(strategy->groups == 2); - CHECK(strategy->resp_codes.contains(404)); - CHECK(strategy->resp_codes.contains(502)); - CHECK(!strategy->resp_codes.contains(604)); - CHECK(strategy->health_checks.active == true); - CHECK(strategy->health_checks.passive == true); - std::shared_ptr h = strategy->host_groups[0][0]; + NextHopRoundRobin *ptr = static_cast(strategy.get()); + REQUIRE(ptr != nullptr); + CHECK(ptr->parent_is_proxy == false); + CHECK(ptr->max_simple_retries == 2); + CHECK(ptr->policy_type == NH_RR_IP); + CHECK(ptr->go_direct == true); + CHECK(ptr->scheme == NH_SCHEME_HTTP); + CHECK(ptr->ring_mode == NH_EXHAUST_RING); + CHECK(ptr->groups == 2); + CHECK(ptr->resp_codes.contains(404)); + CHECK(ptr->resp_codes.contains(502)); + CHECK(!ptr->resp_codes.contains(604)); + CHECK(ptr->health_checks.active == true); + CHECK(ptr->health_checks.passive == true); + std::shared_ptr h = ptr->host_groups[0][0]; CHECK(h != nullptr); - for (unsigned int i = 0; i < strategy->groups; i++) { - CHECK(strategy->host_groups[i].size() == 2); - for (unsigned int j = 0; j < strategy->host_groups[i].size(); j++) { - h = strategy->host_groups[i][j]; + for (unsigned int i = 0; i < ptr->groups; i++) { + CHECK(ptr->host_groups[i].size() == 2); + for (unsigned int j = 0; j < ptr->host_groups[i].size(); j++) { + h = ptr->host_groups[i][j]; switch (i) { case 0: switch (j) { @@ -445,9 +453,9 @@ SCENARIO("factory tests loading yaml configs", "[loadConfig]") } } } - CHECK(strategy->resp_codes.contains(404)); - CHECK(strategy->resp_codes.contains(503)); - CHECK(!strategy->resp_codes.contains(604)); + CHECK(ptr->resp_codes.contains(404)); + CHECK(ptr->resp_codes.contains(503)); + CHECK(!ptr->resp_codes.contains(604)); } } @@ -455,27 +463,29 @@ SCENARIO("factory tests loading yaml configs", "[loadConfig]") { THEN("expect the following results.") { - std::shared_ptr strategy = nhf.strategyInstance("mid-tier-south"); + std::shared_ptr strategy = nhf.strategyInstance("mid-tier-south"); REQUIRE(strategy != nullptr); - CHECK(strategy->policy_type == NH_RR_LATCHED); - CHECK(strategy->parent_is_proxy == false); - CHECK(strategy->ignore_self_detect == false); - CHECK(strategy->max_simple_retries == 2); - CHECK(strategy->go_direct == false); - CHECK(strategy->scheme == NH_SCHEME_HTTP); - CHECK(strategy->ring_mode == NH_ALTERNATE_RING); - CHECK(strategy->groups == 2); - CHECK(strategy->resp_codes.contains(404)); - CHECK(strategy->resp_codes.contains(502)); - CHECK(!strategy->resp_codes.contains(604)); - CHECK(strategy->health_checks.active == true); - CHECK(strategy->health_checks.passive == true); - std::shared_ptr h = strategy->host_groups[0][0]; + NextHopRoundRobin *ptr = static_cast(strategy.get()); + REQUIRE(ptr != nullptr); + CHECK(ptr->policy_type == NH_RR_LATCHED); + CHECK(ptr->parent_is_proxy == false); + CHECK(ptr->ignore_self_detect == false); + CHECK(ptr->max_simple_retries == 2); + CHECK(ptr->go_direct == false); + CHECK(ptr->scheme == NH_SCHEME_HTTP); + CHECK(ptr->ring_mode == NH_ALTERNATE_RING); + CHECK(ptr->groups == 2); + CHECK(ptr->resp_codes.contains(404)); + CHECK(ptr->resp_codes.contains(502)); + CHECK(!ptr->resp_codes.contains(604)); + CHECK(ptr->health_checks.active == true); + CHECK(ptr->health_checks.passive == true); + std::shared_ptr h = ptr->host_groups[0][0]; CHECK(h != nullptr); - for (unsigned int i = 0; i < strategy->groups; i++) { - CHECK(strategy->host_groups[i].size() == 2); - for (unsigned int j = 0; j < strategy->host_groups[i].size(); j++) { - h = strategy->host_groups[i][j]; + for (unsigned int i = 0; i < ptr->groups; i++) { + CHECK(ptr->host_groups[i].size() == 2); + for (unsigned int j = 0; j < ptr->host_groups[i].size(); j++) { + h = ptr->host_groups[i][j]; switch (i) { case 0: switch (j) { @@ -525,9 +535,9 @@ SCENARIO("factory tests loading yaml configs", "[loadConfig]") } } } - CHECK(strategy->resp_codes.contains(404)); - CHECK(strategy->resp_codes.contains(503)); - CHECK(!strategy->resp_codes.contains(604)); + CHECK(ptr->resp_codes.contains(404)); + CHECK(ptr->resp_codes.contains(503)); + CHECK(!ptr->resp_codes.contains(604)); } } @@ -535,27 +545,29 @@ SCENARIO("factory tests loading yaml configs", "[loadConfig]") { THEN("expect the following results.") { - std::shared_ptr strategy = nhf.strategyInstance("mid-tier-east"); + std::shared_ptr strategy = nhf.strategyInstance("mid-tier-east"); REQUIRE(strategy != nullptr); - CHECK(strategy->policy_type == NH_FIRST_LIVE); - CHECK(strategy->parent_is_proxy == false); - CHECK(strategy->ignore_self_detect == true); - CHECK(strategy->max_simple_retries == 2); - CHECK(strategy->go_direct == false); - CHECK(strategy->scheme == NH_SCHEME_HTTPS); - CHECK(strategy->ring_mode == NH_ALTERNATE_RING); - CHECK(strategy->groups == 2); - CHECK(strategy->resp_codes.contains(404)); - CHECK(strategy->resp_codes.contains(502)); - CHECK(!strategy->resp_codes.contains(604)); - CHECK(strategy->health_checks.active == false); - CHECK(strategy->health_checks.passive == true); - std::shared_ptr h = strategy->host_groups[0][0]; + NextHopRoundRobin *ptr = static_cast(strategy.get()); + REQUIRE(ptr != nullptr); + CHECK(ptr->policy_type == NH_FIRST_LIVE); + CHECK(ptr->parent_is_proxy == false); + CHECK(ptr->ignore_self_detect == true); + CHECK(ptr->max_simple_retries == 2); + CHECK(ptr->go_direct == false); + CHECK(ptr->scheme == NH_SCHEME_HTTPS); + CHECK(ptr->ring_mode == NH_ALTERNATE_RING); + CHECK(ptr->groups == 2); + CHECK(ptr->resp_codes.contains(404)); + CHECK(ptr->resp_codes.contains(502)); + CHECK(!ptr->resp_codes.contains(604)); + CHECK(ptr->health_checks.active == false); + CHECK(ptr->health_checks.passive == true); + std::shared_ptr h = ptr->host_groups[0][0]; CHECK(h != nullptr); - for (unsigned int i = 0; i < strategy->groups; i++) { - CHECK(strategy->host_groups[i].size() == 2); - for (unsigned int j = 0; j < strategy->host_groups[i].size(); j++) { - h = strategy->host_groups[i][j]; + for (unsigned int i = 0; i < ptr->groups; i++) { + CHECK(ptr->host_groups[i].size() == 2); + for (unsigned int j = 0; j < ptr->host_groups[i].size(); j++) { + h = ptr->host_groups[i][j]; switch (i) { case 0: switch (j) { @@ -605,9 +617,9 @@ SCENARIO("factory tests loading yaml configs", "[loadConfig]") } } } - CHECK(strategy->resp_codes.contains(404)); - CHECK(strategy->resp_codes.contains(503)); - CHECK(!strategy->resp_codes.contains(604)); + CHECK(ptr->resp_codes.contains(404)); + CHECK(ptr->resp_codes.contains(503)); + CHECK(!ptr->resp_codes.contains(604)); } } @@ -615,26 +627,28 @@ SCENARIO("factory tests loading yaml configs", "[loadConfig]") { THEN("expect the following results.") { - std::shared_ptr strategy = nhf.strategyInstance("mid-tier-west"); + std::shared_ptr strategy = nhf.strategyInstance("mid-tier-west"); REQUIRE(strategy != nullptr); - CHECK(strategy->policy_type == NH_RR_STRICT); - CHECK(strategy->go_direct == true); - CHECK(strategy->scheme == NH_SCHEME_HTTPS); - CHECK(strategy->parent_is_proxy == false); - CHECK(strategy->max_simple_retries == 2); - CHECK(strategy->ring_mode == NH_EXHAUST_RING); - CHECK(strategy->groups == 2); - CHECK(strategy->resp_codes.contains(404)); - CHECK(strategy->resp_codes.contains(502)); - CHECK(!strategy->resp_codes.contains(604)); - CHECK(strategy->health_checks.active == true); - CHECK(strategy->health_checks.passive == false); - std::shared_ptr h = strategy->host_groups[0][0]; + NextHopRoundRobin *ptr = static_cast(strategy.get()); + REQUIRE(ptr != nullptr); + CHECK(ptr->policy_type == NH_RR_STRICT); + CHECK(ptr->go_direct == true); + CHECK(ptr->scheme == NH_SCHEME_HTTPS); + CHECK(ptr->parent_is_proxy == false); + CHECK(ptr->max_simple_retries == 2); + CHECK(ptr->ring_mode == NH_EXHAUST_RING); + CHECK(ptr->groups == 2); + CHECK(ptr->resp_codes.contains(404)); + CHECK(ptr->resp_codes.contains(502)); + CHECK(!ptr->resp_codes.contains(604)); + CHECK(ptr->health_checks.active == true); + CHECK(ptr->health_checks.passive == false); + std::shared_ptr h = ptr->host_groups[0][0]; CHECK(h != nullptr); - for (unsigned int i = 0; i < strategy->groups; i++) { - CHECK(strategy->host_groups[i].size() == 2); - for (unsigned int j = 0; j < strategy->host_groups[i].size(); j++) { - h = strategy->host_groups[i][j]; + for (unsigned int i = 0; i < ptr->groups; i++) { + CHECK(ptr->host_groups[i].size() == 2); + for (unsigned int j = 0; j < ptr->host_groups[i].size(); j++) { + h = ptr->host_groups[i][j]; switch (i) { case 0: switch (j) { @@ -684,9 +698,9 @@ SCENARIO("factory tests loading yaml configs", "[loadConfig]") } } } - CHECK(strategy->resp_codes.contains(404)); - CHECK(strategy->resp_codes.contains(503)); - CHECK(!strategy->resp_codes.contains(604)); + CHECK(ptr->resp_codes.contains(404)); + CHECK(ptr->resp_codes.contains(503)); + CHECK(!ptr->resp_codes.contains(604)); } } @@ -694,33 +708,28 @@ SCENARIO("factory tests loading yaml configs", "[loadConfig]") { THEN("expect the following results.") { - std::shared_ptr strategy = nhf.strategyInstance("mid-tier-midwest"); + std::shared_ptr strategy = nhf.strategyInstance("mid-tier-midwest"); REQUIRE(strategy != nullptr); - CHECK(strategy->policy_type == NH_CONSISTENT_HASH); - CHECK(strategy->parent_is_proxy == false); - CHECK(strategy->max_simple_retries == 2); - - // I need to down cast here using the stored pointer so that I can verify that - // the hash_key was set properly. NextHopConsistentHash *ptr = static_cast(strategy.get()); REQUIRE(ptr != nullptr); + CHECK(ptr->parent_is_proxy == false); + CHECK(ptr->max_simple_retries == 2); CHECK(ptr->hash_key == NH_CACHE_HASH_KEY); - - CHECK(strategy->go_direct == true); - CHECK(strategy->scheme == NH_SCHEME_HTTPS); - CHECK(strategy->ring_mode == NH_EXHAUST_RING); - CHECK(strategy->groups == 2); - CHECK(strategy->resp_codes.contains(404)); - CHECK(strategy->resp_codes.contains(502)); - CHECK(!strategy->resp_codes.contains(604)); - CHECK(strategy->health_checks.active == true); - CHECK(strategy->health_checks.passive == false); - std::shared_ptr h = strategy->host_groups[0][0]; + CHECK(ptr->go_direct == true); + CHECK(ptr->scheme == NH_SCHEME_HTTPS); + CHECK(ptr->ring_mode == NH_EXHAUST_RING); + CHECK(ptr->groups == 2); + CHECK(ptr->resp_codes.contains(404)); + CHECK(ptr->resp_codes.contains(502)); + CHECK(!ptr->resp_codes.contains(604)); + CHECK(ptr->health_checks.active == true); + CHECK(ptr->health_checks.passive == false); + std::shared_ptr h = ptr->host_groups[0][0]; CHECK(h != nullptr); - for (unsigned int i = 0; i < strategy->groups; i++) { - CHECK(strategy->host_groups[i].size() == 2); - for (unsigned int j = 0; j < strategy->host_groups[i].size(); j++) { - h = strategy->host_groups[i][j]; + for (unsigned int i = 0; i < ptr->groups; i++) { + CHECK(ptr->host_groups[i].size() == 2); + for (unsigned int j = 0; j < ptr->host_groups[i].size(); j++) { + h = ptr->host_groups[i][j]; switch (i) { case 0: switch (j) { @@ -770,9 +779,9 @@ SCENARIO("factory tests loading yaml configs", "[loadConfig]") } } } - CHECK(strategy->resp_codes.contains(404)); - CHECK(strategy->resp_codes.contains(503)); - CHECK(!strategy->resp_codes.contains(604)); + CHECK(ptr->resp_codes.contains(404)); + CHECK(ptr->resp_codes.contains(503)); + CHECK(!ptr->resp_codes.contains(604)); } } } @@ -798,26 +807,28 @@ SCENARIO("factory tests loading yaml configs from a directory", "[loadConfig]") { THEN("expect the following results.") { - std::shared_ptr strategy = nhf.strategyInstance("mid-tier-north"); + std::shared_ptr strategy = nhf.strategyInstance("mid-tier-north"); REQUIRE(strategy != nullptr); - CHECK(strategy->parent_is_proxy == false); - CHECK(strategy->max_simple_retries == 2); - CHECK(strategy->policy_type == NH_RR_IP); - CHECK(strategy->go_direct == true); - CHECK(strategy->scheme == NH_SCHEME_HTTP); - CHECK(strategy->ring_mode == NH_EXHAUST_RING); - CHECK(strategy->groups == 2); - CHECK(strategy->resp_codes.contains(404)); - CHECK(strategy->resp_codes.contains(502)); - CHECK(!strategy->resp_codes.contains(604)); - CHECK(strategy->health_checks.active == true); - CHECK(strategy->health_checks.passive == true); - std::shared_ptr h = strategy->host_groups[0][0]; + NextHopRoundRobin *ptr = static_cast(strategy.get()); + REQUIRE(ptr != nullptr); + CHECK(ptr->parent_is_proxy == false); + CHECK(ptr->max_simple_retries == 2); + CHECK(ptr->policy_type == NH_RR_IP); + CHECK(ptr->go_direct == true); + CHECK(ptr->scheme == NH_SCHEME_HTTP); + CHECK(ptr->ring_mode == NH_EXHAUST_RING); + CHECK(ptr->groups == 2); + CHECK(ptr->resp_codes.contains(404)); + CHECK(ptr->resp_codes.contains(502)); + CHECK(!ptr->resp_codes.contains(604)); + CHECK(ptr->health_checks.active == true); + CHECK(ptr->health_checks.passive == true); + std::shared_ptr h = ptr->host_groups[0][0]; CHECK(h != nullptr); - for (unsigned int i = 0; i < strategy->groups; i++) { - CHECK(strategy->host_groups[i].size() == 2); - for (unsigned int j = 0; j < strategy->host_groups[i].size(); j++) { - h = strategy->host_groups[i][j]; + for (unsigned int i = 0; i < ptr->groups; i++) { + CHECK(ptr->host_groups[i].size() == 2); + for (unsigned int j = 0; j < ptr->host_groups[i].size(); j++) { + h = ptr->host_groups[i][j]; switch (i) { case 0: switch (j) { @@ -867,9 +878,9 @@ SCENARIO("factory tests loading yaml configs from a directory", "[loadConfig]") } } } - CHECK(strategy->resp_codes.contains(404)); - CHECK(strategy->resp_codes.contains(503)); - CHECK(!strategy->resp_codes.contains(604)); + CHECK(ptr->resp_codes.contains(404)); + CHECK(ptr->resp_codes.contains(503)); + CHECK(!ptr->resp_codes.contains(604)); } } @@ -877,27 +888,29 @@ SCENARIO("factory tests loading yaml configs from a directory", "[loadConfig]") { THEN("expect the following results.") { - std::shared_ptr strategy = nhf.strategyInstance("mid-tier-south"); + std::shared_ptr strategy = nhf.strategyInstance("mid-tier-south"); REQUIRE(strategy != nullptr); - CHECK(strategy->policy_type == NH_RR_LATCHED); - CHECK(strategy->parent_is_proxy == false); - CHECK(strategy->ignore_self_detect == false); - CHECK(strategy->max_simple_retries == 2); - CHECK(strategy->go_direct == false); - CHECK(strategy->scheme == NH_SCHEME_HTTP); - CHECK(strategy->ring_mode == NH_ALTERNATE_RING); - CHECK(strategy->groups == 2); - CHECK(strategy->resp_codes.contains(404)); - CHECK(strategy->resp_codes.contains(502)); - CHECK(!strategy->resp_codes.contains(604)); - CHECK(strategy->health_checks.active == true); - CHECK(strategy->health_checks.passive == true); - std::shared_ptr h = strategy->host_groups[0][0]; + NextHopRoundRobin *ptr = static_cast(strategy.get()); + REQUIRE(ptr != nullptr); + CHECK(ptr->policy_type == NH_RR_LATCHED); + CHECK(ptr->parent_is_proxy == false); + CHECK(ptr->ignore_self_detect == false); + CHECK(ptr->max_simple_retries == 2); + CHECK(ptr->go_direct == false); + CHECK(ptr->scheme == NH_SCHEME_HTTP); + CHECK(ptr->ring_mode == NH_ALTERNATE_RING); + CHECK(ptr->groups == 2); + CHECK(ptr->resp_codes.contains(404)); + CHECK(ptr->resp_codes.contains(502)); + CHECK(!ptr->resp_codes.contains(604)); + CHECK(ptr->health_checks.active == true); + CHECK(ptr->health_checks.passive == true); + std::shared_ptr h = ptr->host_groups[0][0]; CHECK(h != nullptr); - for (unsigned int i = 0; i < strategy->groups; i++) { - CHECK(strategy->host_groups[i].size() == 2); - for (unsigned int j = 0; j < strategy->host_groups[i].size(); j++) { - h = strategy->host_groups[i][j]; + for (unsigned int i = 0; i < ptr->groups; i++) { + CHECK(ptr->host_groups[i].size() == 2); + for (unsigned int j = 0; j < ptr->host_groups[i].size(); j++) { + h = ptr->host_groups[i][j]; switch (i) { case 0: switch (j) { @@ -947,9 +960,9 @@ SCENARIO("factory tests loading yaml configs from a directory", "[loadConfig]") } } } - CHECK(strategy->resp_codes.contains(404)); - CHECK(strategy->resp_codes.contains(503)); - CHECK(!strategy->resp_codes.contains(604)); + CHECK(ptr->resp_codes.contains(404)); + CHECK(ptr->resp_codes.contains(503)); + CHECK(!ptr->resp_codes.contains(604)); } } } diff --git a/src/traffic_server/HostStatus.cc b/src/traffic_server/HostStatus.cc index ea4372209a5..c993710d658 100644 --- a/src/traffic_server/HostStatus.cc +++ b/src/traffic_server/HostStatus.cc @@ -20,13 +20,15 @@ See the License for the specific language governing permissions and limitations under the License. */ +#include #include "HostStatus.h" #include "ProcessManager.h" inline void -getStatName(std::string &stat_name, const char *name) +getStatName(std::string &stat_name, const std::string_view name) { - stat_name = stat_prefix + name; + stat_name.clear(); + stat_name.append(stat_prefix).append(name); } static void @@ -58,7 +60,7 @@ mgmt_host_status_up_callback(ts::MemSpan span) if (hs.getHostStat(stat_name, buf, 1024) == REC_ERR_FAIL) { hs.createHostStat(name); } - hs.setHostStatus(name, HostStatus_t::HOST_STATUS_UP, down_time, reason); + hs.setHostStatus(name, TSHostStatus::TS_HOST_STATUS_UP, down_time, reason); } } @@ -90,12 +92,12 @@ mgmt_host_status_down_callback(ts::MemSpan span) if (hs.getHostStat(stat_name, buf, 1024) == REC_ERR_FAIL) { hs.createHostStat(name); } - hs.setHostStatus(name, HostStatus_t::HOST_STATUS_DOWN, down_time, reason); + hs.setHostStatus(name, TSHostStatus::TS_HOST_STATUS_DOWN, down_time, reason); } } HostStatRec::HostStatRec() - : status(HOST_STATUS_UP), + : status(TS_HOST_STATUS_UP), reasons(0), active_marked_down(0), local_marked_down(0), @@ -126,9 +128,9 @@ HostStatRec::HostStatRec(std::string str) for (unsigned int i = 0; i < v1.size(); i++) { if (i == 0) { // set the status field if (v1.at(i).compare("HOST_STATUS_UP") == 0) { - status = HOST_STATUS_UP; + status = TS_HOST_STATUS_UP; } else if (v1.at(i).compare("HOST_STATUS_DOWN") == 0) { - status = HOST_STATUS_DOWN; + status = TS_HOST_STATUS_DOWN; } } else { // parse and set remaining reason fields. std::vector v2; @@ -195,7 +197,7 @@ handle_record_read(const RecRecord *rec, void *edata) // 1st move the pointer past the stat prefix. s += stat_prefix.length(); hostname = s; - hs.createHostStat(hostname.c_str(), rec->data.rec_string); + hs.createHostStat(hostname, rec->data.rec_string); HostStatRec h(rec->data.rec_string); hs.loadRecord(hostname, h); } @@ -226,13 +228,13 @@ HostStatus::loadHostStatusFromStats() } void -HostStatus::loadRecord(std::string &name, HostStatRec &h) +HostStatus::loadRecord(std::string_view name, HostStatRec &h) { HostStatRec *host_stat = nullptr; - Debug("host_statuses", "loading host status record for %s", name.c_str()); + Debug("host_statuses", "loading host status record for %.*s", int(name.size()), name.data()); ink_rwlock_wrlock(&host_status_rwlock); { - if (auto it = hosts_statuses.find(name.c_str()); it != hosts_statuses.end()) { + if (auto it = hosts_statuses.find(name); it != hosts_statuses.end()) { host_stat = it->second; } else { host_stat = static_cast(ats_malloc(sizeof(HostStatRec))); @@ -246,7 +248,7 @@ HostStatus::loadRecord(std::string &name, HostStatRec &h) } void -HostStatus::setHostStatus(const char *name, HostStatus_t status, const unsigned int down_time, const unsigned int reason) +HostStatus::setHostStatus(const std::string_view name, TSHostStatus status, const unsigned int down_time, const unsigned int reason) { std::string stat_name; char buf[1024] = {0}; @@ -260,7 +262,7 @@ HostStatus::setHostStatus(const char *name, HostStatus_t status, const unsigned RecErrT result = getHostStat(stat_name, buf, 1024); // update / insert status. - // using the hash table pointer to store the HostStatus_t value. + // using the hash table pointer to store the TSHostStatus value. HostStatRec *host_stat = nullptr; ink_rwlock_wrlock(&host_status_rwlock); { @@ -272,8 +274,8 @@ HostStatus::setHostStatus(const char *name, HostStatus_t status, const unsigned hosts_statuses.emplace(name, host_stat); } if (reason & Reason::ACTIVE) { - Debug("host_statuses", "for host %s set status: %s, Reason:ACTIVE", name, HostStatusNames[status]); - if (status == HostStatus_t::HOST_STATUS_DOWN) { + Debug("host_statuses", "for host %.*s set status: %s, Reason:ACTIVE", int(name.size()), name.data(), HostStatusNames[status]); + if (status == TSHostStatus::TS_HOST_STATUS_DOWN) { host_stat->active_marked_down = time(0); host_stat->active_down_time = down_time; host_stat->reasons |= Reason::ACTIVE; @@ -286,8 +288,8 @@ HostStatus::setHostStatus(const char *name, HostStatus_t status, const unsigned } } if (reason & Reason::LOCAL) { - Debug("host_statuses", "for host %s set status: %s, Reason:LOCAL", name, HostStatusNames[status]); - if (status == HostStatus_t::HOST_STATUS_DOWN) { + Debug("host_statuses", "for host %.*s set status: %s, Reason:LOCAL", int(name.size()), name.data(), HostStatusNames[status]); + if (status == TSHostStatus::TS_HOST_STATUS_DOWN) { host_stat->local_marked_down = time(0); host_stat->local_down_time = down_time; host_stat->reasons |= Reason::LOCAL; @@ -300,8 +302,8 @@ HostStatus::setHostStatus(const char *name, HostStatus_t status, const unsigned } } if (reason & Reason::MANUAL) { - Debug("host_statuses", "for host %s set status: %s, Reason:MANUAL", name, HostStatusNames[status]); - if (status == HostStatus_t::HOST_STATUS_DOWN) { + Debug("host_statuses", "for host %.*s set status: %s, Reason:MANUAL", int(name.size()), name.data(), HostStatusNames[status]); + if (status == TSHostStatus::TS_HOST_STATUS_DOWN) { host_stat->manual_marked_down = time(0); host_stat->manual_down_time = down_time; host_stat->reasons |= Reason::MANUAL; @@ -314,8 +316,9 @@ HostStatus::setHostStatus(const char *name, HostStatus_t status, const unsigned } } if (reason & Reason::SELF_DETECT) { - Debug("host_statuses", "for host %s set status: %s, Reason:SELF_DETECT", name, HostStatusNames[status]); - if (status == HostStatus_t::HOST_STATUS_DOWN) { + Debug("host_statuses", "for host %.*s set status: %s, Reason:SELF_DETECT", int(name.size()), name.data(), + HostStatusNames[status]); + if (status == TSHostStatus::TS_HOST_STATUS_DOWN) { host_stat->self_detect_marked_down = time(0); host_stat->reasons |= Reason::SELF_DETECT; } else { @@ -325,9 +328,9 @@ HostStatus::setHostStatus(const char *name, HostStatus_t status, const unsigned } } } - if (status == HostStatus_t::HOST_STATUS_UP) { + if (status == TSHostStatus::TS_HOST_STATUS_UP) { if (host_stat->reasons == 0) { - host_stat->status = HostStatus_t::HOST_STATUS_UP; + host_stat->status = TSHostStatus::TS_HOST_STATUS_UP; } Debug("host_statuses", "reasons: %d, status: %s", host_stat->reasons, HostStatusNames[host_stat->status]); } else { @@ -342,24 +345,27 @@ HostStatus::setHostStatus(const char *name, HostStatus_t status, const unsigned std::stringstream status_rec; status_rec << *host_stat; RecSetRecordString(stat_name.c_str(), const_cast(status_rec.str().c_str()), REC_SOURCE_EXPLICIT, true); - if (status == HostStatus_t::HOST_STATUS_UP) { - Debug("host_statuses", "set status up for name: %s, status: %d, stat_name: %s", name, status, stat_name.c_str()); + if (status == TSHostStatus::TS_HOST_STATUS_UP) { + Debug("host_statuses", "set status up for name: %.*s, status: %d, stat_name: %s", int(name.size()), name.data(), status, + stat_name.c_str()); } else { - Debug("host_statuses", "set status down for name: %s, status: %d, stat_name: %s", name, status, stat_name.c_str()); + Debug("host_statuses", "set status down for name: %.*s, status: %d, stat_name: %s", int(name.size()), name.data(), status, + stat_name.c_str()); } } - Debug("host_statuses", "name: %s, status: %d", name, status); + Debug("host_statuses", "name: %.*s, status: %d", int(name.size()), name.data(), status); // log it. - if (status == HostStatus_t::HOST_STATUS_DOWN) { - Note("Host %s has been marked down, down_time: %d - %s.", name, down_time, down_time == 0 ? "indefinitely." : "seconds."); + if (status == TSHostStatus::TS_HOST_STATUS_DOWN) { + Note("Host %.*s has been marked down, down_time: %d - %s.", int(name.size()), name.data(), down_time, + down_time == 0 ? "indefinitely." : "seconds."); } else { - Note("Host %s has been marked up.", name); + Note("Host %.*s has been marked up.", int(name.size()), name.data()); } } HostStatRec * -HostStatus::getHostStatus(const char *name) +HostStatus::getHostStatus(const std::string_view name) { HostStatRec *_status = nullptr; time_t now = time(0); @@ -377,29 +383,29 @@ HostStatus::getHostStatus(const char *name) ink_rwlock_unlock(&host_status_rwlock); // if the host was marked down and it's down_time has elapsed, mark it up. - if (lookup && _status->status == HostStatus_t::HOST_STATUS_DOWN) { + if (lookup && _status->status == TSHostStatus::TS_HOST_STATUS_DOWN) { unsigned int reasons = _status->reasons; if ((_status->reasons & Reason::ACTIVE) && _status->active_down_time > 0) { if ((_status->active_down_time + _status->active_marked_down) < now) { - Debug("host_statuses", "name: %s, now: %ld, down_time: %d, marked_down: %ld, reason: %s", name, now, - _status->active_down_time, _status->active_marked_down, Reason::ACTIVE_REASON); - setHostStatus(name, HostStatus_t::HOST_STATUS_UP, 0, Reason::ACTIVE); + Debug("host_statuses", "name: %.*s, now: %ld, down_time: %d, marked_down: %ld, reason: %s", int(name.size()), name.data(), + now, _status->active_down_time, _status->active_marked_down, Reason::ACTIVE_REASON); + setHostStatus(name, TSHostStatus::TS_HOST_STATUS_UP, 0, Reason::ACTIVE); reasons ^= Reason::ACTIVE; } } if ((_status->reasons & Reason::LOCAL) && _status->local_down_time > 0) { if ((_status->local_down_time + _status->local_marked_down) < now) { - Debug("host_statuses", "name: %s, now: %ld, down_time: %d, marked_down: %ld, reason: %s", name, now, - _status->local_down_time, _status->local_marked_down, Reason::LOCAL_REASON); - setHostStatus(name, HostStatus_t::HOST_STATUS_UP, 0, Reason::LOCAL); + Debug("host_statuses", "name: %.*s, now: %ld, down_time: %d, marked_down: %ld, reason: %s", int(name.size()), name.data(), + now, _status->local_down_time, _status->local_marked_down, Reason::LOCAL_REASON); + setHostStatus(name, TSHostStatus::TS_HOST_STATUS_UP, 0, Reason::LOCAL); reasons ^= Reason::LOCAL; } } if ((_status->reasons & Reason::MANUAL) && _status->manual_down_time > 0) { if ((_status->manual_down_time + _status->manual_marked_down) < now) { - Debug("host_statuses", "name: %s, now: %ld, down_time: %d, marked_down: %ld, reason: %s", name, now, - _status->manual_down_time, _status->manual_marked_down, Reason::MANUAL_REASON); - setHostStatus(name, HostStatus_t::HOST_STATUS_UP, 0, Reason::MANUAL); + Debug("host_statuses", "name: %.*s, now: %ld, down_time: %d, marked_down: %ld, reason: %s", int(name.size()), name.data(), + now, _status->manual_down_time, _status->manual_marked_down, Reason::MANUAL_REASON); + setHostStatus(name, TSHostStatus::TS_HOST_STATUS_UP, 0, Reason::MANUAL); reasons ^= Reason::MANUAL; } } @@ -410,7 +416,7 @@ HostStatus::getHostStatus(const char *name) } void -HostStatus::createHostStat(const char *name, const char *data) +HostStatus::createHostStat(const std::string_view name, const char *data) { char buf[1024] = {0}; HostStatRec r; @@ -431,7 +437,7 @@ HostStatus::createHostStat(const char *name, const char *data) } RecErrT -HostStatus::getHostStat(std::string &stat_name, char *buf, unsigned int buf_len) +HostStatus::getHostStat(const std::string &stat_name, char *buf, unsigned int buf_len) { return RecGetRecordString(stat_name.c_str(), buf, buf_len, true); } diff --git a/src/traffic_server/InkAPI.cc b/src/traffic_server/InkAPI.cc index 178fe150119..3faf9d4cdbc 100644 --- a/src/traffic_server/InkAPI.cc +++ b/src/traffic_server/InkAPI.cc @@ -74,6 +74,7 @@ #include "I_Machine.h" #include "HttpProxyServerMain.h" #include "shared/overridable_txn_vars.h" +#include "tscpp/api/parentresult.h" #include "ts/ts.h" @@ -9909,6 +9910,51 @@ TSHttpTxnRedoCacheLookup(TSHttpTxn txnp, const char *url, int length) return TS_ERROR; } +TSReturnCode +TSHostnameIsSelf(const char *hostname) +{ + return Machine::instance()->is_self(hostname) ? TS_SUCCESS : TS_ERROR; +} + +TSReturnCode +TSHostStatusGet(const char *hostname, const size_t hostname_len, TSHostStatus *status, unsigned int *reason) +{ + HostStatRec *hst = HostStatus::instance().getHostStatus(std::string_view(hostname, hostname_len)); + if (hst == nullptr) { + return TS_ERROR; + } + if (status != nullptr) { + *status = hst->status; + } + if (reason != nullptr) { + *reason = hst->reasons; + } + return TS_SUCCESS; +} + +void +TSHostStatusSet(const char *hostname, const size_t hostname_len, TSHostStatus status, const unsigned int down_time, + const unsigned int reason) +{ + HostStatus::instance().setHostStatus(std::string_view(hostname, hostname_len), status, down_time, reason); +} + +void +TSHttpTxnParentResultGet(TSHttpTxn txnp, TSParentResult *result) +{ + HttpSM *sm = reinterpret_cast(txnp); + ParentResult *sm_result = &sm->t_state.parent_result; + sm_result->copyTo(result); +} + +void +TSHttpTxnParentResultSet(TSHttpTxn txnp, TSParentResult *result) +{ + HttpSM *sm = reinterpret_cast(txnp); + ParentResult *sm_result = &sm->t_state.parent_result; + sm_result->copyFrom(result); +} + namespace { // Function that contains the common logic for TSRemapFrom/ToUrlGet().