Skip to content
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

fix(server): sort duplicate key error #55

Merged
merged 5 commits into from
Nov 20, 2024
Merged

fix(server): sort duplicate key error #55

merged 5 commits into from
Nov 20, 2024

Conversation

hexaforce
Copy link
Contributor

@hexaforce hexaforce commented Nov 19, 2024

A duplicate error occurs when the sort key is "id".

Summary by CodeRabbit

  • New Features

    • Enhanced pagination functionality with improved sorting options.
    • Introduced flexible sorting mechanism allowing custom sort keys.
  • Bug Fixes

    • Improved error handling for pagination validation.
  • Refactor

    • Updated method signatures for pagination and aggregation to support new sorting logic.
    • Implemented thread-safe access control in the Members struct.
  • Tests

    • Added a new test case for sorting logic in pagination.
    • Improved formatting in existing tests for better readability.
  • Chores

    • Added a Makefile with targets for help, linting, and testing.

Copy link

coderabbitai bot commented Nov 19, 2024

Walkthrough

The changes in this pull request focus on enhancing the pagination functionality in the mongox/pagination.go file. Key modifications include the introduction of a new sorting mechanism using primitive.D, allowing for more flexible sorting options in the Paginate and PaginateAggregation methods. The sorting logic has been refactored to utilize a variable sort, which can be customized based on user input. Additionally, the error handling and pagination validation processes have been refined, while minor formatting adjustments have been made for code readability.

Changes

File Change Summary
mongox/pagination.go - Enhanced Paginate and PaginateAggregation methods with a new flexible sorting mechanism using primitive.D.
- Updated SetSort option to use a variable sort instead of hardcoded values.
- Modified aggregateFilter to validate pagination, ensuring both cursor and offset are not nil.
- Minor formatting changes for improved readability.
- Updated method signatures for both pagination methods.
mongox/pagination_test.go - Added new test function TestPaginate_SortLogic to verify sorting logic in pagination.
- Modified TestClientCollection_DetailedPagination for consistent struct field formatting.
- Minor formatting adjustments for code readability.
Makefile - Introduced new targets: help, lint, and test for usage instructions, linting, and testing respectively.
account/accountdomain/workspace/member.go - Added mutex mu to Members struct for thread-safe access.
- Modified Leave method to include locking and unlocking of the mutex.

Possibly related PRs

  • fix(mongox): pagination with consistency #45: This PR enhances pagination and sorting functionalities in mongox/pagination.go, which is directly related to the changes made in the main PR regarding the Paginate and PaginateAggregation methods.
  • feat(mongox): fix pagination #47: This PR refactors the Paginate and pageFilter methods in mongox/pagination.go, focusing on improving pagination logic, which aligns closely with the updates made in the main PR.

Poem

🐰 In the land of code where bunnies play,
Pagination dances in a new array.
With sorting so fine, and errors in line,
Our methods now twirl, oh how they shine!
Hopping through data, so swift and so bright,
The rabbit's delight in the code's new light! 🌟


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

@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 (2)
mongox/pagination.go (2)

210-214: Consider making collation settings configurable

The current implementation hardcodes collation settings. While these settings are reasonable defaults, consider making them configurable to support different locales and comparison strengths based on collection requirements.

