Skip to content

fix: Remove dangling reference to msgpack::object in task executor; Increase test value sizes to cover this case. - #90

Merged
sitaowang1998 merged 11 commits into
y-scope:mainfrom
sitaowang1998:large_input
Apr 3, 2025
Merged

fix: Remove dangling reference to msgpack::object in task executor; Increase test value sizes to cover this case.#90
sitaowang1998 merged 11 commits into
y-scope:mainfrom
sitaowang1998:large_input

Conversation

@sitaowang1998

@sitaowang1998 sitaowang1998 commented Apr 3, 2025

Copy link
Copy Markdown
Collaborator

Description

In TaskExecutorMessage.hpp, the get_message_body creates a msgpack::object_handle, gets the message body object and returns it. However, when the msgpack::object_handle goes out of scope and destructed, the underlying memory storing the object is freed, the return object is a dangling reference to invalid memory.

This pr fixes the problem by introducing a class to hold the msgpack::object_handle so it does not goes out of scope.

This pr adds a unit test and an integration test for large task input and output, which expose the dangling reference bug. To support the tests, this pr increases the size of task input/output type and value, and the size of data. This fixes #68.

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

  • New Features
    • Increased storage capacity to support larger data entries, reducing potential data truncation.
  • Refactor
    • Improved task processing with a new object‐oriented request parser for more robust error handling.
  • Tests
    • Introduced new test cases validating handling of extensive data inputs and outputs for enhanced reliability.

@sitaowang1998
sitaowang1998 requested a review from a team as a code owner April 3, 2025 16:05
@coderabbitai

coderabbitai Bot commented Apr 3, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This pull request updates the SQL schema definitions by increasing the maximum lengths of VARCHAR and VARBINARY fields for several tables. It also introduces a new class, TaskExecutorRequestParser, to encapsulate the extraction of request type and message body from msgpack buffers, replacing previous free functions. In addition, the pull request refactors the task executor code to use this new parser and adds multiple tests to validate handling of large input and output strings. The database initialization script is modified correspondingly.

Changes

File(s) Change Summary
src/spider/.../mysql_stmt.hpp
tools/.../init_db.sql
Updated SQL table definitions: increased VARCHAR/VARBINARY lengths (e.g., from 64/128/256 to 999) for tables task_inputs, task_outputs, data, client_kv_data, and task_kv_data.
src/spider/.../TaskExecutorMessage.hpp
src/spider/.../task_executor.cpp
Introduced the TaskExecutorRequestParser class which encapsulates message parsing. Replaced the free functions get_request_type and get_message_body with methods get_type() and get_body().
tests/.../client-test.cpp
tests/.../test-TaskExecutor.cpp
tests/.../worker-test.cpp
tests/.../worker-test.hpp
Added test functions for large input/output scenarios including test_large_input_output and a new task function join_string_test. Introduced constant cLargeInputSize (300) to drive these tests.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Executor as Task Executor
    participant Parser as TaskExecutorRequestParser
    Client->>Executor: Send msgpack::sbuffer request
    Executor->>Parser: Create parser instance with buffer
    Parser-->>Executor: get_type() returns request type
    Parser-->>Executor: get_body() returns message body
    Executor->>Executor: Process request based on parsed data
Loading

Possibly related PRs

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 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.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @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 requested a review from davidlion April 3, 2025 16:05

@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

🧹 Nitpick comments (1)
tests/client/client-test.cpp (1)

208-209: Remove the unnecessary semicolon.

There's an extraneous semicolon on line 209 that should be removed.

 constexpr size_t cLargeInputSize = 300;
-;
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 9949aa5 and 0b3346e.

📒 Files selected for processing (8)
  • src/spider/storage/mysql/mysql_stmt.hpp (4 hunks)
  • src/spider/worker/TaskExecutorMessage.hpp (1 hunks)
  • src/spider/worker/task_executor.cpp (1 hunks)
  • tests/client/client-test.cpp (3 hunks)
  • tests/worker/test-TaskExecutor.cpp (1 hunks)
  • tests/worker/worker-test.cpp (3 hunks)
  • tests/worker/worker-test.hpp (2 hunks)
  • tools/scripts/storage/init_db.sql (4 hunks)
🧰 Additional context used
🧬 Code Definitions (3)
tests/worker/test-TaskExecutor.cpp (3)
src/spider/worker/worker.cpp (2)
  • get_environment_variable (83-102)
  • get_environment_variable (83-85)
src/spider/core/Data.hpp (1)
  • gen (40-43)
tests/storage/StorageTestHelper.hpp (2)
  • get_storage_url (27-29)
  • get_storage_url (27-27)
