I am using ExecutorService with a fixed thread pool of 50, and a fixed database connection pool of 50, using HikariCP. Each worker thread processes a packet (a "report"), checks if it is valid (where each report must have a unique unit_id, time, latitude and longitude), grabs a db connection from the connection pool, and then inserts the report into the reports table. The uniqueness constraint is created with postgresql and dubbed "reports_uniqueness_index". When I have high volume, I get a ton of the following error:
org.postgresql.util.PSQLException: ERROR: duplicate key value
violates unique constraint "reports_uniqueness_index"
Here's what I believe the problem is. Before database insertion, I perform a check to determine whether a report already exists in the table with the same unit_id, time, latitude and longitude. If it doesn't then the report is valid and I perform the insertion. However, I think because I am using concurrency I have 50 threads each checking at the same moment if the report is valid and since none of them have been inserted yet, each thread thinks it has a valid report and when it goes to insert them at same moment, that is when postgresql raises the error.
I would like a solution that doesn't create any latency with the concurrency. I been trying to avoid using synchronized statement or a reentrant lock because the database insertions need to occur as quickly as possible. This is the insertion right here:
private boolean save(){
Connection conn=null;
Statement stmt=null;
int status=0;
DbConnectionPool dbPool = DbConnectionPool.getInstance();
String sql = = "INSERT INTO reports"
sql += " (unit_id, time, time_secs, latitude, longitude, speed, created_at)";
sql += " values (...)";
try {
conn = dbPool.getConnection();
stmt = conn.createStatement();
status = stmt.executeUpdate(sql);
} catch (SQLException e) {
return false;
} finally {
try {
if (stmt != null)
{
stmt.close();
}
if (conn != null)
{
conn.close();
}
} catch(SQLException e){}
}
if(status > 0){
return true;
}
return false;
}
One solution I thought of was using the Class object itself as a lock object:
synchronized(Report.class) {
status = stmt.executeUpdate(sql);
}
But this will delay insertsion for other threads. Is there a better solution?