Skip to content

Conversation

@sitaowang1998
Copy link
Collaborator

@sitaowang1998 sitaowang1998 commented May 12, 2025

Description

User might want to stop a job from executing if the task fails and has no hope of recovery or because of other reason, so Spider need to support job cancellation both from inside inside the task through TaskContext::abort and from user through Job::cancel. The cancelled job will be in JobStatus::Cancelled state.

User also need to get the reason why a job is cancelled. This PR adds job_errors table to store the cancellation messages. The messages are set inside TaskContext::abort as a user argument and set to Job cancelled by user. inside Job::cancel. The table also stores the function name of the task calling TaskContext::abort, or user if the job is cancelled using Job::cancel. User could retrieve both information using Job::get_error.

Checklist

  • The PR satisfies the contribution guidelines.
  • This is a breaking change and that has been indicated in the PR title, OR this isn't a
    breaking change.
  • Necessary docs have been updated, OR no docs need to be updated.

Validation performed

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

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features
    • Added job and task cancellation methods with detailed error message retrieval.
    • Introduced thread-safe management of task execution and cancellation control.
    • Extended database schema with a job_errors table to store cancellation details.
    • Added new task cancellation and abort test tasks.
  • Bug Fixes
    • Enhanced task state handling to properly reflect cancellations and error conditions.
  • Tests
    • Added comprehensive unit and integration tests for cancellation workflows.
    • Included a new cancellation test executable with command-line interface.
  • Refactor
    • Renamed task state check methods for improved clarity and consistency.
  • Chores
    • Updated build and test configurations to support new features and tests.

@sitaowang1998 sitaowang1998 requested a review from a team as a code owner May 12, 2025 20:22
@coderabbitai
Copy link
Contributor

coderabbitai bot commented May 12, 2025

## Walkthrough

This update adds job and task cancellation features, including schema changes, storage API extensions, worker cancellation checks, and related tests. It also introduces thread-safe `ExecutorHandle` management and updates task executor state methods.

## Changes

| File(s)                                                                                 | Summary                                                                                                             |
|-----------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------|
| src/spider/CMakeLists.txt, src/spider/worker/ExecutorHandle.cpp, src/spider/worker/ExecutorHandle.hpp | Adds `ExecutorHandle` class and integrates it into the build process.                                              |
| src/spider/client/Job.hpp                                                               | Implements `cancel()` and `get_error()` methods for job cancellation and error retrieval.                          |
| src/spider/client/TaskContext.cpp                                                       | Adds `abort()` method to cancel a job from within a task, with immediate process termination.                      |
| src/spider/storage/MetadataStorage.hpp, src/spider/storage/mysql/MySqlStorage.hpp       | Extends storage API with job/task cancellation and error message retrieval methods.                                 |
| src/spider/storage/mysql/MySqlStorage.cpp                                               | Implements job/task cancellation and error retrieval in MySQL storage; updates `task_fail` logic.                  |
| src/spider/storage/mysql/mysql_stmt.hpp, tools/scripts/storage/init_db.sql              | Adds new `job_errors` table and corresponding SQL schema for storing job error messages.                           |
| src/spider/worker/TaskExecutor.cpp, src/spider/worker/TaskExecutor.hpp                  | Renames task state query methods; adds `cancelled()` method.                                                      |
| src/spider/worker/worker.cpp                                                            | Implements cooperative task cancellation in the worker, integrates with heartbeat, and updates control flow.        |
| tests/CMakeLists.txt, tests/client/cancel-test.cpp, tests/integration/test_cancel.py    | Adds new client and integration tests for cancellation; updates test build configuration.                          |
| tests/storage/test-MetadataStorage.cpp                                                  | Adds unit tests for job cancellation and cancellation by task.                                                     |
| tests/worker/worker-test.cpp, tests/worker/worker-test.hpp                              | Adds test tasks for sleep and abort scenarios; updates test task declarations.                                     |
| tests/worker/test-TaskExecutor.cpp                                                      | Updates test assertions to use new task executor state method names.                                               |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Client
    participant Driver
    participant MetadataStorage
    participant Worker
    participant ExecutorHandle
    participant TaskExecutor

    Client->>Driver: submit_job()
    Driver->>MetadataStorage: create_job()
    MetadataStorage-->>Driver: job_id
    Driver-->>Client: Job

    Client->>Job: cancel()
    Job->>MetadataStorage: cancel_job(job_id, message)
    MetadataStorage-->>Job: result

    loop Heartbeat
        Worker->>ExecutorHandle: get_task_id()
        alt Task running
            Worker->>MetadataStorage: get_task_state(task_id)
            MetadataStorage-->>Worker: state
            alt state == Cancelled
                Worker->>ExecutorHandle: get_executor()
                Worker->>TaskExecutor: cancel()
            end
        end
    end

    Worker->>TaskExecutor: run()
    alt Task cancelled
        TaskExecutor-->>Worker: is_cancelled() == true
        Worker-->>MetadataStorage: update task/job state to Cancelled
    else Task completes
        Worker-->>MetadataStorage: update task/job state to Succeeded/Failed
    end

Suggested reviewers

  • davidlion

<!-- walkthrough_end -->


---

<details>
<summary>📜 Recent review details</summary>

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


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 18ce4e94f0d1975f71629c5d137630c5d89b1fa8 and 8015388a22453dda8ca5e0417c9a83ab585c480d.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `src/spider/worker/TaskExecutor.hpp` (4 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* src/spider/worker/TaskExecutor.hpp

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (3)</summary>

* GitHub Check: non-storage-unit-tests (ubuntu-24.04)
* GitHub Check: lint
* GitHub Check: non-storage-unit-tests (ubuntu-22.04)

</details>

</details>
<!-- internal state start -->


<!-- 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= -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=y-scope/spider&utm_content=137):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `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 explain this code block.`
  -	`@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 explain its main purpose.`
  - `@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.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

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](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@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](https://docs.coderabbit.ai/guides/configure-coderabbit) 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](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

@sitaowang1998 sitaowang1998 requested a review from davidlion May 29, 2025 20:28
Copy link
Member

@davidlion davidlion left a comment

Choose a reason for hiding this comment

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

In this PR and future PRs, we need to try and do a better job of explaining what and why in PR descriptions and titles. This is important as we want other engineers in YScope to reference these PRs and understand them without needing to read all the code.

Currently, both the title and description just list the names of things that were added with little to no explanation on their motivation, usage, or design.

Comment on lines +148 to +153
return;
}

if (spider::core::TaskState::Canceled != task_state) {
return;
}
Copy link
Member

Choose a reason for hiding this comment

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

This function can fail so we should return an error code.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (6)
src/spider/worker/ExecutorHandle.hpp (2)

27-28: Move implementation comment to class documentation.

The raw pointer comment should be moved to the class documentation as suggested in previous reviews, since it explains an important design decision.

 /**
  * This class acts as a handle for thread-safe access to the task executor and task id.
  * It maintains a weak reference to the task executor to prevent multiple destructor calls and
- * ensures that access remains valid only while the executor itself is valid.
+ * ensures that access remains valid only while the executor itself is valid.
+ * 
+ * Note: Uses a raw pointer instead of std::shared_ptr to avoid calling the TaskExecutor 
+ * destructor multiple times.
  */
 class ExecutorHandle {
 public:
     [[nodiscard]] auto get_task_id() -> std::optional<boost::uuids::uuid>;
     auto executor_cancel() -> void;
     auto set(boost::uuids::uuid task_id, TaskExecutor* executor) -> void;
     auto clear() -> void;

 private:
     boost::uuids::uuid m_task_id;

-    // Do not use std::shared_ptr to avoid calling destructor twice.
     TaskExecutor* m_executor = nullptr;

     std::mutex m_mutex;

25-30: Follow coding guidelines for member variable ordering.

According to the coding guidelines referenced in past reviews, consider reordering member variables to follow the established pattern.

 private:
+    std::mutex m_mutex;
+    
     boost::uuids::uuid m_task_id;
-
-    // Do not use std::shared_ptr to avoid calling destructor twice.
     TaskExecutor* m_executor = nullptr;
-
-    std::mutex m_mutex;
src/spider/client/Job.hpp (2)

87-100: Refactor duplicated storage connection pattern.

This method contains the same storage connection acquisition pattern that was flagged in previous reviews for refactoring into a helper method.

The connection acquisition logic is repeated across multiple methods and should be extracted into a helper as discussed in previous reviews and tracked in issue #158.


173-197: Simplify branching logic and address code duplication.

The method has two issues:

  1. Contains the same duplicated storage connection pattern mentioned in previous reviews
  2. The branching logic for handling connection vs no-connection cases can be simplified

Consider refactoring to eliminate code duplication:

 auto get_error() -> std::pair<std::string, std::string> {
-    if (nullptr == m_conn) {
-        std::variant<std::unique_ptr<core::StorageConnection>, core::StorageErr> conn_result
-                = m_storage_factory->provide_storage_connection();
-        if (std::holds_alternative<core::StorageErr>(conn_result)) {
-            throw ConnectionException(std::get<core::StorageErr>(conn_result).description);
-        }
-        auto conn = std::move(std::get<std::unique_ptr<core::StorageConnection>>(conn_result));
-
-        std::pair<std::string, std::string> res;
-        core::StorageErr const err
-                = m_metadata_storage->get_error_message(*conn, m_id, &res.first, &res.second);
-        if (false == err.success()) {
-            throw ConnectionException{err.description};
-        }
-        return res;
-    }
-
+    auto* connection = m_conn.get();
+    std::unique_ptr<core::StorageConnection> temp_conn;
+    
+    if (nullptr == connection) {
+        // Use helper method once available (issue #158)
+        std::variant<std::unique_ptr<core::StorageConnection>, core::StorageErr> conn_result
+                = m_storage_factory->provide_storage_connection();
+        if (std::holds_alternative<core::StorageErr>(conn_result)) {
+            throw ConnectionException(std::get<core::StorageErr>(conn_result).description);
+        }
+        temp_conn = std::move(std::get<std::unique_ptr<core::StorageConnection>>(conn_result));
+        connection = temp_conn.get();
+    }
+    
     std::pair<std::string, std::string> res;
     core::StorageErr const err
-            = m_metadata_storage->get_error_message(*m_conn, m_id, &res.first, &res.second);
+            = m_metadata_storage->get_error_message(*connection, m_id, &res.first, &res.second);
     if (false == err.success()) {
         throw ConnectionException{err.description};
     }
     return res;
src/spider/storage/mysql/MySqlStorage.cpp (2)

1079-1120: Address the validation concerns from previous reviews

This method still has the issues identified in previous reviews:

  1. No validation of affected rows: The UPDATE statements on lines 1094 and 1102 don't check if any rows were actually updated, meaning the method could silently "succeed" even for non-existent jobs.

  2. Potential duplicate error rows: The INSERT on line 1106 could create multiple identical error messages if called repeatedly for the same job.

Apply the suggested fixes from the previous review:

- task_statement->executeUpdate();
+ int32_t updated = task_statement->executeUpdate();
+ if (updated == 0) {
+     static_cast<MySqlConnection&>(conn)->rollback();
+     return StorageErr{StorageErrType::KeyNotFoundErr,
+                       fmt::format("No running job with id {}", boost::uuids::to_string(id))};
+ }

...

- job_statement->executeUpdate();
+ updated = job_statement->executeUpdate();
+ if (updated == 0) {
+     static_cast<MySqlConnection&>(conn)->rollback();
+     return StorageErr{StorageErrType::KeyNotFoundErr,
+                       fmt::format("No job with id {}", boost::uuids::to_string(id))};
+ }

- "INSERT INTO `job_errors` (`job_id`, `func_name`, `message`) VALUES (?, ?, ?)"
+ "INSERT IGNORE INTO `job_errors` (`job_id`, `func_name`, `message`) VALUES (?, ?, ?)"

1122-1179: Apply the same validation fixes as suggested for cancel_job_by_user

This method has the same validation issues as cancel_job_by_user:

  1. Lines 1153 and 1161: The UPDATE statements don't verify affected row counts
  2. Line 1165: Uses regular INSERT which could create duplicates

Apply the same defensive checks as suggested in the previous review for consistency.

🧹 Nitpick comments (1)
src/spider/storage/mysql/MySqlStorage.cpp (1)

1181-1209: Consider handling multiple error messages

The method only retrieves the first error message if multiple exist for the same job. Consider whether this is the intended behaviour or if you should:

  1. Return the most recent error message (ORDER BY a timestamp)
  2. Concatenate multiple messages
  3. Document that only the first message is returned

If you want the most recent message, modify the query:

- "SELECT `func_name`, `message` FROM `job_errors` WHERE `job_id` = ?"
+ "SELECT `func_name`, `message` FROM `job_errors` WHERE `job_id` = ? ORDER BY `id` DESC LIMIT 1"
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 2f9d378 and a344f88.

📒 Files selected for processing (8)
  • src/spider/client/Job.hpp (2 hunks)
  • src/spider/storage/MetadataStorage.hpp (2 hunks)
  • src/spider/storage/mysql/MySqlStorage.cpp (3 hunks)
  • src/spider/storage/mysql/MySqlStorage.hpp (2 hunks)
  • src/spider/worker/ExecutorHandle.cpp (1 hunks)
  • src/spider/worker/ExecutorHandle.hpp (1 hunks)
  • tests/client/cancel-test.cpp (1 hunks)
  • tests/storage/test-MetadataStorage.cpp (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • tests/client/cancel-test.cpp
  • src/spider/storage/mysql/MySqlStorage.hpp
  • src/spider/worker/ExecutorHandle.cpp
  • src/spider/storage/MetadataStorage.hpp
  • tests/storage/test-MetadataStorage.cpp
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/spider/client/Job.hpp (2)
src/spider/client/Exception.hpp (2)
  • ConnectionException (14-15)
  • ConnectionException (14-14)
src/spider/storage/MetadataStorage.hpp (16)
  • conn (27-27)
  • conn (29-29)
  • conn (30-30)
  • conn (32-33)
  • conn (35-36)
  • conn (40-43)
  • conn (45-51)
  • conn (53-54)
  • conn (56-57)
  • conn (59-60)
  • conn (62-66)
  • conn (69-70)
  • conn (72-76)
  • conn (87-88)
  • conn (99-100)
  • conn (110-115)
src/spider/storage/mysql/MySqlStorage.cpp (1)
src/spider/storage/mysql/MySqlStorage.hpp (16)
  • conn (38-38)
  • conn (39-39)
  • conn (40-40)
  • conn (41-42)
  • conn (43-44)
  • conn (45-50)
  • conn (51-57)
  • conn (58-59)
  • conn (60-61)
  • conn (62-63)
  • conn (64-68)
  • conn (69-70)
  • conn (71-75)
  • conn (77-78)
  • conn (80-81)
  • conn (82-87)
⏰ 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 (4)
src/spider/worker/ExecutorHandle.hpp (1)

12-16: Good improvement on class documentation.

The class documentation now properly explains the weak reference design rationale, addressing the previous feedback about moving important design comments to the class docstring.

src/spider/client/Job.hpp (1)

162-169: Documentation clearly explains the new error retrieval functionality.

The updated documentation properly explains the return value structure and the different sources of cancellation (user vs task).

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

1557-1588: LGTM!

This method correctly implements task state retrieval with proper error handling and follows the established patterns in the codebase.


1873-1882: Excellent improvement to handle race conditions

The modification to only update tasks in 'running' state and check affected row counts is a great improvement that:

  1. Prevents invalid state transitions: Only fails tasks that are actually running
  2. Handles cancellation race conditions: Gracefully handles cases where tasks are cancelled before failing
  3. Optimizes unnecessary operations: Skips job state updates when no task was affected

@sitaowang1998 sitaowang1998 changed the title feat: Add job cancellation. feat: Add job cancellation for early stop of the job execution. Jun 26, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/spider/worker/ExecutorHandle.cpp (1)

19-24: Avoid holding the mutex while executing user code – potential deadlock

The method locks m_mutex and directly calls m_executor->cancel(). If cancel() (or any callback it triggers) tries to access the ExecutorHandle again, the thread will deadlock on m_mutex.

Refactor to follow the "lock-copy-unlock-act" idiom:

auto ExecutorHandle::cancel_executor() -> void {
-    std::lock_guard const lock_guard{m_mutex};
-    if (nullptr != m_executor) {
-        m_executor->cancel();
-    }
+    TaskExecutor* exec = nullptr;
+    {
+        std::lock_guard const lock_guard{m_mutex};
+        exec = m_executor;
+    }
+    if (exec != nullptr) {
+        exec->cancel();   // outside lock
+    }
}
🧹 Nitpick comments (1)
tests/storage/test-MetadataStorage.cpp (1)

452-459: Enhance the documentation to describe task relationships

The current documentation could be clearer about the task graph structure and relationships.

Consider this more descriptive documentation:

/**
- * Create a common job cancel test setup. Create a job with a task graph that consists of two
- * parent tasks and one child task. Set the state of parent 1 to succeed. Parent 2 state remains
- * ready and child state remains pending.
+ * Creates a test job with a task dependency graph for cancellation testing.
+ * 
+ * Task graph structure:
+ *   parent_1 (p1) ──┐
+ *                   ├──> child_task
+ *   parent_2 (p2) ──┘
+ * 
+ * Initial states after setup:
+ *   - parent_1: Succeeded (with output "1.1")
+ *   - parent_2: Ready
+ *   - child_task: Pending (waiting for parent_2 to complete)
+ * 
  * @param storage
  * @param conn
- * @return A tuple containing the job_id, parent_1_id, parent_2_id, and child_task_id.
+ * @return A tuple containing (job_id, parent_1_id, parent_2_id, child_task_id)
  */
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between a344f88 and e4da187.

📒 Files selected for processing (12)
  • src/spider/storage/MetadataStorage.hpp (2 hunks)
  • src/spider/storage/mysql/MySqlStorage.cpp (3 hunks)
  • src/spider/storage/mysql/MySqlStorage.hpp (2 hunks)
  • src/spider/storage/mysql/mysql_stmt.hpp (2 hunks)
  • src/spider/worker/ExecutorHandle.cpp (1 hunks)
  • src/spider/worker/ExecutorHandle.hpp (1 hunks)
  • src/spider/worker/TaskExecutor.cpp (4 hunks)
  • src/spider/worker/TaskExecutor.hpp (4 hunks)
  • src/spider/worker/worker.cpp (7 hunks)
  • tests/storage/test-MetadataStorage.cpp (2 hunks)
  • tests/worker/worker-test.cpp (3 hunks)
  • tools/scripts/storage/init_db.sql (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (9)
  • tests/worker/worker-test.cpp
  • tools/scripts/storage/init_db.sql
  • src/spider/storage/mysql/mysql_stmt.hpp
  • src/spider/worker/TaskExecutor.hpp
  • src/spider/worker/TaskExecutor.cpp
  • src/spider/storage/mysql/MySqlStorage.hpp
  • src/spider/worker/worker.cpp
  • src/spider/storage/MetadataStorage.hpp
  • src/spider/storage/mysql/MySqlStorage.cpp
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/spider/worker/ExecutorHandle.cpp (2)
src/spider/worker/TaskExecutor.cpp (2)
  • get_task_id (21-23)
  • get_task_id (21-21)
src/spider/worker/ExecutorHandle.hpp (1)
  • executor (21-21)
tests/storage/test-MetadataStorage.cpp (3)
src/spider/storage/MetadataStorage.hpp (16)
  • conn (27-27)
  • conn (29-29)
  • conn (30-30)
  • conn (32-33)
  • conn (35-36)
  • conn (40-43)
  • conn (45-51)
  • conn (53-54)
  • conn (56-57)
  • conn (59-60)
  • conn (62-66)
  • conn (69-70)
  • conn (72-76)
  • conn (87-88)
  • conn (100-101)
  • conn (112-117)
src/spider/core/Data.hpp (1)
  • gen (40-43)
src/spider/client/Job.hpp (1)
  • conn_result (87-100)
⏰ 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 (2)
src/spider/worker/ExecutorHandle.hpp (1)

12-40: Well-designed thread-safe singleton implementation

The class documentation clearly explains the design rationale, and the implementation properly enforces singleton semantics by deleting all constructors and assignment operators. The explanation for using raw pointers instead of std::shared_ptr is helpful.

tests/storage/test-MetadataStorage.cpp (1)

506-578: Well-structured cancellation tests

Both test cases comprehensively verify the cancellation functionality:

  • Proper state transitions for all tasks
  • Correct error message and offender recording
  • Appropriate cleanup after tests

The use of the helper function reduces duplication effectively.

@sitaowang1998 sitaowang1998 changed the title feat: Add job cancellation for early stop of the job execution. feat: Add job cancellation for early stop of the job execution (#137). Jun 26, 2025
@sitaowang1998 sitaowang1998 changed the title feat: Add job cancellation for early stop of the job execution (#137). feat: Add job cancellation for early stop of the job execution. Jun 26, 2025
@sitaowang1998 sitaowang1998 requested a review from davidlion June 26, 2025 17:49
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.

2 participants