taoensso / carmine

Redis client + message queue for Clojure
https://www.taoensso.com/carmine
Eclipse Public License 1.0
1.15k stars 131 forks source link

Is it possible for Carmine to cause RejectedExecutionException? #137

Closed sugarsunflower closed 8 years ago

sugarsunflower commented 8 years ago

Could Carmine cause RejectedExecutionException?

I have a fairly simple app, but it does a lot of wcar and hset in Redis.

I think perhaps I am using wcar incorrectly.

I wasn't sure what to do with "pool" so I left it blank:

redis-connection {:pool {} :spec {:host "127.0.0.1" :port 6379 }}

First I add a document to Redis:

(carmine/wcar redis-connection (carmine/hset id "lecture" lecture))

Then I go into a loop and wait for it to be processed:

(loop [processed (carmine/wcar redis-connection (carmine/hget id "processed"))]
  (if (nil? processed)
    (recur (carmine/wcar redis-connection (carmine/hget id "processed")))
    (let [final-results (carmine/wcar redis-connection (carmine/hgetall id))]
      (carmine/wcar redis-connection (carmine/del id))
      final-results)))))

My co-worker and I agreed that when his app was done processing the document, he would write a key named "processed" and give it a value of true.

Perhaps by calling wcar inside of a loop I am exhausting some underlying pool?

When I test the app locally on my machine I erratically get RejectedExecutionException, but sometimes the app runs fine for awhile. However, when my co-worker tries to test my app locally on his machine, the app dies instantly with RejectedExecutionException.

[UPDATE]

Reading through the older issues, I see this has a suggestion that might work for me:

https://github.com/ptaoussanis/carmine/issues/104

But before I re-write my code (and ask my co-worker to re-write his) I would love someone's opinion about whether Carmine (and the underlying thread pool) was a likely cause of the RejectedExecutionException.

ptaoussanis commented 8 years ago

Hi there,

Could Carmine cause RejectedExecutionException?

Indirectly, if you're exhausting resources like your thread pool.

I wasn't sure what to do with "pool" so I left it blank:

That should be fine, that just gives you the default pool options which are reasonable.

Then I go into a loop and wait for it to be processed

Ahh, this could be your problem - your loop is quite aggressive and might be overtaxing your pool's ability to recover. Try adding a polling delay to the nil? branch:

(if (nil? processed)
  (do (Thread/sleep 10) ; Wait 10 msecs (could be much longer, depends on your requirements)
        (recur (carmine/wcar redis-connection (carmine/hget id "processed"))))
  ; else branch
)

Also make sure you're not creating too many of these loops.

Reading through the older issues, I see this has a suggestion that might work for me

Yes, that'd be another option. There's pros and cons to each. Polling (as in your snippet) is a simple quick hack but requires you to choose a polling interval that'll determine the minimum average latency.

Using a blocking read op may be better in production or at scale since it avoids unnecessary polling calls and can achieve lower minimum average latency.

Depends on your requirements, but the first option is just a 1 line change and should work with a reasonable sleep val.

Does that help?

sugarsunflower commented 8 years ago

You have helped me a great deal. Thank you.

ptaoussanis commented 8 years ago

No problem, good luck! :-)