in common/producerstatetable.cpp [334:481]
void ProducerStateTable::apply_temp_view()
{
if (!m_tempViewActive)
{
SWSS_LOG_THROW("apply_temp_view() called for table %s, however no temp view was created.", getTableName().c_str());
}
// Drop all pending operation first
clear();
TableDump currentState;
{
Table mainTable(m_pipe, getTableName(), false);
mainTable.dump(currentState);
}
// Print content of current view and temp view as debug log
SWSS_LOG_INFO("View switch of table %s required.", getTableName().c_str());
SWSS_LOG_INFO("Objects in current view:");
for (auto const & kfvPair : currentState)
{
SWSS_LOG_INFO(" %s: %zd fields;", kfvPair.first.c_str(), kfvPair.second.size());
}
SWSS_LOG_INFO("Objects in target view:");
for (auto const & kfvPair : m_tempViewState)
{
SWSS_LOG_INFO(" %s: %zd fields;", kfvPair.first.c_str(), kfvPair.second.size());
}
std::vector<std::string> keysToSet;
std::vector<std::string> keysToDel;
// Compare based on existing objects.
// Please note that this comparation is literal not contextual -
// e.g. {nexthop: 10.1.1.1, 10.1.1.2} and {nexthop: 10.1.1.2, 10.1.1.1} will be treated as different.
// Application will need to handle it, to make sure contextually identical field values also literally identical.
for (auto const & kfvPair : currentState)
{
const string& key = kfvPair.first;
const TableMap& fieldValueMap = kfvPair.second;
// DEL is needed if object does not exist in new state, or any field is not presented in new state
// SET is almost always needed, unless old state and new state exactly match each other
// (All old fields exists in new state, values match, and there is no additional field in new state)
if (m_tempViewState.find(key) == m_tempViewState.end()) // Key does not exist in new view
{
keysToDel.emplace_back(key);
keysToSet.emplace_back(key);
continue;
}
const TableMap& newFieldValueMap = m_tempViewState[key];
bool needDel = false;
bool needSet = false;
for (auto const& fvPair : fieldValueMap)
{
const string& field = fvPair.first;
const string& value = fvPair.second;
if (newFieldValueMap.find(field) == newFieldValueMap.end()) // Field does not exist in new view
{
needDel = true;
needSet = true;
break;
}
if (newFieldValueMap.at(field) != value) // Field value changed
{
needSet = true;
}
}
if (newFieldValueMap.size() > fieldValueMap.size()) // New field added
{
needSet = true;
}
if (needDel)
{
keysToDel.emplace_back(key);
}
if (needSet)
{
keysToSet.emplace_back(key);
}
else // If exactly match, no need to sync new state to StateHash in DB
{
m_tempViewState.erase(key);
}
}
// Objects that do not exist currently need to be created
for (auto const & kfvPair : m_tempViewState)
{
const string& key = kfvPair.first;
if (currentState.find(key) == currentState.end())
{
keysToSet.emplace_back(key);
}
}
// Assembly redis command args into a string vector
// See comment in producer_state_table_apply_view.lua for argument format
vector<string> args;
args.emplace_back("EVALSHA");
args.emplace_back(m_shaApplyView);
args.emplace_back(to_string(m_tempViewState.size() + 3));
args.emplace_back(getChannelName(m_pipe->getDbId()));
args.emplace_back(getKeySetName());
args.emplace_back(getDelKeySetName());
vector<string> argvs;
argvs.emplace_back("G");
argvs.emplace_back(to_string(keysToSet.size()));
argvs.insert(argvs.end(), keysToSet.begin(), keysToSet.end());
argvs.emplace_back(to_string(keysToDel.size()));
argvs.insert(argvs.end(), keysToDel.begin(), keysToDel.end());
for (auto const & kfvPair : m_tempViewState)
{
const string& key = kfvPair.first;
const TableMap& fieldValueMap = kfvPair.second;
args.emplace_back(getStateHashPrefix() + getKeyName(key));
argvs.emplace_back(to_string(fieldValueMap.size()));
for (auto const& fvPair : fieldValueMap)
{
const string& field = fvPair.first;
const string& value = fvPair.second;
argvs.emplace_back(field);
argvs.emplace_back(value);
}
}
args.insert(args.end(), argvs.begin(), argvs.end());
// Log arguments for debug
{
std::stringstream ss;
for (auto const & item : args)
{
ss << item << " ";
}
SWSS_LOG_DEBUG("apply_view.lua is called with following argument list: %s", ss.str().c_str());
}
// Invoke redis command
RedisCommand command;
command.format(args);
m_pipe->push(command, REDIS_REPLY_NIL);
m_pipe->flush();
// Clear state, temp view operation is now finished
m_tempViewState.clear();
m_tempViewActive = false;
}