void ProducerStateTable::apply_temp_view()

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;
}