5

I get an insanely strange result with Hibernate (4.1.9.Final) and MySQL (14.14 Distrib 5.5.29, InnoDB table):

When I persist something to the database using one thread and try to fetch it using another thread, Hibernate does not always find the entity. (Despite the fact that I'm properly committing the transaction and closing the persist-session before opening the load-session.)

Some observations:

  • I can not reproduce this using a single-threaded program.

  • I can see the "missing" entity in the database, and

  • If I restart the application Hibernate can successfully load the entity.

Here's an SSCCE illustrating the problem. (Imports left out for brevity):

public class StressTest {

    static SessionFactory sessionFactory;

    public static void main(String[] args) throws InterruptedException,
                                                     ExecutionException {

        // Configure Hibernate
        Configuration conf = new Configuration();
        conf.setProperty("hibernate.dialect",
                         "org.hibernate.dialect.MySQLInnoDBDialect");
        conf.configure();

        ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
                .applySettings(conf.getProperties())
                .buildServiceRegistry();        

        sessionFactory = conf.buildSessionFactory(serviceRegistry);

        // Set up producer / consumer
        BlockingQueue<Long> queue = new LinkedBlockingQueue<Long>();

        new Consumer(queue).start();
        new Producer(queue).start();
    }

}

class DummyEntity {
    long id;
    public long getId() { return id; }
    public void setId(long id) { this.id = id; }
}

Producer class (creates DummyEntities and persists them).

class Producer extends Thread {

    BlockingQueue<Long> sink;

    public Producer(BlockingQueue<Long> sink) {
        this.sink = sink;
    }

    @Override
    public void run() {
        try {
            while (true) {

                Session session = StressTest.sessionFactory.openSession();

                DummyEntity entity = new DummyEntity();
                entity.setId(new Random().nextLong());

                session.beginTransaction();
                session.save(entity);
                session.getTransaction().commit();
                session.close();

                sink.put(entity.getId());
            }
        } catch (InterruptedException ignore) {
            System.exit(-1);
        }
    }
}

Consumer class (loads DummyEntities from database):

class Consumer extends Thread {

    BlockingQueue<Long> source;

    public Consumer(BlockingQueue<Long> source) {
        this.source = source;
    }

    @Override
    public void run() {

        try {
            while (true) {

                long entityId = source.take();

                Session session = StressTest.sessionFactory.openSession();
                Object entity = session.get(DummyEntity.class, entityId);
                session.close();

                if (entity == null) {
                    System.err.printf("Entity with id %d NOT FOUND", entityId);
                    System.exit(-1);
                }
            }
        } catch (InterruptedException ignore) {
            System.exit(-1);
        }
    }
}

Finally, here's the mapping-xml for the DummyEntity.

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping
    default-cascade="all"
    default-lazy="false">

    <class name="se.stresstest.DummyEntity">

        <id name="id" type="long">
            <generator class="assigned"/>
        </id>

    </class>

</hibernate-mapping>

The resulting output always ends with something like:

Entity with id -225971146115345 NOT FOUND

Any ideas why?

(This is a refined version of a previous question.)

Community
  • 1
  • 1
aioobe
  • 413,195
  • 112
  • 811
  • 826
  • I gave an answer to your previous question which could fit that one too, please check it out. – didierc Apr 17 '13 at 12:10
  • @aioobe what about other versions of hibernate and/or MySQL – Eugene Apr 17 '13 at 12:50
  • @Eugene, I don't think it's a Hibernate issue, and I don't have other MySQL versions at hand. If I change to PostgreSQL everything works fine. – aioobe Apr 17 '13 at 13:21
  • @aioobe I really suspected this. I had the same problem on a project some while ago, but it was a 'personal' project and I just switched to MariaDB, I have no idea if that is your case. I did not even spent too much time investigating this... I wish I could help more – Eugene Apr 17 '13 at 13:23
  • Interesting. I guess we can conclude that it's a MySQL bug. I'm surprised that I can't find information about it. Every single web-app (with a thread pool of size > 1) will potentially run into this. – aioobe Apr 17 '13 at 13:34
  • @aioobe I can just tell that the guy behind MariaDB worked for mySQL for about 9 years and in his blog he says about numerous bugs that MySQL refused to close or even acknowledge. If only I could find it.. – Eugene Apr 17 '13 at 13:38

2 Answers2

1

This behavior can be consistent with a transaction isolation mode of REPEATABLE READ, which happens to be the default transaction isolation mode for InnoDB:

http://dev.mysql.com/doc/refman/5.5/en/set-transaction.html#isolevel_repeatable-read

If your application logic is dependent upon being able to see data committed in other transactions after the current transaction was started - at the expense of repeatable reads (data can/will change as a result of manipulation in other transactions) - you should set the transaction isolation to READ COMMITTED:

http://dev.mysql.com/doc/refman/5.5/en/set-transaction.html#isolevel_read-committed

0

use MySQL5InnoDBDialect instead.

Pradeep Pati
  • 5,779
  • 3
  • 29
  • 43