Skip to content

feat: Add scheduler policy and data stroage garbage collection - #34

Merged
sitaowang1998 merged 7 commits into
y-scope:mainfrom
sitaowang1998:scheduler-gc
Dec 10, 2024
Merged

feat: Add scheduler policy and data stroage garbage collection#34
sitaowang1998 merged 7 commits into
y-scope:mainfrom
sitaowang1998:scheduler-gc

Conversation

@sitaowang1998

@sitaowang1998 sitaowang1998 commented Dec 9, 2024

Copy link
Copy Markdown
Collaborator

Description

As title.

Validation performed

  • GitHub workflows pass.
  • Unit tests pass in devcontainer.

Summary by CodeRabbit

  • New Features

    • Introduced KeyValueData and TimedCache classes for enhanced data management.
    • Added methods for adding and retrieving key-value data associated with clients and tasks.
    • Updated scheduling policies to improve task management with optional caching mechanisms.
  • Bug Fixes

    • Improved handling of data storage operations and error management.
  • Tests

    • Updated test cases to reflect changes in data handling and structure, including the introduction of driver IDs and key-value data operations.
    • Removed obsolete test cases and updated existing ones to ensure alignment with new data structures.

@coderabbitai

coderabbitai Bot commented Dec 9, 2024

Copy link
Copy Markdown
Contributor

Walkthrough

The pull request introduces several modifications to the Spider project, primarily affecting the build configuration and class definitions. Key changes include updates to the CMakeLists.txt to add new header files and libraries, enhancements to the Data class by removing certain constructors and methods, and the introduction of a new KeyValueData class. Additionally, the FifoPolicy class and related components have been updated to improve task scheduling and data management, while test cases have been revised to reflect these structural changes.

Changes

File Change Summary
src/spider/CMakeLists.txt Added new header files to SPIDER_CORE_HEADERS and SPIDER_CLIENT_SHARED_HEADERS. Updated library and executable definitions without altering existing logic.
src/spider/core/Data.hpp Updated constructor signature for Data, removed several constructors and the get_key method, and modified the set_hard_locality method parameter type.
src/spider/core/KeyValueData.hpp Introduced new class KeyValueData with private members and public methods for accessing key-value pairs.
src/spider/scheduler/FifoPolicy.cpp Modified schedule_next method to use std::optional for job IDs and creation times. Removed cleanup_job method, replaced with cleanup.
src/spider/scheduler/FifoPolicy.hpp Renamed cleanup_job to cleanup and replaced absl::flat_hash_map with core::TimedCache for job mappings and timing.
src/spider/scheduler/SchedulerPolicy.hpp Updated cleanup_job method to cleanup, removing job ID parameter.
src/spider/storage/DataStorage.hpp Replaced add_data with add_driver_data and add_task_data. Removed get_data_by_key and added new methods for key-value data operations.
src/spider/storage/MysqlStorage.cpp Modified database schema to add tables for key-value data. Updated methods for adding and retrieving data to include driver and task IDs.
src/spider/storage/MysqlStorage.hpp Updated add_data to add_driver_data and add_task_data. Removed get_data_by_key. Added new methods for key-value data management.
src/spider/utils/TimedCache.hpp Introduced TimedCache class for key-value pairs with expiration. Added methods for getting, putting, and cleaning up entries.
tests/scheduler/test-SchedulerPolicy.cpp Updated test cases to use client_id for job submissions and modified data submission logic to reflect changes in data handling.
tests/storage/test-DataStorage.cpp Refactored tests to use KeyValueData and updated data handling logic to include driver IDs.
tests/utils/CoreDataUtils.hpp Removed key comparison logic from data_equal function.
tests/worker/test-FunctionManager.cpp Removed test case for data_test function related to spider::core::Data.

Possibly related PRs

Suggested reviewers

  • kirkrodrigues

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Experiment)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Outside diff range and nitpick comments (8)
tests/storage/test-DataStorage.cpp (1)

77-81: Remove unnecessary blank lines for code clarity

There are multiple blank lines between lines 77 and 81. Removing extra blank lines improves code readability.