-func aggregateOptionsFromPagination(_ usecasex.Pagination, _ *usecasex.Sort) *options.AggregateOptions {
+func aggregateOptionsFromPagination(_ usecasex.Pagination, _ *usecasex.Sort, opts ...func(*options.Collation)) *options.AggregateOptions {
 	collation := options.Collation{
 		Locale:   "en",
 		Strength: 2,
 	}
+	for _, opt := range opts {
+		opt(&collation)
+	}
 	return options.Aggregate().SetAllowDiskUse(true).SetCollation(&collation)
}

Line range hint 1-324: Overall implementation assessment

The changes effectively address the sort duplicate key error through proper handling of compound sorts and the introduction of collation settings. The implementation is solid, with a few suggested improvements:

  1. Add validation for sort keys to prevent runtime errors
  2. Make collation settings configurable for better flexibility
  3. Fix potential nil pointer dereference in sortFilter

The core functionality appears correct and the changes align well with the PR objectives.

Consider adding integration tests to verify the sorting behavior with various combinations of sort keys and pagination parameters.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between cd5d2cb and 90a3bf1.

📒 Files selected for processing (1)
  • mongox/pagination.go (4 hunks)
🔇 Additional comments (2)
mongox/pagination.go (2)

11-11: LGTM: Proper use of primitive.D for sort specification

The change correctly uses primitive.D for sort specifications and properly handles both single and compound sort keys while maintaining consistency with idKey.

Also applies to: 44-49


52-52: Verify sort key validation

While the sort implementation is correct, consider adding validation for sort keys to prevent runtime errors when invalid fields are specified.

Comment on lines +291 to +295
var sortOptions bson.D
if s != nil && s.Key != "" && s.Key != idKey {
sortOptions = append(sortOptions, bson.E{Key: s.Key, Value: sortDirection(p, s)})
}
return append(sortOptions, bson.E{Key: idKey, Value: sortDirection(p, s)})
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix potential nil pointer dereference

The condition s != nil && s.Key != "" is correct, but the subsequent code assumes s is not nil when calling sortDirection(p, s). While sortDirection handles nil s, it would be more explicit to handle this in the sort options construction.

 func sortFilter(p usecasex.Pagination, s *usecasex.Sort) bson.D {
 	var sortOptions bson.D
 	if s != nil && s.Key != "" && s.Key != idKey {
-		sortOptions = append(sortOptions, bson.E{Key: s.Key, Value: sortDirection(p, s)})
+		direction := sortDirection(p, s)
+		sortOptions = append(sortOptions, bson.E{Key: s.Key, Value: direction})
+		return append(sortOptions, bson.E{Key: idKey, Value: direction})
 	}
-	return append(sortOptions, bson.E{Key: idKey, Value: sortDirection(p, s)})
+	return bson.D{{Key: idKey, Value: sortDirection(p, nil)}}
 }
📝 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
var sortOptions bson.D
if s != nil && s.Key != "" && s.Key != idKey {
sortOptions = append(sortOptions, bson.E{Key: s.Key, Value: sortDirection(p, s)})
}
return append(sortOptions, bson.E{Key: idKey, Value: sortDirection(p, s)})
func sortFilter(p usecasex.Pagination, s *usecasex.Sort) bson.D {
var sortOptions bson.D
if s != nil && s.Key != "" && s.Key != idKey {
direction := sortDirection(p, s)
sortOptions = append(sortOptions, bson.E{Key: s.Key, Value: direction})
return append(sortOptions, bson.E{Key: idKey, Value: direction})
}
return bson.D{{Key: idKey, Value: sortDirection(p, nil)}}
}

@hexaforce hexaforce requested a review from pyshx November 19, 2024 09:06
@hexaforce hexaforce self-assigned this Nov 19, 2024
Copy link

@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 (2)
mongox/pagination_test.go (2)

424-500: LGTM! Well-structured test cases for sorting logic.

The test cases effectively verify the sorting behavior and specifically address the duplicate key error mentioned in the PR objectives. The implementation properly handles different sort keys and orders, with special handling for the "id" field.

Consider adding a test case that verifies the behavior when sorting with empty/null values in the sort fields to ensure robust handling of edge cases.


250-319: Consider consolidating test cases and reducing setup duplication.

While the test cases provide good coverage, consider:

  1. Moving these test cases to TestClientCollection_DetailedPagination since they test similar scenarios
  2. Creating a helper function for test data setup as it's duplicated across multiple test functions

Example helper function:

func setupTestData(t *testing.T, c *Collection) {
    seeds := []struct {
        id        string
        updatedAt int64
    }{
        {"a", 1000},
        {"b", 2000},
        {"c", 3000},
        {"d", 4000},
        {"e", 5000},
    }
    
    _, err := c.Client().InsertMany(context.Background(), lo.Map(seeds, func(s struct {
        id        string
        updatedAt int64
    }, i int) any {
        return bson.M{"id": s.id, "updatedAt": s.updatedAt}
    }))
    assert.NoError(t, err)
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 90a3bf1 and 162cfe3.

📒 Files selected for processing (1)
  • mongox/pagination_test.go (5 hunks)
🔇 Additional comments (2)
mongox/pagination_test.go (2)

346-397: LGTM! Improved test case structure and readability.

The formatting changes enhance code readability while maintaining comprehensive test coverage for pagination scenarios.


493-497: Verification: Sort duplicate key error fix is properly tested.

The test cases effectively verify that:

  1. When sorting by "id", only the id field is used in sort options
  2. When sorting by other fields, "id" is properly added as a secondary sort key to prevent duplicate key issues

This implementation aligns perfectly with the PR objective of fixing the sort duplicate key error.

✅ Verification successful

Sort implementation is consistent and properly handles duplicate key cases

The codebase shows two consistent sort implementations:

  1. In Find operations: Special handling for "id" field to prevent duplicate sort keys
  2. In Aggregate operations: Using sortFilter function that always appends "id" as secondary sort key

The test cases in TestPaginate_SortLogic comprehensively verify both ascending and descending sort scenarios for both "id" and other fields, confirming the fix works as intended.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify that the sort implementation is consistent across the codebase
# Look for other sort implementations that might need similar fixes

# Search for sort implementations
echo "Searching for sort implementations..."
rg -l "SetSort|sort.*primitive\.D" --type go

# Search for potential test cases involving sort
echo "Searching for sort-related test cases..."
rg -l "test.*sort|sort.*test" --type go

Length of output: 336


Script:

#!/bin/bash
# Let's examine the sort implementations in detail

# Check pagination.go sort implementation
echo "=== Examining pagination.go sort implementation ==="
rg "sort" -B 3 -A 3 mongox/pagination.go

# Check user.go sort implementation
echo "=== Examining account/accountinfrastructure/accountmongo/user.go sort implementation ==="
rg "sort" -B 3 -A 3 account/accountinfrastructure/accountmongo/user.go

# Look for any sort-related test cases with more specific patterns
echo "=== Examining sort-related test cases ==="
rg "TestPaginate.*Sort|test.*sort.*func|sort.*assert" -B 2 -A 2 --type go

Length of output: 2675


Script:

#!/bin/bash
# Let's examine the TestPaginate_SortLogic test function in detail
echo "=== Examining TestPaginate_SortLogic test and its context ==="
rg "TestPaginate_SortLogic" -B 10 -A 50 mongox/pagination_test.go

# Let's also check the implementation of sortFilter function which seems to handle sort logic
echo "=== Examining sortFilter implementation and its usages ==="
rg "sortFilter" -B 5 -A 10 mongox/pagination.go

Length of output: 2665

Copy link

@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 (3)
mongox/pagination_test.go (3)

424-427: Remove unused helper function

The toJSONString function is not used anywhere in the test file. Consider removing it to maintain code cleanliness.


250-319: Consider consolidating overlapping test functions

This test function has significant overlap with TestClientCollection_DetailedPagination. Both test similar pagination scenarios with updatedAt sorting. Consider consolidating these tests to:

  1. Reduce code duplication
  2. Maintain a single source of truth for pagination test cases
  3. Make the test suite more maintainable

429-499: Add test cases for edge scenarios

While the test cases cover the basic sorting scenarios well, consider adding the following edge cases to make the test suite more robust:

  1. Documents with duplicate values in sort fields
  2. Documents with missing sort fields

These scenarios would help verify the sorting behavior in edge cases and prevent potential regressions.

Example test cases to add:

 cases := []struct {
   name          string
   sortKey       string
   sortOrder     int
   expectedOrder []usecasex.Cursor
 }{
+  {
+    name:          "Sort with duplicate updatedAt values",
+    sortKey:       "updatedAt",
+    sortOrder:     1,
+    expectedOrder: []usecasex.Cursor{"a", "b", "c"},
+  },
+  {
+    name:          "Sort with missing updatedAt field",
+    sortKey:       "updatedAt",
+    sortOrder:     1,
+    expectedOrder: []usecasex.Cursor{"missing", "a", "b", "c"},
+  },
 }

And update the test data accordingly:

 seeds := []struct {
   id        string
   updatedAt int64
 }{
   {"a", 1000},
   {"b", 2000},
   {"c", 3000},
+  {"d", 2000},  // duplicate updatedAt
+  {"missing", nil},  // missing updatedAt
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 162cfe3 and d9ff20e.

📒 Files selected for processing (1)
  • mongox/pagination_test.go (5 hunks)
🔇 Additional comments (1)
mongox/pagination_test.go (1)

Line range hint 346-406: LGTM! Well-structured test cases

The test cases are comprehensive and well-organized, covering all pagination scenarios with both ascending and descending orders.

Copy link

@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 (1)
mongox/pagination_test.go (1)

249-318: Consider consolidating duplicate test scenarios.

While the test implementation is good, there's some overlap with TestClientCollection_DetailedPagination. Consider consolidating these tests to reduce duplication and improve maintainability.

You could:

  1. Move common test scenarios to a shared test data structure
  2. Create a helper function for common assertion logic
  3. Keep only unique test cases in each function

Example refactoring:

type paginationTestCase struct {
    name       string
    sort       *usecasex.Sort
    pagination *usecasex.CursorPagination
    expected   []string
}

func commonPaginationTests() []paginationTestCase {
    return []paginationTestCase{
        // Common test cases here
    }
}

func (c *ClientCollection) runPaginationTest(t *testing.T, tc paginationTestCase) {
    con := &consumer{}
    _, err := c.Paginate(context.Background(), bson.M{}, tc.sort, tc.pagination.Wrap(), con)
    assert.NoError(t, err)
    assert.Equal(t, tc.expected, con.Cursors)
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d9ff20e and cdfa1dc.

📒 Files selected for processing (2)
  • Makefile (1 hunks)
  • mongox/pagination_test.go (4 hunks)
✅ Files skipped from review due to trivial changes (1)
  • Makefile
🔇 Additional comments (2)
mongox/pagination_test.go (2)

423-493: LGTM! Well-structured test cases for sorting logic.

The new test function comprehensively covers sorting scenarios for both "id" and "updatedAt" fields in ascending and descending orders. This directly addresses the PR's objective of fixing the sort duplicate key error.


Line range hint 345-405: LGTM! Enhanced test structure with clear naming.

The test cases are well-organized with descriptive names that clearly indicate the test scenario. The structure improvements make the tests more maintainable and easier to understand.

Copy link

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

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between cdfa1dc and 7d2b94e.

📒 Files selected for processing (1)
  • account/accountdomain/workspace/member.go (3 hunks)
🔇 Additional comments (2)
account/accountdomain/workspace/member.go (2)

226-228: LGTM: Proper mutex implementation in Leave method

The mutex locking pattern is correctly implemented with proper use of defer for unlocking.


Line range hint 84-89: Verify thread safety of sorting operations

The sorting operations in UserIDs() and IntegrationIDs() could be affected by concurrent map modifications. This might be related to the reported sort duplicate key error.

Also applies to: 94-99

✅ Verification successful

Thread safety is properly implemented for sorting operations

The sorting operations are safe from concurrent access issues because:

  • The Members struct has a mutex (sync.Mutex) for synchronization
  • Before accessing the maps, defensive copies are made using maps.Clone()
  • The sorting is performed on these cloned copies, not the original data

The reported sort duplicate key error is likely unrelated to these sorting operations as they operate on safely copied data.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for all sorting operations in the codebase that might be affected by concurrent access
rg -n "sort\." --type go

# Search for any reported issues or tests related to sorting and concurrency
rg -n "duplicate.*key|concurrent.*sort" --type go

Length of output: 514


Script:

#!/bin/bash
# Let's examine the context of these sorting operations and their data sources
rg -B 5 -A 5 "sort\.SliceStable" account/accountdomain/workspace/member.go

# Check if there are any mutex/sync operations around these methods
rg -B 10 -A 2 "func.*UserIDs|func.*IntegrationIDs" account/accountdomain/workspace/member.go

# Look for any concurrent access patterns or mutex usage in the file
rg "sync\.|mutex|Lock|Unlock" account/accountdomain/workspace/member.go

Length of output: 1558

@@ -2,6 +2,7 @@ package workspace

import (
"sort"
"sync"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure consistent mutex usage across all map operations

While adding mutex protection is good, the current implementation only protects the Leave method. Other methods that access or modify the users and integrations maps should also be protected to ensure thread safety.

The following methods need mutex protection:

  • Join
  • AddIntegration
  • DeleteIntegration
  • UpdateUserRole
  • UpdateIntegrationRole
  • Any method that modifies the maps

Example pattern to follow:

func (m *Members) MethodName() {
    m.mu.Lock()
    defer m.mu.Unlock()
    // map operations
}

Also applies to: 33-33

@@ -29,6 +30,7 @@
users map[UserID]Member
integrations map[IntegrationID]Member
fixed bool
mu sync.Mutex
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using sync.RWMutex for better performance

Since the struct has both read and write operations, using sync.RWMutex instead of sync.Mutex would allow multiple concurrent reads while maintaining exclusive writes.

Apply this change:

- mu           sync.Mutex
+ mu           sync.RWMutex

Then use RLock()/RUnlock() for read-only methods:

func (m *Members) Users() map[UserID]Member {
    m.mu.RLock()
    defer m.mu.RUnlock()
    return maps.Clone(m.users)
}

This pattern should be applied to all read-only methods:

  • Users()
  • UserIDs()
  • Integrations()
  • IntegrationIDs()
  • HasUser()
  • HasIntegration()

@pyshx pyshx merged commit e4cc7e5 into main Nov 20, 2024
2 checks passed
@pyshx pyshx deleted the fix/sort-duplicate-key branch November 20, 2024 06:00
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