sai_status_t internal_redis_get_stats_process(
        _In_ sai_object_type_t object_type,
        _In_ uint32_t count,
        _Out_ uint64_t *counter_list,
        _In_ swss::KeyOpFieldsValuesTuple &kco)
{
    SWSS_LOG_ENTER();

    // key:         sai_status
    // field:       stat_id
    // value:       stat_value

    const auto &key = kfvKey(kco);
    const auto &values = kfvFieldsValues(kco);

    auto str_sai_status = key;

    sai_status_t status;

    sai_deserialize_status(str_sai_status, status);

    if (status == SAI_STATUS_SUCCESS)
    {
        uint32_t i = 0;
        for (const auto &v : values)
        {
            if (i >= count)
            {
                SWSS_LOG_ERROR("Received more values than expected");
                status = SAI_STATUS_FAILURE;
                break;
            }

            uint64_t value = 0;

            value = stoull(fvValue(v));
            counter_list[i] = value;
            i++;
        }
    }

    return status;
}
void processBulk(
        _In_ sai_common_api_t api,
        _In_ const std::string &line)
{
    SWSS_LOG_ENTER();

    if (!line.size())
    {
        return;
    }

    if (api != (sai_common_api_t)SAI_COMMON_API_BULK_SET)
    {
        SWSS_LOG_THROW("bulk common api %d is not supported yet, FIXME", api);
    }

    /*
     * Here we know we have bulk SET api
     */

    // timestamp|action|objecttype||objectid|attrid=value|...|status||objectid||objectid|attrid=value|...|status||...
    auto fields = tokenize(line, "||");

    auto first = fields.at(0); // timestamp|acion|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<std::string> object_ids;

    std::vector<std::shared_ptr<SaiAttributeList>> attributes;

    std::vector<sai_status_t> statuses;

    for (size_t idx = 1; idx < fields.size(); ++idx)
    {
        // object_id|attr=value|...|status
        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::string str_status = split.back();

        sai_status_t status;

        sai_deserialize_status(str_status, status);

        statuses.push_back(status);

        std::vector<swss::FieldValueTuple> entries; // attributes per object id

        // skip front object_id and back status

        SWSS_LOG_DEBUG("processing: %s", joined.c_str());

        for (size_t i = 1; i < split.size() - 1; ++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<SaiAttributeList> list =
            std::make_shared<SaiAttributeList>(object_type, entries, false);

        sai_attribute_t *attr_list = list->get_attr_list();

        uint32_t attr_count = list->get_attr_count();

        if (api != (sai_common_api_t)SAI_COMMON_API_BULK_GET)
        {
            translate_local_to_redis(object_type, attr_count, attr_list);
        }

        attributes.push_back(list);
    }

    sai_status_t status;

    switch (object_type)
    {
        case SAI_OBJECT_TYPE_ROUTE_ENTRY:
            status = handle_bulk_route(object_ids, api, attributes, statuses);
            break;

        default:

            SWSS_LOG_THROW("bulk op for %s is not supported yet, FIXME",
                    sai_serialize_object_type(object_type).c_str());
    }

    if (status != SAI_STATUS_SUCCESS)
    {
        SWSS_LOG_THROW("failed to execute bulk api, FIXME");
    }
}
void performNotifySyncd(const std::string& request, const std::string response)
{
    SWSS_LOG_ENTER();

    // timestamp|action|data
    auto r = swss::tokenize(request, '|');
    auto R = swss::tokenize(response, '|');

    if (r[1] != "a" || R[1] != "A")
    {
        SWSS_LOG_THROW("invalid syncd notify request/response %s/%s", request.c_str(), response.c_str());
    }

    if (g_notifySyncd == false)
    {
        SWSS_LOG_NOTICE("skipping notify syncd, selected by user");
        return;
    }

    // tell syncd that we are compiling new view
    sai_attribute_t attr;
    attr.id = SAI_REDIS_SWITCH_ATTR_NOTIFY_SYNCD;

    const std::string requestAction = r[2];

    if (requestAction == SYNCD_INIT_VIEW)
    {
        attr.value.s32 = SAI_REDIS_NOTIFY_SYNCD_INIT_VIEW;
    }
    else if (requestAction == SYNCD_APPLY_VIEW)
    {
        attr.value.s32 = SAI_REDIS_NOTIFY_SYNCD_APPLY_VIEW;
    }
    else
    {
        SWSS_LOG_THROW("invalid syncd notify request: %s", request.c_str());
    }

    /*
     * NOTE: We don't need actual switch to set those attributes.
     */

    sai_object_id_t switch_id = SAI_NULL_OBJECT_ID;

    sai_status_t status = sai_metadata_sai_switch_api->set_switch_attribute(switch_id, &attr);

    const std::string& responseStatus = R[2];

    sai_status_t response_status;
    sai_deserialize_status(responseStatus, response_status);

    if (status != response_status)
    {
        SWSS_LOG_THROW("response status %s is different than syncd status %s",
                responseStatus.c_str(),
                sai_serialize_status(status).c_str());
    }

    if (status != SAI_STATUS_SUCCESS)
    {
        SWSS_LOG_THROW("failed to notify syncd %s",
                sai_serialize_status(status).c_str());
    }

    // OK
}