diff --git a/nuxeo-core-storage-sql/src/main/java/org/nuxeo/ecm/core/storage/sql/SessionImpl.java b/nuxeo-core-storage-sql/src/main/java/org/nuxeo/ecm/core/storage/sql/SessionImpl.java
--- a/nuxeo-core-storage-sql/src/main/java/org/nuxeo/ecm/core/storage/sql/SessionImpl.java
+++ b/nuxeo-core-storage-sql/src/main/java/org/nuxeo/ecm/core/storage/sql/SessionImpl.java
@@ -74,6 +74,8 @@
 
     private Node rootNode;
 
+    private long threadId;
+
     SessionImpl(RepositoryImpl repository, SchemaManager schemaManager,
             Mapper mapper, RepositoryImpl.Invalidators invalidators,
             Credentials credentials) throws StorageException {
@@ -104,7 +106,26 @@
             // avoid potential multi-threaded access to active session
             return 0;
         }
-        return context.clearCaches();
+        int n = context.clearCaches();
+        clearThread();
+        return n;
+    }
+
+    protected void checkThread() {
+        long currentThreadId = Thread.currentThread().getId();
+        if (threadId == 0) {
+            threadId = currentThreadId;
+        } else if (threadId != currentThreadId) {
+            log.error(
+                    String.format(
+                            "Session was started in thread %s but is now used in thread %s",
+                            threadId, currentThreadId), new Exception(
+                            "Concurrency Error"));
+        }
+    }
+
+    protected void clearThread() {
+        threadId = 0;
     }
 
     /*
@@ -151,6 +172,7 @@
     }
 
     public Node getRootNode() {
+        checkThread();
         checkLive();
         return rootNode;
     }
@@ -175,11 +197,13 @@
     }
 
     protected void flush() throws StorageException {
+        checkThread();
         context.updateFulltext(this);
         context.save();
     }
 
     public Node getNodeById(Serializable id) throws StorageException {
+        checkThread();
         checkLive();
         if (id == null) {
             throw new IllegalArgumentException("Illegal null id");
diff --git a/nuxeo-core-storage-sql/src/main/java/org/nuxeo/ecm/core/storage/sql/TransactionalSession.java b/nuxeo-core-storage-sql/src/main/java/org/nuxeo/ecm/core/storage/sql/TransactionalSession.java
--- a/nuxeo-core-storage-sql/src/main/java/org/nuxeo/ecm/core/storage/sql/TransactionalSession.java
+++ b/nuxeo-core-storage-sql/src/main/java/org/nuxeo/ecm/core/storage/sql/TransactionalSession.java
@@ -21,6 +21,10 @@
 import javax.transaction.xa.XAResource;
 import javax.transaction.xa.Xid;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.nuxeo.ecm.core.storage.StorageException;
+
 /**
  * The transactional session is an {@link XAResource} for this session.
  *
@@ -28,6 +32,8 @@
  */
 public class TransactionalSession implements XAResource {
 
+    private static final Log log = LogFactory.getLog(TransactionalSession.class);
+
     private final SessionImpl session;
 
     private final Mapper mapper;
@@ -36,7 +42,8 @@
 
     private boolean inTransaction;
 
-    TransactionalSession(SessionImpl session, Mapper mapper, PersistenceContext context) {
+    TransactionalSession(SessionImpl session, Mapper mapper,
+            PersistenceContext context) {
         this.session = session;
         this.mapper = mapper;
         this.context = context;
@@ -60,19 +67,30 @@
         }
         mapper.start(xid, flags);
         inTransaction = true;
+        session.checkThread();
     }
 
     public void end(Xid xid, int flags) throws XAException {
+        boolean failed = true;
         try {
             if (flags != TMFAIL) {
                 try {
                     session.flush();
-                } catch (Exception e) {
+                } catch (StorageException e) {
+                    log.error("Could not end transaction", e);
                     throw new XAException(e.toString());
                 }
             }
+            failed = false;
+            mapper.end(xid, flags);
         } finally {
-            mapper.end(xid, flags);
+            if (failed) {
+                try {
+                    mapper.end(xid, TMFAIL);
+                } finally {
+                    rollback(xid);
+                }
+            }
         }
     }
 
@@ -85,17 +103,28 @@
             mapper.commit(xid, onePhase);
         } finally {
             inTransaction = false;
-            context.notifyInvalidations();
+            try {
+                context.notifyInvalidations();
+            } finally {
+                session.clearThread();
+            }
         }
     }
 
     public void rollback(Xid xid) throws XAException {
         try {
-            mapper.rollback(xid);
-            context.rollback();
+            try {
+                mapper.rollback(xid);
+            } finally {
+                context.rollback();
+            }
         } finally {
             inTransaction = false;
-            context.notifyInvalidations();
+            try {
+                context.notifyInvalidations();
+            } finally {
+                session.clearThread();
+            }
         }
     }
 
