Skip to content

2.2.x Rack::Lint severe performance regression #1625

@ziggythehamster

Description

@ziggythehamster

In version 2.2.x, Rack::Lint does this check in _call:

      assert("response #{ary.inspect} is not an Array , but #{ary.class}") {
        ary.kind_of? Array
      }
      assert("response array #{ary.inspect} has #{ary.size} elements instead of 3") {
        ary.size == 3
      }

Even if the assertion is successful, the result of @app.call(env) is inspected, allocated, and dropped into a string that will never be used. Twice. For every middleware being linted. @app.call(env) returns an array of status (no big deal), headers (this is rather large once you've got stuff like Devise loaded), and body (hundreds to thousands of kilobytes!). So, Rack is allocating RAM for the response body at least three times, but probably dozens of times.

This resulted in this performance regression of our large production application (purple is "Middleware"):

image

I ran a transaction trace and found that each of the ~15 middlewares eats up about 1.2% of the entire request time in #inspect, for a total of 16%.

We've downgraded to 2.1.2 for the time being. I'm not sure if you actually need to inspect the array, but if you do, I'd suggest only doing it in development.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions