HOWTO: Configure the GlassFish jRuby dynamic pool
By Jacob Kessler on Sep 15, 2008
So, now that there is this fancy new dynamic pool at the bottom of Glassfish's JRuby module, I should probably go ahead and explain how to go and make sure that it's doing what you want. It can't be very useful if it doesn't know what you want it to be doing.
The dynamic pool uses three command line arguments to change its behavior:
-Djruby.runtime.min : Sets the fewest runtimes to have in the pool
-Djruby.runtime : Sets the initial number of runtimes to create
-Djruby.runtime.max : Sets the maximum number of runtimes to have active
Since those are sort of long, I'll give examples in the format X/Y/Z, where X is the minimum number of runtimes, Y is the starting number of runtimes, and Z is the maximum number of runtimes. Thus, 1/2/4 would be equivelent to running with -Djruby.runtime.min=1 -Djruby.runtime=2 -Djruby.runtime.max=4. You can also set these values in domain.xml
So first, what that means: If you run with 2/4/8, that means that you will initially create four runtimes, and then the pool is allowed to vary its size between 2 and 8 runtimes. Thus, even if the pool thought that it could handle all of the traffic with only one runtime, it would keep two runtimes in the pool. Similarly, it wouldn't attempt to create a 9th runtime, even if it was having difficulty serving all of the requests with 8 runtimes.
The minimum number of runtimes allows you to maintain the pool at at least a given size, which can help stabilize a server under cyclical load. Also, if you know that you have allocated the space for 4 runtimes, and nothing else will need the memory, you can force the pool to keep 4 runtimes in memory rather than having the pool cut runtimes to free up memory that no other programs will use.
The initial number of runtimes only controls how many runtimes to start out with, it doesn't affect the pool operation after it has been initialized and starts serving requests. A pool started with 1/6/6 and 1/1/6, put under the same load, will end up behaving identically after running for long enough. However, choosing an initial number of runtimes close to where the pool decides to settle will help it settle there faster. The initial number of runtimes also helps set some default values, as I'll explain below.
The maximum number of runtimes is the absolute most number of runtimes that the pool will be willing to create. This does not mean that it will try to keep itself near this number. If you have traffic that can be served by 6 runtimes, and you run with 1/4/1000, the pool will increase itself to 6 runtimes and stay there. However, it does allow you to control the pool's maximum memory usage. For example, if you had a server that the pool wanted to run with 6 runtimes, but you only had the memory available for 4, you could run with 1/4/4 and the pool would limit itself to 4 runtimes and, while you might see less performance than if you were running with 6, you wouldn't get out-of-memory errors, which is always a good thing. It's worth noting that if your server is receiving requests faster than it can process them, CPU-wise, the pool will continue creating runtimes up to runtime.max in an effort to make sure that each incoming request has a runtime so that processing can start, even though creating a new runtime won't allow that processing to go any faster (since the problem is lack of CPU cycles, not lack of runtimes). If you observe this behavior, consider upgrading the server hardware, reducing the complexity of the pages, or making your website less popular.
Now, that's all assuming that you are setting all of those values yourself, in ways that make sense. Happily, if you don't give it reasonable values, the pool is smart enough to try to figure out something that will work on its own. It's only fair to explain how it figures those out, though, so that you aren't sitting there wondering why asking it to run with -1/-3/0 is still working
If you don't specify a number of starting runtimes, or give it a value that doesn't make sense (like anything less than 1), the pool will start with 1 runtime in it. I've listed this one first since it's involved in setting some of the other defaults.
If you don't specify a maximum number of runtimes, or give it a hard maximum that is less than the initial number of runtimes, it will start with the larger of the initial number of runtimes or two. Thus, starting the pool with 1/1/, 1/2/1, and 1/1/-1 would all result in the same maximum number of runtimes: 2. Starting with 1/4/12, however, would set your maximum to 12. Be careful with high maximums, as the JRuby implementation tends to use a lot of permgen space (see here for a bit more on that), so you may need to increase your permgen space with many runtimes or complicated jRuby apps.
If you don't specify a minimum number of runtimes, your minimum will be one. However, if you give a possible minimum number that is more than the initial number of runtimes, your minimum number will be set to your initial runtimes. Since the maximum is at least the initial number, and the minimum is at most the initial number, the minimum will always be less than or equal to the maximum. This makes mathematicians happy. Starting a pool with 0/2/4, 10/1/4, or /1/2 would all result in the same minimum number of runtimes: 1. Starting a pool with 10/4/6 would result in a minimum of 4 runtimes.
Also, if you're feeling really brave, the pool logs all of its thoughts at log level 4, so if you want to watch what it is doing, you can turn your log level up and watch what it is doing. Every borrow and return option, the pool will announce the currently active runtimes, how many runtimes it is willing to create, how many of the active runtimes are currently in use vs. how many of them are idle, and what it thought about that operation (for example: did it vote for the creation of a new object because it took too long for a runtime to become available?)
Hopefully this helps people configure the new features to get the most out of the dynamic pool.