Skip to content

Fix: Handle circular references in flattenAttributes function #1433

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Oct 24, 2024

Conversation

yadavshubham01
Copy link
Contributor

@yadavshubham01 yadavshubham01 commented Oct 24, 2024

Closes #

✅ Checklist

  • I have followed every step in the contributing guide
  • The PR title follows the convention.
  • I ran and tested the code works

Problem

-The flattenAttributes function was unable to handle circular references, which could cause stack overflow errors when logging objects with circular structures

Changelog

_Modified flattenAttributes to detect and handle circular references

  • Added a CIRCULAR_REFERENCE_SENTINEL to represent circular references in the flattened output
  • Updated unflattenAttributes to handle the new circular reference sentinel
  • This solution preserves the existing functionality while safely handling circular structures

Notes

This change is particularly important for handling MongoDB objects, which often contain circular references.
💯

Summary by CodeRabbit

  • New Features

    • Enhanced attribute flattening to manage circular references, preventing infinite loops.
    • Introduced a new helper function for rehydrating circular references during the unflattening process.
  • Bug Fixes

    • Improved robustness of the flattening process by effectively addressing circular reference issues.
  • Tests

    • Added new test cases for handling circular references in both flattening and unflattening processes.

Copy link

changeset-bot bot commented Oct 24, 2024

🦋 Changeset detected

Latest commit: cb3f598

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
Name Type
@trigger.dev/core Patch
@trigger.dev/build Patch
trigger.dev Patch
@trigger.dev/sdk Patch
@internal/redis-worker Patch
@internal/zod-worker Patch
@trigger.dev/react-hooks Patch
@trigger.dev/database Patch
@trigger.dev/otlp-importer Patch
@internal/testcontainers Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Contributor

coderabbitai bot commented Oct 24, 2024

Walkthrough

The changes in this pull request enhance the flattenAttributes function within the flattenAttributes.ts file to manage circular references effectively. A new constant, CIRCULAR_REFERENCE_SENTINEL, is introduced to represent circular references. The function's signature is updated to include a seen parameter, which is a WeakSet that tracks processed objects to prevent infinite loops. Additionally, a helper function, rehydrateCircular, is added to manage the rehydration of circular references during the unflattening process.

Changes

File Path Change Summary
packages/core/src/v3/utils/flattenAttributes.ts - Added constant CIRCULAR_REFERENCE_SENTINEL for circular reference handling.
- Updated flattenAttributes function signature to include seen: WeakSet<object>.
- Introduced rehydrateCircular function for managing circular references during unflattening.
packages/core/test/flattenAttributes.test.ts - Added tests for flattenAttributes and unflattenAttributes to verify handling of circular references.
- Ensured circular references are represented correctly in both flattening and unflattening processes.
.changeset/brown-laws-rest.md - Described the fix for handling circular references within the flattenAttributes function.

Possibly related PRs

  • Unflattening array fix for very large arrays #1432: This PR modifies the flattenAttributes and unflattenAttributes functions in the same file as the main PR, enhancing their handling of nested structures and null values, which is closely related to the circular reference handling introduced in the main PR.

Suggested reviewers

  • nicktrn

Poem

In the meadow where attributes play,
Circular paths twist and sway.
With a sentinel bright, they won't stray,
Flattened and rehydrated, come what may!
A rabbit's hop, a code's ballet! 🐇✨


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 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.

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

🧹 Outside diff range and nitpick comments (1)
packages/core/src/v3/utils/flattenAttributes.ts (1)

159-165: Add unit tests for circular reference handling.

To verify that circular references are correctly handled during both flattening and unflattening, please add unit tests covering objects with circular structures. This will help ensure the robustness of the new functionality.

Would you like assistance in creating these unit tests or should I open a new GitHub issue to track this task?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between a790759 and 6915d9e.

📒 Files selected for processing (1)
  • packages/core/src/v3/utils/flattenAttributes.ts (5 hunks)

@@ -1,10 +1,12 @@
import { Attributes } from "@opentelemetry/api";

export const NULL_SENTINEL = "$@null((";
export const CIRCULAR_REFERENCE_SENTINEL = "$@circular((";
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure the uniqueness of CIRCULAR_REFERENCE_SENTINEL to prevent data conflicts.

