Skip to content

Using 100s of routes with ring async handlers causes StackOverflowError #187

@vincentjames501

Description

@vincentjames501

We have a large ring/compojure application with lots of api routes. We are trying to use ring's async handlers but we eventually run out of stack if a matching path is not found in time.

(let [handler (apply compojure/routes
                    (repeat 3000 (compojure/GET "/foo" [] {:status 200 :body "bar"})))]
  (handler {:uri "/foo2" :request-method :get}
           println 
           println))
=> java.lang.StackOverflowError: 

This is fine because it matches a route soon enough

(let [handler (apply compojure/routes
                    (repeat 3000 (compojure/GET "/foo" [] {:status 200 :body "bar"})))]
  (handler {:uri "/foo" :request-method :get}
           println 
           println))
=> {:status 200, :headers {}, :body bar}
=> nil

The issue is that it will create a new entry in the stack for each handler that doesn't match:

(defn routes
  "Create a Ring handler by combining several handlers into one."
  [& handlers]
  (fn
    ([request]
     (apply routing request handlers))
    ([request respond raise]
     (letfn [(f [handlers]
               (if (seq handlers)
                 (let [handler  (first handlers)
                       respond' #(if % (respond %) (f (rest handlers)))]
                   (handler request respond' raise))
                 (respond nil)))]
       (f handlers)))))

Even if you break it into chunks like so, you still get the same error.

(let [handler (apply compojure/routes
                     (map (fn [_] (apply compojure/routes (repeat 30 (compojure/GET "/foo" [] {:status 200 :body "bar"}))))
                          (range 100)))]
  (handler {:uri "/bar" :request-method :get}
           println 
           println))
=> java.lang.StackOverflowError: 

This can be rewritten using something like a go loop, but it's possible code is blocking and may unknowingly block peoples core async threads.

Any suggestions?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions