Skip to content

Conversation

@gmcgibbon
Copy link
Member

@gmcgibbon gmcgibbon commented Aug 5, 2024

Motivation / Background

Followup #52422

Detail

This Pull Request changes the routing mapper to use keywords instead of hashes in most places, which results in a slight speed boost, and clearer code.

Additional information

Using the benchmark in the initial deprecation PR:

Before:

ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
Warming up --------------------------------------
                draw   113.000 i/100ms
Calculating -------------------------------------
                draw      1.110k (± 3.1%) i/s -      5.650k in   5.095601s

After:

ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
Warming up --------------------------------------
                draw   116.000 i/100ms
Calculating -------------------------------------
                draw      1.174k (± 3.2%) i/s -      5.916k in   5.042676s

It turns out the real speed difference is next to nothing, which is unfortunate. There was a bug in my initial implementation that made it faster 🤦. However, I think now that the mapper is easier to reason about, we should be able to do see optimization potential a little bit easier. I can see two in the scopes and AST parsing that I'll work on next.

Checklist

Before submitting the PR make sure the following are checked:

  • This Pull Request is related to one change. Unrelated changes should be opened in separate PRs.
  • Commit message has a detailed description of what changed and why. If this PR fixes a related issue include it in the commit message. Ex: [Fix #issue-number]
  • Tests are added or updated if you fix a bug or add a feature.
  • CHANGELOG files are updated for the changed libraries if there is a behavior change or additional feature. Minor bug fixes and documentation changes should not be included.

@rails-bot rails-bot bot added the actionpack label Aug 5, 2024
@gmcgibbon gmcgibbon force-pushed the mapper_with_keywords branch from 9ae462c to 29f5205 Compare August 5, 2024 22:12
Copy link
Member

@tenderlove tenderlove left a comment

Choose a reason for hiding this comment

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

Seems good, just a couple questions.

options.delete(app) if app
hash_key_app = true
end
def mount(app, as: nil, via: nil, at: nil, defaults: nil, constraints: nil, anchor: false, format: false, path: nil, internal: nil, **mapping, &block)
Copy link
Member

Choose a reason for hiding this comment

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

what is **mapping? A method call to something?

Copy link
Member Author

@gmcgibbon gmcgibbon Aug 5, 2024

Choose a reason for hiding this comment

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

Mapping is route mapping parameters as a keyword splat like:

# get '/articles/:year/:month/:day', to: 'articles#find_by_id', constraints: {
# year: /\d{4}/,
# month: /\d{1,2}/,
# day: /\d{1,2}/
# }

To be honest, I'm not familiar with this usage outside of constraints. We might want to deprecate this too.

Copy link
Member Author

@gmcgibbon gmcgibbon Aug 5, 2024

Choose a reason for hiding this comment

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

Yeah actually I think this is just a shorthand for constraints:

get "page/:year/:month/:day/:title", to: "page#show", as: "article",
year: /\d+/, month: /\d+/, day: /\d+/

These params get passed down to the route that gets built too, which they might not need to be:

options = ast.wildcard_options.merge!(options)

I'll dig a little deeper and see what's going on with this usage. Thanks!

Copy link
Member Author

Choose a reason for hiding this comment

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

As far as I can tell, strings passed as mapper parameters are treated as route defaults, and regexes are constraints for route parameters. I vote to deprecate this since there's already keywords for this.

Copy link
Member Author

@gmcgibbon gmcgibbon Aug 8, 2024

Choose a reason for hiding this comment

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

I think I can try to deal with mapping parameter deprecations above in a future PR. I need to introduce keywords in resource calls too which could be done as a followup or now.

get "/c/:id", as: :collision, to: "collision#show"
get "/collision", to: "collision#show"
get "/no_collision", to: "collision#show", as: nil
get "/no_collision", to: "collision#show", as: false
Copy link
Member Author

@gmcgibbon gmcgibbon Aug 5, 2024

Choose a reason for hiding this comment

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

This might be a regression. The previous behaviour is get "/no_collision", to: "collision#show", as: nil wouldn't name the route and now it will because keyword defaults are nil.

Copy link
Member Author

@gmcgibbon gmcgibbon Aug 9, 2024

Choose a reason for hiding this comment

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

Fixed in the latest commit. I don't like this usage either IMO so I'd like to deprecate this behaviour too IMO.

@rafaelfranca
Copy link
Member

We can't merge this one right? We can't remove behavior that we just deprecated. We can only merge this after Rails 8.0 is released.

@gmcgibbon
Copy link
Member Author

gmcgibbon commented Aug 6, 2024

We can only merge this after Rails 8.0 is released.

Correct. I might be able to make it work without removing the deprecations so we can get the benefits sooner. It might look weird, so I'll take a look.

@gmcgibbon
Copy link
Member Author

gmcgibbon commented Aug 8, 2024

There, this should now be mergable today. The deprecations added can be removed in the next release to clean up the router.

Main:

ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
Warming up --------------------------------------
                draw   127.000 i/100ms
Calculating -------------------------------------
                draw      1.280k (± 1.9%) i/s -      6.477k in   5.062105s

This branch:

ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23]
Warming up --------------------------------------
                draw   129.000 i/100ms
Calculating -------------------------------------
                draw      1.316k (± 1.9%) i/s -      6.579k in   5.002806s

Same-ish difference with the deprecations re-added and the regression above patched.

@gmcgibbon gmcgibbon force-pushed the mapper_with_keywords branch from be8d9ff to 214022e Compare August 8, 2024 02:53
Converts hashes to keywords in ActionDispatch::Routing::Mapper. This
makes the mapper a little easier to reason about, and slightly faster.
@gmcgibbon gmcgibbon force-pushed the mapper_with_keywords branch from 518478c to 6be2367 Compare August 10, 2024 02:48
@gmcgibbon gmcgibbon merged commit a27076d into rails:main Aug 10, 2024
@gmcgibbon gmcgibbon deleted the mapper_with_keywords branch August 10, 2024 04:55
This was referenced Aug 14, 2024
raise ArgumentError, "Route path not specified" if path.nil?

def match(*path_or_actions, as: DEFAULT, via: nil, to: nil, controller: nil, action: nil, on: nil, defaults: nil, constraints: nil, anchor: nil, format: nil, path: nil, internal: nil, **mapping, &block)
if path_or_actions.count > 1
Copy link
Member

Choose a reason for hiding this comment

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

I don't think the conditional here matches what the previous code was doing and the deprecation inside doesn't seem to match having multiple actions specified (which I'm also not clear on whether that should even be deprecated).

jhawthorn added a commit to jhawthorn/rails that referenced this pull request Aug 21, 2024
…rds"

This reverts commit a27076d, reversing
changes made to e13b251.
DanielaVelasquez pushed a commit to DanielaVelasquez/rails that referenced this pull request Oct 3, 2024
…rds"

This reverts commit a27076d, reversing
changes made to e13b251.
@gmcgibbon gmcgibbon mentioned this pull request Nov 21, 2024
4 tasks
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.

4 participants