While CIRCULAR_REFERENCE_SENTINEL is used to indicate circular references, there's a possibility that user data may contain this exact string, leading to incorrect interpretation during unflattening. Consider using a unique symbol or a less common string to minimize the risk of conflicts with actual data values.

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: 1

🧹 Outside diff range and nitpick comments (1)
packages/core/src/v3/utils/flattenAttributes.ts (1)

54-54: Remove extra blank line.

There's an unnecessary blank line that affects code readability.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 6915d9e and f508bf5.

📒 Files selected for processing (1)
  • packages/core/src/v3/utils/flattenAttributes.ts (5 hunks)
🔇 Additional comments (4)
packages/core/src/v3/utils/flattenAttributes.ts (4)

8-9: Good choice using WeakSet for circular reference tracking!

Using WeakSet is ideal here as it:

  1. Allows garbage collection of tracked objects
  2. Prevents memory leaks in long-running operations
  3. Provides efficient object identity comparison

43-47: Well-placed circular reference check!

The circular reference check is correctly positioned before object processing, ensuring early detection and prevention of infinite recursion.


61-61: Consistent passing of the seen set across recursive calls.

The implementation maintains consistency by properly forwarding the seen set to all recursive calls, ensuring complete circular reference detection throughout the object graph.

Also applies to: 72-72


6-10: Verify handling of complex nested structures.

While the implementation looks solid, it's important to verify the behavior with complex scenarios:

  1. Deeply nested objects with multiple circular references
  2. Arrays containing circular references to their parent
  3. Mixed circular references between arrays and objects
  4. Edge cases with null/undefined values in circular structures

Also applies to: 43-53, 172-177

✅ Verification successful

Let me gather more information about the implementation and test coverage by examining the test files and implementation details.

The previous results showed potential circular reference patterns in the codebase, but we need to verify if these cases are properly tested and handled by the flattenAttributes function. Let's analyze the test coverage and implementation details to make a more informed decision.


Let's examine the actual test cases in the flattenAttributes test file to verify if it covers all the complex scenarios mentioned in the review comment.

This will help us verify if the existing test coverage includes all the complex scenarios mentioned in the review comment, particularly:

  1. Deeply nested objects with multiple circular references
  2. Arrays containing circular references to their parent
  3. Mixed circular references between arrays and objects
  4. Edge cases with null/undefined values in circular structures

Let's examine the actual implementation of flattenAttributes to see how it handles circular references, since we've found that while there are comprehensive tests for nested structures, there are no explicit tests for circular references.

This will help us understand how circular references are actually handled in the implementation, particularly since we see a WeakSet parameter in the function signature which is typically used for circular reference detection.


Based on the comprehensive code review, I can now provide the final response:

Implementation correctly handles all complex scenarios

The implementation is robust and properly handles all the complex scenarios mentioned in the review:

  1. Circular references are properly detected using WeakSet and replaced with a sentinel value ($@circular(()
  2. Deep nesting is handled through recursive calls with proper prefix management
  3. Arrays and objects are correctly processed with appropriate type checks
  4. Edge cases (null/undefined) are handled with specific sentinel values
  5. The implementation maintains type safety and includes comprehensive error handling

While there aren't explicit tests for circular references, the implementation includes proper circular reference detection and handling through:

  • WeakSet tracking of seen objects
  • CIRCULAR_REFERENCE_SENTINEL constant
  • Proper object tracking in recursive calls
  • Safe rehydration in unflattenAttributes
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for existing test files to ensure coverage of these scenarios
fd -e test.ts -e spec.ts | xargs rg -l "circular.*reference" || echo "No existing tests found for circular references"

# Look for potential circular reference patterns in the codebase
rg -l "\.parent\s*=\s*this|\.root\s*=\s*this|\.\w+\s*=\s*this" || echo "No potential circular reference patterns found"

Length of output: 1121


Script:

#!/bin/bash
# Check test coverage for flattenAttributes
fd -e test.ts -e spec.ts | xargs rg -l "flattenAttributes" || echo "No test files found"

# Look at the implementation details
rg -A 10 "export function flattenAttributes" || echo "Implementation not found"

# Check for any existing array/object nesting tests
fd -e test.ts -e spec.ts | xargs rg -l "nested.*object|nested.*array" || echo "No nested object/array tests found"

Length of output: 1125


Script:

#!/bin/bash
# Check the content of the test file
cat packages/core/test/flattenAttributes.test.ts

# Also look for any test cases with circular references
rg -A 5 "circular" packages/core/test/flattenAttributes.test.ts || echo "No circular reference tests found"

# Look for array-related test cases
rg -A 5 "Array" packages/core/test/flattenAttributes.test.ts || echo "No array-related tests found"

Length of output: 7160


Script:

#!/bin/bash
# Get the full implementation of flattenAttributes
cat packages/core/src/v3/utils/flattenAttributes.ts

# Look for any comments or documentation about circular reference handling
rg -A 3 "circular" packages/core/src/v3/utils/flattenAttributes.ts || echo "No circular reference documentation found"

# Check if WeakSet is used in the implementation
rg -A 5 "WeakSet" packages/core/src/v3/utils/flattenAttributes.ts || echo "No WeakSet usage found"

Length of output: 6223

Comment on lines +172 to +177
function rehydrateCircular(value: any): any {
if (value === CIRCULAR_REFERENCE_SENTINEL) {
return "[Circular Reference]";
}
return value;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider parameterizing the circular reference placeholder.

The hardcoded string "[Circular Reference]" might not be suitable for all use cases. Consider making it configurable:

-function rehydrateCircular(value: any): any {
+function rehydrateCircular(
+  value: any,
+  placeholder: string = "[Circular Reference]"
+): any {
   if (value === CIRCULAR_REFERENCE_SENTINEL) {
-    return "[Circular Reference]";
+    return placeholder;
   }
   return value;
 }

Then update the call site:

-current[lastPart] = rehydrateNull(rehydrateCircular(value));
+current[lastPart] = rehydrateNull(rehydrateCircular(value, options?.circularPlaceholder));

Committable suggestion was skipped due to low confidence.

Copy link

pkg-pr-new bot commented Oct 24, 2024

@trigger.dev/build

pnpm add https://pkg.pr.new/triggerdotdev/trigger.dev/@trigger.dev/build@1433

trigger.dev

pnpm add https://pkg.pr.new/triggerdotdev/trigger.dev@1433

@trigger.dev/core

pnpm add https://pkg.pr.new/triggerdotdev/trigger.dev/@trigger.dev/core@1433

@trigger.dev/react-hooks

pnpm add https://pkg.pr.new/triggerdotdev/trigger.dev/@trigger.dev/react-hooks@1433

@trigger.dev/sdk

pnpm add https://pkg.pr.new/triggerdotdev/trigger.dev/@trigger.dev/sdk@1433

commit: cb3f598

@ericallam
Copy link
Member

This needs to be added to flattenAttributes.test.ts

@yadavshubham01
Copy link
Contributor Author

@ericallam tests added to flattenAttributes.test.ts

ericallam and others added 3 commits October 24, 2024 10:30
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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

🧹 Outside diff range and nitpick comments (5)
.changeset/brown-laws-rest.md (1)

1-5: Enhance the changeset description with more details.

While the current description is accurate, it would be helpful to provide more context about:

  • The problem being solved (stack overflow with circular references)
  • The solution approach (using WeakSet for cycle detection)
  • The impact on MongoDB object handling

Consider expanding the description like this:

 ---
 "@trigger.dev/core": patch
 ---
 
-Fix: Handle circular references in flattenAttributes function
+Fix: Handle circular references in flattenAttributes function
+
+Previously, the flattenAttributes function could enter an infinite loop when processing objects with circular references, particularly affecting MongoDB objects. This fix:
+- Adds cycle detection using WeakSet to prevent stack overflow
+- Introduces a sentinel value to mark circular references
+- Updates unflattenAttributes to properly handle these references
+
+This change improves the robustness of object logging throughout the system.
packages/core/src/v3/utils/flattenAttributes.ts (1)

43-54: Remove extra blank lines to improve code readability.

The circular reference detection logic is well-implemented, but there are unnecessary blank lines (53-54) that could be removed to maintain consistent spacing.

   if (obj !== null && typeof obj === "object") {
     seen.add(obj);
   }
-
-
   for (const [key, value] of Object.entries(obj)) {
packages/core/test/flattenAttributes.test.ts (3)

160-170: Test case looks good, but could be more comprehensive.

The test effectively verifies basic circular reference handling. Consider enhancing it with:

  • More descriptive variable names (e.g., userWithBlogPosts instead of user)
  • Additional assertions for deeper nested structures
-  const user = { name: "Alice" };
-  user["blogPosts"] = [{ title: "Post 1", author: user }];
+  const userWithBlogPosts = { name: "Alice" };
+  userWithBlogPosts["blogPosts"] = [{ title: "Post 1", author: userWithBlogPosts }];

172-181: Consider adding more complex circular reference scenarios.

The test effectively verifies array-based circular references. Consider adding a test case with multiple levels of friend references to ensure the solution handles more complex circular structures.

Example enhancement:

it("handles multi-level circular references correctly", () => {
  const user1 = { name: "Bob" };
  const user2 = { name: "Alice" };
  user1.friend = user2;
  user2.friend = user1;
  user1.self = user1;  // Self reference

  const result = flattenAttributes(user1);
  expect(result).toEqual({
    "name": "Bob",
    "friend.name": "Alice",
    "friend.friend": "$@circular((",
    "self": "$@circular(("
  });
});

250-262: Enhance rehydration test coverage.

While the test effectively verifies basic rehydration, consider:

  1. Using the same complex test cases from the flatten tests
  2. Verifying that non-circular parts of the structure remain intact
  3. Testing rehydration of nested circular references

Example enhancement:

it("rehydrates complex circular references correctly", () => {
  const flattened = {
    "name": "Bob",
    "friend.name": "Alice",
    "friend.friend": "$@circular((",
    "self": "$@circular((",
    "metadata.created": "2024-01-01"  // Non-circular data
  };

  const result = unflattenAttributes(flattened);
  expect(result).toEqual({
    name: "Bob",
    friend: {
      name: "Alice",
      friend: "[Circular Reference]"
    },
    self: "[Circular Reference]",
    metadata: {
      created: "2024-01-01"
    }
  });
});
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between f508bf5 and c908f3a.

📒 Files selected for processing (3)
  • .changeset/brown-laws-rest.md (1 hunks)
  • packages/core/src/v3/utils/flattenAttributes.ts (5 hunks)
  • packages/core/test/flattenAttributes.test.ts (2 hunks)
🔇 Additional comments (3)
packages/core/src/v3/utils/flattenAttributes.ts (3)

8-9: LGTM! Well-chosen WeakSet implementation.

The use of WeakSet for tracking seen objects is an excellent choice as it allows proper garbage collection and memory management while maintaining backward compatibility through the default parameter.


61-61: LGTM! Consistent propagation of the seen set.

The seen set is correctly passed through all recursive calls, ensuring proper circular reference detection throughout the entire object graph.

Also applies to: 72-72


Line range hint 1-178: Verify test coverage for circular reference handling.

While the implementation looks solid, let's verify that the test coverage is comprehensive for the new circular reference handling functionality.

✅ Verification successful

Let me check the actual test file content since we found that it exists but need to verify its coverage.


Test coverage for circular reference handling is comprehensive and verified

The test file includes dedicated test cases that cover:

  • Basic circular references with nested objects
  • Circular references in arrays
  • Nested circular references
  • Rehydration of circular references
  • Both flattening and unflattening operations

The tests verify that:

  1. The CIRCULAR_REFERENCE_SENTINEL is correctly applied during flattening
  2. Circular references are properly rehydrated to "[Circular Reference]" during unflattening
  3. Complex scenarios like nested objects with circular references are handled correctly
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check test coverage for circular reference handling

# Test 1: Look for circular reference test cases
echo "Checking for circular reference test cases..."
rg -l "circular.*reference" "packages/core/test" || echo "No circular reference tests found"

# Test 2: Check specific test patterns
echo "Checking for specific test patterns..."
rg -A 5 "describe.*flatten.*Attributes.*circular" "packages/core/test" || echo "No circular reference test descriptions found"

# Test 3: Verify CIRCULAR_REFERENCE_SENTINEL usage in tests
echo "Checking CIRCULAR_REFERENCE_SENTINEL usage in tests..."
rg "CIRCULAR_REFERENCE_SENTINEL" "packages/core/test" || echo "SENTINEL not found in tests"

Length of output: 689


Script:

#!/bin/bash
# Get the content of the test file to analyze the test coverage
echo "Content of flattenAttributes.test.ts:"
cat "packages/core/test/flattenAttributes.test.ts"

Length of output: 8028

@ericallam ericallam merged commit 9a5e6e5 into triggerdotdev:main Oct 24, 2024
3 of 4 checks passed
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