From 1325cdf3f9311cdb67dda6acd80e83f455072e85 Mon Sep 17 00:00:00 2001 From: Kamil Cudnik Date: Fri, 16 Oct 2020 18:51:37 +0200 Subject: [PATCH] Add support for saiplayer bulk API and add performance timers (#666) * [sairedis] Add PerformanceIntervalTimer class * [sairedis] Add deserialize tests * [sairedis] Add performance timer to route_entry bulk create * [sairedis] Add PerformanceIntervalTimer class to makefile * [saiplayer] Add support for bulk generic API and bulk route_entry * [vs] Add performance timer for route_entry create API * [syncd] Fix for ZeroMQSelectableChannel thread join * [syncd] Add performance timers to syncd route_entry create and db --- lib/inc/PerformanceIntervalTimer.h | 51 +++++ lib/src/Makefile.am | 1 + lib/src/PerformanceIntervalTimer.cpp | 63 ++++++ lib/src/RedisRemoteSaiInterface.cpp | 15 +- lib/src/tests.cpp | 186 ++++++++++++++++++ saiplayer/SaiPlayer.cpp | 251 ++++++++++++++++++++---- saiplayer/SaiPlayer.h | 7 + syncd/Syncd.cpp | 62 +++++- syncd/ZeroMQSelectableChannel.cpp | 5 +- vslib/src/Makefile.am | 1 + vslib/src/VirtualSwitchSaiInterface.cpp | 30 ++- 11 files changed, 627 insertions(+), 45 deletions(-) create mode 100644 lib/inc/PerformanceIntervalTimer.h create mode 100644 lib/src/PerformanceIntervalTimer.cpp diff --git a/lib/inc/PerformanceIntervalTimer.h b/lib/inc/PerformanceIntervalTimer.h new file mode 100644 index 000000000000..4129f89c0379 --- /dev/null +++ b/lib/inc/PerformanceIntervalTimer.h @@ -0,0 +1,51 @@ +#pragma once + +#include "swss/sal.h" + +#include +#include + +namespace sairediscommon +{ + class PerformanceIntervalTimer + { + public: + + static constexpr unsigned int DEFAULT_LIMIT = 10000; + + public: + + PerformanceIntervalTimer( + _In_ const char* msg, + _In_ uint64_t limit = DEFAULT_LIMIT); + + ~PerformanceIntervalTimer() = default; // non virtual + + public: + + void start(); + + void stop(); + + void inc( + _In_ uint64_t val = 1); + + void reset(); + + public: + + static bool m_enable; + + private: + + std::string m_msg; + + std::chrono::time_point m_start; + std::chrono::time_point m_stop; + + uint64_t m_limit; + uint64_t m_count; + uint64_t m_calls; + uint64_t m_total; + }; +} diff --git a/lib/src/Makefile.am b/lib/src/Makefile.am index eb84b7244130..71910678a05a 100644 --- a/lib/src/Makefile.am +++ b/lib/src/Makefile.am @@ -10,6 +10,7 @@ lib_LTLIBRARIES = libsairedis.la noinst_LIBRARIES = libSaiRedis.a libSaiRedis_a_SOURCES = \ + PerformanceIntervalTimer.cpp \ ZeroMQChannel.cpp \ Channel.cpp \ Context.cpp \ diff --git a/lib/src/PerformanceIntervalTimer.cpp b/lib/src/PerformanceIntervalTimer.cpp new file mode 100644 index 000000000000..13d44d2c029d --- /dev/null +++ b/lib/src/PerformanceIntervalTimer.cpp @@ -0,0 +1,63 @@ +#include "PerformanceIntervalTimer.h" + +#include "swss/logger.h" + +using namespace sairediscommon; + +bool PerformanceIntervalTimer::m_enable = true; + +PerformanceIntervalTimer::PerformanceIntervalTimer( + _In_ const char*msg, + _In_ uint64_t limit): + m_msg(msg), + m_limit(limit) +{ + SWSS_LOG_ENTER(); + + reset(); +} + +void PerformanceIntervalTimer::start() +{ + SWSS_LOG_ENTER(); + + m_start = std::chrono::high_resolution_clock::now(); +} + +void PerformanceIntervalTimer::stop() +{ + SWSS_LOG_ENTER(); + + m_stop = std::chrono::high_resolution_clock::now(); +} + +void PerformanceIntervalTimer::inc( + _In_ uint64_t val) +{ + SWSS_LOG_ENTER(); + + m_count += val; + + m_calls++; + + m_total += std::chrono::duration_cast(m_stop-m_start).count(); + + if (m_count >= m_limit) + { + if (m_enable) + { + SWSS_LOG_NOTICE("%zu (calls %zu) %s op took: %zu ms", m_count, m_calls, m_msg.c_str(), m_total/1000000); + } + + reset(); + } +} + +void PerformanceIntervalTimer::reset() +{ + SWSS_LOG_ENTER(); + + m_count = 0; + m_calls = 0; + m_total = 0; +} diff --git a/lib/src/RedisRemoteSaiInterface.cpp b/lib/src/RedisRemoteSaiInterface.cpp index 429b803dc8bc..7cfe49cf70fa 100644 --- a/lib/src/RedisRemoteSaiInterface.cpp +++ b/lib/src/RedisRemoteSaiInterface.cpp @@ -6,6 +6,7 @@ #include "SkipRecordAttrContainer.h" #include "SwitchContainer.h" #include "ZeroMQChannel.h" +#include "PerformanceIntervalTimer.h" #include "sairediscommon.h" @@ -16,6 +17,7 @@ using namespace sairedis; using namespace saimeta; +using namespace sairediscommon; using namespace std::placeholders; std::string joinFieldValues( @@ -1541,6 +1543,10 @@ sai_status_t RedisRemoteSaiInterface::bulkCreate( // TODO support mode + static PerformanceIntervalTimer timer("RedisRemoteSaiInterface::bulkCreate(route_entry)"); + + timer.start(); + std::vector serialized_object_ids; // on create vid is put in db by syncd @@ -1550,15 +1556,20 @@ sai_status_t RedisRemoteSaiInterface::bulkCreate( serialized_object_ids.push_back(str_object_id); } - return bulkCreate( + auto status = bulkCreate( SAI_OBJECT_TYPE_ROUTE_ENTRY, serialized_object_ids, attr_count, attr_list, mode, object_statuses); -} + timer.stop(); + + timer.inc(object_count); + + return status; +} sai_status_t RedisRemoteSaiInterface::bulkCreate( _In_ uint32_t object_count, diff --git a/lib/src/tests.cpp b/lib/src/tests.cpp index 4a759d9504f8..894a58feb5c8 100644 --- a/lib/src/tests.cpp +++ b/lib/src/tests.cpp @@ -6,6 +6,7 @@ extern "C" { #include "swss/logger.h" #include "swss/table.h" +#include "swss/tokenize.h" #include "meta/sai_serialize.h" #include "meta/SaiAttributeList.h" @@ -104,6 +105,63 @@ void test_serialize_remove_route_entry(int n) std::cout << "ms: " << (double)us.count()/1000 << " / " << n << std::endl; } +void test_deserialize_route_entry_meta(int n) +{ + SWSS_LOG_ENTER(); + + // meta key 123ms/10k + auto s = serialize_route_entry(); + // auto s = sai_serialize_route_entry(get_route_entry()); + + std::cout << s << std::endl; + + auto start = std::chrono::high_resolution_clock::now(); + + for (int i = 0; i < n; i++) + { + sai_object_meta_key_t mk; + sai_deserialize_object_meta_key(s, mk); + + //sai_route_entry_t route_entry; + //sai_deserialize_route_entry(s, route_entry); + } + + auto end = std::chrono::high_resolution_clock::now(); + auto time = end - start; + auto us = std::chrono::duration_cast(time); + std::cout << "ms: " << (double)us.count()/1000 << " / " << n << std::endl; +} + +void test_deserialize_route_entry(int n) +{ + SWSS_LOG_ENTER(); + + // 7.2 ms + // 8.9 ms with try/catch + // 13ms for entire object meta key + + char buffer[1000]; + + sai_route_entry_t route_entry = get_route_entry(); + sai_serialize_route_entry(buffer, &route_entry); + + auto s = std::string(buffer); + + std::cout << s << std::endl; + + auto start = std::chrono::high_resolution_clock::now(); + + for (int i = 0; i < n; i++) + { + sai_deserialize_route_entry(s.c_str(), &route_entry); + } + + auto end = std::chrono::high_resolution_clock::now(); + auto time = end - start; + auto us = std::chrono::duration_cast(time); + std::cout << "ms: " << (double)us.count()/1000 << " / " << n << std::endl; +} + void test_serialize_remove_oid(int n) { SWSS_LOG_ENTER(); @@ -664,14 +722,142 @@ void test_ContextConfigContainer() auto ccc = ContextConfigContainer::loadFromFile("context_config.json"); } +static std::vector tokenize( + _In_ std::string input, + _In_ const std::string &delim) +{ + SWSS_LOG_ENTER(); + + /* + * input is modified so it can't be passed as reference + */ + + std::vector tokens; + + size_t pos = 0; + + while ((pos = input.find(delim)) != std::string::npos) + { + std::string token = input.substr(0, pos); + + input.erase(0, pos + delim.length()); + tokens.push_back(token); + } + + tokens.push_back(input); + + return tokens; +} + +static sai_object_type_t deserialize_object_type( + _In_ const std::string& s) +{ + SWSS_LOG_ENTER(); + + sai_object_type_t object_type; + + sai_deserialize_object_type(s, object_type); + + return object_type; +} + +void test_tokenize_bulk_route_entry() +{ + SWSS_LOG_ENTER(); + + auto header = "2020-09-24.21:06:54.045505|C|SAI_OBJECT_TYPE_ROUTE_ENTRY"; + auto route = "||{\"dest\":\"20c1:bb0:0:80::/64\",\"switch_id\":\"oid:0x21000000000000\",\"vr\":\"oid:0x3000000000022\"}|SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID=oid:0x500000000066c"; + + std::string line = header; + + int per = 100; + + for (int i = 0; i < per; i++) + { + line += route; + } + + auto tstart = std::chrono::high_resolution_clock::now(); + + int n = 1000; + + for (int c = 0; c < n; c++) + { + auto fields = tokenize(line, "||"); + + auto first = fields.at(0); // timestamp|action|objecttype + + std::string str_object_type = swss::tokenize(first, '|').at(2); + + sai_object_type_t object_type = deserialize_object_type(str_object_type); + + std::vector object_ids; + + std::vector> attributes; + + for (size_t idx = 1; idx < fields.size(); ++idx) + { + // object_id|attr=value|... + const std::string &joined = fields[idx]; + + auto split = swss::tokenize(joined, '|'); + + std::string str_object_id = split.front(); + + object_ids.push_back(str_object_id); + + std::vector entries; // attributes per object id + + SWSS_LOG_DEBUG("processing: %s", joined.c_str()); + + for (size_t i = 1; i < split.size(); ++i) + { + const auto &item = split[i]; + + auto start = item.find_first_of("="); + + auto field = item.substr(0, start); + auto value = item.substr(start + 1); + + swss::FieldValueTuple entry(field, value); + + entries.push_back(entry); + } + + // since now we converted this to proper list, we can extract attributes + + std::shared_ptr list = + std::make_shared(object_type, entries, false); + + attributes.push_back(list); + } + } + + auto tend = std::chrono::high_resolution_clock::now(); + auto time = tend - tstart; + auto us = std::chrono::duration_cast(time); + + std::cout << "ms: " << (double)us.count()/1000.0 / n << " n " << n << " / per " << per << std::endl; + std::cout << "s: " << (double)us.count()/1000000.0 << " for total routes: " <<( n * per) << std::endl; +} + int main() { SWSS_LOG_ENTER(); + std::cout << " * test tokenize bulk route entry" << std::endl; + + test_tokenize_bulk_route_entry(); + std::cout << " * test ContextConfigContainer" << std::endl; test_ContextConfigContainer(); + std::cout << " * test deserialize route_entry" << std::endl; + + test_deserialize_route_entry_meta(10000); + test_deserialize_route_entry(10000); + std::cout << " * test remove" << std::endl; test_serialize_remove_route_entry(10000); diff --git a/saiplayer/SaiPlayer.cpp b/saiplayer/SaiPlayer.cpp index c6bd2c09f9a1..43797d8cd035 100644 --- a/saiplayer/SaiPlayer.cpp +++ b/saiplayer/SaiPlayer.cpp @@ -3,6 +3,7 @@ #include "sairedis.h" #include "sairediscommon.h" #include "VirtualObjectIdManager.h" +#include "PerformanceIntervalTimer.h" #include "meta/sai_serialize.h" #include "meta/SaiAttributeList.h" @@ -29,6 +30,7 @@ using namespace saiplayer; using namespace saimeta; +using namespace sairediscommon; using namespace std::placeholders; SaiPlayer::SaiPlayer( @@ -869,7 +871,7 @@ void SaiPlayer::performSleep( } void SaiPlayer::performNotifySyncd( - _In_ const std::string& request, + _In_ const std::string& request, _In_ const std::string& response) { SWSS_LOG_ENTER(); @@ -1024,7 +1026,7 @@ sai_status_t SaiPlayer::handle_bulk_route( _In_ const std::vector &object_ids, _In_ sai_common_api_t api, _In_ const std::vector> &attributes, - _In_ const std::vector &recorded_statuses) + _In_ const std::vector& expectedStatuses) { SWSS_LOG_ENTER(); @@ -1044,7 +1046,7 @@ sai_status_t SaiPlayer::handle_bulk_route( std::vector statuses; - statuses.resize(recorded_statuses.size()); + statuses.resize(expectedStatuses.size()); if (api == SAI_COMMON_API_BULK_SET) { @@ -1067,6 +1069,8 @@ sai_status_t SaiPlayer::handle_bulk_route( attrs.push_back(a->get_attr_list()[0]); } + SWSS_LOG_INFO("executing BULK set route_entry with %zu routes", routes.size()); + sai_status_t status = m_sai->bulkSet( (uint32_t)routes.size(), routes.data(), @@ -1074,18 +1078,11 @@ sai_status_t SaiPlayer::handle_bulk_route( SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR, // TODO we need to get that from recording statuses.data()); - if (status != SAI_STATUS_SUCCESS) - { - /* - * Entire API fails, so no need to compare statuses. - */ - - return status; - } + // even if API will fail, we swill need to compare all statuses for each entry for (size_t i = 0; i < statuses.size(); ++i) { - if (statuses[i] != recorded_statuses[i]) + if (statuses[i] != expectedStatuses[i]) { /* * If recorded statuses are different than received, throw @@ -1093,7 +1090,7 @@ sai_status_t SaiPlayer::handle_bulk_route( */ SWSS_LOG_THROW("recorded status is %s but returned is %s on %s", - sai_serialize_status(recorded_statuses[i]).c_str(), + sai_serialize_status(expectedStatuses[i]).c_str(), sai_serialize_status(statuses[i]).c_str(), object_ids[i].c_str()); } @@ -1114,7 +1111,11 @@ sai_status_t SaiPlayer::handle_bulk_route( attr_count.push_back(alist->get_attr_count()); } - SWSS_LOG_NOTICE("executing BULK route create with %zu routes", attr_count.size()); + SWSS_LOG_INFO("executing BULK create route_entry with %zu routes", routes.size()); + + static PerformanceIntervalTimer timer("SaiPlayer::handle_bulk_route::bulkCreate(route_entry)"); + + timer.start(); sai_status_t status = m_sai->bulkCreate( (uint32_t)routes.size(), @@ -1124,15 +1125,13 @@ sai_status_t SaiPlayer::handle_bulk_route( SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR, // TODO we need to get that from recording statuses.data()); - if (status != SAI_STATUS_SUCCESS) - { - // Entire API fails, so no need to compare statuses. - return status; - } + timer.stop(); + + timer.inc(routes.size()); for (size_t i = 0; i < statuses.size(); ++i) { - if (statuses[i] != recorded_statuses[i]) + if (statuses[i] != expectedStatuses[i]) { /* * If recorded statuses are different than received, throw @@ -1140,7 +1139,7 @@ sai_status_t SaiPlayer::handle_bulk_route( */ SWSS_LOG_THROW("recorded status is %s but returned is %s on %s", - sai_serialize_status(recorded_statuses[i]).c_str(), + sai_serialize_status(expectedStatuses[i]).c_str(), sai_serialize_status(statuses[i]).c_str(), object_ids[i].c_str()); } @@ -1155,6 +1154,179 @@ sai_status_t SaiPlayer::handle_bulk_route( } } +sai_status_t SaiPlayer::handle_bulk_generic( + _In_ sai_object_type_t objectType, + _In_ const std::vector& strObjectIds, + _In_ sai_common_api_t api, + _In_ const std::vector>& attributes, + _In_ const std::vector& expectedStatuses) +{ + SWSS_LOG_ENTER(); + + std::vector objectIds; + + for (const auto& oid: strObjectIds) + { + sai_object_id_t objectId; + sai_deserialize_object_id(oid, objectId); + + if (api != SAI_COMMON_API_BULK_CREATE) + { + // when creating OID objects, ID don't exists yet, it needs to be created + // and after success create, it should be matched to local redis db + objectId = translate_local_to_redis(objectId); + } + + objectIds.push_back(objectId); + } + + std::vector statuses; + + statuses.resize(expectedStatuses.size()); + + if (api == SAI_COMMON_API_BULK_CREATE) + { + std::vector oids; + + oids.resize(expectedStatuses.size()); + + std::vector attr_count; + + std::vector attr_list; + + // object can have multiple attributes, so we need to handle them all + for (const auto &alist: attributes) + { + attr_list.push_back(alist->get_attr_list()); + attr_count.push_back(alist->get_attr_count()); + } + + SWSS_LOG_INFO("executing BULK create %s with %zu entries", + sai_serialize_object_type(objectType).c_str(), + objectIds.size()); + + // get switch ID from first local object + sai_object_id_t localSwitchId = m_sai->switchIdQuery(objectIds[0]); + sai_object_id_t switchId = translate_local_to_redis(localSwitchId); + + sai_status_t status = m_sai->bulkCreate( + objectType, + switchId, + (uint32_t)oids.size(), + attr_count.data(), + attr_list.data(), + SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR, // TODO we need to get that from recording + oids.data(), + statuses.data()); + + for (size_t i = 0; i < statuses.size(); ++i) + { + if (statuses[i] != expectedStatuses[i]) + { + /* + * If recorded statuses are different than received, throw + * exception since data don't match. + */ + + SWSS_LOG_THROW("recorded status is %s but returned is %s on %s", + sai_serialize_status(expectedStatuses[i]).c_str(), + sai_serialize_status(statuses[i]).c_str(), + strObjectIds[i].c_str()); + } + + if (statuses[i] == SAI_STATUS_SUCCESS) + { + // object was created, we need to match local id and redis id + match_redis_with_rec(oids[i], objectIds[i]); + } + } + + return status; + } + else if (api == SAI_COMMON_API_BULK_REMOVE) + { + SWSS_LOG_INFO("executing BULK remove %s with %zu entries", + sai_serialize_object_type(objectType).c_str(), + objectIds.size()); + + sai_status_t status = m_sai->bulkRemove( + objectType, + (uint32_t)objectIds.size(), + objectIds.data(), + SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR, // TODO we need to get that from recording + statuses.data()); + + // even if API will fail, we swill need to compare all statuses for each entry + + for (size_t i = 0; i < statuses.size(); ++i) + { + if (statuses[i] != expectedStatuses[i]) + { + /* + * If recorded statuses are different than received, throw + * exception since data don't match. + */ + + SWSS_LOG_THROW("recorded status is %s but returned is %s on %s", + sai_serialize_status(expectedStatuses[i]).c_str(), + sai_serialize_status(statuses[i]).c_str(), + strObjectIds[i].c_str()); + } + } + + return status; + } + else if (api == SAI_COMMON_API_BULK_SET) + { + std::vector attrs; + + for (const auto &a: attributes) + { + // set has only 1 attribute, so we can just join them nicely here. + + attrs.push_back(a->get_attr_list()[0]); + } + + SWSS_LOG_INFO("executing BULK set %s with %zu entries", + sai_serialize_object_type(objectType).c_str(), + objectIds.size()); + + sai_status_t status = m_sai->bulkSet( + objectType, + (uint32_t)objectIds.size(), + objectIds.data(), + attrs.data(), + SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR, // TODO we need to get that from recording + statuses.data()); + + // even if API will fail, we swill need to compare all statuses for each entry + + for (size_t i = 0; i < statuses.size(); ++i) + { + if (statuses[i] != expectedStatuses[i]) + { + /* + * If recorded statuses are different than received, throw + * exception since data don't match. + */ + + SWSS_LOG_THROW("recorded status is %s but returned is %s on %s", + sai_serialize_status(expectedStatuses[i]).c_str(), + sai_serialize_status(statuses[i]).c_str(), + strObjectIds[i].c_str()); + } + } + + return status; + } + else + { + SWSS_LOG_THROW("api %s is not supported in bulk %s", + sai_serialize_common_api(api).c_str(), + sai_serialize_object_type(objectType).c_str()); + } +} + void SaiPlayer::processBulk( _In_ sai_common_api_t api, _In_ const std::string &line) @@ -1167,7 +1339,8 @@ void SaiPlayer::processBulk( } if (api != SAI_COMMON_API_BULK_SET && - api != SAI_COMMON_API_BULK_CREATE) + api != SAI_COMMON_API_BULK_CREATE && + api != SAI_COMMON_API_BULK_REMOVE) { SWSS_LOG_THROW("bulk common api %d is not supported yet, FIXME", api); } @@ -1176,7 +1349,7 @@ void SaiPlayer::processBulk( * Here we know we have bulk SET api */ - // timestamp|action|objecttype||objectid|attrid=value|...|status||objectid||objectid|attrid=value|...|status||... + // timestamp|action|objecttype||objectid|attrid=value|...||objectid||objectid|attrid=value|...||... auto fields = tokenize(line, "||"); auto first = fields.at(0); // timestamp|action|objecttype @@ -1189,11 +1362,11 @@ void SaiPlayer::processBulk( std::vector> attributes; - std::vector statuses; + std::vector expectedStatuses; for (size_t idx = 1; idx < fields.size(); ++idx) { - // object_id|attr=value|...|status + // object_id|attr=value|... const std::string &joined = fields[idx]; auto split = swss::tokenize(joined, '|'); @@ -1202,13 +1375,9 @@ void SaiPlayer::processBulk( object_ids.push_back(str_object_id); - std::string str_status = split.back(); - - sai_status_t status; - - sai_deserialize_status(str_status, status); - - statuses.push_back(status); + // TODO currently we expect all bulk API will always succeed in sync mode + // we will need to update that, needs to be obtained from recording file + expectedStatuses.push_back(SAI_STATUS_SUCCESS); std::vector entries; // attributes per object id @@ -1216,7 +1385,7 @@ void SaiPlayer::processBulk( SWSS_LOG_DEBUG("processing: %s", joined.c_str()); - for (size_t i = 1; i < split.size() - 1; ++i) + for (size_t i = 1; i < split.size(); ++i) { const auto &item = split[i]; @@ -1252,7 +1421,11 @@ void SaiPlayer::processBulk( switch (object_type) { case SAI_OBJECT_TYPE_ROUTE_ENTRY: - status = handle_bulk_route(object_ids, api, attributes, statuses); + status = handle_bulk_route(object_ids, api, attributes, expectedStatuses); + break; + + case SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER: + status = handle_bulk_generic(object_type, object_ids, api, attributes, expectedStatuses); break; default: @@ -1261,6 +1434,8 @@ void SaiPlayer::processBulk( sai_serialize_object_type(object_type).c_str()); } + // TODO currently bulk API assume that always succeed, but this may not be + // the case when using synchronous mode, so this needs to be updated if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_THROW("failed to execute bulk api, FIXME"); @@ -1360,6 +1535,9 @@ int SaiPlayer::replay() case 'C': processBulk(SAI_COMMON_API_BULK_CREATE, line); continue; + case 'R': + processBulk(SAI_COMMON_API_BULK_REMOVE, line); + continue; case 'g': api = SAI_COMMON_API_GET; break; @@ -1610,6 +1788,11 @@ int SaiPlayer::run() EXIT_ON_ERROR(m_sai->set(SAI_OBJECT_TYPE_SWITCH, switch_id, &attr)); } + attr.id = SAI_REDIS_SWITCH_ATTR_RECORD; + attr.value.booldata = false; + + EXIT_ON_ERROR(m_sai->set(SAI_OBJECT_TYPE_SWITCH, switch_id, &attr)); + int exitcode = 0; if (m_commandLineOptions->m_files.size() > 0) diff --git a/saiplayer/SaiPlayer.h b/saiplayer/SaiPlayer.h index 85152a940ca1..42f9528841e9 100644 --- a/saiplayer/SaiPlayer.h +++ b/saiplayer/SaiPlayer.h @@ -45,6 +45,13 @@ namespace saiplayer _In_ const std::vector> &attributes, _In_ const std::vector &recorded_statuses); + sai_status_t handle_bulk_generic( + _In_ sai_object_type_t objectType, + _In_ const std::vector &object_ids, + _In_ sai_common_api_t api, + _In_ const std::vector> &attributes, + _In_ const std::vector &recorded_statuses); + std::vector tokenize( _In_ std::string input, _In_ const std::string &delim); diff --git a/syncd/Syncd.cpp b/syncd/Syncd.cpp index 0f6ccc74674d..30395ef35d8b 100644 --- a/syncd/Syncd.cpp +++ b/syncd/Syncd.cpp @@ -13,6 +13,7 @@ #include "ZeroMQNotificationProducer.h" #include "RedisSelectableChannel.h" #include "ZeroMQSelectableChannel.h" +#include "PerformanceIntervalTimer.h" #include "sairediscommon.h" @@ -33,6 +34,7 @@ using namespace syncd; using namespace saimeta; +using namespace sairediscommon; using namespace std::placeholders; Syncd::Syncd( @@ -699,7 +701,7 @@ sai_status_t Syncd::processBulkQuadEvent( attributes.push_back(list); } - SWSS_LOG_NOTICE("bulk %s execute with %zu items", + SWSS_LOG_INFO("bulk %s executing with %zu items", strObjectType.c_str(), objectIds.size()); @@ -846,7 +848,22 @@ sai_status_t Syncd::processBulkEntry( if (api == SAI_COMMON_API_BULK_CREATE) { - status = processEntry(metaKey, SAI_COMMON_API_CREATE, attr_count, attr_list); + if (objectType == SAI_OBJECT_TYPE_ROUTE_ENTRY) + { + static PerformanceIntervalTimer timer("Syncd::processBulkEntry::processEntry(route_entry) CREATE"); + + timer.start(); + + status = processEntry(metaKey, SAI_COMMON_API_CREATE, attr_count, attr_list); + + timer.stop(); + + timer.inc(); + } + else + { + status = processEntry(metaKey, SAI_COMMON_API_CREATE, attr_count, attr_list); + } } else if (api == SAI_COMMON_API_BULK_REMOVE) { @@ -1402,6 +1419,10 @@ void Syncd::syncUpdateRedisQuadEvent( const bool initView = isInitViewMode(); + static PerformanceIntervalTimer timer("Syncd::syncUpdateRedisQuadEvent"); + + timer.start(); + switch (api) { case SAI_COMMON_API_CREATE: @@ -1412,7 +1433,7 @@ void Syncd::syncUpdateRedisQuadEvent( else m_client->createAsicObject(metaKey, values); - return; + break; } case SAI_COMMON_API_REMOVE: @@ -1423,7 +1444,7 @@ void Syncd::syncUpdateRedisQuadEvent( else m_client->removeAsicObject(metaKey); - return; + break; } case SAI_COMMON_API_SET: @@ -1439,16 +1460,20 @@ void Syncd::syncUpdateRedisQuadEvent( else m_client->setAsicObject(metaKey, attr, value); - return; + break; } case SAI_COMMON_API_GET: - return; // ignore get since get is not modifying db + break; // ignore get since get is not modifying db default: SWSS_LOG_THROW("api %d is not supported", api); } + + timer.stop(); + + timer.inc(); } void Syncd::syncUpdateRedisBulkQuadEvent( @@ -1470,6 +1495,10 @@ void Syncd::syncUpdateRedisBulkQuadEvent( // changes and we only want to apply changes when api succeeded. This // applies to init view mode and apply view mode. + static PerformanceIntervalTimer timer("Syncd::syncUpdateRedisBulkQuadEvent"); + + timer.start(); + const std::string strObjectType = sai_serialize_object_type(objectType); for (size_t idx = 0; idx < statuses.size(); idx++) @@ -1541,6 +1570,10 @@ void Syncd::syncUpdateRedisBulkQuadEvent( SWSS_LOG_THROW("api %d is not supported", api); } } + + timer.stop(); + + timer.inc(statuses.size()); } sai_status_t Syncd::processQuadEvent( @@ -1627,7 +1660,22 @@ sai_status_t Syncd::processQuadEvent( if (info->isnonobjectid) { - status = processEntry(metaKey, api, attr_count, attr_list); + if (info->objecttype == SAI_OBJECT_TYPE_ROUTE_ENTRY) + { + static PerformanceIntervalTimer timer("Syncd::processQuadEvent::processEntry(route_entry)"); + + timer.start(); + + status = processEntry(metaKey, api, attr_count, attr_list); + + timer.stop(); + + timer.inc(); + } + else + { + status = processEntry(metaKey, api, attr_count, attr_list); + } } else { diff --git a/syncd/ZeroMQSelectableChannel.cpp b/syncd/ZeroMQSelectableChannel.cpp index 2995efb782a7..fc891455464a 100644 --- a/syncd/ZeroMQSelectableChannel.cpp +++ b/syncd/ZeroMQSelectableChannel.cpp @@ -64,7 +64,7 @@ ZeroMQSelectableChannel::~ZeroMQSelectableChannel() SWSS_LOG_NOTICE("ending zmq poll thread"); - m_zmlPollThread = nullptr; + m_zmlPollThread->join(); SWSS_LOG_NOTICE("ended zmq poll thread"); } @@ -86,6 +86,9 @@ void ZeroMQSelectableChannel::zmqPollThread() int rc = zmq_poll(items, 1, ZMQ_POLL_TIMEOUT); + if (m_runThread == false) + break; + if (rc <= 0 && zmq_errno() == ETERM) { SWSS_LOG_NOTICE("zmq_poll ETERM"); diff --git a/vslib/src/Makefile.am b/vslib/src/Makefile.am index 2a14a44e74a0..d548712d2bce 100644 --- a/vslib/src/Makefile.am +++ b/vslib/src/Makefile.am @@ -13,6 +13,7 @@ libSaiVS_a_SOURCES = \ ../../lib/src/NotificationFdbEvent.cpp \ ../../lib/src/Notification.cpp \ ../../lib/src/NotificationPortStateChange.cpp \ + ../../lib/src/PerformanceIntervalTimer.cpp \ ResourceLimiter.cpp \ ResourceLimiterContainer.cpp \ ResourceLimiterParser.cpp \ diff --git a/vslib/src/VirtualSwitchSaiInterface.cpp b/vslib/src/VirtualSwitchSaiInterface.cpp index e080a909085e..0ae11d968737 100644 --- a/vslib/src/VirtualSwitchSaiInterface.cpp +++ b/vslib/src/VirtualSwitchSaiInterface.cpp @@ -1,5 +1,7 @@ #include "VirtualSwitchSaiInterface.h" +#include "../../lib/inc/PerformanceIntervalTimer.h" + #include "swss/logger.h" #include "meta/sai_serialize.h" @@ -23,6 +25,7 @@ using namespace saivs; using namespace saimeta; +using namespace sairediscommon; VirtualSwitchSaiInterface::VirtualSwitchSaiInterface( _In_ const std::shared_ptr scc) @@ -477,13 +480,38 @@ sai_status_t VirtualSwitchSaiInterface::create( \ attr_list); \ } +sai_status_t VirtualSwitchSaiInterface::create( + _In_ const sai_route_entry_t* entry, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + static PerformanceIntervalTimer timer("VirtualSwitchSaiInterface::create(route_entry)"); + + timer.start(); + + auto status = create( + entry->switch_id, + SAI_OBJECT_TYPE_ROUTE_ENTRY, + sai_serialize_route_entry(*entry), + attr_count, + attr_list); + + timer.stop(); + + timer.inc(); + + return status; +} + DECLARE_CREATE_ENTRY(FDB_ENTRY,fdb_entry); DECLARE_CREATE_ENTRY(INSEG_ENTRY,inseg_entry); DECLARE_CREATE_ENTRY(IPMC_ENTRY,ipmc_entry); DECLARE_CREATE_ENTRY(L2MC_ENTRY,l2mc_entry); DECLARE_CREATE_ENTRY(MCAST_FDB_ENTRY,mcast_fdb_entry); DECLARE_CREATE_ENTRY(NEIGHBOR_ENTRY,neighbor_entry); -DECLARE_CREATE_ENTRY(ROUTE_ENTRY,route_entry); +//DECLARE_CREATE_ENTRY(ROUTE_ENTRY,route_entry); DECLARE_CREATE_ENTRY(NAT_ENTRY,nat_entry); #define DECLARE_SET_ENTRY(OT,ot) \