core-async

p-himik 2021-05-30T20:47:32.009200Z

I probably have some fundamental misunderstanding of how go is supposed to work. There's an underlying fixed thread pool of size 8 by default. I'm trying to execute thousands of AWS commands using aws-api's async interface. And I end up seeing a bunch of

[warning][os,thread] Failed to start thread - pthread_create failed (EAGAIN) for attributes: stacksize: 512k, guardsize: 0k, detached
followed by a bunch of
WARN  org.eclipse.jetty.util.thread.strategy.EatWhatYouKill [qtp523638263-108] - 
java.lang.OutOfMemoryError: unable to create native thread: possibly out of memory or process/resource limits reached
	at java.base/java.lang.Thread.start0(Native Method)
	at java.base/java.lang.Thread.start(Thread.java:798)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.startThread(QueuedThreadPool.java:660)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.execute(QueuedThreadPool.java:547)
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.execute(EatWhatYouKill.java:373)
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:308)
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:171)
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:129)
	at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:375)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:773)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:905)
	at java.base/java.lang.Thread.run(Thread.java:829)
I could find only https://ask.clojure.org/index.php/9196/core-async-thread-pool-vs-willy-nilly-java-threads but it doesn't really give any details. How can the above error be explained? Is it possible to prevent it while still using aws-api's async interface?

phronmophobic 2021-05-30T21:09:44.009400Z

do you have a sense of which library is potentially creating too many threads?

phronmophobic 2021-05-30T21:10:47.009600Z

if you can reproduce the issue locally, I've used jvisualvm for simple jvm inspection

phronmophobic 2021-05-30T21:12:41.009800Z

otherwise, it's possible to get a list of all threads with https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#getAllStackTraces(), which you could periodically poll to try and see where the threads are coming from

phronmophobic 2021-05-30T21:13:36.010Z

from the stack trace, it looks like jetty might be the culprit. Are you spawning aws requests from a webserver or similar?

2021-05-30T21:59:40.010200Z

aws-api uses the jetty http client

phronmophobic 2021-05-30T22:05:15.010400Z

Ah, in that case, I would consider the following options: • increase the OS thread limit, https://stackoverflow.com/questions/344203/maximum-number-of-threads-per-process-in-linux • rate limit in flight aws requests • the aws sdk for java docs say you can provide a custom ExecutorService, https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/basics-async.html • make sure you don't have long running blocking code executed on any thread pool threads

p-himik 2021-05-30T23:11:03.010900Z

@smith.adriane aws-api != AWS SDK. It's the Cognitect library that uses core.async. But my question is not really about aws-api at its root, but rather about core.async. So I have this code:

(dotimes [_ 1000000]
    (a/go (a/<! (a/timeout 1000000))))
that I run in a REPL with these Java parameters:
-XX:ThreadStackSize=1048576 -Xmx512m
and it throws a bunch of errors such as this one and never completes:
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "async-dispatch-7"
I did a heap dump and it seems that pretty much all of the space is taken by tasks that are in the queue. So the rate limiting is there, implemented by the fixed thread pool of core.async. But there are so many new tasks where each task consumes a noticeable amount of space, that that pool's queue ends up consuming all the memory. I guess the only way for me to proceed here is to build a rate limiter for queuing async tasks, because the original data doesn't take that much space.

phronmophobic 2021-05-30T23:18:55.011100Z

Adding back pressure seems like a good answer. It's not clear what environment you're running in, but I can think of at least two options that might alleviate the memory pressure: • increasing the memory limit and add a swapfile. • If tasks are too big, you could store the task descriptions on disk and just enqueue the filename that stores the task info

p-himik 2021-05-30T23:22:25.011400Z

Task descriptions are small - it's just a list of strings. It's the tasks themselves, I think, that are big. So basically for each string I create an a/go block via aws-api, which schedules a significantly sized task for the executor. I'll try to batch the inputs before they get sent to aws-api.

👍 1