tests/client/client-test.cpp (2)
tests/worker/worker-test.hpp (1)
  • join_string_test (24-28)
tests/worker/worker-test.cpp (2)
  • join_string_test (65-71)
  • join_string_test (65-69)
tests/worker/worker-test.cpp (1)
tests/worker/worker-test.hpp (1)
  • join_string_test (24-28)
⏰ 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 (15)
tests/worker/worker-test.hpp (2)

4-4: Good addition of the string header

This is necessary to support the new function declaration that uses std::string.


24-28: Function signature looks good

The new function declaration is well-formed with proper parameter types and return type. The trailing return type syntax -> std::string is consistent with the codebase style.

tests/worker/test-TaskExecutor.cpp (2)

211-211: Well-defined constant

Good practice using a named constant for the input size rather than hardcoding the value in multiple places.


213-246: Well-structured test for large I/O

This test case effectively validates that the task executor can handle large input and output strings. It creates two 300-character strings and verifies that their concatenation works correctly through the task execution framework.

The test aligns with the PR's goal of improving memory management for message objects by ensuring large string handling works properly.

tests/worker/worker-test.cpp (3)

6-6: Necessary header inclusion

Good addition of the string header to support the function implementation.


65-71: Simple and effective implementation

The function implementation is straightforward and correctly concatenates the two input strings. The /*context*/ comment on the unused parameter is a good practice to prevent compiler warnings.


81-81: Task registration looks good

Properly registering the new function as a task makes it available to the task execution framework.

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

145-146: Good use of TaskExecutorRequestParser

This change addresses the core issue mentioned in the PR - replacing direct calls to free functions with a class that can properly manage the lifetime of the msgpack object handle. This prevents dangling references when the object handle goes out of scope.


150-150: Fixed dangling reference issue

Using request_parser.get_body() instead of directly calling a free function ensures that the msgpack object reference remains valid as long as the parser object exists. This directly addresses the memory management issue described in the PR objective.

tests/client/client-test.cpp (2)

183-206: Comprehensive test for large input/output handling.

The test covers a scenario to ensure the system correctly handles large string inputs and outputs, which aligns with the increased VARCHAR and VARBINARY field sizes in the database schema. The implementation is clean and follows the pattern of existing tests.


275-278: LGTM!

Good integration of the new test into the main test runner.

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

40-69: Good encapsulation of request parsing.

The new TaskExecutorRequestParser class properly addresses the dangling reference issue mentioned in the PR objectives by holding the msgpack::object_handle as a member variable.


76-79: LGTM!

The private member variable correctly stores the object handle to prevent dangling references.

tools/scripts/storage/init_db.sql (1)

61-61:

Details

✅ Verification successful

Consistent increase of field sizes matches application requirements.

All the VARCHAR and VARBINARY fields have been consistently increased to 999 characters/bytes to support the larger input/output strings being tested in the new test case.

Also applies to: 70-71, 81-82, 84-85, 138-138, 145-145


🏁 Script executed:

#!/bin/bash
# Verify that the field size changes are consistent between the SQL script and the C++ header file
echo "Comparing field sizes in SQL script and C++ header:"
echo "======================================================"

# Check SQL script for field sizes
echo "Field sizes in SQL script (init_db.sql):"
grep -E "(VARCHAR|VARBINARY)\([0-9]+\)" tools/scripts/storage/init_db.sql | grep -E "type|value"

echo "----------------------------------------------------"

# Check C++ header for field sizes
echo "Field sizes in C++ header (mysql_stmt.hpp):"
grep -E "(VARCHAR|VARBINARY)\([0-9]+\)" src/spider/storage/mysql/mysql_stmt.hpp | grep -E "type|value"

Length of output: 1340


Verified Field Size Increase Consistency
After executing the verification script, the output confirms that all instances of VARCHAR and VARBINARY fields in both tools/scripts/storage/init_db.sql and src/spider/storage/mysql/mysql_stmt.hpp have been updated uniformly to 999. The grep results clearly show consistent changes for both the type and value columns.

  • The SQL script (init_db.sql) contains the updated field sizes as required.
  • The C++ header (mysql_stmt.hpp) mirrors these updates for consistent behaviour.

Great work on aligning these field sizes with the application requirements.

src/spider/storage/mysql/mysql_stmt.hpp (1)

70-70:

Details

✅ Verification successful

Database schema field sizes consistently increased.

The field size increases in this file match those in the initialization SQL script, which is good for consistency. The size of 999 should be sufficient for handling larger input/output strings being tested.

Also applies to: 73-73, 84-85, 111-111, 144-144, 151-151


🌐 Web query:

What is the maximum recommended size for VARCHAR and VARBINARY fields in MySQL?

💡 Result:

In MySQL, the maximum lengths for VARCHAR and VARBINARY fields are constrained by row-size limits and practical performance considerations:

VARCHAR Recommendations

  • Maximum theoretical length:
    Up to 65,535 characters, but this is shared across all columns in a row and depends on the character set:
    • For utf8mb4 (4 bytes per character): ~16,383 characters (65,535 / 4).
    • For latin1 (1 byte per character): 65,535 characters[1][7].
  • Recommended practice:
    Declare the smallest size needed for your data (e.g., VARCHAR(255) for emails). Oversized columns waste memory during query processing and can lead to row-size errors[3][6].

VARBINARY Recommendations

  • Maximum theoretical length:
    Up to 65,535 bytes per column, subject to the same 65,535-byte row-size limit[5][8][9].
  • Recommended practice:
    Use exact lengths for fixed-size binary data (e.g., VARBINARY(16) for UUIDs, VARBINARY(64) for SHA-256 hashes). Avoid unnecessary large declarations[5][8].

Key Considerations

  1. Row-Size Limit:
    Total bytes for all columns in a row cannot exceed 65,535 bytes, including overhead (e.g., 1–2 bytes for VARCHAR length prefixes)[7].
  2. Performance Implications:
    • Over-declaring sizes forces MySQL to allocate larger memory buffers for sorting/grouping operations[3][6].
    • For indexed columns, prefer smaller lengths to avoid hitting InnoDB’s 1,073,741,824-byte index prefix limit[3].
  3. Character Set Impact:
    Multi-byte character sets like utf8mb4 reduce usable VARCHAR length (e.g., utf8mb4 allows ~16,383 characters vs. latin1’s 65,535)[7].

Example Declarations

-- Recommended for short strings (e.g., usernames):
username VARCHAR(50) CHARACTER SET utf8mb4,

-- Fixed-length binary data (e.g., UUID):
user_token VARBINARY(16),

-- Avoid unnecessarily large declarations:
description VARCHAR(2000)  -- Use 2000 if data rarely exceeds this

In summary, prioritize minimal viable sizes for VARCHAR/VARBINARY to optimize storage, memory usage, and query performance.

Citations:


Field size update verified; changes are consistent with initialization scripts.
The increased field sizes, including the use of VARCHAR(999) on line 70 (and similarly on lines 73, 84-85, 111, 144, and 151), have been confirmed to align with the initialization SQL. Although MySQL permits much larger sizes theoretically, using 999 for handling larger I/O strings in tests is both practical and consistent.

Comment on lines 70 to 75
auto get_body() const -> msgpack::object {
// NOLINTBEGIN(cppcoreguidelines-pro-type-union-access,cppcoreguidelines-pro-bounds-pointer-arithmetic)
msgpack::object const object = m_obj.get();
return object.via.array.ptr[1];
// NOLINTEND(cppcoreguidelines-pro-type-union-access,cppcoreguidelines-pro-bounds-pointer-arithmetic)
}

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 bounds checking in get_body method.

The get_body() method accesses object.via.array.ptr[1] without checking if the array has at least 2 elements, which could lead to out-of-bounds access if called independently of get_type().

 auto get_body() const -> msgpack::object {
     // NOLINTBEGIN(cppcoreguidelines-pro-type-union-access,cppcoreguidelines-pro-bounds-pointer-arithmetic)
     msgpack::object const object = m_obj.get();
+    if (object.type != msgpack::type::ARRAY || object.via.array.size < 2) {
+        throw msgpack::type_error();
+    }
     return object.via.array.ptr[1];
     // NOLINTEND(cppcoreguidelines-pro-type-union-access,cppcoreguidelines-pro-bounds-pointer-arithmetic)
 }
📝 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 get_body() const -> msgpack::object {
// NOLINTBEGIN(cppcoreguidelines-pro-type-union-access,cppcoreguidelines-pro-bounds-pointer-arithmetic)
msgpack::object const object = m_obj.get();
return object.via.array.ptr[1];
// NOLINTEND(cppcoreguidelines-pro-type-union-access,cppcoreguidelines-pro-bounds-pointer-arithmetic)
}
auto get_body() const -> msgpack::object {
// NOLINTBEGIN(cppcoreguidelines-pro-type-union-access,cppcoreguidelines-pro-bounds-pointer-arithmetic)
msgpack::object const object = m_obj.get();
if (object.type != msgpack::type::ARRAY || object.via.array.size < 2) {
throw msgpack::type_error();
}
return object.via.array.ptr[1];
// NOLINTEND(cppcoreguidelines-pro-type-union-access,cppcoreguidelines-pro-bounds-pointer-arithmetic)
}

@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

♻️ Duplicate comments (1)
src/spider/worker/TaskExecutorMessage.hpp (1)

70-74: ⚠️ Potential issue

Add bounds checking in get_body method.

The get_body() method accesses object.via.array.ptr[1] without checking if the array has at least 2 elements, which could lead to out-of-bounds access if called independently of get_type().

 auto get_body() const -> msgpack::object {
     // NOLINTBEGIN(cppcoreguidelines-pro-type-union-access,cppcoreguidelines-pro-bounds-pointer-arithmetic)
     msgpack::object const object = m_obj.get();
+    if (object.type != msgpack::type::ARRAY || object.via.array.size < 2) {
+        throw msgpack::type_error();
+    }
     return object.via.array.ptr[1];
     // NOLINTEND(cppcoreguidelines-pro-type-union-access,cppcoreguidelines-pro-bounds-pointer-arithmetic)
 }
🧹 Nitpick comments (5)
src/spider/worker/TaskExecutorMessage.hpp (3)

67-69: Improve documentation for get_body method.

The current documentation notes that the return value "Cannot outlive the TaskExecutorRequestParser object," which is good. However, it would be helpful to also document the expected structure of the object and potential exceptions that might be thrown, especially after adding the bounds checking suggested in the other comment.

 /**
  * @return The body of the message. Cannot outlive the `TaskExecutorRequestParser` object.
+ * @throw msgpack::type_error if the underlying object does not have the expected structure.
  */

46-47: Documentation mismatch in constructor.

The constructor's documentation mentions a potential std::bad_cast exception, but this doesn't appear to match what msgpack::unpack would throw. According to msgpack documentation, it would more likely throw msgpack::insufficient_bytes, msgpack::parse_error, or std::bad_alloc. Consider updating the documentation to reflect the actual exceptions that could be thrown.


52-65: Consider simplifying error handling.

The get_type() method has good error handling, checking both array type and size before accessing elements. However, you're returning TaskExecutorRequestType::Unknown for both invalid array structure and invalid type conversion. For debugging purposes, it might be useful to distinguish between these different error cases, perhaps by adding more specific enum values or by logging.

tests/client/client-test.cpp (2)

208-208: Consider if 300 characters is sufficient for a "large" input test.

The constant cLargeInputSize is set to 300, which might not be large enough to thoroughly test the system's handling of large inputs, especially if the fix addresses dangling references in memory management. If the purpose is to stress test the system, a larger value might be more appropriate.


183-206: Consider adding documentation explaining test purpose.

Adding a brief comment explaining the purpose of this test function, particularly its relation to testing memory management with large strings, would improve code readability and maintenance.

+/**
+ * Tests the system's ability to properly handle large string inputs and outputs
+ * to verify that memory management functions correctly, especially regarding
+ * serialization and deserialization of large data.
+ */
 auto test_large_input_output(
         spider::Driver& driver,
         size_t const input_size_1,
         size_t const input_size_2
 ) -> int {
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 0b3346e and 3f6ebc1.

📒 Files selected for processing (2)
  • src/spider/worker/TaskExecutorMessage.hpp (1 hunks)
  • tests/client/client-test.cpp (3 hunks)
⏰ 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/worker/TaskExecutorMessage.hpp (1)

40-79: Good approach using RAII to prevent dangling references.

The introduction of TaskExecutorRequestParser class that holds the msgpack::object_handle is an effective solution to the dangling reference problem mentioned in the PR objectives. This follows the RAII (Resource Acquisition Is Initialization) pattern, ensuring the unpacked object remains valid as long as the parser exists.

tests/client/client-test.cpp (2)

183-206: Test function looks correct and well implemented.

This test function follows the pattern of other test functions in the file and properly validates the system's ability to handle larger string inputs and outputs. It creates two input strings of specified sizes, executes a job that joins them, and verifies both job success and correct result concatenation.


274-277: Integration with main function follows established pattern.

The addition of the large input test to the main function follows the same pattern as other tests, making it a clean integration into the existing test suite.

@davidlion davidlion changed the title fix: Remove dangling reference to msgpack::object in task executor > fix: Remove dangling reference to msgpack::object in task executor; Increase test value sizes to cover this case. Apr 3, 2025
@davidlion davidlion changed the title > fix: Remove dangling reference to msgpack::object in task executor; Increase test value sizes to cover this case. fix: Remove dangling reference to msgpack::object in task executor; Increase test value sizes to cover this case. Apr 3, 2025
@sitaowang1998
sitaowang1998 merged commit fb7ec79 into y-scope:main Apr 3, 2025
@sitaowang1998
sitaowang1998 deleted the large_input branch April 3, 2025 19:57
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.

Expand size of task input/output in database

2 participants