Apply this diff to condense the code:

 TEMPLATE_LIST_TEST_CASE(
             "Add and get task key value data",
             "[storage]",
             spider::test::StorageTypeList
-)
+) {
src/spider/storage/MysqlStorage.cpp (3)

Line range hint 1208-1235: Refactor to eliminate code duplication between add_driver_data and add_task_data.

The methods add_driver_data and add_task_data share significant overlapping code. Consider extracting the common functionality into a private helper method or utility function to enhance maintainability and reduce redundancy.

Also applies to: 1247-1279


1286-1299: Avoid unnecessary rollback on read-only operations in get_data.

The get_data method calls m_conn->rollback(); when no data is found. Since SELECT statements are read-only, rolling back the transaction is unnecessary and may impact performance. Recommend removing the rollback in this case.


1489-1517: Avoid unnecessary rollback on read-only operations in get_client_kv_data and get_task_kv_data.

In the methods get_client_kv_data and get_task_kv_data, calling m_conn->rollback(); when no results are found is unnecessary for read-only SELECT queries. Removing these rollbacks can improve performance without affecting functionality.

Also applies to: 1524-1552

src/spider/utils/TimedCache.hpp (2)

12-12: Consider making threshold configurable at compile time

The default threshold of 5 seconds is hardcoded. Consider making it a template parameter for more flexibility.

-constexpr unsigned cDefaultThreshold = 5;
+template<unsigned DefaultThreshold = 5>
 class TimedCache {

40-45: Consider automatic cleanup and size limits

The cleanup method:

  1. Must be called explicitly, which could lead to unbounded memory growth
  2. No maximum size limit for the cache

Consider:

  1. Adding a size-based eviction policy
  2. Implementing automatic cleanup in put operations
  3. Adding a max size parameter to constructor
tests/scheduler/test-SchedulerPolicy.cpp (2)

129-132: Consider extracting common test setup logic.

The setup code for both hard and soft locality tests follows the same pattern. Consider creating a helper function to reduce code duplication.

Here's a suggested refactor:

+ namespace {
+ struct TestSetup {
+     boost::uuids::uuid client_id;
+     boost::uuids::uuid job_id;
+     spider::core::Task task;
+     spider::core::Data data;
+ };
+ 
+ TestSetup create_test_setup(
+     std::shared_ptr<spider::core::MetadataStorage> const& metadata_store,
+     std::shared_ptr<spider::core::DataStorage> const& data_store,
+     bool hard_locality = false) {
+     boost::uuids::random_generator gen;
+     TestSetup setup;
+     setup.client_id = gen();
+     setup.job_id = gen();
+     setup.task = spider::core::Task{"task"};
+     setup.data = spider::core::Data{};
+     setup.data.set_hard_locality(hard_locality);
+     setup.data.set_locality({"127.0.0.1"});
+     
+     metadata_store->add_driver(setup.client_id, "127.0.0.1");
+     data_store->add_driver_data(setup.client_id, setup.data);
+     return setup;
+ }
+ }

Also applies to: 137-138, 142-142


Line range hint 86-142: Verify test coverage for edge cases.

While the happy path testing is thorough, consider adding test cases for:

  • Driver registration failure scenarios
  • Data addition with non-existent drivers
  • Multiple drivers with the same data

Would you like me to help generate additional test cases for these scenarios?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 083b26f and 42f7ff7.

📒 Files selected for processing (14)
  • src/spider/CMakeLists.txt (1 hunks)
  • src/spider/core/Data.hpp (2 hunks)
  • src/spider/core/KeyValueData.hpp (1 hunks)
  • src/spider/scheduler/FifoPolicy.cpp (2 hunks)
  • src/spider/scheduler/FifoPolicy.hpp (2 hunks)
  • src/spider/scheduler/SchedulerPolicy.hpp (1 hunks)
  • src/spider/storage/DataStorage.hpp (3 hunks)
  • src/spider/storage/MysqlStorage.cpp (7 hunks)
  • src/spider/storage/MysqlStorage.hpp (3 hunks)
  • src/spider/utils/TimedCache.hpp (1 hunks)
  • tests/scheduler/test-SchedulerPolicy.cpp (2 hunks)
  • tests/storage/test-DataStorage.cpp (4 hunks)
  • tests/utils/CoreDataUtils.hpp (0 hunks)
  • tests/worker/test-FunctionManager.cpp (0 hunks)
💤 Files with no reviewable changes (2)
  • tests/utils/CoreDataUtils.hpp
  • tests/worker/test-FunctionManager.cpp
🧰 Additional context used
🪛 cppcheck (2.10-2)
src/spider/scheduler/FifoPolicy.cpp

[performance] 88-88: Variable 'id' is assigned in constructor body. Consider performing initialization in initialization list.

(useInitializationList)

tests/storage/test-DataStorage.cpp

[error] 48-48: syntax error

(syntaxError)

🔇 Additional comments (16)
src/spider/storage/DataStorage.hpp (4)

10-10: Include KeyValueData.hpp for key-value data operations

Including ../core/KeyValueData.hpp is appropriate and necessary for handling key-value data within the DataStorage class.


26-27: Consistent method names for adding data

The introduction of add_driver_data and add_task_data methods enhances clarity by specifying the context of the data being added. This improves the interface's expressiveness and maintains consistency.


38-38: Add remove_dangling_data method for data integrity

Implementing remove_dangling_data is a valuable addition to manage data integrity and prevent orphaned data within the storage system.


40-51: Implement key-value data management methods

The new methods for handling client and task key-value data (add_client_kv_data, add_task_kv_data, get_client_kv_data, get_task_kv_data) are well-defined and align with the system's requirements for key-value data operations.

src/spider/storage/MysqlStorage.hpp (3)

16-16: Include KeyValueData.hpp for key-value operations

Including ../core/KeyValueData.hpp is necessary for the implementation of key-value data methods in MySqlDataStorage.


85-86: Implement data addition methods with context identifiers

The methods add_driver_data and add_task_data correctly override the base class methods and provide context-specific data storage functionality.


97-110: Add key-value data management methods

The addition of key-value data methods enhances the MySqlDataStorage class's capabilities and aligns with the interface defined in DataStorage.

src/spider/storage/MysqlStorage.cpp (1)

33-33: The inclusion of KeyValueData.hpp is appropriate.

Including the KeyValueData.hpp header file is necessary for the newly added key-value data functionality.

src/spider/core/KeyValueData.hpp (1)

1-30: Well-implemented KeyValueData class.

The KeyValueData class is correctly designed with proper use of std::move in the constructor and [[nodiscard]] attributes on the accessor methods. The code follows best practices in terms of encapsulation and coding standards.

src/spider/scheduler/FifoPolicy.hpp (2)

26-26: Simplified cleanup method improves interface clarity.

Changing the method from cleanup_job(boost::uuids::uuid job_id) to cleanup() simplifies the interface, making it more general and potentially allowing for broader cleanup operations.


29-30: Adoption of TimedCache enhances cache management.

Replacing absl::flat_hash_map with core::TimedCache for m_task_job_cache and m_job_time_cache introduces cache expiration and improved resource management. This change is beneficial for maintaining optimal performance and preventing stale data.

src/spider/scheduler/SchedulerPolicy.hpp (1)

30-30: Updated cleanup method aligns with interface changes.

The modification of the cleanup method in the base SchedulerPolicy class ensures consistency across derived classes and reflects the updated design that no longer requires a job-specific cleanup parameter.

src/spider/core/Data.hpp (2)

34-34: LGTM: Improved const correctness

Good improvement adding const to the hard parameter in set_hard_locality.


18-18: Verify impact of constructor signature change

The addition of const to the id parameter is a good improvement for const correctness. However, this change could affect existing code that uses this constructor.

✅ Verification successful

Let me search for any direct instantiations of the Data class to ensure we have a complete picture.


Let me try a different approach to find usages and inheritance.


Let me check the inheritance in MysqlStorage.hpp and look for any other potential usages.


The constructor signature change is safe to proceed

Based on the comprehensive analysis of the codebase:

  1. The Data constructor is used directly only in test files and internal storage implementations
  2. The const addition to the UUID parameter is a non-breaking change that improves const correctness
  3. The only direct usage in production code is through the storage layer, which already handles the UUID as const
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for constructor usage to verify impact
rg -A 2 "Data\(.*uuid.*\)" --type cpp

Length of output: 526


Script:

#!/bin/bash
# Search for direct Data instantiations
ast-grep --pattern 'Data($$$)'

# Also search for any inheritance from Data class
ast-grep --pattern 'class $_ : $$$Data$$$'

Length of output: 77


Script:

#!/bin/bash
# Search for Data class inheritance and usage patterns
rg -l "class.*:.*Data" --type cpp
rg -l "std::make.*<Data>" --type cpp
rg -l "new Data\(" --type cpp

# Also check for any test files that might use the constructor
fd -e cpp -e hpp test | xargs rg "Data\("

Length of output: 186


Script:

#!/bin/bash
# Check MysqlStorage.hpp content
cat src/spider/storage/MysqlStorage.hpp

# Search for any Data type usage in method parameters or return types
rg "Data[&\*]?" --type cpp -A 2

Length of output: 32203

src/spider/CMakeLists.txt (1)

13-13: Verify header dependencies

The new headers KeyValueData.hpp and TimedCache.hpp are added to core headers. Please ensure:

  1. All required dependencies are listed in the target_link_libraries
  2. Header include order is correct

Also applies to: 21-21

✅ Verification successful

Dependencies are properly configured

The verification shows that all required dependencies are correctly set up:

  • KeyValueData.hpp uses boost::uuid, which is covered by Boost::headers in spider_core's target_link_libraries
  • TimedCache.hpp uses absl::flat_hash_map, which is explicitly linked via absl::flat_hash_map in spider_core's target_link_libraries
  • Both headers are properly listed in SPIDER_CORE_HEADERS and included in the spider_core target
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for missing dependencies in new headers
rg -l "^#include.*boost|^#include.*absl" core/KeyValueData.hpp utils/TimedCache.hpp

Length of output: 208


Script:

#!/bin/bash
# Find the actual paths of these header files
fd -e hpp -e h "KeyValueData|TimedCache"

# After finding the files, let's check their content and dependencies

Length of output: 107


Script:

#!/bin/bash
# Now check the actual content of these files for dependencies
echo "=== KeyValueData.hpp dependencies ==="
rg "^#include" src/spider/core/KeyValueData.hpp

echo -e "\n=== TimedCache.hpp dependencies ==="
rg "^#include" src/spider/utils/TimedCache.hpp

# Also check CMakeLists.txt for target_link_libraries
echo -e "\n=== CMakeLists.txt target_link_libraries ==="
rg "target_link_libraries" src/spider/CMakeLists.txt

Length of output: 1062


Script:

#!/bin/bash
# Let's check the complete CMakeLists.txt content to see all dependencies
cat src/spider/CMakeLists.txt

Length of output: 4106

tests/scheduler/test-SchedulerPolicy.cpp (1)

86-86: LGTM! The driver-data relationship setup looks good.

The changes properly establish the relationship between client, driver, and data before testing the scheduling behaviour. The sequence of operations is correct: adding the driver first, then the driver data, and finally the job.

Let's verify the driver-data relationship handling:

Also applies to: 92-93, 97-97

✅ Verification successful

Driver-data relationship is properly implemented and verified

After reviewing the test file content, I can confirm that the driver-data relationship is correctly handled in both the "Schedule hard locality" and "Schedule soft locality" test cases. The sequence follows the proper pattern:

  1. Driver registration with add_driver(client_id, "127.0.0.1")
  2. Data addition with add_driver_data(client_id, data)
  3. Job submission with the task using the data

The changes in lines 86, 92-93, and 97 maintain consistency with this pattern and properly test the scheduling behaviour with respect to data locality.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent driver-data relationship handling across tests
# Look for patterns where driver registration precedes data addition

ast-grep --pattern 'add_driver($_, $_)' -A 5

Length of output: 46


Script:

#!/bin/bash
# Let's try a different approach to find driver-related test patterns
# First, let's see the content of the test file
cat tests/scheduler/test-SchedulerPolicy.cpp

Length of output: 6740

Comment thread src/spider/scheduler/FifoPolicy.cpp Outdated
Comment on lines +119 to +121
auto FifoPolicy::cleanup() -> void {
m_task_job_cache.cleanup();
m_task_job_cache.cleanup();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Correct cache cleanup in cleanup() method

There is a duplicate call to m_task_job_cache.cleanup();, and m_job_time_cache.cleanup(); is missing. Ensure that both caches are properly cleaned up.

Apply this diff to fix the cleanup logic:

     auto FifoPolicy::cleanup() -> void {
         m_task_job_cache.cleanup();
-        m_task_job_cache.cleanup();
+        m_job_time_cache.cleanup();
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
auto FifoPolicy::cleanup() -> void {
m_task_job_cache.cleanup();
m_task_job_cache.cleanup();
auto FifoPolicy::cleanup() -> void {
m_task_job_cache.cleanup();
m_job_time_cache.cleanup();

Comment thread src/spider/scheduler/FifoPolicy.cpp Outdated
Comment on lines +98 to +101
if (std::optional<std::chrono::system_clock::time_point> const optional_time;
optional_time.has_value())
{
return optional_time.value();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Initialize optional_time to prevent logical error

Similarly, optional_time is declared without initialization, making optional_time.has_value() always return false. This prevents the intended caching mechanism from functioning.

Apply this diff to initialize optional_time correctly:

-                    if (std::optional<std::chrono::system_clock::time_point> const optional_time;
+                    if (std::optional<std::chrono::system_clock::time_point> const optional_time = m_job_time_cache.get(job_id);
                         optional_time.has_value())
                     {
                         return optional_time.value();
                     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (std::optional<std::chrono::system_clock::time_point> const optional_time;
optional_time.has_value())
{
return optional_time.value();
if (std::optional<std::chrono::system_clock::time_point> const optional_time = m_job_time_cache.get(job_id);
optional_time.has_value())
{
return optional_time.value();

Comment thread src/spider/scheduler/FifoPolicy.cpp Outdated
Comment on lines +85 to +87
std::optional<boost::uuids::uuid> optional_job_id;
if (optional_job_id.has_value()) {
job_id = optional_job_id.value();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Initialize optional_job_id to prevent logical error

The variable optional_job_id is declared but not initialized, resulting in optional_job_id.has_value() always returning false. This causes the code block that assigns job_id to be skipped.

Apply this diff to initialize optional_job_id correctly:

-                    std::optional<boost::uuids::uuid> optional_job_id;
+                    std::optional<boost::uuids::uuid> optional_job_id = m_task_job_cache.get(task_id);
                     if (optional_job_id.has_value()) {
                         job_id = optional_job_id.value();
                     } else {

Committable suggestion skipped: line range outside the PR's diff.

== data_storage->get_data(data.get_id(), &result).type);
}

TEMPLATE_LIST_TEST_CASE(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix syntax error due to unmatched parentheses in macro

There is a syntax error at line 48, likely caused by missing closing parentheses in the TEMPLATE_LIST_TEST_CASE macro. Ensure all parentheses are properly matched.

Apply this diff to close the macro definition correctly:

 TEMPLATE_LIST_TEST_CASE(
             "Add and get driver key value data",
             "[storage]",
             spider::test::StorageTypeList
+)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
TEMPLATE_LIST_TEST_CASE(
TEMPLATE_LIST_TEST_CASE(
"Add and get driver key value data",
"[storage]",
spider::test::StorageTypeList
)
🧰 Tools
🪛 cppcheck (2.10-2)

[error] 48-48: syntax error

(syntaxError)

Comment thread src/spider/storage/MysqlStorage.cpp Outdated

std::array<char const* const, 12> const cCreateStorage = {
char const* const cCreateClientKVDataTable = R"(CREATE TABLE IF NOT EXISTS `client_kv_data` (
`key` VARCHAR(64) NOT NULL,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid using key as a column name to prevent SQL keyword conflicts.

The column name key is a reserved keyword in SQL and may cause conflicts or require special handling. Consider renaming it to kv_key or data_key to improve clarity and avoid potential issues.

Apply this diff to rename the column:

-char const* const cCreateClientKVDataTable = R"(CREATE TABLE IF NOT EXISTS `client_kv_data` (
-    `key` VARCHAR(64) NOT NULL,
+    `kv_key` VARCHAR(64) NOT NULL,
     `value` VARCHAR(128) NOT NULL,
     `client_id` BINARY(16) NOT NULL,
-    PRIMARY KEY (`client_id`, `key`)
+    PRIMARY KEY (`client_id`, `kv_key`)
))";

-char const* const cCreateTaskKVDataTable = R"(CREATE TABLE IF NOT EXISTS `task_kv_data` (
-    `key` VARCHAR(64) NOT NULL,
+    `kv_key` VARCHAR(64) NOT NULL,
     `value` VARCHAR(128) NOT NULL,
     `task_id` BINARY(16) NOT NULL,
-    PRIMARY KEY (`task_id`, `key`)
+    PRIMARY KEY (`task_id`, `kv_key`)
))";

Also applies to: 169-169

Comment thread src/spider/utils/TimedCache.hpp Outdated
Comment on lines +30 to +38
auto put(Key const& key, Value const& value) {
auto iter = m_map.find(key);
if (iter == m_map.end()) {
m_map[key] = std::make_pair(std::chrono::steady_clock::now(), value);
} else {
iter->second.first = std::chrono::steady_clock::now();
iter->second.second = value;
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add return type and consider thread safety

The put method:

  1. Is missing a return type (should be void)
  2. Could have race conditions in a multi-threaded environment
-    auto put(Key const& key, Value const& value) {
+    void put(Key const& key, Value const& value) {

Consider adding mutex protection for thread safety:

private:
    mutable std::mutex m_mutex;
    // ... existing members ...

public:
    void put(Key const& key, Value const& value) {
        std::lock_guard<std::mutex> lock(m_mutex);
        // ... existing implementation ...
    }

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (5)
src/spider/scheduler/FifoPolicy.cpp (1)

Line range hint 99-112: Consider combining metadata store queries for better performance.

While the implementation is correct, we could optimize performance by combining the metadata store queries. Currently, we might hit the metadata store twice for the same job (once for ID and once for creation time).

Consider caching both pieces of information in a single query when either is missing.

Here's a suggested optimization:

                 if (optional_job_id.has_value()) {
                     job_id = optional_job_id.value();
+                    if (auto const optional_time = m_job_time_cache.get(job_id); 
+                        optional_time.has_value()) {
+                        return optional_time.value();
+                    }
                 } else {
                     if (false == metadata_store->get_task_job_id(task_id, &job_id).success()) {
                         throw std::runtime_error(fmt::format(
                                 "Task with id {} not exists.",
                                 boost::uuids::to_string(task_id)
                         ));
                     }
                     m_task_job_cache.put(task_id, job_id);
-                }
-
-                std::optional<std::chrono::system_clock::time_point> const optional_time
-                        = m_job_time_cache.get(job_id);
-                if (optional_time.has_value()) {
-                    return optional_time.value();
                 }

                 core::JobMetadata job_metadata;
🧰 Tools
🪛 cppcheck (2.10-2)

[performance] 88-88: Variable 'id' is assigned in constructor body. Consider performing initialization in initialization list.

(useInitializationList)

src/spider/storage/MysqlStorage.cpp (4)

1248-1275: Consider extracting common data insertion logic

The code is duplicated between add_driver_data and add_task_data. Consider extracting the common data and locality insertion logic into a private helper method.

Example refactor:

+private:
+    auto insert_data_and_locality(Data const& data) -> StorageErr {
+        std::unique_ptr<sql::PreparedStatement> statement(m_conn->prepareStatement(
+                "INSERT INTO `data` (`id`, `value`, `hard_locality`) VALUES(?, ?, ?)"
+        ));
+        sql::bytes id_bytes = uuid_get_bytes(data.get_id());
+        statement->setBytes(1, &id_bytes);
+        statement->setString(2, data.get_value());
+        statement->setBoolean(3, data.is_hard_locality());
+        statement->executeUpdate();
+
+        for (std::string const& addr : data.get_locality()) {
+            std::unique_ptr<sql::PreparedStatement> locality_statement(
+                    m_conn->prepareStatement("INSERT INTO `data_locality` (`id`, "
+                                             "`address`) VALUES (?, ?)")
+            );
+            locality_statement->setBytes(1, &id_bytes);
+            locality_statement->setString(2, addr);
+            locality_statement->executeUpdate();
+        }
+        return StorageErr{};
+    }

Then use it in both methods:

 auto MySqlDataStorage::add_driver_data(boost::uuids::uuid const driver_id, Data const& data)
         -> StorageErr {
     try {
-        std::unique_ptr<sql::PreparedStatement> statement(m_conn->prepareStatement(
-                "INSERT INTO `data` (`id`, `value`, `hard_locality`) VALUES(?, ?, ?)"
-        ));
-        sql::bytes id_bytes = uuid_get_bytes(data.get_id());
-        statement->setBytes(1, &id_bytes);
-        statement->setString(2, data.get_value());
-        statement->setBoolean(3, data.is_hard_locality());
-        statement->executeUpdate();
-
-        for (std::string const& addr : data.get_locality()) {
-            std::unique_ptr<sql::PreparedStatement> locality_statement(
-                    m_conn->prepareStatement("INSERT INTO `data_locality` (`id`, "
-                                             "`address`) VALUES (?, ?)")
-            );
-            locality_statement->setBytes(1, &id_bytes);
-            locality_statement->setString(2, addr);
-            locality_statement->executeUpdate();
-        }
+        auto err = insert_data_and_locality(data);
+        if (err.has_error()) {
+            return err;
+        }
+        sql::bytes id_bytes = uuid_get_bytes(data.get_id());
         std::unique_ptr<sql::PreparedStatement> driver_ref_statement(m_conn->prepareStatement(
                 "INSERT INTO `data_ref_driver` (`id`, `driver_id`) VALUES(?, ?)"
         ));

1429-1441: Consider adding indexes to optimize the cleanup query

The subqueries in the DELETE statement would benefit from indexes on the id columns of the reference tables.

Add these indexes to the table creation SQL:

 char const* const cCreateDataRefDriverTable = R"(CREATE TABLE IF NOT EXISTS `data_ref_driver` (
     `id` BINARY(16) NOT NULL,
     `driver_id` BINARY(16) NOT NULL,
-    KEY (`id`) USING BTREE,
+    KEY (`id`, `driver_id`) USING BTREE,
     KEY (`driver_id`) USING BTREE,
     CONSTRAINT `data_driver_ref_id` FOREIGN KEY (`id`) REFERENCES `data` (`id`) ON UPDATE NO ACTION ON DELETE CASCADE,
     CONSTRAINT `data_ref_driver_id` FOREIGN KEY (`driver_id`) REFERENCES `drivers` (`id`) ON UPDATE NO ACTION ON DELETE CASCADE
 ))";

 char const* const cCreateDataRefTaskTable = R"(CREATE TABLE IF NOT EXISTS `data_ref_task` (
     `id` BINARY(16) NOT NULL,
     `task_id` BINARY(16) NOT NULL,
-    KEY (`id`) USING BTREE,
+    KEY (`id`, `task_id`) USING BTREE,
     KEY (`task_id`) USING BTREE,
     CONSTRAINT `data_task_ref_id` FOREIGN KEY (`id`) REFERENCES `data` (`id`) ON UPDATE NO ACTION ON DELETE CASCADE,
     CONSTRAINT `data_ref_task_id` FOREIGN KEY (`task_id`) REFERENCES `tasks` (`id`) ON UPDATE NO ACTION ON DELETE CASCADE
 ))";

1443-1483: Extract common key-value data insertion logic

Both methods share similar insertion logic. Consider extracting the common code into a template method.

Example refactor:

+private:
+    template<typename T>
+    auto add_kv_data(T const& data, const char* table_name, const char* id_column) -> StorageErr {
+        try {
+            std::unique_ptr<sql::PreparedStatement> statement(m_conn->prepareStatement(
+                    fmt::format("INSERT INTO `{}` (`kv_key`, `value`, `{}`) VALUES(?, ?, ?)",
+                              table_name, id_column)
+            ));
+            statement->setString(1, data.get_key());
+            statement->setString(2, data.get_value());
+            sql::bytes id_bytes = uuid_get_bytes(data.get_id());
+            statement->setBytes(3, &id_bytes);
+            statement->executeUpdate();
+        } catch (sql::SQLException& e) {
+            m_conn->rollback();
+            if (e.getErrorCode() == ErDupKey || e.getErrorCode() == ErDupEntry) {
+                return StorageErr{StorageErrType::DuplicateKeyErr, e.what()};
+            }
+            return StorageErr{StorageErrType::OtherErr, e.what()};
+        }
+        m_conn->commit();
+        return StorageErr{};
+    }

Then simplify both methods:

 auto MySqlDataStorage::add_client_kv_data(KeyValueData const& data) -> StorageErr {
-    try {
-        std::unique_ptr<sql::PreparedStatement> statement(m_conn->prepareStatement(
-                "INSERT INTO `client_kv_data` (`kv_key`, `value`, `client_id`) VALUES(?, ?, ?)"
-        ));
-        statement->setString(1, data.get_key());
-        statement->setString(2, data.get_value());
-        sql::bytes id_bytes = uuid_get_bytes(data.get_id());
-        statement->setBytes(3, &id_bytes);
-        statement->executeUpdate();
-    } catch (sql::SQLException& e) {
-        m_conn->rollback();
-        if (e.getErrorCode() == ErDupKey || e.getErrorCode() == ErDupEntry) {
-            return StorageErr{StorageErrType::DuplicateKeyErr, e.what()};
-        }
-        return StorageErr{StorageErrType::OtherErr, e.what()};
-    }
-    m_conn->commit();
-    return StorageErr{};
+    return add_kv_data(data, "client_kv_data", "client_id");
 }

1485-1518: Extract common key-value data retrieval logic

The method shares similar retrieval logic with get_task_kv_data. Consider extracting the common code.

Example refactor:

+private:
+    template<typename ID>
+    auto get_kv_data(
+            ID const& id,
+            std::string const& key,
+            std::string* value,
+            const char* table_name,
+            const char* id_column
+    ) -> StorageErr {
+        try {
+            std::unique_ptr<sql::PreparedStatement> statement(m_conn->prepareStatement(
+                    fmt::format("SELECT `value` FROM `{}` WHERE `{}` = ? AND `kv_key` = ?",
+                              table_name, id_column)
+            ));
+            sql::bytes id_bytes = uuid_get_bytes(id);
+            statement->setBytes(1, &id_bytes);
+            statement->setString(2, key);
+            std::unique_ptr<sql::ResultSet> res(statement->executeQuery());
+            if (res->rowsCount() == 0) {
+                m_conn->rollback();
+                return StorageErr{
+                        StorageErrType::KeyNotFoundErr,
+                        fmt::format(
+                                "no data for {} {} with key {}",
+                                id_column,
+                                boost::uuids::to_string(id),
+                                key
+                        )
+                };
+            }
+            res->next();
+            *value = res->getString(1);
+        } catch (sql::SQLException& e) {
+            m_conn->rollback();
+            return StorageErr{StorageErrType::OtherErr, e.what()};
+        }
+        m_conn->commit();
+        return StorageErr{};
+    }

Then simplify both methods:

 auto MySqlDataStorage::get_client_kv_data(
         boost::uuids::uuid const& client_id,
         std::string const& key,
         std::string* value
 ) -> StorageErr {
-    try {
-        std::unique_ptr<sql::PreparedStatement> statement(m_conn->prepareStatement(
-                "SELECT `value` "
-                "FROM `client_kv_data` WHERE `client_id` = ? AND `kv_key` = ?"
-        ));
-        sql::bytes id_bytes = uuid_get_bytes(client_id);
-        statement->setBytes(1, &id_bytes);
-        statement->setString(2, key);
-        std::unique_ptr<sql::ResultSet> res(statement->executeQuery());
-        if (res->rowsCount() == 0) {
-            m_conn->rollback();
-            return StorageErr{
-                    StorageErrType::KeyNotFoundErr,
-                    fmt::format(
-                            "no data for client {} with key {}",
-                            boost::uuids::to_string(client_id),
-                            key
-                    )
-            };
-        }
-        res->next();
-        *value = res->getString(1);
-    } catch (sql::SQLException& e) {
-        m_conn->rollback();
-        return StorageErr{StorageErrType::OtherErr, e.what()};
-    }
-    m_conn->commit();
-    return StorageErr{};
+    return get_kv_data(client_id, key, value, "client_kv_data", "client_id");
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 42f7ff7 and 6627a34.

📒 Files selected for processing (3)
  • src/spider/scheduler/FifoPolicy.cpp (2 hunks)
  • src/spider/storage/MysqlStorage.cpp (7 hunks)
  • src/spider/utils/TimedCache.hpp (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/spider/utils/TimedCache.hpp
🧰 Additional context used
🪛 cppcheck (2.10-2)
src/spider/scheduler/FifoPolicy.cpp

[performance] 88-88: Variable 'id' is assigned in constructor body. Consider performing initialization in initialization list.

(useInitializationList)

🔇 Additional comments (4)
src/spider/scheduler/FifoPolicy.cpp (2)

85-96: LGTM! Proper cache usage and error handling.

The implementation correctly uses optional for cache retrieval and includes appropriate error handling with descriptive messages.

🧰 Tools
🪛 cppcheck (2.10-2)

[performance] 88-88: Variable 'id' is assigned in constructor body. Consider performing initialization in initialization list.

(useInitializationList)


120-122: LGTM! Fixed cache cleanup implementation.

The cleanup method now correctly handles both caches, addressing the issue from previous reviews where there was a duplicate call to m_task_job_cache.cleanup().

src/spider/storage/MysqlStorage.cpp (2)

Line range hint 1209-1236: LGTM!

The implementation is secure, properly handles transactions, and correctly manages data references.


1520-1553: LGTM!

The implementation is secure and follows good practices for database access.

Comment on lines +161 to +166
char const* const cCreateClientKVDataTable = R"(CREATE TABLE IF NOT EXISTS `client_kv_data` (
`kv_key` VARCHAR(64) NOT NULL,
`value` VARCHAR(128) NOT NULL,
`client_id` BINARY(16) NOT NULL,
PRIMARY KEY (`client_id`, `kv_key`)
))";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add foreign key constraint for client_id

The client_kv_data table should have a foreign key constraint on client_id to maintain referential integrity with the clients table, similar to how task_kv_data has a constraint on task_id.

Apply this diff:

 char const* const cCreateClientKVDataTable = R"(CREATE TABLE IF NOT EXISTS `client_kv_data` (
     `kv_key` VARCHAR(64) NOT NULL,
     `value` VARCHAR(128) NOT NULL,
     `client_id` BINARY(16) NOT NULL,
-    PRIMARY KEY (`client_id`, `kv_key`)
+    PRIMARY KEY (`client_id`, `kv_key`),
+    CONSTRAINT `kv_data_client_id` FOREIGN KEY (`client_id`) REFERENCES `drivers` (`id`) ON UPDATE NO ACTION ON DELETE CASCADE
 ))";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
char const* const cCreateClientKVDataTable = R"(CREATE TABLE IF NOT EXISTS `client_kv_data` (
`kv_key` VARCHAR(64) NOT NULL,
`value` VARCHAR(128) NOT NULL,
`client_id` BINARY(16) NOT NULL,
PRIMARY KEY (`client_id`, `kv_key`)
))";
char const* const cCreateClientKVDataTable = R"(CREATE TABLE IF NOT EXISTS `client_kv_data` (
`kv_key` VARCHAR(64) NOT NULL,
`value` VARCHAR(128) NOT NULL,
`client_id` BINARY(16) NOT NULL,
PRIMARY KEY (`client_id`, `kv_key`),
CONSTRAINT `kv_data_client_id` FOREIGN KEY (`client_id`) REFERENCES `drivers` (`id`) ON UPDATE NO ACTION ON DELETE CASCADE
))";

@sitaowang1998
sitaowang1998 merged commit e4e988b into y-scope:main Dec 10, 2024
@sitaowang1998
sitaowang1998 deleted the scheduler-gc branch December 10, 2024 02:16
@coderabbitai coderabbitai Bot mentioned this pull request Aug 21, 2025
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant