From cf9f340817cba01d56efe23dbc2fb40e64e3ed98 Mon Sep 17 00:00:00 2001 From: junchao Date: Fri, 2 Sep 2022 13:39:27 +0800 Subject: [PATCH 1/9] [Fast/Warm restart] Implement helper class for waiting restart done --- common/Makefile.am | 3 +- common/restart_waiter.cpp | 130 ++++++++++++++++++++++++++++++++++++ common/restart_waiter.h | 32 +++++++++ pyext/swsscommon.i | 2 + tests/Makefile.am | 1 + tests/restart_waiter_ut.cpp | 113 +++++++++++++++++++++++++++++++ 6 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 common/restart_waiter.cpp create mode 100644 common/restart_waiter.h create mode 100644 tests/restart_waiter_ut.cpp diff --git a/common/Makefile.am b/common/Makefile.am index b47cfd217..0add8ff67 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -70,7 +70,8 @@ libswsscommon_la_SOURCES = \ warm_restart.cpp \ luatable.cpp \ countertable.cpp \ - redisutility.cpp + redisutility.cpp \ + restart_waiter.cpp libswsscommon_la_CXXFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(LIBNL_CFLAGS) $(CODE_COVERAGE_CXXFLAGS) libswsscommon_la_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(LIBNL_CPPFLAGS) $(CODE_COVERAGE_CPPFLAGS) diff --git a/common/restart_waiter.cpp b/common/restart_waiter.cpp new file mode 100644 index 000000000..a4084a0fe --- /dev/null +++ b/common/restart_waiter.cpp @@ -0,0 +1,130 @@ +#include "restart_waiter.h" +#include "redispipeline.h" +#include "select.h" +#include "schema.h" +#include "subscriberstatetable.h" +#include "table.h" +#include + +using namespace swss; + +static const std::string STATE_DB_NAME = "STATE_DB"; +static const std::string STATE_DB_SEPARATOR = "|"; +static const std::string RESTART_KEY = "system"; +static const std::string RESTART_ENABLE_FIELD = "enable"; +static const std::string FAST_REBOOT_TABLE_NAME = "FAST_REBOOT"; + +bool RestartWaiter::waitRestartDone( + unsigned int maxWaitSec, + unsigned int dbTimeout, + bool isTcpConn) +{ + DBConnector stateDb(STATE_DB_NAME, dbTimeout, isTcpConn); + return isWarmOrFastRestartInProgress(stateDb) ? doWait(stateDb, maxWaitSec) : true; +} + +bool RestartWaiter::waitWarmRestartDone(unsigned int maxWaitSec, + unsigned int dbTimeout, + bool isTcpConn) +{ + DBConnector stateDb(STATE_DB_NAME, dbTimeout, isTcpConn); + if (isFastRestartInProgress(stateDb)) + { + // It is fast boot, just return + return true; + } + + return isWarmOrFastRestartInProgress(stateDb) ? doWait(stateDb, maxWaitSec) : true; +} + +bool RestartWaiter::waitFastRestartDone(unsigned int maxWaitSec, + unsigned int dbTimeout, + bool isTcpConn) +{ + DBConnector stateDb(STATE_DB_NAME, dbTimeout, isTcpConn); + if (!isFastRestartInProgress(stateDb)) + { + // Fast boot is not in progress + return true; + } + + return isWarmOrFastRestartInProgress(stateDb) ? doWait(stateDb, maxWaitSec) : true; +} + +bool RestartWaiter::doWait(DBConnector &stateDb, + unsigned int maxWaitSec) +{ + int selectTimeout = static_cast(maxWaitSec); + + SubscriberStateTable restartEnableTable(&stateDb, STATE_WARM_RESTART_ENABLE_TABLE_NAME); + Select s; + s.addSelectable(&restartEnableTable); + + auto start = std::chrono::steady_clock::now(); + while (1) + { + Selectable *sel = NULL; + int ret = s.select(&sel, selectTimeout * 1000, true); + + if (ret == Select::OBJECT) + { + KeyOpFieldsValuesTuple kco; + restartEnableTable.pop(kco); + auto &key = kfvKey(kco); + if (key == RESTART_KEY) + { + auto& values = kfvFieldsValues(kco); + for (auto& fvt: values) + { + auto& field = fvField(fvt); + auto& value = fvValue(fvt); + if (field == RESTART_ENABLE_FIELD) + { + if (value == "false") + { + return true; + } + else + { + break; + } + } + } + } + } + else if (ret == Select::ERROR) + { + SWSS_LOG_NOTICE("Error: wait restart done got error - %s!", strerror(errno)); + } + else if (ret == Select::TIMEOUT) + { + SWSS_LOG_INFO("Timeout: wait restart done got select timeout"); + } + else if (ret == Select::SIGNALINT) + { + return false; + } + + auto end = std::chrono::steady_clock::now(); + int delay = static_cast( + std::chrono::duration_cast(end - start).count()); + + selectTimeout -= delay; + if (selectTimeout < 0) + { + return false; + } + } +} + +bool RestartWaiter::isWarmOrFastRestartInProgress(DBConnector &stateDb) +{ + auto ret = stateDb.hget(STATE_WARM_RESTART_ENABLE_TABLE_NAME + STATE_DB_SEPARATOR + RESTART_KEY, RESTART_ENABLE_FIELD); + return ret && *ret.get() == "true"; +} + +bool RestartWaiter::isFastRestartInProgress(DBConnector &stateDb) +{ + auto ret = stateDb.get(FAST_REBOOT_TABLE_NAME + STATE_DB_SEPARATOR + RESTART_KEY); + return ret.get() != nullptr; +} diff --git a/common/restart_waiter.h b/common/restart_waiter.h new file mode 100644 index 000000000..81e7e26ca --- /dev/null +++ b/common/restart_waiter.h @@ -0,0 +1,32 @@ +#pragma once + +#include "dbconnector.h" + +namespace swss +{ + +// Helper class to wait for warm/fast reboot done +class RestartWaiter +{ +public: + static bool waitRestartDone(unsigned int maxWaitSec = 180, + unsigned int dbTimeout = 0, + bool isTcpConn = false); + + static bool waitWarmRestartDone(unsigned int maxWaitSec = 180, + unsigned int dbTimeout = 0, + bool isTcpConn = false); + + static bool waitFastRestartDone(unsigned int maxWaitSec = 180, + unsigned int dbTimeout = 0, + bool isTcpConn = false); + +private: + static bool doWait(swss::DBConnector &stateDb, + unsigned int maxWaitSec); + + static bool isWarmOrFastRestartInProgress(swss::DBConnector &stateDb); + static bool isFastRestartInProgress(swss::DBConnector &stateDb); +}; + +} diff --git a/pyext/swsscommon.i b/pyext/swsscommon.i index 41f2280cb..097d96bb1 100644 --- a/pyext/swsscommon.i +++ b/pyext/swsscommon.i @@ -38,6 +38,7 @@ #include "events.h" #include "configdb.h" #include "status_code_util.h" +#include "restart_waiter.h" %} %include @@ -219,3 +220,4 @@ T castSelectableObj(swss::Selectable *temp) %include "events.h" %include "status_code_util.h" +%include "restart_waiter.h" diff --git a/tests/Makefile.am b/tests/Makefile.am index d53f88995..0185808e5 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -41,6 +41,7 @@ tests_SOURCES = redis_ut.cpp \ events_common_ut.cpp \ events_service_ut.cpp \ events_ut.cpp \ + restart_waiter_ut.cpp \ main.cpp tests_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_GTEST) $(LIBNL_CFLAGS) diff --git a/tests/restart_waiter_ut.cpp b/tests/restart_waiter_ut.cpp new file mode 100644 index 000000000..227dcd7ab --- /dev/null +++ b/tests/restart_waiter_ut.cpp @@ -0,0 +1,113 @@ +#include +#include +#include +#include +#include + +#include "common/dbconnector.h" +#include "common/restart_waiter.h" +#include "common/schema.h" +#include "common/table.h" + +using namespace swss; +using namespace std; + +static const string FAST_REBOOT_KEY = "FAST_REBOOT|system"; + +static void set_reboot_status(string status, int delay = 0) +{ + if (delay > 0) + { + sleep(delay); + } + + DBConnector db("STATE_DB", 0); + Table table(&db, STATE_WARM_RESTART_ENABLE_TABLE_NAME); + table.hset("system", "enable", status); +} + +class FastBootHelper +{ +public: + FastBootHelper(): db("STATE_DB", 0) + { + db.set(FAST_REBOOT_KEY, "1"); + } + + ~FastBootHelper() + { + db.del({FAST_REBOOT_KEY}); + } +private: + DBConnector db; +}; + +TEST(RestartWaiter, success) +{ + set_reboot_status("true"); + thread t(set_reboot_status, "false", 3); + EXPECT_TRUE(RestartWaiter::waitRestartDone()); + t.join(); +} + +TEST(RestartWaiter, successWarmRestart) +{ + set_reboot_status("true"); + thread t(set_reboot_status, "false", 3); + EXPECT_TRUE(RestartWaiter::waitWarmRestartDone()); + t.join(); +} + +TEST(RestartWaiter, successFastRestart) +{ + FastBootHelper helper; + set_reboot_status("true"); + thread t(set_reboot_status, "false", 3); + EXPECT_TRUE(RestartWaiter::waitFastRestartDone()); + t.join(); +} + +TEST(RestartWaiter, timeout) +{ + set_reboot_status("true"); + EXPECT_FALSE(RestartWaiter::waitRestartDone(1)); + EXPECT_FALSE(RestartWaiter::waitWarmRestartDone(1)); + + FastBootHelper helper; + EXPECT_FALSE(RestartWaiter::waitFastRestartDone(1)); + + set_reboot_status("false"); +} + +TEST(RestartWaiter, successNoDelay) +{ + set_reboot_status("false"); + EXPECT_TRUE(RestartWaiter::waitRestartDone()); + EXPECT_TRUE(RestartWaiter::waitWarmRestartDone()); + + FastBootHelper helper; + EXPECT_TRUE(RestartWaiter::waitFastRestartDone()); +} + +TEST(RestartWaiter, successNoKey) +{ + DBConnector db("STATE_DB", 0); + string key = string(STATE_WARM_RESTART_ENABLE_TABLE_NAME) + string("|system"); + db.del({key}); + EXPECT_TRUE(RestartWaiter::waitRestartDone()); + EXPECT_TRUE(RestartWaiter::waitWarmRestartDone()); + + FastBootHelper helper; + EXPECT_TRUE(RestartWaiter::waitFastRestartDone()); +} + +TEST(RestartWaiter, waitWarmButFastInProgress) +{ + FastBootHelper helper; + EXPECT_TRUE(RestartWaiter::waitWarmRestartDone()); +} + +TEST(RestartWaiter, waitFastButFastNotInProgress) +{ + EXPECT_TRUE(RestartWaiter::waitFastRestartDone()); +} From 8102d058fa246688c6d6b618b7a645d4fc10d04e Mon Sep 17 00:00:00 2001 From: junchao Date: Tue, 20 Sep 2022 09:27:47 +0800 Subject: [PATCH 2/9] Fix review comment --- common/restart_waiter.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/common/restart_waiter.cpp b/common/restart_waiter.cpp index a4084a0fe..32602c1a6 100644 --- a/common/restart_waiter.cpp +++ b/common/restart_waiter.cpp @@ -54,6 +54,12 @@ bool RestartWaiter::waitFastRestartDone(unsigned int maxWaitSec, bool RestartWaiter::doWait(DBConnector &stateDb, unsigned int maxWaitSec) { + if (maxWaitSec == 0) + { + SWSS_LOG_ERROR("Error: invalid maxWaitSec value 0, must be larger than 0"); + return false; + } + int selectTimeout = static_cast(maxWaitSec); SubscriberStateTable restartEnableTable(&stateDb, STATE_WARM_RESTART_ENABLE_TABLE_NAME); @@ -110,7 +116,7 @@ bool RestartWaiter::doWait(DBConnector &stateDb, std::chrono::duration_cast(end - start).count()); selectTimeout -= delay; - if (selectTimeout < 0) + if (selectTimeout <= 0) { return false; } From afdbb4e3a9a6836c2aaf54f6a6fca3f0779842b6 Mon Sep 17 00:00:00 2001 From: junchao Date: Tue, 20 Sep 2022 13:49:49 +0800 Subject: [PATCH 3/9] Fix review comment --- common/restart_waiter.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/restart_waiter.cpp b/common/restart_waiter.cpp index 32602c1a6..b885ea228 100644 --- a/common/restart_waiter.cpp +++ b/common/restart_waiter.cpp @@ -60,7 +60,7 @@ bool RestartWaiter::doWait(DBConnector &stateDb, return false; } - int selectTimeout = static_cast(maxWaitSec); + int selectTimeout = static_cast(maxWaitSec) * 1000; SubscriberStateTable restartEnableTable(&stateDb, STATE_WARM_RESTART_ENABLE_TABLE_NAME); Select s; @@ -70,7 +70,7 @@ bool RestartWaiter::doWait(DBConnector &stateDb, while (1) { Selectable *sel = NULL; - int ret = s.select(&sel, selectTimeout * 1000, true); + int ret = s.select(&sel, selectTimeout, true); if (ret == Select::OBJECT) { @@ -113,7 +113,7 @@ bool RestartWaiter::doWait(DBConnector &stateDb, auto end = std::chrono::steady_clock::now(); int delay = static_cast( - std::chrono::duration_cast(end - start).count()); + std::chrono::duration_cast(end - start).count()); selectTimeout -= delay; if (selectTimeout <= 0) From e6d4139f4996d54192624b538a154b5c4c796a0c Mon Sep 17 00:00:00 2001 From: junchao Date: Sat, 24 Sep 2022 09:18:57 +0800 Subject: [PATCH 4/9] Fix review comment --- common/restart_waiter.cpp | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/common/restart_waiter.cpp b/common/restart_waiter.cpp index b885ea228..c753f4d50 100644 --- a/common/restart_waiter.cpp +++ b/common/restart_waiter.cpp @@ -4,6 +4,7 @@ #include "schema.h" #include "subscriberstatetable.h" #include "table.h" +#include #include using namespace swss; @@ -76,23 +77,31 @@ bool RestartWaiter::doWait(DBConnector &stateDb, { KeyOpFieldsValuesTuple kco; restartEnableTable.pop(kco); - auto &key = kfvKey(kco); - if (key == RESTART_KEY) + auto &op = kfvOp(kco); + if (op == SET_COMMAND) { - auto& values = kfvFieldsValues(kco); - for (auto& fvt: values) + auto &key = kfvKey(kco); + if (key == RESTART_KEY) { - auto& field = fvField(fvt); - auto& value = fvValue(fvt); - if (field == RESTART_ENABLE_FIELD) + auto& values = kfvFieldsValues(kco); + for (auto& fvt: values) { - if (value == "false") + auto& field = fvField(fvt); + auto& value = fvValue(fvt); + if (field == RESTART_ENABLE_FIELD) { - return true; - } - else - { - break; + // During system warm/fast restart, STATE_DB WARM_RESTART_ENABLE_TABLE|system enable + // field will be set to "true", it indicates warm/fast restart is in progress. + // After warm/fast restart done, warm reboot finalizer set the field back to false, + // it indicates warm/fast restart is done. So, we wait for this field here. + if (boost::to_lower(value) == "false") + { + return true; + } + else + { + break; + } } } } @@ -126,7 +135,7 @@ bool RestartWaiter::doWait(DBConnector &stateDb, bool RestartWaiter::isWarmOrFastRestartInProgress(DBConnector &stateDb) { auto ret = stateDb.hget(STATE_WARM_RESTART_ENABLE_TABLE_NAME + STATE_DB_SEPARATOR + RESTART_KEY, RESTART_ENABLE_FIELD); - return ret && *ret.get() == "true"; + return ret && boost::to_lower(*ret.get()) == "true"; } bool RestartWaiter::isFastRestartInProgress(DBConnector &stateDb) From b3a9fb06893779e33d21d44ecd75082040238124 Mon Sep 17 00:00:00 2001 From: junchao Date: Mon, 26 Sep 2022 09:45:44 +0800 Subject: [PATCH 5/9] Make isFastRestartInProgress and isWarmRestartInProgress public for other library to use --- common/restart_waiter.cpp | 5 +++++ common/restart_waiter.h | 7 ++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/common/restart_waiter.cpp b/common/restart_waiter.cpp index c753f4d50..c2b86e779 100644 --- a/common/restart_waiter.cpp +++ b/common/restart_waiter.cpp @@ -143,3 +143,8 @@ bool RestartWaiter::isFastRestartInProgress(DBConnector &stateDb) auto ret = stateDb.get(FAST_REBOOT_TABLE_NAME + STATE_DB_SEPARATOR + RESTART_KEY); return ret.get() != nullptr; } + +bool RestartWaiter::isWarmRestartInProgress(swss::DBConnector &stateDb) +{ + return isWarmOrFastRestartInProgress(stateDb) && !isFastRestartInProgress(stateDb); +} diff --git a/common/restart_waiter.h b/common/restart_waiter.h index 81e7e26ca..9a0612fb9 100644 --- a/common/restart_waiter.h +++ b/common/restart_waiter.h @@ -21,12 +21,13 @@ class RestartWaiter unsigned int dbTimeout = 0, bool isTcpConn = false); + static bool isWarmOrFastRestartInProgress(swss::DBConnector &stateDb); + static bool isFastRestartInProgress(swss::DBConnector &stateDb); + static bool isWarmRestartInProgress(swss::DBConnector &stateDb); + private: static bool doWait(swss::DBConnector &stateDb, unsigned int maxWaitSec); - - static bool isWarmOrFastRestartInProgress(swss::DBConnector &stateDb); - static bool isFastRestartInProgress(swss::DBConnector &stateDb); }; } From c961e4305123cb5f2ec057df1d1eeb5529959906 Mon Sep 17 00:00:00 2001 From: junchao Date: Mon, 26 Sep 2022 13:26:09 +0800 Subject: [PATCH 6/9] Fix build issue --- common/restart_waiter.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/common/restart_waiter.cpp b/common/restart_waiter.cpp index c2b86e779..65a195df7 100644 --- a/common/restart_waiter.cpp +++ b/common/restart_waiter.cpp @@ -87,14 +87,16 @@ bool RestartWaiter::doWait(DBConnector &stateDb, for (auto& fvt: values) { auto& field = fvField(fvt); - auto& value = fvValue(fvt); + if (field == RESTART_ENABLE_FIELD) { // During system warm/fast restart, STATE_DB WARM_RESTART_ENABLE_TABLE|system enable // field will be set to "true", it indicates warm/fast restart is in progress. // After warm/fast restart done, warm reboot finalizer set the field back to false, // it indicates warm/fast restart is done. So, we wait for this field here. - if (boost::to_lower(value) == "false") + std::string value = fvValue(fvt); + boost::to_lower(value); + if (value == "false") { return true; } @@ -135,7 +137,12 @@ bool RestartWaiter::doWait(DBConnector &stateDb, bool RestartWaiter::isWarmOrFastRestartInProgress(DBConnector &stateDb) { auto ret = stateDb.hget(STATE_WARM_RESTART_ENABLE_TABLE_NAME + STATE_DB_SEPARATOR + RESTART_KEY, RESTART_ENABLE_FIELD); - return ret && boost::to_lower(*ret.get()) == "true"; + if (ret) { + std::string value = *ret.get(); + boost::to_lower(value); + return value == "true"; + } + return false; } bool RestartWaiter::isFastRestartInProgress(DBConnector &stateDb) From 0f69104bae512873375bbd742f3fa9e621dac636 Mon Sep 17 00:00:00 2001 From: junchao Date: Sun, 9 Oct 2022 09:28:35 +0800 Subject: [PATCH 7/9] Fix review comment --- common/restart_waiter.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/common/restart_waiter.cpp b/common/restart_waiter.cpp index 65a195df7..3162dfafe 100644 --- a/common/restart_waiter.cpp +++ b/common/restart_waiter.cpp @@ -126,11 +126,12 @@ bool RestartWaiter::doWait(DBConnector &stateDb, int delay = static_cast( std::chrono::duration_cast(end - start).count()); - selectTimeout -= delay; - if (selectTimeout <= 0) + if (delay >= static_cast(maxWaitSec) * 1000) { return false; } + + selectTimeout -= delay; } } From 007798d1f3e9513645a54cf9783edebc08e8a2c3 Mon Sep 17 00:00:00 2001 From: junchao Date: Mon, 10 Oct 2022 18:22:33 +0800 Subject: [PATCH 8/9] Implement general redis table waiter --- common/Makefile.am | 3 +- common/redis_table_waiter.cpp | 145 ++++++++++++++++++++++++++++++++ common/redis_table_waiter.h | 43 ++++++++++ common/restart_waiter.cpp | 128 +++++++--------------------- common/restart_waiter.h | 16 ++-- pyext/swsscommon.i | 2 + tests/Makefile.am | 1 + tests/redis_table_waiter_ut.cpp | 105 +++++++++++++++++++++++ tests/restart_waiter_ut.cpp | 32 +++---- 9 files changed, 353 insertions(+), 122 deletions(-) create mode 100644 common/redis_table_waiter.cpp create mode 100644 common/redis_table_waiter.h create mode 100644 tests/redis_table_waiter_ut.cpp diff --git a/common/Makefile.am b/common/Makefile.am index 0add8ff67..7feb84ea5 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -71,7 +71,8 @@ libswsscommon_la_SOURCES = \ luatable.cpp \ countertable.cpp \ redisutility.cpp \ - restart_waiter.cpp + restart_waiter.cpp \ + redis_table_waiter.cpp libswsscommon_la_CXXFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(LIBNL_CFLAGS) $(CODE_COVERAGE_CXXFLAGS) libswsscommon_la_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(LIBNL_CPPFLAGS) $(CODE_COVERAGE_CPPFLAGS) diff --git a/common/redis_table_waiter.cpp b/common/redis_table_waiter.cpp new file mode 100644 index 000000000..a007f95e5 --- /dev/null +++ b/common/redis_table_waiter.cpp @@ -0,0 +1,145 @@ +#include "redis_table_waiter.h" +#include "select.h" +#include "subscriberstatetable.h" + +using namespace swss; + +bool RedisTableWaiter::waitUntil( + DBConnector &db, + const std::string &tableName, + unsigned int maxWaitSec, + CheckFunc checkFunc) +{ + if (maxWaitSec == 0) + { + SWSS_LOG_ERROR("Error: invalid maxWaitSec value 0, must be larger than 0"); + return false; + } + + SubscriberStateTable table(&db, tableName); + Select s; + s.addSelectable(&table); + + int maxWaitMs = static_cast(maxWaitSec) * 1000; + int selectTimeout = maxWaitMs; + auto start = std::chrono::steady_clock::now(); + while(1) + { + Selectable *sel = NULL; + int ret = s.select(&sel, selectTimeout, true); + if (ret == Select::OBJECT) + { + KeyOpFieldsValuesTuple kco; + table.pop(kco); + if (checkFunc(kco)) + { + return true; + } + } + else if (ret == Select::ERROR) + { + SWSS_LOG_NOTICE("Error: wait redis table got error - %s!", strerror(errno)); + } + else if (ret == Select::TIMEOUT) + { + SWSS_LOG_INFO("Timeout: wait redis table got select timeout"); + } + else if (ret == Select::SIGNALINT) + { + return false; + } + + auto end = std::chrono::steady_clock::now(); + int delay = static_cast( + std::chrono::duration_cast(end - start).count()); + + if (delay >= maxWaitMs) + { + return false; + } + + selectTimeout = maxWaitMs - delay; + } + + return false; +} + +bool RedisTableWaiter::waitUntilFieldSet( + DBConnector &db, + const std::string &tableName, + const std::string &key, + const std::string &fieldName, + unsigned int maxWaitSec, + ConditionFunc cond) +{ + auto sep = SonicDBConfig::getSeparator(&db); + auto value = db.hget(tableName + sep + key, fieldName); + if (value && cond(*value.get())) + { + return true; + } + + auto checkFunc = [&](const KeyOpFieldsValuesTuple &kco) -> bool { + if (SET_COMMAND == kfvOp(kco)) + { + if (key == kfvKey(kco)) + { + auto& values = kfvFieldsValues(kco); + for (auto& fvt: values) + { + if (fieldName == fvField(fvt)) + { + return cond(fvValue(fvt)); + } + } + } + } + + return false; + }; + return waitUntil(db, tableName, maxWaitSec, checkFunc); +} + +bool RedisTableWaiter::waitUntilKeySet( + DBConnector &db, + const std::string &tableName, + const std::string &key, + unsigned int maxWaitSec) +{ + auto sep = SonicDBConfig::getSeparator(&db); + if (db.exists(tableName + sep + key)) + { + return true; + } + + auto checkFunc = [&](const KeyOpFieldsValuesTuple &kco) -> bool { + if (SET_COMMAND == kfvOp(kco)) + { + return key == kfvKey(kco); + } + return false; + }; + return waitUntil(db, tableName, maxWaitSec, checkFunc); +} + +bool RedisTableWaiter::waitUntilKeyDel( + DBConnector &db, + const std::string &tableName, + const std::string &key, + unsigned int maxWaitSec) +{ + auto sep = SonicDBConfig::getSeparator(&db); + if (!db.exists(tableName + sep + key)) + { + return true; + } + + auto checkFunc = [&](const KeyOpFieldsValuesTuple &kco) -> bool { + if (DEL_COMMAND == kfvOp(kco)) + { + return key == kfvKey(kco); + } + return false; + }; + return waitUntil(db, tableName, maxWaitSec, checkFunc); +} diff --git a/common/redis_table_waiter.h b/common/redis_table_waiter.h new file mode 100644 index 000000000..f11668532 --- /dev/null +++ b/common/redis_table_waiter.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +#include "dbconnector.h" + +namespace swss +{ + +class RedisTableWaiter +{ +public: + typedef std::function ConditionFunc; + typedef std::function CheckFunc; + + static bool waitUntilFieldSet(DBConnector &db, + const std::string &tableName, + const std::string &key, + const std::string &fieldName, + unsigned int maxWaitSec, + ConditionFunc cond); + + + static bool waitUntilKeySet(DBConnector &db, + const std::string &tableName, + const std::string &key, + unsigned int maxWaitSec); + + static bool waitUntilKeyDel(DBConnector &db, + const std::string &tableName, + const std::string &key, + unsigned int maxWaitSec); + + static bool waitUntil( + DBConnector &db, + const std::string &tableName, + unsigned int maxWaitSec, + CheckFunc checkFunc); + +}; + +} \ No newline at end of file diff --git a/common/restart_waiter.cpp b/common/restart_waiter.cpp index 3162dfafe..038b8fb3c 100644 --- a/common/restart_waiter.cpp +++ b/common/restart_waiter.cpp @@ -1,9 +1,7 @@ #include "restart_waiter.h" +#include "redis_table_waiter.h" #include "redispipeline.h" -#include "select.h" #include "schema.h" -#include "subscriberstatetable.h" -#include "table.h" #include #include @@ -15,127 +13,63 @@ static const std::string RESTART_KEY = "system"; static const std::string RESTART_ENABLE_FIELD = "enable"; static const std::string FAST_REBOOT_TABLE_NAME = "FAST_REBOOT"; -bool RestartWaiter::waitRestartDone( +// waitAdvancedBootDone +bool RestartWaiter::waitAdvancedBootDone( unsigned int maxWaitSec, unsigned int dbTimeout, bool isTcpConn) { DBConnector stateDb(STATE_DB_NAME, dbTimeout, isTcpConn); - return isWarmOrFastRestartInProgress(stateDb) ? doWait(stateDb, maxWaitSec) : true; + return isWarmOrFastBootInProgress(stateDb) ? doWait(stateDb, maxWaitSec) : true; } -bool RestartWaiter::waitWarmRestartDone(unsigned int maxWaitSec, - unsigned int dbTimeout, - bool isTcpConn) +bool RestartWaiter::waitWarmBootDone( + unsigned int maxWaitSec, + unsigned int dbTimeout, + bool isTcpConn) { DBConnector stateDb(STATE_DB_NAME, dbTimeout, isTcpConn); - if (isFastRestartInProgress(stateDb)) + if (isFastBootInProgress(stateDb)) { // It is fast boot, just return return true; } - return isWarmOrFastRestartInProgress(stateDb) ? doWait(stateDb, maxWaitSec) : true; + return isWarmOrFastBootInProgress(stateDb) ? doWait(stateDb, maxWaitSec) : true; } -bool RestartWaiter::waitFastRestartDone(unsigned int maxWaitSec, - unsigned int dbTimeout, - bool isTcpConn) +bool RestartWaiter::waitFastBootDone( + unsigned int maxWaitSec, + unsigned int dbTimeout, + bool isTcpConn) { DBConnector stateDb(STATE_DB_NAME, dbTimeout, isTcpConn); - if (!isFastRestartInProgress(stateDb)) + if (!isFastBootInProgress(stateDb)) { // Fast boot is not in progress return true; } - return isWarmOrFastRestartInProgress(stateDb) ? doWait(stateDb, maxWaitSec) : true; + return isWarmOrFastBootInProgress(stateDb) ? doWait(stateDb, maxWaitSec) : true; } bool RestartWaiter::doWait(DBConnector &stateDb, unsigned int maxWaitSec) { - if (maxWaitSec == 0) - { - SWSS_LOG_ERROR("Error: invalid maxWaitSec value 0, must be larger than 0"); - return false; - } - - int selectTimeout = static_cast(maxWaitSec) * 1000; - - SubscriberStateTable restartEnableTable(&stateDb, STATE_WARM_RESTART_ENABLE_TABLE_NAME); - Select s; - s.addSelectable(&restartEnableTable); - - auto start = std::chrono::steady_clock::now(); - while (1) - { - Selectable *sel = NULL; - int ret = s.select(&sel, selectTimeout, true); - - if (ret == Select::OBJECT) - { - KeyOpFieldsValuesTuple kco; - restartEnableTable.pop(kco); - auto &op = kfvOp(kco); - if (op == SET_COMMAND) - { - auto &key = kfvKey(kco); - if (key == RESTART_KEY) - { - auto& values = kfvFieldsValues(kco); - for (auto& fvt: values) - { - auto& field = fvField(fvt); - - if (field == RESTART_ENABLE_FIELD) - { - // During system warm/fast restart, STATE_DB WARM_RESTART_ENABLE_TABLE|system enable - // field will be set to "true", it indicates warm/fast restart is in progress. - // After warm/fast restart done, warm reboot finalizer set the field back to false, - // it indicates warm/fast restart is done. So, we wait for this field here. - std::string value = fvValue(fvt); - boost::to_lower(value); - if (value == "false") - { - return true; - } - else - { - break; - } - } - } - } - } - } - else if (ret == Select::ERROR) - { - SWSS_LOG_NOTICE("Error: wait restart done got error - %s!", strerror(errno)); - } - else if (ret == Select::TIMEOUT) - { - SWSS_LOG_INFO("Timeout: wait restart done got select timeout"); - } - else if (ret == Select::SIGNALINT) - { - return false; - } - - auto end = std::chrono::steady_clock::now(); - int delay = static_cast( - std::chrono::duration_cast(end - start).count()); - - if (delay >= static_cast(maxWaitSec) * 1000) - { - return false; - } - - selectTimeout -= delay; - } + auto condFunc = [](const std::string &value) -> bool { + std::string copy = value; + boost::to_lower(copy); + return copy == "false"; + }; + return RedisTableWaiter::waitUntilFieldSet(stateDb, + STATE_WARM_RESTART_ENABLE_TABLE_NAME, + RESTART_KEY, + RESTART_ENABLE_FIELD, + maxWaitSec, + condFunc); } -bool RestartWaiter::isWarmOrFastRestartInProgress(DBConnector &stateDb) +bool RestartWaiter::isWarmOrFastBootInProgress(DBConnector &stateDb) { auto ret = stateDb.hget(STATE_WARM_RESTART_ENABLE_TABLE_NAME + STATE_DB_SEPARATOR + RESTART_KEY, RESTART_ENABLE_FIELD); if (ret) { @@ -146,13 +80,13 @@ bool RestartWaiter::isWarmOrFastRestartInProgress(DBConnector &stateDb) return false; } -bool RestartWaiter::isFastRestartInProgress(DBConnector &stateDb) +bool RestartWaiter::isFastBootInProgress(DBConnector &stateDb) { auto ret = stateDb.get(FAST_REBOOT_TABLE_NAME + STATE_DB_SEPARATOR + RESTART_KEY); return ret.get() != nullptr; } -bool RestartWaiter::isWarmRestartInProgress(swss::DBConnector &stateDb) +bool RestartWaiter::isWarmBootInProgress(swss::DBConnector &stateDb) { - return isWarmOrFastRestartInProgress(stateDb) && !isFastRestartInProgress(stateDb); + return isWarmOrFastBootInProgress(stateDb) && !isFastBootInProgress(stateDb); } diff --git a/common/restart_waiter.h b/common/restart_waiter.h index 9a0612fb9..0e3f48c7a 100644 --- a/common/restart_waiter.h +++ b/common/restart_waiter.h @@ -9,21 +9,21 @@ namespace swss class RestartWaiter { public: - static bool waitRestartDone(unsigned int maxWaitSec = 180, - unsigned int dbTimeout = 0, - bool isTcpConn = false); + static bool waitAdvancedBootDone(unsigned int maxWaitSec = 180, + unsigned int dbTimeout = 0, + bool isTcpConn = false); - static bool waitWarmRestartDone(unsigned int maxWaitSec = 180, + static bool waitWarmBootDone(unsigned int maxWaitSec = 180, unsigned int dbTimeout = 0, bool isTcpConn = false); - static bool waitFastRestartDone(unsigned int maxWaitSec = 180, + static bool waitFastBootDone(unsigned int maxWaitSec = 180, unsigned int dbTimeout = 0, bool isTcpConn = false); - static bool isWarmOrFastRestartInProgress(swss::DBConnector &stateDb); - static bool isFastRestartInProgress(swss::DBConnector &stateDb); - static bool isWarmRestartInProgress(swss::DBConnector &stateDb); + static bool isWarmOrFastBootInProgress(swss::DBConnector &stateDb); + static bool isFastBootInProgress(swss::DBConnector &stateDb); + static bool isWarmBootInProgress(swss::DBConnector &stateDb); private: static bool doWait(swss::DBConnector &stateDb, diff --git a/pyext/swsscommon.i b/pyext/swsscommon.i index 097d96bb1..f7f0a8c3c 100644 --- a/pyext/swsscommon.i +++ b/pyext/swsscommon.i @@ -38,6 +38,7 @@ #include "events.h" #include "configdb.h" #include "status_code_util.h" +#include "redis_table_waiter.h" #include "restart_waiter.h" %} @@ -220,4 +221,5 @@ T castSelectableObj(swss::Selectable *temp) %include "events.h" %include "status_code_util.h" +#include "redis_table_waiter.h" %include "restart_waiter.h" diff --git a/tests/Makefile.am b/tests/Makefile.am index 0185808e5..d6ddc291e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -42,6 +42,7 @@ tests_SOURCES = redis_ut.cpp \ events_service_ut.cpp \ events_ut.cpp \ restart_waiter_ut.cpp \ + redis_table_waiter_ut.cpp \ main.cpp tests_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_GTEST) $(LIBNL_CFLAGS) diff --git a/tests/redis_table_waiter_ut.cpp b/tests/redis_table_waiter_ut.cpp new file mode 100644 index 000000000..354510275 --- /dev/null +++ b/tests/redis_table_waiter_ut.cpp @@ -0,0 +1,105 @@ +#include +#include +#include +#include + +#include "common/dbconnector.h" +#include "common/redis_table_waiter.h" +#include "common/table.h" + +using namespace swss; +using namespace std; + +const std::string dbName = "STATE_DB"; +const std::string tableName = "TestTable"; +const std::string key = "testKey"; +const std::string field = "testField"; +const std::string setValue = "1234"; + +static void set_field(int delay) +{ + if (delay > 0) + { + sleep(delay); + } + + DBConnector db(dbName, 0); + Table table(&db, tableName); + table.hset(key, field, setValue); +} + +static void del_key(int delay) +{ + if (delay > 0) + { + sleep(delay); + } + + DBConnector db(dbName, 0); + Table table(&db, tableName); + table.del(key); +} + +TEST(RedisTableWaiter, waitUntilFieldSet) +{ + del_key(0); + DBConnector db(dbName, 0); + auto condFunc = [&](const std::string &value) -> bool { + return value == setValue; + }; + thread t(set_field, 1); + auto ret = RedisTableWaiter::waitUntilFieldSet(db, + tableName, + key, + field, + 3, + condFunc); + EXPECT_TRUE(ret); + t.join(); + // field already set + ret = RedisTableWaiter::waitUntilFieldSet(db, + tableName, + key, + field, + 3, + condFunc); + EXPECT_TRUE(ret); +} + +TEST(RedisTableWaiter, waitUntilKeySet) +{ + del_key(0); + DBConnector db(dbName, 0); + thread t(set_field, 1); + auto ret = RedisTableWaiter::waitUntilKeySet(db, + tableName, + key, + 3); + EXPECT_TRUE(ret); + t.join(); + // key already exist + ret = RedisTableWaiter::waitUntilKeySet(db, + tableName, + key, + 3); + EXPECT_TRUE(ret); +} + +TEST(RedisTableWaiter, waitUntilKeyDel) +{ + set_field(0); + DBConnector db(dbName, 0); + thread t(del_key, 1); + auto ret = RedisTableWaiter::waitUntilKeyDel(db, + tableName, + key, + 3); + EXPECT_TRUE(ret); + t.join(); + // key already removed + ret = RedisTableWaiter::waitUntilKeyDel(db, + tableName, + key, + 3); + EXPECT_TRUE(ret); +} diff --git a/tests/restart_waiter_ut.cpp b/tests/restart_waiter_ut.cpp index 227dcd7ab..47e619e48 100644 --- a/tests/restart_waiter_ut.cpp +++ b/tests/restart_waiter_ut.cpp @@ -46,35 +46,35 @@ TEST(RestartWaiter, success) { set_reboot_status("true"); thread t(set_reboot_status, "false", 3); - EXPECT_TRUE(RestartWaiter::waitRestartDone()); + EXPECT_TRUE(RestartWaiter::waitAdvancedBootDone()); t.join(); } -TEST(RestartWaiter, successWarmRestart) +TEST(RestartWaiter, successWarmReboot) { set_reboot_status("true"); thread t(set_reboot_status, "false", 3); - EXPECT_TRUE(RestartWaiter::waitWarmRestartDone()); + EXPECT_TRUE(RestartWaiter::waitWarmBootDone()); t.join(); } -TEST(RestartWaiter, successFastRestart) +TEST(RestartWaiter, successFastReboot) { FastBootHelper helper; set_reboot_status("true"); thread t(set_reboot_status, "false", 3); - EXPECT_TRUE(RestartWaiter::waitFastRestartDone()); + EXPECT_TRUE(RestartWaiter::waitFastBootDone()); t.join(); } TEST(RestartWaiter, timeout) { set_reboot_status("true"); - EXPECT_FALSE(RestartWaiter::waitRestartDone(1)); - EXPECT_FALSE(RestartWaiter::waitWarmRestartDone(1)); + EXPECT_FALSE(RestartWaiter::waitAdvancedBootDone(1)); + EXPECT_FALSE(RestartWaiter::waitWarmBootDone(1)); FastBootHelper helper; - EXPECT_FALSE(RestartWaiter::waitFastRestartDone(1)); + EXPECT_FALSE(RestartWaiter::waitFastBootDone(1)); set_reboot_status("false"); } @@ -82,11 +82,11 @@ TEST(RestartWaiter, timeout) TEST(RestartWaiter, successNoDelay) { set_reboot_status("false"); - EXPECT_TRUE(RestartWaiter::waitRestartDone()); - EXPECT_TRUE(RestartWaiter::waitWarmRestartDone()); + EXPECT_TRUE(RestartWaiter::waitAdvancedBootDone()); + EXPECT_TRUE(RestartWaiter::waitWarmBootDone()); FastBootHelper helper; - EXPECT_TRUE(RestartWaiter::waitFastRestartDone()); + EXPECT_TRUE(RestartWaiter::waitFastBootDone()); } TEST(RestartWaiter, successNoKey) @@ -94,20 +94,20 @@ TEST(RestartWaiter, successNoKey) DBConnector db("STATE_DB", 0); string key = string(STATE_WARM_RESTART_ENABLE_TABLE_NAME) + string("|system"); db.del({key}); - EXPECT_TRUE(RestartWaiter::waitRestartDone()); - EXPECT_TRUE(RestartWaiter::waitWarmRestartDone()); + EXPECT_TRUE(RestartWaiter::waitAdvancedBootDone()); + EXPECT_TRUE(RestartWaiter::waitWarmBootDone()); FastBootHelper helper; - EXPECT_TRUE(RestartWaiter::waitFastRestartDone()); + EXPECT_TRUE(RestartWaiter::waitFastBootDone()); } TEST(RestartWaiter, waitWarmButFastInProgress) { FastBootHelper helper; - EXPECT_TRUE(RestartWaiter::waitWarmRestartDone()); + EXPECT_TRUE(RestartWaiter::waitWarmBootDone()); } TEST(RestartWaiter, waitFastButFastNotInProgress) { - EXPECT_TRUE(RestartWaiter::waitFastRestartDone()); + EXPECT_TRUE(RestartWaiter::waitFastBootDone()); } From 9547a3833812b6463d7277755d24d798691b0294 Mon Sep 17 00:00:00 2001 From: junchao Date: Tue, 11 Oct 2022 09:44:07 +0800 Subject: [PATCH 9/9] Fix review comment --- common/redis_table_waiter.cpp | 10 +++++----- common/redis_table_waiter.h | 4 ++-- common/restart_waiter.cpp | 12 ++++++------ common/restart_waiter.h | 2 +- tests/redis_table_waiter_ut.cpp | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/common/redis_table_waiter.cpp b/common/redis_table_waiter.cpp index a007f95e5..3b7acfe92 100644 --- a/common/redis_table_waiter.cpp +++ b/common/redis_table_waiter.cpp @@ -8,7 +8,7 @@ bool RedisTableWaiter::waitUntil( DBConnector &db, const std::string &tableName, unsigned int maxWaitSec, - CheckFunc checkFunc) + CheckFunc &checkFunc) { if (maxWaitSec == 0) { @@ -70,7 +70,7 @@ bool RedisTableWaiter::waitUntilFieldSet( const std::string &key, const std::string &fieldName, unsigned int maxWaitSec, - ConditionFunc cond) + ConditionFunc &cond) { auto sep = SonicDBConfig::getSeparator(&db); auto value = db.hget(tableName + sep + key, fieldName); @@ -79,7 +79,7 @@ bool RedisTableWaiter::waitUntilFieldSet( return true; } - auto checkFunc = [&](const KeyOpFieldsValuesTuple &kco) -> bool { + CheckFunc checkFunc = [&](const KeyOpFieldsValuesTuple &kco) -> bool { if (SET_COMMAND == kfvOp(kco)) { if (key == kfvKey(kco)) @@ -112,7 +112,7 @@ bool RedisTableWaiter::waitUntilKeySet( return true; } - auto checkFunc = [&](const KeyOpFieldsValuesTuple &kco) -> bool { + CheckFunc checkFunc = [&](const KeyOpFieldsValuesTuple &kco) -> bool { if (SET_COMMAND == kfvOp(kco)) { return key == kfvKey(kco); @@ -134,7 +134,7 @@ bool RedisTableWaiter::waitUntilKeyDel( return true; } - auto checkFunc = [&](const KeyOpFieldsValuesTuple &kco) -> bool { + CheckFunc checkFunc = [&](const KeyOpFieldsValuesTuple &kco) -> bool { if (DEL_COMMAND == kfvOp(kco)) { return key == kfvKey(kco); diff --git a/common/redis_table_waiter.h b/common/redis_table_waiter.h index f11668532..5cc773ae7 100644 --- a/common/redis_table_waiter.h +++ b/common/redis_table_waiter.h @@ -19,7 +19,7 @@ class RedisTableWaiter const std::string &key, const std::string &fieldName, unsigned int maxWaitSec, - ConditionFunc cond); + ConditionFunc &cond); static bool waitUntilKeySet(DBConnector &db, @@ -36,7 +36,7 @@ class RedisTableWaiter DBConnector &db, const std::string &tableName, unsigned int maxWaitSec, - CheckFunc checkFunc); + CheckFunc &checkFunc); }; diff --git a/common/restart_waiter.cpp b/common/restart_waiter.cpp index 038b8fb3c..aca1c305b 100644 --- a/common/restart_waiter.cpp +++ b/common/restart_waiter.cpp @@ -20,7 +20,7 @@ bool RestartWaiter::waitAdvancedBootDone( bool isTcpConn) { DBConnector stateDb(STATE_DB_NAME, dbTimeout, isTcpConn); - return isWarmOrFastBootInProgress(stateDb) ? doWait(stateDb, maxWaitSec) : true; + return isAdvancedBootInProgress(stateDb) ? doWait(stateDb, maxWaitSec) : true; } bool RestartWaiter::waitWarmBootDone( @@ -35,7 +35,7 @@ bool RestartWaiter::waitWarmBootDone( return true; } - return isWarmOrFastBootInProgress(stateDb) ? doWait(stateDb, maxWaitSec) : true; + return isAdvancedBootInProgress(stateDb) ? doWait(stateDb, maxWaitSec) : true; } bool RestartWaiter::waitFastBootDone( @@ -50,13 +50,13 @@ bool RestartWaiter::waitFastBootDone( return true; } - return isWarmOrFastBootInProgress(stateDb) ? doWait(stateDb, maxWaitSec) : true; + return isAdvancedBootInProgress(stateDb) ? doWait(stateDb, maxWaitSec) : true; } bool RestartWaiter::doWait(DBConnector &stateDb, unsigned int maxWaitSec) { - auto condFunc = [](const std::string &value) -> bool { + RedisTableWaiter::ConditionFunc condFunc = [](const std::string &value) -> bool { std::string copy = value; boost::to_lower(copy); return copy == "false"; @@ -69,7 +69,7 @@ bool RestartWaiter::doWait(DBConnector &stateDb, condFunc); } -bool RestartWaiter::isWarmOrFastBootInProgress(DBConnector &stateDb) +bool RestartWaiter::isAdvancedBootInProgress(DBConnector &stateDb) { auto ret = stateDb.hget(STATE_WARM_RESTART_ENABLE_TABLE_NAME + STATE_DB_SEPARATOR + RESTART_KEY, RESTART_ENABLE_FIELD); if (ret) { @@ -88,5 +88,5 @@ bool RestartWaiter::isFastBootInProgress(DBConnector &stateDb) bool RestartWaiter::isWarmBootInProgress(swss::DBConnector &stateDb) { - return isWarmOrFastBootInProgress(stateDb) && !isFastBootInProgress(stateDb); + return isAdvancedBootInProgress(stateDb) && !isFastBootInProgress(stateDb); } diff --git a/common/restart_waiter.h b/common/restart_waiter.h index 0e3f48c7a..ea9eec7b6 100644 --- a/common/restart_waiter.h +++ b/common/restart_waiter.h @@ -21,7 +21,7 @@ class RestartWaiter unsigned int dbTimeout = 0, bool isTcpConn = false); - static bool isWarmOrFastBootInProgress(swss::DBConnector &stateDb); + static bool isAdvancedBootInProgress(swss::DBConnector &stateDb); static bool isFastBootInProgress(swss::DBConnector &stateDb); static bool isWarmBootInProgress(swss::DBConnector &stateDb); diff --git a/tests/redis_table_waiter_ut.cpp b/tests/redis_table_waiter_ut.cpp index 354510275..6ed8116b3 100644 --- a/tests/redis_table_waiter_ut.cpp +++ b/tests/redis_table_waiter_ut.cpp @@ -44,7 +44,7 @@ TEST(RedisTableWaiter, waitUntilFieldSet) { del_key(0); DBConnector db(dbName, 0); - auto condFunc = [&](const std::string &value) -> bool { + RedisTableWaiter::ConditionFunc condFunc = [&](const std::string &value) -> bool { return value == setValue; }; thread t(set_field, 1);