Eventlet is a concurrent networking library for python using coroutines, which means that code using Eventlet appears to be single-threaded and blocking, but is actually using Cooperative Scheduling and Asynchronous I/O.
Eventlet has a database connection pool (db_pool) which I have seen get battle-tested in an extremely demanding real-time production environment at Linden Lab. So I decided hey, let's port that to Java! Except I haven't used Java in about a decade. And the programming model is completely different from Eventlet. Yet this code has the same functionality as db_pool!
- Checkout the repository, cd into it
- mvn compile
- mvn test
- mvn package
- mvn freebird
If you don't already have Apache Maven set up, you are probably a well-adjusted person. To fix that, read the slightly hidden installation instructions and then run the above commands. (mvn freebird doesn't do anything useful, but wouldn't it be cool if it did?)
The db_pool-alike connection pool class implements this interface:
public interface ConnectionPool {
java.sql.Connection getConnection() throws java.sql.SQLException;
void releaseConnection(java.sql.Connection con) throws java.sql.SQLException;
}
This is only overkill in the absence of healthy fear and respect for databases. If you've ever been paged in the middle of the night to help the ops team diagnose a strange data persistence issue that cropped up after a code push, ever watched a company lose a measureable amount of customers (and requisite income) from a "simple bug", then you know how much attention needs to be paid to infrastructure code.
By using an existing and well-tested production-ready codebase as inspiration, the value of the knowledge in creating that system is kept. By focusing on real issues (like prefetching in a non-blocking manner, having connection wrappers to encourage normal database coding idioms with close(), noticing potential pitfalls in using the ham-fisted "synchronized" monitor system, etc) even an example connection pool is worth the time as an educational refresher.
Yes, if that's how you look at learning and avoiding unnecessary work. There're a few fronts to my theftery:
- I took & reworked the unit tests from 3martini's submission
- I "translated" the unit tests from eventlet's db_pool_test
- I kinda copied the way eventlet's pools and db_pool work
However, I had to hack the hell out of each of these. They're all recognizable, but they're also all very different from where they started. The 3martini tests are probably the closest because they started from the same project.
The first thing I did when I saw the submission is look at the forks on github. There's no honor in re-deriving trigonometry, and there's no shame in getting better ideas from reading. Besides, I would feel like an idiot if I didn't look. In this case, I looked at all the forks then looked at the user profiles of the people who created them, then found out who got hired at OPOWER.
After looking at all of that code, I decided that there needed to be a beefier solution. I instantly thought of eventlet (which I have used more recently than at Linden) and decided to copy that functionality. What a lesson in java and python differences that was!
I tried. You can tell by reading them side by side there's an attempt to stay in sync, but there are a large number of fundamental differences, the least of which is that eventlet is not preemptable. That means the synchronization issues aren't very big there. The channel system just isn't there. Further, things that are very simple in eventlet (like seeing how many callers are waiting for put() on a queue) are mind-bogglingly difficult in Java. (I basically had to make a cheap hack on ThreadMXBean to get the same thing.)
I know python decently well right now, but not much Java. What I know now is a result of this project. The last time I wrote Java in any seriousness was several years ago, and I have never written any that got into production (as far as I know). This past week I went from not really knowing what all the keywords were (volatile?) or remembering the syntax (class... uh, inherits? embodies?) to using ScheduledExecutorService to set a ScheduledFuture to expire old connections in a LinkedBlockingDeque. Hopefully the code in this project is adequate.
Go back in the checkin history to the first version to see the simplest working version. Everything from there is frosting on the cake. (There's a lot of frosting!)
- Basic core version
- ...plus Deque lock wait monitoring
- ...plus connection wrapping / close() overriding
- ...plus connection expiration and expiration check scheduling
Without these important, but tangential, things, the core is about 200 lines of code.
I have it on good authority that they are the best sandwich shop in the tri-county area.
If you have to ask, please don't read my twitter feed.