I am caching prepared statement so that I don't have to prepare it again while working with datastax java driver (cassandra).. Below is my code and it works:
private static final ConcurrentHashMap<String, PreparedStatement> cache = new ConcurrentHashMap<>();
public ResultSetFuture send(final String cql, final Object... values) {
return executeWithSession(new SessionCallable<ResultSetFuture>() {
#Override
public ResultSetFuture executeWithSession(Session session) {
BoundStatement bs = getStatement(cql, values);
bs.setConsistencyLevel(consistencyLevel);
return session.executeAsync(bs);
}
});
}
private BoundStatement getStatement(final String cql, final Object... values) {
Session session = getSession();
PreparedStatement ps = cache.get(cql);
// no statement cached, create one and cache it now.
// below line is causing thread safety issue..
if (ps == null) {
ps = session.prepare(cql);
PreparedStatement old = cache.putIfAbsent(cql, ps);
if (old != null)
ps = old;
}
return ps.bind(values);
}
But problem is send method will be called by multiple threads so I suspect my getStatement method is not thread safe because of if (ps == null) check.. How can I make it thread safe?
I wanted to avoid using synchronize keyword so wanted to see if there is any better way. I am working with Java 7 as of now.
Related
I have a webapp where I need to retrieve data from DB via JDBC and display it front end. Its a static data and it wont be changed. So I just need the data to be retrieved just once and store in a static variable and so I can use that data every time instead of querying the database. Below is the sample code:
public class SampleClass(){
static Map<String, BigDecimal> productMap = null;
public Map<String, BigDecimal> getDatafromDB(Connection conn, PreparedStatement stmt, ResultSet rs) throws SQLException{
if(productMap == null){
productMap = getProductID(conn, stmt, rs);
}
return productMap;
}
public Map<String, BigDecimal> getProductID(Connection conn, PreparedStatement stmt, ResultSet rs) throws SQLException {
logger.debug("retrieving product ID's from product table..");
Map<String, BigDecimal> productMap = new HashMap<String, BigDecimal>();
stmt = conn.prepareStatement("Select * from PRODUCTTABLE");
rs = stmt.executeQuery();
logger.debug("Product ID's retrieved");
while(rs.next()){
productMap.put(rs.getString("PRODUCT_NAME"),rs.getBigDecimal("PRODUCT_ID"));
}
if (stmt != null) {
stmt.close();
}
if (rs != null) {
rs.close();
}
return productMap;
}
}
I am calling this method from UI through a http servlet request. For the first time when I run it, since the map is null it queries the data base and gets the data and sends it to UI. When I hit the servlet again for the second time with the new request the product map is null and again its querying the database and getting the data. Since its a static variable it should be only initialized once rite so why is it still null for the second request. Can someone please correct my code ?
Thanks in advance
The root cause of your problem is that a servlet is memoryless. So a static variable will not help. What you need is a session attribute instead.
I suggest adding your variable as a session attribute. In the first place you need to create an encapsulating class that implements Serializable:
public class SampleResult implements Serializable {
private Map<String, String> productMap;
public void setProductMap(Map<String, String> productMap) {
this.productMap = productMap;
}
public Map<String, String> getProductMap() {
return productMap;
}
}
Now in your servlet:
HttpSesssion session = request.getSession();
Map<String, String> productMap;
SampleResult result;
if (session.getAttribute("productMap") == null) {
productMap = retrievedatafromDB();
result = new SampleResult();
sampleResult.setProductMap(productMap);
session.setAttribute("productMap", result);// session attribute created
} else {
result = session.getAttribute("productMap");// retrieve session attribute
productMap = result.getProductMap();
}
I am using datastax java driver 3.1.0 to connect to cassandra cluster and my cassandra cluster version is 2.0.10. I am writing asynchronously with QUORUM consistency.
public void save(final String process, final int clientid, final long deviceid) {
String sql = "insert into storage (process, clientid, deviceid) values (?, ?, ?)";
try {
BoundStatement bs = CacheStatement.getInstance().getStatement(sql);
bs.setConsistencyLevel(ConsistencyLevel.QUORUM);
bs.setString(0, process);
bs.setInt(1, clientid);
bs.setLong(2, deviceid);
ResultSetFuture future = session.executeAsync(bs);
Futures.addCallback(future, new FutureCallback<ResultSet>() {
#Override
public void onSuccess(ResultSet result) {
logger.logInfo("successfully written");
}
#Override
public void onFailure(Throwable t) {
logger.logError("error= ", t);
}
}, Executors.newFixedThreadPool(10));
} catch (Exception ex) {
logger.logError("error= ", ex);
}
}
And below is my CacheStatement class:
public class CacheStatement {
private static final Map<String, PreparedStatement> cache =
new ConcurrentHashMap<>();
private static class Holder {
private static final CacheStatement INSTANCE = new CacheStatement();
}
public static CacheStatement getInstance() {
return Holder.INSTANCE;
}
private CacheStatement() {}
public BoundStatement getStatement(String cql) {
Session session = CassUtils.getInstance().getSession();
PreparedStatement ps = cache.get(cql);
// no statement cached, create one and cache it now.
if (ps == null) {
synchronized (this) {
ps = cache.get(cql);
if (ps == null) {
cache.put(cql, session.prepare(cql));
}
}
}
return ps.bind();
}
}
My above save method will be called from multiple threads and I think BoundStatement is not thread safe. Btw StatementCache class is thread safe as shown above.
Since BoundStatement is not thread safe. Will there be any problem in my above code if I write asynchronously from multiple threads?
And secondly, I am using Executors.newFixedThreadPool(10) in the addCallback parameter. Is this ok or there will be any problem? Or should I use MoreExecutors.directExecutor. What is the difference between these two then? And what is the best way for this?
Below is my connection setting to connect to cassandra using datastax java driver:
Builder builder = Cluster.builder();
cluster =
builder
.addContactPoints(servers.toArray(new String[servers.size()]))
.withRetryPolicy(new LoggingRetryPolicy(DowngradingConsistencyRetryPolicy.INSTANCE))
.withPoolingOptions(poolingOptions)
.withReconnectionPolicy(new ConstantReconnectionPolicy(100L))
.withLoadBalancingPolicy(
DCAwareRoundRobinPolicy
.builder()
.withLocalDc(
!TestUtils.isProd() ? "DC2" : TestUtils.getCurrentLocation()
.get().name().toLowerCase()).withUsedHostsPerRemoteDc(3).build())
.withCredentials(username, password).build();
I think what you're doing is fine. You could optimize a bit further by preparing all the statements at application startup, so you have everything already cached, so you don't get any performance hit for preparing statement when "saving", and you don't lock anything in your workflow.
BoundStatement is not threadsafe, but PreparedStatement yes, and you are returning a new BoundStatement every time you call your getStatement. Indeed, the .bind() function of the PreparedStatement is actually a shortcut for new BoundStatement(ps).bind(). And you are not accessing the same BoundStatement from multiple thread. So your code is fine.
For thread pool, instead, you are actually creating a new thread pool on each addCallback function. This is a waste of resources. I don't use this callback method and I prefer managing plain FutureResultSet by myself, but I saw examples on datastax documentation that use MoreExecutors.sameThreadExecutor() instead of MoreExecutors.directExecutor().
Recently I was looking at the frequent hangs of the tomcat server and came across some exceptions thrown frequently in some part of the code.
When I examined the code, this what it looked like
public static String doSomething() {
String returnVakue = "default value";
try {
ResultSet rs = getMyResultSet();
rs.first();
returnValue = rs.getString("my_field"); // Note that Exception happens at times when the ResultSet is empty
} catch (Exception e) {
throw new Exception(e);
} finally {
return returnValue;
}
}
While I am aware that it is perfectly OK to have both throws exception and return, wondering if this can cause any kind of leaks in tomcat. And Is there any potential risk with performance. ? Hoowever my caller function stops the execution at this point. Any views on this? Does it affect the GC?
EDIT : NOTE : I Know how to correct this code. Please share your views whether this can potentially cause tomcat hanging.
First check if returned ResultSet is empty.
while( rs.next() ) {
// ResultSet processing here
// Result set is not empty
}
In my opinion throwing exception is your decision, but in finally you should be doing clean up e.g. closing Connections.
Open Connections if not closed will cause tomcat to hang because new requests coming to server will be waiting for connections to become available.
Any object in Java which is referenced is not garabage collected, in your case if Connections are not closing then these objects will not be garbage collected.
Cheers !!
If a query takes a long time, not a question of JDBC. The database is responsible. Of course, if JDBC is used properly. In another hand, if you use simple JDBC, it is best that you add a layer DAO in your application.
public class UserDAO extends DAO {
private static final String FIND_BY_PK_SQL =
"SELECT mail, password " +
" FROM user WHERE mail = ?";
public User findByPk(final String mail) throws DAOException {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = getConnection();
ps = conn.prepareStatement(FIND_BY_PK_SQL);
ps.setString(1, mail);
rs = ps.executeQuery();
if (rs.next()) {
return fill(rs);
}
return null;
} catch (final SQLException e) {
throw new DAOException(e);
} finally {
DbUtils.closeQuietly(conn, ps, rs);
}
}
private User fill(final ResultSet rs) throws SQLException {
final User user = new User();
user.setMail(rs.getString("mail"));
user.setPassword(rs.getString("password"));
return user;
}
}
I feel as though I haven't got my design right here and that I'm stuck between two techniques. I'm attempting to write a class which hands out connections to a database. The code is below:
public final class DBUtil {
private static String databaseDriver = null;
private static String databaseConnectionString = null;
private static String databaseUser = null;
private static String databasePassword = null;
private static String serverName = null;
private static ComboPooledDataSource dataSource = null;
private DBUtil() {
throw new AssertionError();
}
private static void getParameters() {
final Properties configFile = new Properties();
try {
configFile.load(DBUtil.class.getClassLoader().getResourceAsStream("my.properties"));
if (configFile.containsKey("databaseConnectionString") && configFile.containsKey("databaseUser") && configFile.containsKey("databasePassword") && configFile.containsKey("databaseDriver")) {
DBUtil.databaseConnectionString = configFile.getProperty("databaseConnectionString");
DBUtil.databaseDriver = configFile.getProperty("databaseDriver");
DBUtil.databaseUser = configFile.getProperty("databaseUser");
DBUtil.databasePassword = configFile.getProperty("databasePassword");
}
else {
// Properties file not configured correctly for database connection
}
}
catch (IOException e) {}
}
public static Connection getDatabaseConnection() {
if (Strings.isNullOrEmpty(databaseConnectionString) || Strings.isNullOrEmpty(databaseUser) || Strings.isNullOrEmpty(databasePassword) || Strings.isNullOrEmpty(databaseDriver)) {
DBUtil.getParameters();
}
dataSource = getDataSource();
int retryCount = 0;
Connection connection = null;
while (connection == null) {
try {
connection = dataSource.getConnection();
}
catch (SQLException sqle) {}
}
return connection;
}
private static ComboPooledDataSource getDataSource() {
if (dataSource == null) {
dataSource = new ComboPooledDataSource();
try {
dataSource.setDriverClass(databaseDriver);
dataSource.setJdbcUrl(databaseConnectionString);
dataSource.setUser(databaseUser);
dataSource.setPassword(databasePassword);
}
catch (PropertyVetoException pve) {}
}
return dataSource;
}
public static void cleanUpDataSource() {
try {
DataSources.destroy(dataSource);
}
catch (SQLException sqle) {}
}
}
FindBugs is returing Incorrect lazy initialization and update of static field when I do:
if (dataSource == null) {
dataSource = new ComboPooledDataSource();
...
Any advice greatly appreciated. I feel as though I'm stuck here somewhere between the Singleton pattern and a class which consists of simply a set of static methods.
More generally, is this a good way to hand out database connections to DAOs?
Many thanks.
That method should be synchronized in order to avoid two parallel executions (two threads calling it at the same time).
Added
Not synchronized exception could lead to two threads executing this:
T1 if (datasource == null) YES
T2 if (datasource == null) YES
T1 datasource = new Datasource...
T2 datasource = new Datasource(...); AGAIN!
and T1 and T2 calling methods on one of two datasources (depending when T2 overrides the T1 object creation).
Volatile
As #Jean-Marc proposes you should declare datasource field as volatile. That keyword ensures that thread don't use a thread-local copy of the variable (that would potentially cause problems if a thread reads an outdated cached value).
I'm not sure if this case happens between different method invocations, or if syncrhonized deals with it but it's better to be sure :)
Findbugs is complaining because your getDataSource method is not synchronized. With your current code it is possible for two concurrent threads to call getDataSource and retrieve to separate DataSource objects.
The problem with that code is that in the presence of multitheading you could end up creating multiple instances of ComboPooledDataSource, and having dataSource point to different instances at different times.
This could happen if the method were to be called by several threads at about the same time.
Let's say dataSource is null, and the execution of two threads is interleaved as follows:
Thread 1: if (dataSource == null) { // condition is true
Thread 2: if (dataSource == null) { // condition is true
Thread 1: dataSource = new ComboPooledDataSource();
Thread 2: dataSource = new ComboPooledDataSource();
An easy way to fix the concurrency issue is by adding synchronization.
This is why FindBugs is complaining:
http://findbugs.sourceforge.net/bugDescriptions.html#LI_LAZY_INIT_UPDATE_STATIC
As mentioned, if you don't have multiple threads you shouldn't have a problem.
I am dealing with a legacy code where there the connection object in the singleton dao class is a member variable and is prone to race-conditions.
I know this is a potential design issue however I am interested in knowing about the different types of problems that could be thought of when dealing with the jdbc connection object in java.
Following is the EventLoggerDAO class code:
package com.code.ref.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import com.code.ref.utils.common.DBUtil;
import com.code.ref.utils.common.PCMLLogger;
public class EventLoggerDAO {
private static EventLoggerDAO staticobj_EventLoggerDAO;
private Connection obj_ClsConnection;
private PreparedStatement obj_ClsPreparedStmt;
private EventLoggerDAO() {
try {
obj_ClsConnection = DBUtil.getConnection();
} catch (Exception e) {
PCMLLogger.logMessage(EventLoggerDAO.class, "EventLoggerDAO()", "Some problem in creating db connection:" + e);
}
}
public static synchronized EventLoggerDAO getInstance() {
if (staticobj_EventLoggerDAO == null) {
synchronized (EventLoggerDAO.class) {
if (staticobj_EventLoggerDAO == null)
staticobj_EventLoggerDAO = new EventLoggerDAO();
}
}
return staticobj_EventLoggerDAO;
}
public void addEvent(String sName, String sType, String sAction, String sModifiedBy) throws Exception {
StringBuffer sbQuery = new StringBuffer();
sbQuery.append("INSERT INTO TM_EVENT_LOG (NAME, TYPE, ACTION, MODIFIED_BY) ").append("VALUES (?, ?, ?, ?) ");
if(obj_ClsConnection == null)
obj_ClsConnection = DBUtil.getConnection();
obj_ClsPreparedStmt = obj_ClsConnection.prepareStatement(sbQuery.toString());
obj_ClsPreparedStmt.setString(1, sName);
obj_ClsPreparedStmt.setString(2, sType);
obj_ClsPreparedStmt.setString(3, sAction);
obj_ClsPreparedStmt.setString(4, sModifiedBy);
obj_ClsPreparedStmt.executeUpdate();
if (obj_ClsPreparedStmt != null) {
obj_ClsPreparedStmt.close();
obj_ClsPreparedStmt = null;
}
}
}
Problem observed:
Sometimes it happens that the table TM_EVENT_LOG stops inserting and there is not even exception in the server logs.
I suspect that during race conditions the connection objects held by different threads might be leading to inconsistent state and might also not be commiting the data. The connection is derived through a websphere datasource maintaining a connection pool.
Any thoughts or ideas why this might be happening?
Everything can happen here. Note that obj_ClsPreparedStmt is a member variable whereas it's used as a local variable - it seems to be a much more serious problem than shared Connection.