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

feat(dojo-core): add support to read/write the same member from multiple models #2939

Merged
merged 7 commits into from
Jan 24, 2025

Conversation

bengineer42
Copy link
Contributor

@bengineer42 bengineer42 commented Jan 21, 2025

Description

Related issue

Tests

  • Yes
  • No, because they aren't needed
  • No, because I need help

Added to documentation?

  • README.md
  • Dojo Book
  • No documentation needed

Checklist

  • I've formatted my code (scripts/prettier.sh, scripts/rust_fmt.sh, scripts/cairo_fmt.sh)
  • I've linted my code (scripts/clippy.sh, scripts/docs.sh)
  • I've commented my code
  • I've requested a review after addressing the comments

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for batch reading and writing model members.
    • Introduced new methods to handle multiple model pointers simultaneously.
  • Tests

    • Enhanced test coverage for model operations, including reading and writing members across multiple model instances.
  • Improvements

    • Expanded ModelStorage trait functionality with new methods for handling multiple models.
    • Added ModelPtrsTrait to provide more flexible model pointer handling.

The updates improve the efficiency and flexibility of model operations in the Dojo framework.

Copy link

coderabbitai bot commented Jan 21, 2025

Walkthrough

Ohayo, sensei! This pull request introduces several enhancements to the Dojo core model handling capabilities. The changes focus on expanding model storage and testing functionalities, adding new methods for reading and writing multiple model members across different components. The modifications span across several files in the core module, introducing traits and implementations that support batch operations on model pointers and storage, alongside new test functions to ensure the correctness of these operations.

Changes

File Change Summary
core-cairo-test/src/tests/model/model.cairo Added 4 new test functions: test_read_member, test_read_members, test_write_member, test_write_members for model testing
core/src/lib.cairo Updated model module exports to include ModelPtrsTrait
core/src/model/model.cairo Introduced ModelPtrsTrait<M> with to_indexes and to_member_indexes methods
core/src/model/storage.cairo Added read_member_of_models and write_member_of_models methods to ModelStorage trait
core/src/world/storage.cairo Implemented read_member_of_models and write_member_of_models in ModelStorageWorldStorageImpl

Possibly related PRs

  • feat: batch entities #2633: The changes in this PR enhance the functionality of the ModelStorage trait, which is directly related to the reading and writing operations on model members, similar to the new test functions added in the main PR that verify these operations.
  • Re added members and converted ModelPtr to a struct #2634: This PR includes modifications to the model.cairo file, which is the same file where the main PR introduces new test functions for the Foo model, indicating a direct relationship in terms of testing and functionality.
  • fix(dojo-lang): remove restriction for u256 as keys in models #2890: The addition of Foo3 in this PR relates to the main PR's focus on the Foo model, as both involve modifications to model structures and their handling.

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

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 comments (1)
crates/dojo/core/src/world/storage.cairo (1)

Line range hint 198-239: Ohayo sensei! Don't forget the test implementations.

The new batch operations methods need corresponding test implementations in the ModelStorageTestWorldStorageImpl trait, similar to other methods in this file.

Consider adding these test implementations:

#[cfg(target: "test")]
fn read_members_test<T, +Serde<T>, +Drop<T>>(
    self: @WorldStorage, ptrs: Span<ModelPtr<M>>, field_selector: felt252,
) -> Array<T> {
    let world_test = dojo::world::IWorldTestDispatcher {
        contract_address: self.dispatcher.contract_address,
    };
    // Implementation similar to read_members but using test dispatcher
}

#[cfg(target: "test")]
fn write_members_test<T, +Serde<T>, +Drop<T>>(
    ref self: WorldStorage, ptrs: Span<ModelPtr<M>>, field_selector: felt252, values: Span<T>,
) {
    let world_test = dojo::world::IWorldTestDispatcher {
        contract_address: self.dispatcher.contract_address,
    };
    // Implementation similar to write_members but using test dispatcher
}
🧹 Nitpick comments (5)
crates/dojo/core/src/model/storage.cairo (1)

41-44: Ohayo! The batch operations look solid, sensei!

The new methods read_members and write_members are well-designed with appropriate generic constraints. They complement the existing single-item operations nicely.

Consider adding a note in the documentation about the expected behavior when the lengths of ptrs and values don't match in write_members.

Also applies to: 51-54

crates/dojo/core/src/model/model.cairo (1)

20-36: Implementation looks efficient, but could be optimized further!

The implementation is clean and straightforward.

Consider pre-allocating the array size if possible:

-        let mut ids = ArrayTrait::<ModelIndex>::new();
+        let mut ids = ArrayTrait::<ModelIndex>::with_capacity(self.len());
crates/dojo/core-cairo-test/src/tests/model/model.cairo (3)

198-207: Ohayo! Good basic test coverage for read_member, sensei!

Consider adding a test case for reading a non-existent member to verify error handling.


209-220: Batch read operations are well-tested!

Consider adding test cases for:

  • Empty array of pointers
  • Array with duplicate pointers

222-230: Write operations test coverage looks good!

Consider adding test cases for:

  • Writing to non-existent models
  • Writing with mismatched ptrs and values lengths

Also applies to: 231-244

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ac35492 and b344e23.

📒 Files selected for processing (5)
  • crates/dojo/core-cairo-test/src/tests/model/model.cairo (1 hunks)
  • crates/dojo/core/src/lib.cairo (1 hunks)
  • crates/dojo/core/src/model/model.cairo (2 hunks)
  • crates/dojo/core/src/model/storage.cairo (1 hunks)
  • crates/dojo/core/src/world/storage.cairo (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: fmt
🔇 Additional comments (4)
crates/dojo/core/src/lib.cairo (1)

45-45: Ohayo! LGTM on the model exports update, sensei!

The addition of ModelPtrsTrait to the public interface is well-structured and follows the existing export pattern.

crates/dojo/core/src/model/model.cairo (1)

15-18: Ohayo! Clean trait design, sensei!

The ModelPtrsTrait is well-designed with a clear, focused purpose.

crates/dojo/core/src/world/storage.cairo (2)

5-7: LGTM! Clean import additions.

The new imports ModelPtr and ModelPtrsTrait are properly added to support the new batch operations functionality.


198-211: Ohayo sensei! Consider adding length validation.

The implementation efficiently handles batch reading of members, but it would be safer to verify that the number of values returned matches the number of pointers provided.

Consider adding a validation check:

 fn read_members<T, +Serde<T>, +Drop<T>>(
     self: @WorldStorage, ptrs: Span<ModelPtr<M>>, field_selector: felt252,
 ) -> Array<T> {
     let mut values: Array<T> = array![];
+    let ptrs_len = ptrs.len();
     for entity in IWorldDispatcherTrait::entities(
         *self.dispatcher,
         Model::<M>::selector(*self.namespace_hash),
         ptrs.to_member_indexes(field_selector),
         field_layout_unwrap::<M>(field_selector),
     ) {
         values.append(deserialize_unwrap(*entity));
     };
+    assert(values.len() == ptrs_len, 'Mismatched values count');
     values
 }
✅ Verification successful

Ohayo sensei! The length validation suggestion is verified as beneficial.

The test cases show that read_members is expected to return values for all provided pointers, maintaining exact correspondence. Adding length validation would help catch contract violations early and make the implicit assumption explicit.

• The validation aligns with existing test cases in core-cairo-test/src/tests/model/model.cairo which verify exact array contents
• It would help detect issues before they propagate to array content assertions

🏁 Scripts executed

The following scripts were executed for the analysis:

Error: No shell scripts found


Script:

#!/bin/bash
# Look for similar read operations and patterns
ast-grep --pattern 'fn read_$_($$$) { $$$deserialize_unwrap($$$) }'

# Search for test cases involving read_members
rg "read_members" -A 5 -B 5

# Look for entities implementation
ast-grep --pattern 'fn entities($$$) -> $$$'

Length of output: 4505

Comment on lines 225 to 239
fn write_members<T, +Serde<T>, +Drop<T>>(
ref self: WorldStorage, ptrs: Span<ModelPtr<M>>, field_selector: felt252, values: Span<T>,
) {
let mut serialized_values = ArrayTrait::<Span<felt252>>::new();
for value in values {
serialized_values.append(serialize_inline(value));
};
IWorldDispatcherTrait::set_entities(
self.dispatcher,
Model::<M>::selector(self.namespace_hash),
ptrs.to_member_indexes(field_selector),
serialized_values.span(),
field_layout_unwrap::<M>(field_selector),
);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Ohayo sensei! Consider these essential improvements.

The implementation would benefit from two important changes:

  1. Length validation between ptrs and values spans
  2. Memory optimization by avoiding the intermediate array

Here's the suggested implementation:

 fn write_members<T, +Serde<T>, +Drop<T>>(
     ref self: WorldStorage, ptrs: Span<ModelPtr<M>>, field_selector: felt252, values: Span<T>,
 ) {
+    assert(ptrs.len() == values.len(), 'Length mismatch');
-    let mut serialized_values = ArrayTrait::<Span<felt252>>::new();
-    for value in values {
-        serialized_values.append(serialize_inline(value));
-    };
     IWorldDispatcherTrait::set_entities(
         self.dispatcher,
         Model::<M>::selector(self.namespace_hash),
         ptrs.to_member_indexes(field_selector),
-        serialized_values.span(),
+        values.map(|v| serialize_inline(v)),
         field_layout_unwrap::<M>(field_selector),
     );
 }

This implementation:

  1. Validates input lengths upfront
  2. Uses functional map operation to avoid intermediate array allocation
  3. Maintains the same functionality with better safety and performance

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

Copy link

codecov bot commented Jan 21, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 56.70%. Comparing base (3405af9) to head (851d1ad).
Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2939   +/-   ##
=======================================
  Coverage   56.69%   56.70%           
=======================================
  Files         420      420           
  Lines       55554    55554           
=======================================
+ Hits        31497    31501    +4     
+ Misses      24057    24053    -4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@glihm glihm changed the title feat(dojo): Added read/write_members support and tests feat(dojo-core): add read/write members support Jan 21, 2025
Copy link
Collaborator

@glihm glihm left a comment

Choose a reason for hiding this comment

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

Hey @bengineer42, let's rename for something more consistent with the naming, and let's merge. 👍

fn read_member<T, +Serde<T>>(self: @S, ptr: ModelPtr<M>, field_selector: felt252) -> T;

/// Retrieves a model of type `M` using the provided entity id.
/// Retrieves a single member from multiple models.
fn read_members<T, +Serde<T>, +Drop<T>>(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would rename to read_member_models, since we're reading only one member. Would you mind applying this change to all functions added to this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think read_models_member is better, thoughts?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Wanted to keep the s at then end since we return an array. Sounds good for you?

@glihm glihm changed the title feat(dojo-core): add read/write members support feat(dojo-core): add support to read/write the same member from multiple models Jan 24, 2025
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

🧹 Nitpick comments (3)
crates/dojo/core/src/world/storage.cairo (1)

198-211: Ohayo sensei! Consider adding length validation.

The function should validate that the number of values returned matches the number of pointers provided to ensure data integrity.

 fn read_member_of_models<T, +Serde<T>, +Drop<T>>(
     self: @WorldStorage, ptrs: Span<ModelPtr<M>>, field_selector: felt252,
 ) -> Array<T> {
     let mut values: Array<T> = array![];
+    let expected_len = ptrs.len();
     for entity in IWorldDispatcherTrait::entities(
         *self.dispatcher,
         Model::<M>::selector(*self.namespace_hash),
         ptrs.to_member_indexes(field_selector),
         field_layout_unwrap::<M>(field_selector),
     ) {
         values.append(deserialize_unwrap(*entity));
     };
+    assert(values.len() == expected_len, 'Length mismatch');
     values
 }
crates/dojo/core-cairo-test/src/tests/model/model.cairo (2)

209-220: Test looks good but could cover more edge cases!

The test effectively verifies batch reading functionality. Consider adding edge cases like empty pointer array or single pointer.

+#[test]
+fn test_read_members_edge_cases() {
+    let mut world = spawn_foo_world();
+    // Test empty array
+    let empty_ptrs = array![].span();
+    let empty_v1s: Array<u128> = world.read_member_of_models(empty_ptrs, selector!("v1"));
+    assert!(empty_v1s.len() == 0);
+    
+    // Test single model
+    let foo = Foo { k1: 1, k2: 2, v1: 3, v2: 4 };
+    world.write_model(@foo);
+    let single_ptr = array![foo.ptr()].span();
+    let single_v1s: Array<u128> = world.read_member_of_models(single_ptr, selector!("v1"));
+    assert!(single_v1s == array![foo.v1]);
+}

231-244: Test looks good but could handle error cases!

The test effectively verifies batch writing functionality. Consider adding error cases like length mismatch between pointers and values.

+#[should_panic(expected: ('Length mismatch'))]
+#[test]
+fn test_write_members_length_mismatch() {
+    let mut world = spawn_foo_world();
+    let foo = Foo { k1: 1, k2: 2, v1: 3, v2: 4 };
+    let foo2 = Foo { k1: 5, k2: 6, v1: 7, v2: 8 };
+    world.write_models([@foo, @foo2].span());
+    let ptrs = [foo.ptr(), foo2.ptr()].span();
+    let v1s = array![42]; // Only one value for two pointers
+    world.write_member_of_models(ptrs, selector!("v1"), v1s.span());
+}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b344e23 and bb9d34a.

📒 Files selected for processing (3)
  • crates/dojo/core-cairo-test/src/tests/model/model.cairo (1 hunks)
  • crates/dojo/core/src/model/storage.cairo (1 hunks)
  • crates/dojo/core/src/world/storage.cairo (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/dojo/core/src/model/storage.cairo
🔇 Additional comments (3)
crates/dojo/core/src/world/storage.cairo (1)

225-239: Previous review suggestions still apply.

The implementation would benefit from length validation and memory optimization as suggested in the previous review.

crates/dojo/core-cairo-test/src/tests/model/model.cairo (2)

198-207: Ohayo sensei! The test looks good!

The test thoroughly verifies reading individual members with proper assertions.


222-230: Ohayo sensei! The write test is well implemented!

The test effectively verifies single member write functionality with proper assertions.

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

🧹 Nitpick comments (5)
crates/dojo/core-cairo-test/src/tests/model/model.cairo (4)

198-207: Ohayo sensei! Consider adding edge cases to strengthen the test.

The test verifies basic member reading functionality, but could be enhanced with:

  1. Reading from a non-existent model
  2. Reading after model deletion

209-220: Ohayo sensei! Consider enhancing the test robustness.

The test effectively verifies bulk reading, but could be strengthened by:

  1. Testing with an empty array of pointers
  2. Testing with a mix of existing and non-existing models

222-230: Ohayo sensei! Consider expanding test coverage.

The test only verifies writing to v1. Consider:

  1. Testing writing to v2 as well
  2. Testing writing invalid values (if applicable)
  3. Testing writing to a non-existent model

231-244: Ohayo sensei! Consider enhancing the bulk write test.

The test effectively verifies bulk writing of v1, but could be improved by:

  1. Testing writing to both v1 and v2 in the same operation
  2. Testing with mismatched lengths between ptrs and values
crates/dojo/core/src/world/storage.cairo (1)

213-226: Ohayo sensei! Consider adding input validation.

The implementation is efficient, but could be improved by:

  1. Validating that ptrs is not empty
  2. Adding error handling for failed deserialization
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb9d34a and 851d1ad.

⛔ Files ignored due to path filters (2)
  • spawn-and-move-db.tar.gz is excluded by !**/*.gz
  • types-test-db.tar.gz is excluded by !**/*.gz
📒 Files selected for processing (4)
  • crates/dojo/core-cairo-test/src/tests/model/model.cairo (2 hunks)
  • crates/dojo/core/src/lib.cairo (1 hunks)
  • crates/dojo/core/src/model/model.cairo (2 hunks)
  • crates/dojo/core/src/world/storage.cairo (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/dojo/core/src/lib.cairo
  • crates/dojo/core/src/model/model.cairo
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: ensure-wasm
  • GitHub Check: docs
  • GitHub Check: build
🔇 Additional comments (1)
crates/dojo/core/src/world/storage.cairo (1)

240-254: Previous optimization suggestions still apply.

The implementation would benefit from length validation and memory optimization as suggested in the previous review.

Copy link
Collaborator

@glihm glihm left a comment

Choose a reason for hiding this comment

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

Thank you for the addition @bengineer42.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants