Hi Craig, See comments inline below...
At 11:23 AM 10/28/2002 -0800, Craig R. McClanahan wrote:
On Mon, 28 Oct 2002, Jacob Kjome wrote:You know what, I just re-read what I stated above and that is not quite correct. I do have a Manager class which the other manager classes extend, however Manager itself does not extend anything. It just uses the ConnectionManager as a static singleton. Manager has the following methods which the other manager classes use to get and close their connections.
> Date: Mon, 28 Oct 2002 13:04:18 -0600
> From: Jacob Kjome <[EMAIL PROTECTED]>
> Reply-To: Tomcat Users List <[EMAIL PROTECTED]>,
> Jacob Kjome <[EMAIL PROTECTED]>
> To: Tomcat Users List <[EMAIL PROTECTED]>
> Subject: Re[2]: DBCP speed of lookup -vs- stored reference to Datasource?
>
> Hello Craig,
>
> seem comments inline below....
>
> Monday, October 28, 2002, 12:14:05 PM, you wrote:
> CRM> On Mon, 28 Oct 2002, Jacob Kjome wrote:
>
> >> Date: Mon, 28 Oct 2002 08:08:09 -0600
> >> From: Jacob Kjome <[EMAIL PROTECTED]>
> >> Reply-To: Tomcat Users List <[EMAIL PROTECTED]>
> >> To: Tomcat Users List <[EMAIL PROTECTED]>
> >> Subject: DBCP speed of lookup -vs- stored reference to Datasource?
> >>
> >>
> >> I'm wondering what kind of performance penalty there is, if any, when doing
> >> a full lookup of the Datasource object each and every time through JNDI
> >> calls? Basically, does it make sense to do one lookup and store a local
> >> copy for future use? The one problem I see with doing that is that if one
> >> ever wants to use the Tomcat Manager app to modify configuration on the
> >> fly, using the local copy of the Datasource would make it so you never
> >> really realize that a change to configuration has been made. Am I simply
> >> losing flexibility without gaining any performance?
>
> CRM> The JNDI lookup, after some manipulations of the name, turns into a
> CRM> HashMap lookup inside the server. It's not particularly expensive -- and
> CRM> isn't even worth looking at (from a performance perspective) unless you've
> CRM> already tuned all your database queries for maximum performance. That's
> CRM> where the vast majority of problems occur.
>
> CRM> Performance aside, there are two functionality reasons to do the lookup
> CRM> every time:
>
> CRM> * Keeping a reference to the data source yourself means that
> CRM> you need to either pass it on to every method, or make it
> CRM> available some other way. The lookup code requires zero references
> CRM> to things like the ServletContext or any static variables.
>
> In my setup, I have data object and a single manager class for each
> data object where the queries are actually done. Each manager class
> extends Manager which, itself, extends ConnectionManager which
> contains the getConnection() and returnConnection(Connection) methods
> so holding a static variable in the ConnectionManager isn't all that
> big of a deal. I don't have to pass the DataSource around at all. I
> just ask for a connection and the stored DataSource returns an
> available connection object.
>
But you have to pass a reference to your ConnectionManager around, or
reference it with a static variable, right? That makes any class that
uses this approach dependent on the ConnectionManager class. Business
objects that depend only on the standard JNDI lookups for DataSource have
no external dependencies on anything other than the javax.sql APIs.
It is also not clear to me what value add your ConnectionManager has over
the standard DataSource APIs, but I'm sure there must be something.
protected Connection openConn() throws SQLException {
return ConnectionManager.getConnection();
}
protected void closeConn(Connection _conn) throws SQLException {
ConnectionManager.returnConnection(_conn);
}
My manager classes have no clue as to how the connection gets created. They just know that by calling those methods, they can get a close connections. ConnectionManager encapsulates the actual way that the connection is gotten and I already showed you what my ConnectionManager looks like. In fact, I can switch it from using a DBCP connection pool via a DataSource object to any other connection pool. I previously used BitMechanic's JDBC pool ( http://www.bitmechanic.com/projects/jdbcpool/ ). The *only* class I changed in the entire app moving from jdbcpool to DBCP was the ConnectionManger class and everything just worked transparently. I'm not the best Java developer in the world, but I do think I made a pretty decent design decision here. Wouldn't you agree or am I being naive? What would be better?
Yeah, I'll try testing this out. If it doesn't work, I'm not sure why the Admin app is there, though?> CRM> * In a high-available application server environment (i.e. Tomcat > CRM> by itself doesn't support this), doing the lookup every time gives > CRM> the server an opportunity to gracefully deal with things like > CRM> switching to a backup database, or dynamically reconfiguring the > CRM> connection pool by giving you a new instance from now on. > > Doesn't the Tomcat Admin app support managing DBCP DataSource > configuration? I think I can switch the connection string an other > variables on the fly there. This is the primary reason that I am > interested in whether doing the lookup incurs a performance penalty or > not. Sounds like there isn't really a performance penalty so if Tomcat > could switch the DataSource at runtime, then I'd > continue keeping a local static copy of the DataSource. So, can the > Admin app do this for me or not? > I'd have to look at the sources to see if the dynamic swapout at runtime actually works or not -- it might well.
> >>I'm thinking that things might have gotten confused by the fact that I mistakenly stated that Manager extended ConnectionManager when ConnectionManager is actualy supposed to be a static singleton (see my comments above) Whether I've achieve a proper static singleton using the ThreadLocal technique, I'm not sure. I'm really not sure what the ThreadLocal class does. I was just making an attempt to avoid the Double-Checked Locking issue and the article http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html under the title "Fixing Double-Checked Locking using Thread Local Storage" provided an issue that they said worked. Maybe you could provide an example showing me what you would do to create a proper static singleton that doesn't suffer from the Double-Checked Locking issue without resorting to the ThreadLocal solution? And, of course, I could just not bother with that and just do the JNDI lookup and, hopefuly, benefit from being able to modify the DBCP configuration on the fly.
> >> The way I have the lookup performed is the following. Please let me know
> >> if this is totally unnecessary....
> >>
> >> final class MyDataSourceClass {
> >>
> >> private static DataSource ds = null;
> >> public static Boolean DS_INITIALIZED = Boolean.FALSE;
> >>
> >> public static Connection getConnection() throws SQLException {
> >> if (DS_INITIALIZED.equals(Boolean.FALSE) || ds == null) {
> >> synchronized (DS_INITIALIZED) {
> >> if (DS_INITIALIZED.equals(Boolean.FALSE) || ds == null) {
> >> try {
> >> Context ctx = new InitialContext();
> >> ds =
> >> (DataSource)ctx.lookup("java:comp/env/jdbc/myDB");
> >> if (ds == null) throw new SQLException("No
> >> DataSource available for Connection");
> >> DS_INITIALIZED = Boolean.TRUE;
> >> }
> >> catch (NamingException ne) {
> >> throw new SQLException("JNDI Lookup Failed: " +
> >> ne.getMessage());
> >> }
> >> }
> >> }
> >> }
> >> return ds.getConnection();
> >> }
> >>
> >> public static void returnConnection(Connection conn) throws SQLException {
> >> conn.close();
> >> }
> >>
> >> }
> >>
> >> So, basically I want to know if the lookup isn't expensive enough to bother
> >> with storing the DataSource locally.
> >>
>
> CRM> As others will undoubtedly point out, the "Double-Checked Locking"
> CRM> algorithm you use above is not guaranteed to work. There was a JavaWorld
> CRM> article on this topic last year.
>
> Yep, this was brought to my attention in a previous post. I have
> modified the Double-checking Locking scheme to something that
> supposedly works and is safe.
>
> See http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html under the heading
> "Fixing Double-Checked Locking using Thread Local Storage"
>
> In my case, it is slightly modified since I am using static variables
> and a static method:
>
>
> final class ConnectionManager {
>
> /**
> * If perThreadInstance.get() returns a non-null value, this thread
> * has done synchronization needed to see initialization
> * of helper:
> * http://www.javaworld.com/javaworld/javaqa/2002-04/01-qa-0412-doublelock.html
> * http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
> */
> private final static ThreadLocal perThreadInstance = new ThreadLocal();
> private static DataSource ds = null;
>
> /**
> * Used by servlets and JSPs to get a connection from the pool.
> */
> public static Connection getConnection() throws SQLException {
> if (perThreadInstance.get() == null) createDataSource();
> return ds.getConnection();
> }
>
> private final static void createDataSource() throws SQLException {
> synchronized(ConnectionManager.class) {
> if (ds == null) {
> try {
> Context ctx = new InitialContext();
> ds = (DataSource)ctx.lookup("java:comp/env/jdbc/MyDB");
> if (ds == null) throw new SQLException("No DataSource available for Connection");
> if (logger.isDebugEnabled()) logger.debug("The Datasource is: " + ds);
> }
> catch (NamingException ne) {
> throw new SQLException("JNDI Lookup Failed: " + ne.getMessage());
> }
> }
> }
> // Any non-null value would do as the argument here
> perThreadInstance.set(perThreadInstance);
> }
>
> /**
> * Returns the connection back to the pool.
> */
> public static void returnConnection(Connection conn) throws SQLException {
> conn.close();
> }
>
> }
>
Why mess around with ThreadLocal variables at all? It looks to me like
this code is going to retrieve the DataSource from the JNDI context once
per request processing thread, instead of once and only once.
> >> thanks, > >> > >> Jake > > CRM> Craig > > Jake Craig
Jake
