Forum Stats

  • 3,768,732 Users
  • 2,252,842 Discussions
  • 7,874,701 Comments

Discussions

Problem with Berkeley DB JE using JCA

527078
527078 Member Posts: 33
edited Jan 17, 2007 4:29AM in Berkeley DB Java Edition
Hello, dear developers of JE

I have implemented Lucene Indexing on Berkeley DB JE. And I'm using the JBoss as application server. Accessing the Breekely DB using JCA.

The problem arises when i'm calling the Environment.close(). It's displaying following error message.

12:01:39,712 WARN [JBossManagedConnectionPool] Exception destroying ManagedConnection org.jboss.resource.connectionmanager.TxCo
javax.resource.ResourceException: com.sleepycat.je.DatabaseException: Attempt to use non-open Environment object().
at com.sleepycat.je.jca.ra.JEManagedConnection.destroy(JEManagedConnection.java:190)
at org.jboss.resource.connectionmanager.InternalManagedConnectionPool.doDestroy(InternalManagedConnectionPool.java:550)
at org.jboss.resource.connectionmanager.InternalManagedConnectionPool.getConnection(InternalManagedConnectionPool.java:1
at org.jboss.resource.connectionmanager.JBossManagedConnectionPool$BasePool.getConnection(JBossManagedConnectionPool.jav
at org.jboss.resource.connectionmanager.BaseConnectionManager2.getManagedConnection(BaseConnectionManager2.java:410)
at org.jboss.resource.connectionmanager.TxConnectionManager.getManagedConnection(TxConnectionManager.java:342)
at org.jboss.resource.connectionmanager.BaseConnectionManager2.allocateConnection(BaseConnectionManager2.java:462)
at org.jboss.resource.connectionmanager.BaseConnectionManager2$ConnectionManagerProxy.allocateConnection(BaseConnectionM
at com.sleepycat.je.jca.ra.JEConnectionFactoryImpl.getConnection(JEConnectionFactoryImpl.java:56)
at com.sleepycat.je.jca.ra.JEConnectionFactoryImpl.getConnection(JEConnectionFactoryImpl.java:43)

IT WOULD BE HIGHLY APPRECIATED IF EXPLANIN ME THE REASON

Thanks,

Katta
«1

Comments

  • Charles Lamb
    Charles Lamb Member Posts: 836
    Hello Katta,

    Are you calling close() on the Environment or on the JEConnection? If you are calling it on the Environment, then I think you should call it on the JEConnection instead.

    Regards,

    Charles Lamb
  • 527078
    527078 Member Posts: 33
    edited Jan 17, 2007 4:28AM
    Hi Charles ,

    It's working fine if i call the JEConnection.close() instand of Environment.close(). But the problem arises

    when i run the lucene incremental indexing( command line program which is used to update the live index ) it's giving the following LogException.


    com.sleepycat.je.log.LogException: A je.lck file exists in D:\berkely\kindex The environment can not be locked for single writer access.
    at com.sleepycat.je.log.FileManager.lockEnvironment(FileManager.java:1298)
    at com.sleepycat.je.log.FileManager.<init>(FileManager.java:182)
    at com.sleepycat.je.dbi.EnvironmentImpl.<init>(EnvironmentImpl.java:245)
    at com.sleepycat.je.dbi.DbEnvPool.getEnvironment(DbEnvPool.java:102)
    at com.sleepycat.je.dbi.DbEnvPool.getEnvironment(DbEnvPool.java:54)
    at com.sleepycat.je.Environment.<init>(Environment.java:80)
    at com.tka.command.UpdateLuceneIndexCommand.executeRequest(UpdateLuceneIndexCommand.java:161)
    at com.tka.command.BaseCommand.execute(BaseCommand.java:81)
    at com.tka.lucene.BuildLuceneIndex.main(BuildLuceneIndex.java:47).


    The main thing is i have to do incremental indexing on the same database and updated values should get affect in search results.


    Thanks,

    Katta.

    Message was edited by:
    user524075
  • Charles Lamb
    Charles Lamb Member Posts: 836
    Hi Katta,

    Only one writer process can exist at a time with JE. Apparently your app server still has the environment open for write when you try to access it (for write) from another process.

    Charles Lamb
  • Charles Lamb
    Charles Lamb Member Posts: 836
    Hi Katta,

    One other thing to keep in mind is a reader process only sees a snapshot of the data at the time that the open was done. So if you are updating the Lucene data from a writer process, a reader process which was already started prior to the update will not see the updates until the reader process closes and re-opens the Environment.

    Charles Lamb
  • 527078
    527078 Member Posts: 33
    Hi Charles ,

    The problem not solved. The following is my code which is used to connect dbEnvHome for Lucene search using JCA. App server is JBoss. From this code get the IndexSearcher object.

    public class BerkelyDBConfig
    {
    private static Logger logger = Logger.getLogger(BerkelyDBConfig.class);

    private static final String JE_ENV_HOME = "D//berkely//dbHome";

    protected static JEConnection jeConnection = null;

    /**
    * @param dbEnvHome
    * @return directory [Directory]
    * @throws DatabaseException
    * @throws JEException
    */
    public static Directory getDirectory(String dbEnvHome)
    throws DatabaseException, JEException
    {
    Directory directory = null;
    Database index, blocks;
    try
    {
    jeConnection = getConnection(dbEnvHome);

    DatabaseConfig dbConfig = new DatabaseConfig();
    dbConfig.setAllowCreate(true);
    dbConfig.setTransactional(true);
    dbConfig.setReadOnly(true);

    /*
    * Use JEConnection.openDatabase() to obtain a cached Database
    * handle. Do not call close() on Database handles obtained
    * using this method.
    */
    index = jeConnection.openDatabase("__index__", dbConfig);
    blocks = jeConnection.openDatabase("__blocks__", dbConfig);

    directory = new JEDirectory(null, index, blocks);

    } catch (Exception e)
    {
    logger.error("Caught Exception and message is::"+ e.getMessage());
    throw new DatabaseException(e);
    } finally
    {
    if (jeConnection != null)
    jeConnection.close();
    }

    return directory;
    }

    /**
    * @param envDir
    *
    * @return dc [JEConnection]
    * @throws NamingException
    * @throws JEException
    *
    */
    public static JEConnection getConnection(String envDir)
    throws NamingException, JEException
    {
    EnvironmentConfig envConfig = new EnvironmentConfig();
    envConfig.setAllowCreate(true);
    envConfig.setTransactional(true);
    envConfig.setReadOnly(true);

    InitialContext iniCtx = new InitialContext();
    Context enc = (Context) iniCtx.lookup("java:comp/env");
    Object ref = enc.lookup("ra/JEConnectionFactory");
    JEConnectionFactory dcf = (JEConnectionFactory) ref;
    JEConnection dc = dcf.getConnection(envDir, envConfig);
    return dc;
    }

    /**
    * @return searcher [Searcher]
    * @throws DatabaseException
    */
    public static IndexSearcher getSearcher() throws DatabaseException
    {
    if (logger.isInfoEnabled())
    {
    logger.info("Entered getSearcher() of BerkelyDBUtil");
    }
    IndexSearcher searcher;
    try
    {
    Directory directory = getDirectory(JE_ENV_HOME);
    searcher = new IndexSearcher(directory);

    } catch (Exception e)
    {
    logger.error("Exception occured At:" + e.getMessage());
    throw new DatabaseException(e.getMessage());
    }
    if (logger.isInfoEnabled())
    {
    logger.info("Leaving getSearcher() of BerkelyDBUtil");
    }
    return searcher;
    }
    }


    If i set the enfConfig.setReadOnly(true); then only it's allowing to update the database (Lucene Incremental update index ( command line ) program). But still the updated values not get affected in search results. If i restart my JBoss app server then only i'm able to see the updated values in search results.

    The following is the Lucene Update Index program [The incremental update process will be run at intervals that can be configured after deployment (i.e. run from a cron job or some other scheduled task). ]


    EnvironmentConfig envConfig = new EnvironmentConfig();
    envConfig.setTransactional(true);
    envConfig.setAllowCreate(true);

    DatabaseConfig dbConfig = new DatabaseConfig();
    dbConfig.setAllowCreate(true);
    dbConfig.setTransactional(true);

    Environment env = new Environment(dbHome, envConfig);
    Transaction txn = null;
    Database index, blocks;

    try {
    txn = env.beginTransaction(null, null);
    index = env.openDatabase(txn, "__index__", dbConfig);
    blocks = env.openDatabase(txn, "__blocks__", dbConfig);
    } catch (DatabaseException e) {
    if (txn != null)
    {
    txn.abort();
    txn = null;
    }
    throw e;
    } finally {
    if (txn != null)
    txn.commit();
    txn = null;
    }
    try{
    txn = env.beginTransaction(null, null);
    indexDir = new JEDirectory(txn, index, blocks);
    System.out.println("INDEXING INFO: Start Indexing updated content.");

    IndexWriter indexWriter = new IndexWriter(indexDir,new StandardAnalyzer(), false);
    indexWriter.setUseCompoundFile(false);
    // configure the writer
    indexWriter.setMergeFactor(1000);
    indexWriter.setMaxMergeDocs(9999999);
    indexWriter.setMaxBufferedDocs(1000);

    // call the updateLuceneIndex method
    updateLuceneIndex(indexWriter );

    indexWriter.optimize();
    indexWriter.close();

    System.out.println("INDEXING INFO: Optimizing Index finished......");
    } catch (IOException e)
    {
    txn.abort();
    txn = null;
    e.printStackTrace();
    throw e;
    } finally
    {
    if (txn != null)
    txn.commit();

    index.close();
    blocks.close();
    env.close();
    }


    Please go through the above code and suggest me if any alternative.


    Thanks,

    Katta.
  • Hi Katta,

    I would expect to see this behavior. The problem is that with the JBoss server, you are using the JCA/RA interface to JE. This maintains a connection pool for JE. The connection pool keeps the JE environment open. As long as the read-only JE environment is open, then you will not see the updates from the Lucene updater process.

    Here are some thoughts:

    (1) Is it possible to have the updater run inside jboss using the JCA connection pool?

    (2) Is it possible to not use the JCA api in your application within JBoss? Presumably it is possible, but it will require opening the environment and database for each action within JBoss. If performance permits that then this may work.

    (3) Is it possible to get JBoss to call ManagedConnection.destroy() on the JEManagedConnection? If so, then the destroy() method will close up the environment and databases (see <JE_HOME>/src/com/sleepycat/je/jca/ra/JEManagedConnection.java's destroy() method).

    (4) You could add a new method to JEConnection.java (in the same directory as (3) above), called destroy() which just calls mc.destroy(). That would require rebuilding JEConnection.java and putting it in the relevant jar file, but that is not hard.

    I realize that these are not great options, but the problem stems from the fact that JE was not intended to be updated from multiple processes, and the read-only processes only see a consistent snapshot of the environment when they open it. If you can force the index updates to occur within JBoss, then you'd only have a single writer process.

    I hope this is useful.
  • 527078
    527078 Member Posts: 33
    Hi Charles ,

    Thanx Charles for detailed explanation about JE & JCA connection pool.

    It's working fine if i run the updater inside Jboss using JCA connection pool.

    As per your 4th suggestion i added destroy() method (which calls mc.destroy()) to JEConnection.java and updated the relevant jar file. When i call the destroy() method it's giving me following exception.


    12:01:39,712 WARN [JBossManagedConnectionPool] Exception destroying ManagedConnection org.jboss.resource.connectionmanager.TxCo
    javax.resource.ResourceException: com.sleepycat.je.DatabaseException: Attempt to use non-open Environment object().
    at com.sleepycat.je.jca.ra.JEManagedConnection.destroy(JEManagedConnection.java:190)
    at org.jboss.resource.connectionmanager.InternalManagedConnectionPool.doDestroy(InternalManagedConnectionPool.java:550)
    at org.jboss.resource.connectionmanager.InternalManagedConnectionPool.getConnection(InternalManagedConnectionPool.java:1
    at org.jboss.resource.connectionmanager.JBossManagedConnectionPool$BasePool.getConnection(JBossManagedConnectionPool.jav
    at org.jboss.resource.connectionmanager.BaseConnectionManager2.getManagedConnection(BaseConnectionManager2.java:410)
    at org.jboss.resource.connectionmanager.TxConnectionManager.getManagedConnection(TxConnectionManager.java:342)
    at org.jboss.resource.connectionmanager.BaseConnectionManager2.allocateConnection(BaseConnectionManager2.java:462)
    at org.jboss.resource.connectionmanager.BaseConnectionManager2$ConnectionManagerProxy.allocateConnection(BaseConnectionM
    at com.sleepycat.je.jca.ra.JEConnectionFactoryImpl.getConnection(JEConnectionFactoryImpl.java:56)
    at com.sleepycat.je.jca.ra.JEConnectionFactoryImpl.getConnection(JEConnectionFactoryImpl.java:43)


    Thans,

    Katta.
  • Charles Lamb
    Charles Lamb Member Posts: 836
    Is it acceptable for you to run the updater inside JBoss? I have (obviously) not tested the suggestion I made to add a destroy() method.

    Charles Lamb
  • 527078
    527078 Member Posts: 33
    Hi Charles ,

    While updater is running inside JBoss, search is not working. It's throwing the
    following exception.

    17:19:41,828 INFO [STDOUT] com.sleepycat.je.DatabaseException: (JE 3.0.12) (JE 3.0.12) Lock expired. Locker 15_http-0.0.0.0-8080-2_Txn: waited for lock on database=__index__ node=195 type=READ grant=WAIT_NEW timeoutMillis=500 startTime=1155815380828 endTime=1155815381828
    Owners: [<LockInfo locker="14_RMI TCP Connection(2)-192.168.10.60_Txn" type="WRITE"/>]
    Waiters: []

    Whenever updater finished it's job search is working. I want to search while updater is running.

    Thanks,

    Katta.
  • Charles Lamb
    Charles Lamb Member Posts: 836
    Hi Katta,

    The exception you are seeing indicates that a lock request is timing out. Lock requests, by default, timeout after 500msec. You can increase the lock timeout with the Transaction.setLockTimeout() entrypoint. The timeout argument is in microsecs.

    I am not sure how the Lucene index updating works. If it is holding loks for a long time (e.g. for the entire operation), then you may have to increase the timeout to something that is as long as the lucene update. You can experiment with it and see what works.

    Regards,

    Charles Lamb
This discussion has been closed.