dkpro / dkpro-uby

Framework for creating and accessing UBY resources – sense-linked lexical resources in standard UBY-LMF format
https://dkpro.github.io/dkpro-uby
Other
22 stars 3 forks source link

Exception in multi-thread application #167

Open aleSuglia opened 8 years ago

aleSuglia commented 8 years ago

Hello,

I am using your library with the Hibernate support and it works great. At the moment I have started to use an improved implementation of my algorithm that uses multiple threads. When the number of threads starts to grow, I receive strange errors like this one:

org.hibernate.AssertionFailure: bug adding collection twice

I use your API only to read resources from the supported lexicons in UBY. The data structures that I use have a restricted visibility so only a single thread can access them. In this case, the database is the only shared resource of the whole program.

Any idea about this error? Can you give me some suggestions? Thank you in advance.

Alessandro

chmeyer commented 8 years ago

Tough one... Do you happen to have a stack trace and a bit more code context (or repo case)?

As an educated guess: maybe two threads write to the same internal Hibernate data structure. A solution could be to separate the (Hibernate-managed) data structures per thread.

reckart commented 8 years ago

I doubt that Hibernate is thread-safe - and also that Uby API is. I would recommend not sharing a Uby context across threads or in a webapp, not sharing a Uby context across page requests.

betoboullosa commented 8 years ago

How are you dealing with sessions? Hibernate sessions are not thread-safe, every thread should have its own session.

reckart commented 8 years ago

Afair the Uby API wraps the hibernate session and keeps it. So if a single instance of the Uby API is shared in an application, so is the Hibernate session.

betoboullosa commented 8 years ago

That probably would lead to problems, not to mention transaction isolation issues.

aleSuglia commented 8 years ago

At the moment I am not able to share to you the whole stacktrace (I will do it when I go home). What I can say to you at the moment is that I use the UBY API only for reading information. But, I have noticed that this does not give me any guarantees due to Hibernate's lazy-loading.

Analyzing the stacktrace that I got, I have found that the reason of the error (apparently) is that I use an HashSet in order to collect some _SemanticPredicate_s that I need in my algorithm. As you know, when you use an HashSet, the elements in the container should implement equals and hashCode. In this case, something pretty weird happens when those methods are invoked and an exception is raised in this situation.

Any ideas about this specific behaviour? Changing the data structure to a List, seems to solve this error. It is a nice trick but I want to know more about this.

Thank you to all for your help. I really appreciate it.

EDIT: At this link (https://bpaste.net/show/66ab1f67c481) you can find the stacktrace.

Here there is the code that I use:

   public List<Sense> getSenses(String lemma, String lexiconName, String rawPosTag) {
        Lexicon lexicon = uby.getLexiconByName(lexiconName);
        EPartOfSpeech posTag = getUbyPostag(rawPosTag);
        List<LexicalEntry> lexEntries;

        if (posTag != null)
            lexEntries = uby.getLexicalEntries(lemma, posTag, lexicon);
        else
            lexEntries = uby.getLexicalEntries(lemma, lexicon);

        List<Sense> senses = new ArrayList<>();
        for (LexicalEntry entry : lexEntries) {
            senses.addAll(entry.getSenses());
        }

        return senses;
  }

I hope that someone can help me with this. After some extensive tests I've found that the List trick doesn't work.

EDIT-2: I have tried to use a single Uby object for each thread. The exception above seems solved but if I have a lot of thread that are executed I get this error:

Caused by: java.sql.SQLException: Connections could not be acquired from the underlying database!
    at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:106)
    at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:529)
    at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:128)
    at org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider.getConnection(C3P0ConnectionProvider.java:84)
    at org.hibernate.internal.AbstractSessionImpl$NonContextualJdbcConnectionAccess.obtainConnection(AbstractSessionImpl.java:292)
    at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:297)
    ... 53 more
Caused by: com.mchange.v2.resourcepool.CannotAcquireResourceException: A ResourcePool could not acquire a resource from its primary factory or source.
    at com.mchange.v2.resourcepool.BasicResourcePool.awaitAvailable(BasicResourcePool.java:1319)
    at com.mchange.v2.resourcepool.BasicResourcePool.prelimCheckoutResource(BasicResourcePool.java:557)
    at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource(BasicResourcePool.java:477)
    at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:525)
    ... 57 more
aleSuglia commented 8 years ago

I have solved my problem.

Apparently your implementation of the class Uby is not well suited for a multi thread application. I am not able to create multiple Uby instances because the SessionFactory cannot be instanciated multiple times (according to this StackOverflow question).

For this reason, I have implemented a custom Uby class in this way:

public class MultiUBY extends Uby {
    private static SessionFactory sessionFactory;

    public MultiUBY(DBConfig dbConfig) throws IllegalArgumentException {
        super(dbConfig);

        if (dbConfig == null) {
            throw new IllegalArgumentException("database configuration is null");
        }
        if (sessionFactory == null) {
            this.dbConfig = dbConfig;
            cfg = MultiHibernateConnect.getConfiguration(dbConfig);
            ServiceRegistryBuilder serviceRegistryBuilder = new ServiceRegistryBuilder()
                    .applySettings(cfg.getProperties());
            sessionFactory = cfg.buildSessionFactory(serviceRegistryBuilder.buildServiceRegistry());
        }

        this.session = sessionFactory.openSession();
    }

}

I have implemented some custom features in my fork, so if you want I can make a pull request in order to merge my results. I hope that could help you to add this feature to this really useful library.

Thank you again for your time.

Alessandro

chmeyer commented 8 years ago

Thanks for sharing your solution! I keep this issue open, such that this or a similar solution can be integrated in the master branch.

aleSuglia commented 8 years ago

If you need any help, don't hesitate to contact me. I will be very happy to contribute.