Skip to content

feat: Cache ready tasks query results in scheduler - #58

Merged
sitaowang1998 merged 15 commits into
y-scope:mainfrom
sitaowang1998:scheduler_cache
Jan 20, 2025
Merged

feat: Cache ready tasks query results in scheduler#58
sitaowang1998 merged 15 commits into
y-scope:mainfrom
sitaowang1998:scheduler_cache

Conversation

@sitaowang1998

@sitaowang1998 sitaowang1998 commented Jan 20, 2025

Copy link
Copy Markdown
Collaborator

Description

Scheduler fetches all ready tasks from storage on each schedule task request, which is inefficient. Now scheduler caches the tasks queried from storage for a short period or number of schedule requests before another storage query.

Validation performed

  • GitHub workflows pass
  • Unit tests pass in dev container
  • Integration tests pass in dev container

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a task caching mechanism for improved scheduler performance.
    • Enhanced task scheduling with more flexible task instance management.
  • Improvements

    • Updated task scheduling policy to support more complex task retrieval.
    • Improved metadata and data storage interactions.
    • Refined task timeout and instance handling.
  • Changes

    • Modified task ID management to support multiple task identifiers.
    • Updated method signatures across scheduler and worker components.
    • Streamlined task scheduling logic, reducing the number of parameters in method calls.
  • Bug Fixes

    • Improved error handling in task instance creation.
    • Enhanced task locality checking mechanisms.

@coderabbitai

coderabbitai Bot commented Jan 20, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

The pull request introduces enhancements to the task scheduling and management system in the Spider project. Key changes include the addition of a new SchedulerTaskCache class for task caching, updates to the FifoPolicy to utilize task caches, modifications to task identification to use tuples of task and task instance IDs, and refactoring of storage and worker components to support these new mechanisms. The overall structure of the CMake configuration remains intact while integrating these new functionalities.

Changes

File Change Summary
src/spider/CMakeLists.txt Added scheduler/SchedulerTaskCache.cpp and scheduler/SchedulerTaskCache.hpp to SPIDER_SCHEDULER_SOURCES
src/spider/scheduler/FifoPolicy.{cpp,hpp} Updated constructor, modified schedule_next method, added get_next_task method, introduced member variables for metadata and data stores
src/spider/scheduler/SchedulerMessage.hpp Modified ScheduleTaskResponse to use tuple of task IDs instead of single task ID
src/spider/scheduler/SchedulerPolicy.hpp Simplified schedule_next method signature by removing storage parameters
src/spider/scheduler/SchedulerServer.cpp Updated task scheduling and instance creation logic
src/spider/scheduler/SchedulerTaskCache.{cpp,hpp} New implementation for task caching and retrieval
src/spider/storage/MetadataStorage.hpp Added create_task_instance method, updated get_task_timeout method signature
src/spider/storage/MySqlStorage.{cpp,hpp} Implemented create_task_instance, updated get_task_timeout method
src/spider/worker/WorkerClient.{cpp,hpp} Modified get_next_task to return tuple of task IDs
src/spider/worker/worker.cpp Updated task fetching and handling logic
tests/scheduler/test-*.cpp Updated test cases to reflect new scheduling and task management approaches

Sequence Diagram

sequenceDiagram
    participant Worker
    participant WorkerClient
    participant SchedulerServer
    participant FifoPolicy
    participant SchedulerTaskCache
    participant MetadataStorage

    Worker->>WorkerClient: Request next task
    WorkerClient->>SchedulerServer: Get next task
    SchedulerServer->>FifoPolicy: schedule_next()
    FifoPolicy->>SchedulerTaskCache: get_ready_task()
    SchedulerTaskCache->>MetadataStorage: fetch_ready_tasks()
    MetadataStorage-->>SchedulerTaskCache: Return tasks
    SchedulerTaskCache-->>FifoPolicy: Return task ID
    FifoPolicy-->>SchedulerServer: Return task ID
    SchedulerServer-->>WorkerClient: Return task and task instance IDs
    WorkerClient-->>Worker: Provide task details
Loading

Possibly related PRs

Finishing Touches

  • 📝 Generate Docstrings (Beta)

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. (Beta)
  • @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.

@sitaowang1998 sitaowang1998 changed the title feat: Add ready tasks cache in scheduler feat: Cache ready tasks query results in scheduler Jan 20, 2025

@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: 3

🧹 Nitpick comments (12)
src/spider/scheduler/SchedulerMessage.hpp (2)

58-79: Enhance readability by replacing std::tuple with a struct for task IDs

