Skip to content

Support custom recursion limits at build time - #785

Open
seanlinsley wants to merge 1 commit into
tokio-rs:masterfrom
pganalyze:recursion-limit-macro
Open

seanlinsley wants to merge 1 commit into
tokio-rs:masterfrom
pganalyze:recursion-limit-macro

Conversation

@seanlinsley

@seanlinsley seanlinsley commented Dec 15, 2022 •

Copy link
Copy Markdown

This PR allows users to set a custom recursion limit for specific protobuf structures at build time:

let mut config = prost_build::Config::new();
config.recursion_limit("my_messages.MyMessageType", 1000);

@seanlinsley
seanlinsley force-pushed the recursion-limit-macro branch 3 times, most recently from b2bb84c to 9a50224 Compare December 15, 2022 22:58
@seanlinsley

Copy link
Copy Markdown
Author

thoughts @LucioFranco @nrc @danburkert?

Comment thread src/message.rs Outdated
Comment thread src/lib.rs Outdated
// See `encoding::DecodeContext` for more info.
// 100 is the default recursion limit in the C++ implementation.
#[cfg(not(feature = "no-recursion-limit"))]
const RECURSION_LIMIT: u32 = 100;

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Since it sounds like this will never change, I opted to inline it everywhere it's used. That allows documentation to say explicitly that the default recursion limit is 100 instead of requiring that users look up this constant.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I actually kinda like having this as a constant. I wonder if we could just make a constants module that contains just this one. And we can then have all the deep dive docs on the recursion implementation there and then we just need to link to there from the lib doc page. I feel like that would make it easier to maintain down the line and centralize it a bit.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🤷 I'm not sure if a constants module adds much. If you still feel a constant is necessary, I'd lean towards simply adding back the constant here in lib.rs

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I like it to be a constant as well. It makes it explicit in the code. But I do like it to be in the documentation as well.

@seanlinsley

Copy link
Copy Markdown
Author

Here's a downstream PR using this feature: pganalyze/pg_query.rs#17

Note that to prevent a stack overflow from the recursion, I had to increase the stack size in a separate thread. I wonder if that should be documented here.

@LucioFranco LucioFranco left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Overall LGTM! Some small suggestions and we can get this merged. Thanks for the patience I went on vacation in between :)

Comment thread src/encoding.rs Outdated
Comment thread src/message.rs Outdated
@seanlinsley

Copy link
Copy Markdown
Author

@LucioFranco this should be ready for re-review

@seanlinsley

Copy link
Copy Markdown
Author

@LucioFranco ping. happy to make any other changes if needed

@LucioFranco LucioFranco left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Sorry for the super long delay on the review here and thank you for the ping (I generally don't mind them if you're waiting for a review). I left a few comments that I would like to see changed but they are pretty minor and I think once those are good we are good to merge. Thank you for pushing through on this!

Comment thread prost-derive/src/lib.rs Outdated
Comment thread src/encoding.rs Outdated
Comment thread src/lib.rs Outdated
// See `encoding::DecodeContext` for more info.
// 100 is the default recursion limit in the C++ implementation.
#[cfg(not(feature = "no-recursion-limit"))]
const RECURSION_LIMIT: u32 = 100;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I actually kinda like having this as a constant. I wonder if we could just make a constants module that contains just this one. And we can then have all the deep dive docs on the recursion implementation there and then we just need to link to there from the lib doc page. I feel like that would make it easier to maintain down the line and centralize it a bit.

@caspermeijn caspermeijn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you for coming back to this after two years. I see why this is useful and this is a smart solution.

I am a bit concerned whether this could be a breaking change. I would like to see some prove that it is not.

Comment thread README.md Outdated
Comment thread prost-derive/src/field/mod.rs
Comment thread prost-derive/src/lib.rs Outdated
#(#clear;)*
}

fn recursion_limit() -> u32 {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this a breaking change for messages with a field named recursion_limit?

I think for enum fields, an accessor is created. However, this function is in a trait, so not directly accessible.

I would like to see a test case for that.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I'm not sure I follow: how could this possibly affect a field named recursion_limit? This is implemented on the Message trait, not on the struct itself.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Sorry for another long delay. Well, if a .proto file has a enum field named recursion_limit, it will generate a function named recursion_limit() on the struct. I would like to see a test that proved that the struct function can still be called.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This is tested in tests/src/recursion_limit_field.rs

Comment thread prost/src/encoding.rs Outdated
/// or it can be disabled entirely using the `no-recursion-limit` feature.
#[cfg(not(feature = "no-recursion-limit"))]
recurse_count: u32,
#[doc(hidden)]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why is this marked as doc hidden?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@LucioFranco previously suggested that it should be doc(hidden) because the field was changed to public. I assume a previous version of this PR needed it to be public but it seems like it's no longer needed. I'll revert this field back to private.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes, private is better. My experience is that pub and doc(hidden) makes a sort of secret public API. That will be used by someone.

Comment thread prost/src/message.rs Outdated

@caspermeijn caspermeijn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you for your contribution and patience. The code needs a rebase with master.

Comment thread prost/src/encoding.rs Outdated
/// or it can be disabled entirely using the `no-recursion-limit` feature.
#[cfg(not(feature = "no-recursion-limit"))]
recurse_count: u32,
#[doc(hidden)]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes, private is better. My experience is that pub and doc(hidden) makes a sort of secret public API. That will be used by someone.

Comment thread prost/src/message.rs Outdated
Comment thread src/lib.rs Outdated
// See `encoding::DecodeContext` for more info.
// 100 is the default recursion limit in the C++ implementation.
#[cfg(not(feature = "no-recursion-limit"))]
const RECURSION_LIMIT: u32 = 100;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I like it to be a constant as well. It makes it explicit in the code. But I do like it to be in the documentation as well.

Comment thread prost-derive/src/lib.rs Outdated
.iter()
.any(|a| a.path().is_ident("prost") && a.parse_args::<skip_debug>().is_ok());

let mut recursion_limit: u32 = 100;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think the default should not be named here. If the attribute is not set, then no code should be generated. Which automatically chooses the default implementation of the trait. This makes sure that prost-derive doesn't have to know about the default value,

@seanlinsley

Copy link
Copy Markdown
Author

@caspermeijn all review comments should be addressed

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.

3 participants