Make WordPress Core

Opened 3 months ago

Last modified 10 days ago

#48530 assigned enhancement

Match REST API route by namespace before performing regex checks

Reported by: TimothyBlynJacobs Owned by:
Milestone: 5.4 Priority: normal
Severity: normal Version: 4.4
Component: REST API Keywords: has-patch has-unit-tests
Focuses: performance Cc:
PR Number:


Right now, in order to match a request to a specific route handler, we iterate over every route and perform a regex check.

Whenever a route is registered, you must specify a namespace. If we first checked the namespaces for a match, we'd be able to eliminate whole groups of routes from the regex check.

Attachments (1)

48530.diff (2.7 KB) - added by TimothyBlynJacobs 2 months ago.

Download all attachments as: .zip

Change History (4)

#1 @kadamwhite
3 months ago

  • Milestone changed from Awaiting Review to 5.4

#2 @TimothyBlynJacobs
2 months ago

  • Keywords has-patch has-unit-tests added

Here is a first pass at doing this filtering. I opted to add a filter argument to WP_REST_Server::get_routes() because the transformation process it applies results in losing the namespace registration route argument being lost so we can't easily filter the list after.

Right now, the list is filtered before apply_filters( 'rest_endpoints' ), that feels somewhat better to me since whatever is happening in that filter would be able to work on less items. We could filter the list after the apply_filters( 'rest_endpoints' ) too. I guess that would make sense if we were worried that someone might be caching the results of their filter call?

I did a simple performance test. Please double check my workings, it could be completely bogus :)

Registering 10 namespaces with 15 routes each.

function test_dispatch_performance() {
        $namespaces = 10;
        $routes     = 15;

        for ( $i = 0; $i < $namespaces; $i ++ ) {
                for ( $j = 0; $j < $routes; $j ++ ) {
                        register_rest_route( 'my-namespace-' . $i, '/my-' . $j . '-route/(?P<id>[\d]+)' , array(
                                'methods'  => 'GET',
                                'callback' => '__return_empty_array',
                        ) );

        $start = microtime( true );
        rest_get_server()->dispatch( new WP_REST_Request( 'GET', sprintf( '/my-namespace-%d/my-%d-route/5', 0, 0 ) ) );
        var_dump( microtime( true ) - $start );

        $start = microtime( true );
        rest_get_server()->dispatch( new WP_REST_Request( 'GET', sprintf( '/my-namespace-%d/my-%d-route/5', $namespaces / 2, $routes / 2 ) ) );
        var_dump( microtime( true ) - $start );

        $start = microtime( true );
        rest_get_server()->dispatch( new WP_REST_Request( 'GET', sprintf( '/my-namespace-%d/my-%d-route/5', $namespaces - 1, $routes - 1 ) ) );
        var_dump( microtime( true ) - $start );
// Before patch

// After patch

It looks about 2.5x faster? While the absolute numbers are small, this can be run hundreds of times during a request for a collection response with embedding enabled.

I'd like to try and figure out a real world test that would work. But my initial tests using a bunch of plugins with REST API routes showed way too much variance between API requests to get meaningful data out of it. If anyone has any ideas that'd be awesome.

This ticket was mentioned in Slack in #core-restapi by timothybjacobs. View the logs.

10 days ago

Note: See TracTickets for help on using tickets.