Using std::tuple<boost::uuids::uuid, boost::uuids::uuid> for m_task_ids may reduce code clarity. Consider defining a struct with named members, such as TaskIdentifiers, to improve readability and maintainability.

Apply this change to define a struct and update the code:

// Define the struct
struct TaskIdentifiers {
    boost::uuids::uuid task_id;
    boost::uuids::uuid task_instance_id;
};

// Update the member variable
std::optional<TaskIdentifiers> m_task_ids = std::nullopt;

// Update constructors
ScheduleTaskResponse(
    boost::uuids::uuid const task_id,
    boost::uuids::uuid const task_instance_id
)
    : m_task_ids{TaskIdentifiers{task_id, task_instance_id}} {}

explicit ScheduleTaskResponse(TaskIdentifiers const& task_ids)
    : m_task_ids{task_ids} {}

// Update methods
[[nodiscard]] auto get_task_ids() const -> TaskIdentifiers const& {
    return m_task_ids.value();
}

// Update MSGPACK definition
MSGPACK_DEFINE_ARRAY(m_task_ids);

68-68: Rename method to reflect plural task IDs

The method has_task_id() checks for multiple task IDs. For clarity and consistency, consider renaming it to has_task_ids().

Apply this diff to rename the method:

-[[nodiscard]] auto has_task_id() const -> bool { return m_task_ids.has_value(); }
+[[nodiscard]] auto has_task_ids() const -> bool { return m_task_ids.has_value(); }
src/spider/scheduler/SchedulerTaskCache.cpp (1)

54-58: Optimize task retrieval to avoid unnecessary copying

In pop_next_task, copying m_tasks into a vector tasks can be inefficient for large datasets. Consider iterating over m_tasks directly or modifying m_get_next_task_function to accept the map or its iterators to improve performance.

Apply this change to pass m_tasks directly:

// Modify the function signature if possible
auto pop_next_task(
        boost::uuids::uuid const& worker_id,
        std::string const& worker_addr
) -> std::optional<core::Task> {
    // Pass m_tasks directly
    std::optional<boost::uuids::uuid> const task_id = std::invoke(
            m_get_next_task_function,
            std::ref(m_tasks),
            std::cref(worker_id),
            std::cref(worker_addr)
    );
    // ... rest of the code
}
src/spider/scheduler/FifoPolicy.cpp (4)

62-76: Use initialization list for member variables

In the constructor, member variables are being assigned within the constructor body. It's more efficient and idiomatic in C++ to initialize them using an initialization list.

Apply this diff to use an initialization list:

-FifoPolicy::FifoPolicy(
-        std::shared_ptr<core::MetadataStorage> const& metadata_store,
-        std::shared_ptr<core::DataStorage> const& data_store
-)
-        : m_metadata_store{metadata_store},
-          m_data_store{data_store},
-          m_task_cache{
-                  metadata_store,
-                  data_store,
-                  [&](std::vector<core::Task>& tasks,
-                      boost::uuids::uuid const& worker_id,
-                      std::string const& worker_addr) -> std::optional<boost::uuids::uuid> {
-                      return get_next_task(tasks, worker_id, worker_addr);
-                  }
-          } {}
+FifoPolicy::FifoPolicy(
+        std::shared_ptr<core::MetadataStorage> const& metadata_store,
+        std::shared_ptr<core::DataStorage> const& data_store
+) :
+        m_metadata_store{metadata_store},
+        m_data_store{data_store},
+        m_task_cache{
+                metadata_store,
+                data_store,
+                [&](std::vector<core::Task>& tasks,
+                    boost::uuids::uuid const& worker_id,
+                    std::string const& worker_addr) -> std::optional<boost::uuids::uuid> {
+                    return get_next_task(tasks, worker_id, worker_addr);
+                }
+        } {}

83-84: Handle potential exceptions in lambda function

The lambda function used in std::erase_if may throw exceptions if task_locality_satisfied fails. Consider wrapping the lambda's body with exception handling to ensure robustness.


102-102: Correct grammatical error in exception message

The error message "Task with id {} not exists." should be "Task with id {} does not exist." to be grammatically correct.

Apply this diff to fix the message:

-                        throw std::runtime_error(fmt::format(
-                                "Task with id {} not exists.",
-                                boost::uuids::to_string(task_id)
-                        ));
+                        throw std::runtime_error(fmt::format(
+                                "Task with id {} does not exist.",
+                                boost::uuids::to_string(task_id)
+                        ));

118-118: Correct grammatical error in exception message

Similarly, the error message "Job with id {} not exists." should be "Job with id {} does not exist."

Apply this diff to fix the message:

-                        throw std::runtime_error(fmt::format(
-                                "Job with id {} not exists.",
-                                boost::uuids::to_string(job_id)
-                        ));
+                        throw std::runtime_error(fmt::format(
+                                "Job with id {} does not exist.",
-                                boost::uuids::to_string(job_id)
+                        ));
src/spider/scheduler/SchedulerTaskCache.hpp (2)

50-51: Document the reason for NOLINT directive.

Add a brief comment explaining why the include-cleaner check is disabled here.


52-53: Document the cache update strategy.

Add documentation explaining:

  • The purpose of m_last_update
  • The significance of m_update_count
  • The cache invalidation strategy
src/spider/scheduler/SchedulerServer.cpp (1)

159-169: Consider retrying task instance creation.

The error handling is good, but for transient storage errors, consider implementing a retry mechanism with exponential backoff.

Example implementation:

+constexpr int MAX_RETRIES = 3;
+constexpr std::chrono::milliseconds INITIAL_DELAY{100};
+
 if (task_id.has_value()) {
     core::TaskInstance const instance{task_id.value()};
-    core::StorageErr const err = m_metadata_store->create_task_instance(instance);
-    if (err.success()) {
-        response = ScheduleTaskResponse{task_id.value(), instance.id};
-    } else {
+    core::StorageErr err;
+    for (int retry = 0; retry < MAX_RETRIES; ++retry) {
+        err = m_metadata_store->create_task_instance(instance);
+        if (err.success()) {
+            response = ScheduleTaskResponse{task_id.value(), instance.id};
+            break;
+        }
+        if (retry < MAX_RETRIES - 1) {
+            std::this_thread::sleep_for(INITIAL_DELAY * (1 << retry));
+            continue;
+        }
         spdlog::error(
                 "Cannot create task instance {}: {}",
                 boost::uuids::to_string(task_id.value()),
                 err.description
         );
-    }
+    }
 }
src/spider/scheduler/scheduler.cpp (1)

192-192: LGTM! Consider adding cache performance monitoring.

The FifoPolicy is correctly initialized with the required storage components. Consider adding metrics to monitor cache hit rates and performance improvements.

src/spider/storage/MetadataStorage.hpp (1)

57-58: Add documentation for new create_task_instance method

Consider adding comments or documentation to explain the purpose and usage of the create_task_instance method for clarity and maintainability.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f6e82ce and e15f5f4.

📒 Files selected for processing (17)
  • src/spider/CMakeLists.txt (1 hunks)
  • src/spider/scheduler/FifoPolicy.cpp (4 hunks)
  • src/spider/scheduler/FifoPolicy.hpp (1 hunks)
  • src/spider/scheduler/SchedulerMessage.hpp (2 hunks)
  • src/spider/scheduler/SchedulerPolicy.hpp (1 hunks)
  • src/spider/scheduler/SchedulerServer.cpp (2 hunks)
  • src/spider/scheduler/SchedulerTaskCache.cpp (1 hunks)
  • src/spider/scheduler/SchedulerTaskCache.hpp (1 hunks)
  • src/spider/scheduler/scheduler.cpp (1 hunks)
  • src/spider/storage/MetadataStorage.hpp (2 hunks)
  • src/spider/storage/MySqlStorage.cpp (4 hunks)
  • src/spider/storage/MySqlStorage.hpp (2 hunks)
  • src/spider/worker/WorkerClient.cpp (3 hunks)
  • src/spider/worker/WorkerClient.hpp (2 hunks)
  • src/spider/worker/worker.cpp (3 hunks)
  • tests/scheduler/test-SchedulerPolicy.cpp (3 hunks)
  • tests/scheduler/test-SchedulerServer.cpp (2 hunks)
🧰 Additional context used
🪛 cppcheck (2.10-2)
src/spider/scheduler/SchedulerTaskCache.cpp

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

(useInitializationList)

src/spider/scheduler/FifoPolicy.cpp

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

(useInitializationList)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: non-storage-unit-tests (ubuntu-24.04)
  • GitHub Check: non-storage-unit-tests (ubuntu-22.04)
  • GitHub Check: lint
🔇 Additional comments (29)
src/spider/scheduler/FifoPolicy.hpp (5)

23-26: Constructor initializes member variables appropriately

The new constructor effectively initializes FifoPolicy with references to MetadataStorage and DataStorage, enhancing encapsulation and simplifying method interfaces.


28-29: Simplified schedule_next method improves interface clarity

By removing the metadata and data store parameters from schedule_next, the method interface is cleaner and less error-prone, enhancing usability.


33-37: Addition of get_next_task enhances modularity

The new private method get_next_task encapsulates task selection logic, promoting code modularity and readability within the FifoPolicy class.


39-40: Member variables for storage references improve state management

Introducing m_metadata_store and m_data_store as member variables centralizes access to storage components, streamlining state management within the scheduler policy.


42-42: Inclusion of SchedulerTaskCache supports efficient task caching

Adding m_task_cache as a member variable integrates task caching into the scheduling policy, potentially enhancing performance by reducing redundant storage queries.

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

76-76: Verify MSGPACK serialization of optional tuple

Ensure that MSGPACK_DEFINE_ARRAY(m_task_ids); correctly serializes and deserializes the std::optional<std::tuple<boost::uuids::uuid, boost::uuids::uuid>> member. It's important to confirm that the optionality and tuple are properly handled by MSGPACK to prevent runtime serialization errors.

src/spider/worker/WorkerClient.cpp (3)

10-10: Include <tuple> header for tuple usage

Including <tuple> is necessary for the use of std::tuple in the updated return type.


98-98: Update response handling to match new return type

Returning response.get_task_ids() aligns with the updated return type. Ensure that response.get_task_ids() returns the expected tuple and that downstream code correctly handles both task IDs.


38-38: Ensure all usages of get_next_task handle the new return type

The return type of get_next_task has changed to std::optional<std::tuple<boost::uuids::uuid, boost::uuids::uuid>>. Please verify that all calls to this function and any code that processes its return value are updated to handle the tuple appropriately.

Run the following script to find all references to get_next_task and check their usage:

✅ Verification successful

All usages of get_next_task properly handle the new return type

The only usage of WorkerClient::get_next_task is in worker.cpp, and it correctly handles the new std::optional<std::tuple<boost::uuids::uuid, boost::uuids::uuid>> return type with proper optional and tuple handling.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all usages of `get_next_task` and display surrounding context.

rg -A 3 -B 3 'get_next_task\('

Length of output: 2946

src/spider/scheduler/FifoPolicy.cpp (1)

132-135: Ensure consistent use of member functions

The schedule_next method now relies on m_task_cache.get_ready_task. Verify that get_ready_task is implemented correctly and that it interacts properly with the FifoPolicy.

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

19-20: Update derived classes to match new method signature

The schedule_next method signature has changed by removing the metadata_store and data_store parameters. Ensure all classes deriving from SchedulerPolicy are updated to reflect this change.

Run the following script to identify all implementations of schedule_next:

src/spider/worker/WorkerClient.hpp (2)

7-7: LGTM! Include directive added for tuple support.

The include directive is correctly placed and necessary for the updated return type.


33-33: LGTM! Method signature updated to return task and instance IDs.

The return type change to std::optional<std::tuple<boost::uuids::uuid, boost::uuids::uuid>> is well-structured and supports the new task instance tracking feature.

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

23-34: LGTM! Well-structured constructor with dependency injection.

The constructor properly takes shared pointers to storage and task function dependencies, using initialization list for efficient member initialization.

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

49-49: LGTM! Updated FifoPolicy initialization with required dependencies.

The initialization correctly passes metadata and data store dependencies to support task caching.

src/spider/scheduler/SchedulerServer.cpp (1)

155-156: LGTM! Simplified policy interface.

The scheduling call is now more focused, with storage dependencies handled by the policy itself.

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

105-111: LGTM! Hard locality constraints are properly tested with the cache.

The test effectively validates that the task cache respects hard locality constraints when scheduling tasks.


152-155: LGTM! Soft locality preferences are properly tested with the cache.

The test effectively validates that the task cache handles soft locality preferences correctly while maintaining FIFO ordering.


59-65: LGTM! Test coverage validates FIFO ordering with the new cache.

The test correctly validates that tasks are scheduled in FIFO order using the updated FifoPolicy constructor and schedule_next signatures.

Let's verify the test coverage for the new cache implementation:

✅ Verification successful

Cache implementation is properly tested through store interfaces

The test suite comprehensively validates the cache functionality through:

  • FIFO scheduling tests with metadata and data stores
  • Task scheduling with both hard and soft locality constraints
  • Server integration tests with store operations

The cache implementation is effectively tested through these store interfaces, even though not explicitly named as "SchedulerTaskCache" in the tests.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check test coverage for the new cache implementation
# Test: Search for cache-related test cases
rg -l "SchedulerTaskCache" "tests/"

Length of output: 33


Script:

#!/bin/bash
# Search for scheduler policy tests and store usage
rg -l "Policy.*Test|test.*Policy" tests/
rg -C 3 "metadata_store|data_store" "tests/scheduler/"

Length of output: 10990

src/spider/worker/worker.cpp (2)

128-134: LGTM! Task ID handling is properly implemented.

The function correctly handles the new tuple-based task identification system, maintaining proper error handling and retry logic.


219-220: LGTM! Task instance creation is properly implemented.

The code correctly destructures the task IDs and creates the task instance with both IDs.

src/spider/CMakeLists.txt (1)

113-114: LGTM! Build system properly includes the new cache implementation.

The SchedulerTaskCache components are correctly added to the scheduler sources.

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

75-75: Appropriate addition of index on creation_time column

Adding an index on the creation_time column in the jobs table enhances query performance when filtering or sorting by this field.


Line range hint 1494-1536: Fix SQL syntax errors in get_task_timeout method

In the get_task_timeout method, the SQL query at lines 1509-1514 has syntax issues:

  • Missing columns in SELECT statement: The query starts with SELECT FROM, which is invalid. Specify the columns to select or use SELECT *.

  • Incorrect backticks in WHERE clause: In the condition WHERE \t1.task_id` = ?, backticks are misapplied. The correct syntax is WHERE `t1`.`task_id` = ?`.

Apply this diff to fix the SQL query:

-            conn->prepareStatement("SELECT FROM `task_instances` as `t1` JOIN `tasks` ON"
+            conn->prepareStatement("SELECT * FROM `task_instances` as `t1` JOIN `tasks` ON "
                                 "`t1`.`task_id` = `tasks`.`id` WHERE `t1.task_id` = ? AND "
-                                "TIMESTAMPDIFF(MICROSECOND, `t1`.`start_time`, "
+                                "TIMESTAMPDIFF(MICROSECOND, `t1`.`start_time`, "
                                 "CURRENT_TIMESTAMP()) < `tasks`.`timeout` * 1000")

Please ensure the corrected SQL query retrieves the intended data and functions as expected.

✅ Verification successful

SQL syntax errors in get_task_timeout method are correctly identified

The review comment accurately identifies both SQL syntax issues and provides the correct fix that aligns with the codebase's SQL query patterns. The suggested changes:

  • Fix the invalid "SELECT FROM" by using "SELECT *"
  • Correct the backtick usage to properly quote table and column identifiers
🏁 Scripts executed

The following scripts were executed for the analysis:

Error: No shell scripts found


Script:

#!/bin/bash
# Search for SQL SELECT statements in the codebase
rg -U "SELECT.*FROM.*task_instances.*JOIN.*tasks" --type cpp -A 2 -B 2

# Search for backtick usage patterns in SQL queries
rg -U "WHERE.*\`.*\`.*=" --type cpp -A 2 -B 2

# Look for other prepared statements
rg "prepareStatement\(" --type cpp -A 3 -B 3

Length of output: 73845

src/spider/storage/MetadataStorage.hpp (2)

5-5: Proper inclusion of <tuple> header

Including the <tuple> header is necessary due to the usage of std::tuple in method signatures.


63-64: Update method signature for get_task_timeout

The method signature for get_task_timeout has been updated to accept a vector of std::tuple<TaskInstance, Task>, which appropriately reflects the need to return both task instances and their associated tasks.

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

6-6: Duplicate inclusion of <tuple> header

The inclusion of the <tuple> header is appropriate here due to the use of std::tuple in method signatures.


61-61: Ensure correct implementation of create_task_instance method

The create_task_instance method is declared and correctly overrides the base class method. Ensure that its implementation aligns with the intended functionality.


65-66: Update method signature for get_task_timeout

The updated method signature for get_task_timeout reflects the changes made to return tuples of TaskInstance and Task, which provides additional context for timeout handling.


namespace {
constexpr int cUpdateCount = 100;
constexpr int cUpdateInterval = 5; // 10 ms

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

Correct the inconsistency between cUpdateInterval value and comment

The value of cUpdateInterval is defined as 5, but the comment states 10 ms. Please update the value or the comment for consistency.

Apply this diff to fix the inconsistency:

Option 1 (if the intended interval is 10 ms):

-constexpr int cUpdateInterval = 5;  // 10 ms
+constexpr int cUpdateInterval = 10;  // 10 ms

Option 2 (if the interval should be 5 ms):

-constexpr int cUpdateInterval = 5;  // 10 ms
+constexpr int cUpdateInterval = 5;  // 5 ms

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

= object.as<spider::scheduler::ScheduleTaskResponse>();
REQUIRE(res.has_task_id());
REQUIRE(res.get_task_id() == parent_task.get_id());
REQUIRE(std::get<0>(res.get_task_ids()) == parent_task.get_id());

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.

💡 Codebase verification

Missing validation for task instance ID in tuple response

The test only validates the task ID (first element) but not the task instance ID (second element) from the tuple returned by get_task_ids(). Both IDs are essential parts of the response structure.

  • Add validation for the second tuple element using std::get<1>(res.get_task_ids()) to ensure the task instance ID is correct.
🔗 Analysis chain

LGTM! Updated task ID retrieval for tuple support.

The test correctly validates the first element (task ID) from the response tuple.

Let's verify that we're testing both tuple elements:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other test cases that might be validating the instance ID
rg "get_task_ids\(\)" -A 5 "tests/"

Length of output: 418


Script:

#!/bin/bash
# Find the definition of get_task_ids()
ast-grep --pattern 'get_task_ids()' 

# Look for any usage of std::get<1> in test files
rg "std::get<1>" tests/

# Find any files related to task IDs
rg "task_ids" --type cpp

Length of output: 4391

Comment on lines +1301 to +1354
auto MySqlMetadataStorage::create_task_instance(TaskInstance const& instance) -> StorageErr {
std::variant<MySqlConnection, StorageErr> conn_result = MySqlConnection::create(m_url);
if (std::holds_alternative<StorageErr>(conn_result)) {
return std::get<StorageErr>(conn_result);
}
auto& conn = std::get<MySqlConnection>(conn_result);
try {
// Check the state of the task
std::unique_ptr<sql::PreparedStatement> ready_statement(conn->prepareStatement(
"SELECT `state` FROM `tasks` WHERE `id` = ? AND `state` = 'ready'"
));
sql::bytes id_bytes = uuid_get_bytes(instance.task_id);
ready_statement->setBytes(1, &id_bytes);
std::unique_ptr<sql::ResultSet> const ready_res(ready_statement->executeQuery());
bool const task_ready = ready_res->rowsCount() > 0;
// Check all task instances have timed out
std::unique_ptr<sql::PreparedStatement> not_timeout_statement(conn->prepareStatement(
"SELECT `t1`.`id` FROM `task_instances` as `t1` JOIN `tasks` ON `t1`.`task_id` = "
"`tasks`.`id` WHERE `t1`.`task_id` = ? AND `tasks`.`timeout` < 0.0001 AND "
"TIMESTAMPDIFF(MICROSECOND, `t1`.`start_time`, CURRENT_TIMESTAMP()) < "
"`tasks`.`timeout` * 1000"
));
not_timeout_statement->setBytes(1, &id_bytes);
std::unique_ptr<sql::ResultSet> const not_timeout_res(not_timeout_statement->executeQuery()
);
bool const all_timeout = not_timeout_res->rowsCount() == 0;
if (!task_ready && !all_timeout) {
conn->rollback();
return StorageErr{StorageErrType::OtherErr, "Task not ready or timed out"};
}
// Set the state to running
std::unique_ptr<sql::PreparedStatement> const running_statement(
conn->prepareStatement("UPDATE `tasks` SET `state` = 'running' WHERE `id` = ?")
);
running_statement->setBytes(1, &id_bytes);
running_statement->executeUpdate();
// Insert task instance
std::unique_ptr<sql::PreparedStatement> const instance_statement(conn->prepareStatement(
"INSERT INTO `task_instances` (`id`, `task_id`, `start_time`) VALUES(?, ?, "
"CURRENT_TIMESTAMP())"
));
sql::bytes instance_id_bytes = uuid_get_bytes(instance.id);
instance_statement->setBytes(1, &instance_id_bytes);
instance_statement->setBytes(2, &id_bytes);
instance_statement->executeUpdate();
} catch (sql::SQLException& e) {
conn->rollback();
return StorageErr{StorageErrType::OtherErr, e.what()};
}

conn->commit();
return StorageErr{};
}

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

Address logical errors in create_task_instance method

There are potential logical issues in the create_task_instance method:

  • Inaccurate condition for task timeout check: In lines 1319-1321, the SQL query uses AND \tasks`.`timeout` < 0.0001, which filters tasks with timeouts less than 0.0001. This seems counterintuitive, as it effectively ignores tasks with valid timeouts. Consider changing the condition to > 0.0001` to include tasks with meaningful timeouts.

  • Incorrect logical operator in condition: At line 1327, the condition if (!task_ready && !all_timeout) may not correctly handle scenarios where either the task is not ready or not all instances have timed out. It might be more appropriate to use the logical OR operator || to ensure that the method only proceeds when the task is ready and all instances have timed out.

Apply this diff to correct the condition:

-        if (!task_ready && !all_timeout) {
+        if (!task_ready || !all_timeout) {

Please review and adjust the conditions to ensure correct task instance creation logic.

📝 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 MySqlMetadataStorage::create_task_instance(TaskInstance const& instance) -> StorageErr {
std::variant<MySqlConnection, StorageErr> conn_result = MySqlConnection::create(m_url);
if (std::holds_alternative<StorageErr>(conn_result)) {
return std::get<StorageErr>(conn_result);
}
auto& conn = std::get<MySqlConnection>(conn_result);
try {
// Check the state of the task
std::unique_ptr<sql::PreparedStatement> ready_statement(conn->prepareStatement(
"SELECT `state` FROM `tasks` WHERE `id` = ? AND `state` = 'ready'"
));
sql::bytes id_bytes = uuid_get_bytes(instance.task_id);
ready_statement->setBytes(1, &id_bytes);
std::unique_ptr<sql::ResultSet> const ready_res(ready_statement->executeQuery());
bool const task_ready = ready_res->rowsCount() > 0;
// Check all task instances have timed out
std::unique_ptr<sql::PreparedStatement> not_timeout_statement(conn->prepareStatement(
"SELECT `t1`.`id` FROM `task_instances` as `t1` JOIN `tasks` ON `t1`.`task_id` = "
"`tasks`.`id` WHERE `t1`.`task_id` = ? AND `tasks`.`timeout` < 0.0001 AND "
"TIMESTAMPDIFF(MICROSECOND, `t1`.`start_time`, CURRENT_TIMESTAMP()) < "
"`tasks`.`timeout` * 1000"
));
not_timeout_statement->setBytes(1, &id_bytes);
std::unique_ptr<sql::ResultSet> const not_timeout_res(not_timeout_statement->executeQuery()
);
bool const all_timeout = not_timeout_res->rowsCount() == 0;
if (!task_ready && !all_timeout) {
conn->rollback();
return StorageErr{StorageErrType::OtherErr, "Task not ready or timed out"};
}
// Set the state to running
std::unique_ptr<sql::PreparedStatement> const running_statement(
conn->prepareStatement("UPDATE `tasks` SET `state` = 'running' WHERE `id` = ?")
);
running_statement->setBytes(1, &id_bytes);
running_statement->executeUpdate();
// Insert task instance
std::unique_ptr<sql::PreparedStatement> const instance_statement(conn->prepareStatement(
"INSERT INTO `task_instances` (`id`, `task_id`, `start_time`) VALUES(?, ?, "
"CURRENT_TIMESTAMP())"
));
sql::bytes instance_id_bytes = uuid_get_bytes(instance.id);
instance_statement->setBytes(1, &instance_id_bytes);
instance_statement->setBytes(2, &id_bytes);
instance_statement->executeUpdate();
} catch (sql::SQLException& e) {
conn->rollback();
return StorageErr{StorageErrType::OtherErr, e.what()};
}
conn->commit();
return StorageErr{};
}
auto MySqlMetadataStorage::create_task_instance(TaskInstance const& instance) -> StorageErr {
std::variant<MySqlConnection, StorageErr> conn_result = MySqlConnection::create(m_url);
if (std::holds_alternative<StorageErr>(conn_result)) {
return std::get<StorageErr>(conn_result);
}
auto& conn = std::get<MySqlConnection>(conn_result);
try {
// Check the state of the task
std::unique_ptr<sql::PreparedStatement> ready_statement(conn->prepareStatement(
"SELECT `state` FROM `tasks` WHERE `id` = ? AND `state` = 'ready'"
));
sql::bytes id_bytes = uuid_get_bytes(instance.task_id);
ready_statement->setBytes(1, &id_bytes);
std::unique_ptr<sql::ResultSet> const ready_res(ready_statement->executeQuery());
bool const task_ready = ready_res->rowsCount() > 0;
// Check all task instances have timed out
std::unique_ptr<sql::PreparedStatement> not_timeout_statement(conn->prepareStatement(
"SELECT `t1`.`id` FROM `task_instances` as `t1` JOIN `tasks` ON `t1`.`task_id` = "
"`tasks`.`id` WHERE `t1`.`task_id` = ? AND `tasks`.`timeout` < 0.0001 AND "
"TIMESTAMPDIFF(MICROSECOND, `t1`.`start_time`, CURRENT_TIMESTAMP()) < "
"`tasks`.`timeout` * 1000"
));
not_timeout_statement->setBytes(1, &id_bytes);
std::unique_ptr<sql::ResultSet> const not_timeout_res(not_timeout_statement->executeQuery()
);
bool const all_timeout = not_timeout_res->rowsCount() == 0;
if (!task_ready || !all_timeout) {
conn->rollback();
return StorageErr{StorageErrType::OtherErr, "Task not ready or timed out"};
}
// Set the state to running
std::unique_ptr<sql::PreparedStatement> const running_statement(
conn->prepareStatement("UPDATE `tasks` SET `state` = 'running' WHERE `id` = ?")
);
running_statement->setBytes(1, &id_bytes);
running_statement->executeUpdate();
// Insert task instance
std::unique_ptr<sql::PreparedStatement> const instance_statement(conn->prepareStatement(
"INSERT INTO `task_instances` (`id`, `task_id`, `start_time`) VALUES(?, ?, "
"CURRENT_TIMESTAMP())"
));
sql::bytes instance_id_bytes = uuid_get_bytes(instance.id);
instance_statement->setBytes(1, &instance_id_bytes);
instance_statement->setBytes(2, &id_bytes);
instance_statement->executeUpdate();
} catch (sql::SQLException& e) {
conn->rollback();
return StorageErr{StorageErrType::OtherErr, e.what()};
}
conn->commit();
return StorageErr{};
}

@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: 0

🧹 Nitpick comments (2)
src/spider/scheduler/SchedulerTaskCache.cpp (2)

54-58: Optimize task collection to improve performance.

In pop_next_task, you create a new std::vector<core::Task> and copy tasks from m_tasks, which may be inefficient for large task sets. Consider iterating over m_tasks directly or modifying m_get_next_task_function to accept an iterator or a different data structure to avoid unnecessary copying.


76-79: Simplify the should_fetch_tasks condition.

You can streamline the logic in should_fetch_tasks by combining the conditions into a single return statement:

-return m_update_count > cUpdateCount;
+return (m_last_update + std::chrono::milliseconds(cUpdateInterval) < now) || (m_update_count > cUpdateCount);

This enhances readability by reducing the number of return points.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e15f5f4 and e80630a.

📒 Files selected for processing (1)
  • src/spider/scheduler/SchedulerTaskCache.cpp (1 hunks)
🧰 Additional context used
🪛 cppcheck (2.10-2)
src/spider/scheduler/SchedulerTaskCache.cpp

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

(useInitializationList)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: lint
  • GitHub Check: non-storage-unit-tests (ubuntu-24.04)
  • GitHub Check: non-storage-unit-tests (ubuntu-22.04)
🔇 Additional comments (3)
src/spider/scheduler/SchedulerTaskCache.cpp (3)

26-43: 🛠️ Refactor suggestion

Review the multiple increments of m_update_count in get_ready_task.

The m_update_count variable is incremented multiple times within get_ready_task, specifically at lines 33, 37, and 42. This could lead to m_update_count increasing faster than expected, potentially causing the cache to refresh more frequently than intended. Please verify if this behaviour is intentional or adjust the increments accordingly.


86-88: ⚠️ Potential issue

Ensure uniqueness of task IDs when populating m_tasks.

When inserting tasks into m_tasks from get_ready_tasks and get_task_timeout, there is a possibility of duplicate task IDs, which could overwrite existing entries. Verify that this behaviour is intended, or implement a check to handle duplicates appropriately.

Also applies to: 91-93


69-71: ⚠️ Potential issue

Handle potential exceptions when accessing m_tasks.

At line 69, m_tasks.at(task_id.value()) is used, which can throw an exception if the task_id is not found. Although you've checked that task_id exists, it's safer to use find to prevent any unexpected exceptions:

-core::Task task = m_tasks.at(task_id.value());
+m_tasks.erase(task_id.value());
+core::Task task = std::move(it->second);
+m_tasks.erase(it);

Likely invalid or redundant comment.

@sitaowang1998
sitaowang1998 merged commit 34f17ef into y-scope:main Jan 20, 2025
@sitaowang1998
sitaowang1998 deleted the scheduler_cache branch February 7, 2025 04:44
@coderabbitai coderabbitai Bot mentioned this pull request Apr 23, 2025
6 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