int ADUC_SourceUpdateCacheUtils_PurgeOldestFromUpdateCache()

in src/extensions/download_handlers/plugin_examples/microsoft_delta_download_handler/source_update_cache/src/source_update_cache_utils.cpp [81:199]


int ADUC_SourceUpdateCacheUtils_PurgeOldestFromUpdateCache(
    const ADUC_WorkflowHandle workflowHandle, off_t totalSize, const char* updateCacheBasePath)
{
    int result = -1;

    // Algorithm
    //
    // 1. Create priority queue of type (UpdateCachePurgeFile*), where UpdateCachePurgeFile items sort by last modified time
    // 2. Find all files under the dir and add a corresponding UpdateCachePurgeFile entry to the priority queue
    // 3. from workflowHandle, get the inode for each payload file and remove from the priority queue (if exists)
    //         (the inode is saved at the time of moving the payload from sandbox to cache)
    // 4. while pq has items and totalSize > 0
    //        pop pq and delete the non-payload file at that item's path
    //
    try
    {
        // get the files currently in the update cache
        std::vector<std::string> filesInCache;
        aduc::findFilesInDir(
            updateCacheBasePath == nullptr ? ADUC_DELTA_DOWNLOAD_HANDLER_SOURCE_UPDATE_CACHE_DIR : updateCacheBasePath,
            &filesInCache);

        std::priority_queue<UpdateCachePurgeFile> oldestCacheFiles;

        // populate set of inodes of any payloads that have been moved to cache from sandbox
        std::set<ino_t> updatePayloadInodes;
        size_t countPayloads = workflow_get_update_files_count(workflowHandle);
        for (size_t index = 0; index < countPayloads; ++index)
        {
            ino_t payload_inode = workflow_get_update_file_inode(workflowHandle, index);
            if (payload_inode != ADUC_INODE_SENTINEL_VALUE)
            {
                updatePayloadInodes.insert(payload_inode);
            }
        }

        // filter out the current update's payloads that were moved to cache to avoid removal
        if (updatePayloadInodes.size() > 0)
        {
            Log_Debug("Removing %d payload paths from the cache purge list.", updatePayloadInodes.size());

            auto newEnd = std::remove_if(
                filesInCache.begin(), filesInCache.end(), [&updatePayloadInodes](const std::string& filePath) {
                    struct stat st
                    {
                    };
                    if (stat(filePath.c_str(), &st) != 0)
                    {
                        Log_Warn("filter - stat '%s', errno: %d", filePath.c_str(), errno);
                        st.st_ino = ADUC_INODE_SENTINEL_VALUE;
                        return false; // err on the side of not removing it
                    }

                    const ino_t& updateCacheInode = st.st_ino;

                    // Remove from list of files to be purged when the file's inode is in the set of update payload inodes
                    std::set<ino_t>::iterator iter = updatePayloadInodes.find(updateCacheInode);
                    if (iter == updatePayloadInodes.end())
                    {
                        return false;
                    }

                    return true;
                });

            filesInCache.erase(newEnd);
        }

        // insert into purge list
        std::for_each(filesInCache.begin(), filesInCache.end(), [&oldestCacheFiles](const std::string& filePath) {
            struct stat st
            {
            };
            if (stat(filePath.c_str(), &st) != 0)
            {
                Log_Warn("pq push - stat '%s', errno: %d", filePath.c_str(), errno);
            }
            else
            {
                oldestCacheFiles.emplace(st.st_ino, st.st_mtime, st.st_size, filePath);
            }
        });

        // delete files until made enough room as per totalSize needed for new update payloads
        while (!oldestCacheFiles.empty() && totalSize > 0)
        {
            UpdateCachePurgeFile cachePurgeFile = oldestCacheFiles.top();
            oldestCacheFiles.pop();
            off_t fileSize = cachePurgeFile.GetSizeInBytes();

            std::string filePathForDelete{ std::move(cachePurgeFile.GetFilePath()) };
            int res = unlink(filePathForDelete.c_str());
            if (res != 0)
            {
                Log_Error(
                    "unlink '%s', inode %d - errno: %d", filePathForDelete.c_str(), cachePurgeFile.GetInode(), errno);
                result = -1; // overall it is a failure, but keep going to attempt to free up space.
                continue;
            }
            else
            {
                totalSize -= fileSize; // off_t is signed
            }
        }

        result = 0;
    }
    catch (const std::exception& e)
    {
        const char* what = e.what();
        Log_Error("Unhandled std exception: %s", what);
    }
    catch (...)
    {
        Log_Error("Unhandled exception");
    }

    return result;
}