I have a Java web app (WAR deployed to Tomcat) that keeps a cache (Map<Long,Widget>) in memory. I have a Postgres database that contains a widgets table:
widget_id | widget_name | widget_value
(INT) (VARCHAR 50) (INT)
To O/R map between Widget POJOs and widgets table records, I am using MyBatis. I would like to implement a solution whereby the Java cache (the Map) is updated in real-time whenever a value in the widgets table changes. I could have a polling component that checks the table every, say, 30 seconds, but polling just doesn’t feel like the right solution here. So here’s what I’m proposing:
- Write a Postgres trigger that calls a stored procedure (
run_cache_updater()) - The procedure in turns runs a shell script (
run_cache_updater.sh) - The script base-64 encodes the changed
widgetsrecord and then cURLs the encoded record to an HTTP URL - The Java WAR has a servlet listening on the cURLed URL and handles any
HttpServletRequestssent to it. It base-64 decodes the record and somehow transforms it into aWidgetPOJO. - The cache (
Map<Long,Widget>) is updated with the correct key/value.
This solution feels awkward, and so I am first wondering how any Java/Postgres gurus out there would handle such a situation. Is polling the better/simpler choice here (am I just being stubborn?) Is there another/better/more standard solution I am overlooking?
If not, and this solution is the standard way of pushing changed records from Postgres to the application layer, then I’m choking on how to write the trigger, stored procedure, and shell script so that the entire widgets record gets passed into the cURL statement. Thanks in advance for any help here.
I can’t speak to MyBatis, but I can tell you that PostgreSQL has a publish/subscribe system baked in, which would let you do this with much less hackery.
First, set up a trigger on
widgetsthat runs on every insert, update, and delete operation. Have it extract the primary key andNOTIFYwidgets_changed, id. (Well, from PL/pgSQL, you’d probably wantPERFORM pg_notify(...).) PostgreSQL will broadcast your notification if and when that transaction commits, making both the notification and the corresponding data changes visible to other connections.In the client, you’d want to run a thread dedicated to keeping this map up-to-date. It would connect to PostgreSQL,
LISTENwidgets_changedto start queueing notifications,SELECT * FROM widgetsto populate the map, and wait for notifications to arrive. (Checking for notifications apparently involves polling the JDBC driver, which sucks, but not as bad as you might think. See PgNotificationPoller for a concrete implementation.) Once you see a notification, look up the indicated record and update your map. Note that it’s important toLISTENbefore the initialSELECT *, since records could be changed betweenSELECT *andLISTEN.This approach doesn’t require PostgreSQL to know anything about your application. All it has to do is send notifications; your application does the rest. There’s no shell scripts, no HTTP, and no callbacks, letting you reconfigure/redeploy your application without also having to reconfigure the database. It’s just a database, and it can be backed up, restored, replicated, etc. with no extra complications. Similarly, your application has no extra complexities: all it needs is a connection to PostgreSQL, which you already have.