[Mulgara-svn] r106 - in trunk: . src/jar/resolver/java/org/mulgara/resolver src/jar/resolver-store/java/org/mulgara/resolver/store
andrae at mulgara.org
andrae at mulgara.org
Sun Oct 22 12:00:10 UTC 2006
Author: andrae
Date: 2006-10-22 07:00:10 -0500 (Sun, 22 Oct 2006)
New Revision: 106
Added:
trunk/src/jar/resolver/java/org/mulgara/resolver/InternalSystemResolverFactory.java
Modified:
trunk/log4j-conf.xml
trunk/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreResolver.java
trunk/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreResolverFactory.java
trunk/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreXAResource.java
trunk/src/jar/resolver/java/org/mulgara/resolver/Database.java
trunk/src/jar/resolver/java/org/mulgara/resolver/InternalResolverFactory.java
trunk/src/jar/resolver/java/org/mulgara/resolver/StringPoolSession.java
Log:
http://mulgara.org/jira/browse/MGR-2
"Multiple writers not supported" transactioning error occurs if reading and writing concurrently
This revision should fix MGR-2.
I believe the bug was the result of a race condition in StringPoolSession. It
is possible for multiple transactions to enter prepare at the same time -
specifically a read-only transaction can enter prepare while a write transaction
is still flushing data to disk. StringPoolSession was designed to detect
concurrent prepares and abort the latter attempts to enter the prepare state.
It is not sufficient to simply stop attempting to detect concurrent prepares,
as the store layer only permits a single call to prepare prior to a commit.
Removing the test leads to multiple possible prepares within a single
transaction, and so there is a need to add extra code to ensure this doesn't
happen.
Modified: trunk/log4j-conf.xml
===================================================================
--- trunk/log4j-conf.xml 2006-10-17 12:10:50 UTC (rev 105)
+++ trunk/log4j-conf.xml 2006-10-22 12:00:10 UTC (rev 106)
@@ -51,6 +51,12 @@
</appender>
<!-- (Insert category elements here) -->
+ <category name="org.mulgara.resolver.store.StatementStoreXAResource">
+ <priority value="debug"/>
+ </category>
+ <category name="org.mulgara.resolver.StringPoolSession">
+ <priority value="debug"/>
+ </category>
<!-- Default is to log messages of "warn" priority to the logfile appender -->
<root>
Modified: trunk/src/jar/resolver/java/org/mulgara/resolver/Database.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/Database.java 2006-10-17 12:10:50 UTC (rev 105)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/Database.java 2006-10-22 12:00:10 UTC (rev 106)
@@ -731,9 +731,9 @@
internalResolverFactoryMap.put(
metadata.getSystemModelTypeURI(),
- new InternalResolverFactory(systemResolverFactory,
- metadata.getRdfTypeNode(),
- metadata.getSystemModelNode())
+ new InternalSystemResolverFactory(systemResolverFactory,
+ metadata.getRdfTypeNode(),
+ metadata.getSystemModelNode())
);
// Add the mandatory security adapter that protects the system model
Modified: trunk/src/jar/resolver/java/org/mulgara/resolver/InternalResolverFactory.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/InternalResolverFactory.java 2006-10-17 12:10:50 UTC (rev 105)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/InternalResolverFactory.java 2006-10-22 12:00:10 UTC (rev 106)
@@ -16,7 +16,9 @@
* created by Plugged In Software Pty Ltd are Copyright (C) 2001,2002
* Plugged In Software Pty Ltd. All Rights Reserved.
*
- * Contributor(s): N/A.
+ * Contributor(s):
+ * Change to field visibility from private to protected
+ * (c) Netymon Pty Ltd 2006 All Rights Reserved.
*
* [NOTE: The text of this Exhibit A may differ slightly from the text
* of the notices in the Source Code files of the Original Code. You
@@ -56,17 +58,17 @@
/**
* The preallocated node for <code>rdf:type</code>.
*/
- private final long rdfType;
+ protected final long rdfType;
/**
* The wrapped factory.
*/
- private final ResolverFactory resolverFactory;
+ protected final ResolverFactory resolverFactory;
/**
* The preallocated node for the system model (<code>#</code>).
*/
- private final long systemModel;
+ protected final long systemModel;
//
// Constructor
Added: trunk/src/jar/resolver/java/org/mulgara/resolver/InternalSystemResolverFactory.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/InternalSystemResolverFactory.java 2006-10-17 12:10:50 UTC (rev 105)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/InternalSystemResolverFactory.java 2006-10-22 12:00:10 UTC (rev 106)
@@ -0,0 +1,72 @@
+/*
+ * The contents of this file are subject to the Open Software License
+ * Version 3.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.rosenlaw.com/OSL3.0.htm
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * This file is an original work developed by Netymon Pty Ltd
+ * (http://www.netymon.com, mailto:mail at netymon.com). Portions created
+ * by Netymon Pty Ltd are Copyright (c) 2006 Netymon Pty Ltd.
+ * All Rights Reserved.
+ */
+
+package org.mulgara.resolver;
+
+// Local packages
+import org.mulgara.resolver.spi.Resolver;
+import org.mulgara.resolver.spi.ResolverFactory;
+import org.mulgara.resolver.spi.ResolverFactoryException;
+import org.mulgara.resolver.spi.ResolverSession;
+
+/**
+ * Wraps the system resolver as an internal resolver without obtaining a new one.
+ *
+ * Duplicate transactional resources cause problems with prepare/commit, so ensure
+ * that only one is created.
+ *
+ * @created 2006-10-06
+ *
+ * @author <a href="mailto:andrae at netymon.com">Andrae Muys</a>
+ *
+ * @version $Revision: $
+ *
+ * @modified $Date: $
+ *
+ * @maintenanceAuthor $Author: $
+ *
+ * @company <A href="mailto:mail at netymon.com">Netymon Pty Ltd</A>
+ *
+ * @copyright ©2006 <a href="http://www.netymon.com/">Netymon Pty Ltd</a>
+ *
+ * @licence Open Software License v3.0</a>
+ */
+
+
+class InternalSystemResolverFactory extends InternalResolverFactory
+{
+ InternalSystemResolverFactory(ResolverFactory resolverFactory,
+ long rdfType,
+ long systemModel)
+ {
+ super(resolverFactory, rdfType, systemModel);
+ }
+
+ public Resolver newResolver(boolean canWrite,
+ ResolverSession resolverSession,
+ Resolver systemResolver)
+ throws ResolverFactoryException
+ {
+ return new InternalResolver(
+ systemResolver,
+ rdfType,
+ resolverSession,
+ systemModel,
+ systemResolver
+ );
+ }
+}
Modified: trunk/src/jar/resolver/java/org/mulgara/resolver/StringPoolSession.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/StringPoolSession.java 2006-10-17 12:10:50 UTC (rev 105)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/StringPoolSession.java 2006-10-22 12:00:10 UTC (rev 106)
@@ -145,7 +145,7 @@
Object globalLock)
{
if (logger.isDebugEnabled()) {
- logger.debug("Constructing StringPoolSession " + System.identityHashCode(this));
+ logger.debug("Constructing StringPoolSession " + System.identityHashCode(this), new Throwable());
}
assert databaseURI.getFragment() == null;
@@ -239,10 +239,15 @@
public void prepare() throws SimpleXAResourceException
{
- logger.debug("Preparing phase on StringPoolSession " + System.identityHashCode(this));
- if (state != OBTAIN) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Preparing phase on StringPoolSession " + System.identityHashCode(this) + " SP=" + System.identityHashCode(persistentStringPool));
+ }
+ if (state == PREPARE) {
+ return;
+ } else if (state != OBTAIN) {
throw new SimpleXAResourceException("Attempting to prepare phase without obtaining phase");
}
+
state = PREPARE;
persistentStringPool.prepare();
@@ -255,11 +260,17 @@
public void commit() throws SimpleXAResourceException
{
- logger.debug("Committing phase on StringPoolSession " + System.identityHashCode(this));
- if (state != PREPARE) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Committing phase on StringPoolSession " + System.identityHashCode(this));
+ }
+ if (state == COMMIT) {
+ return;
+ } else if (state != PREPARE) {
throw new SimpleXAResourceException("Attempting to commit phase without preparing");
}
+
state = COMMIT;
+
synchronized (globalLock) {
persistentStringPool.commit();
persistentNodePool.commit();
@@ -288,10 +299,14 @@
public void release() throws SimpleXAResourceException
{
logger.debug("Release phase on StringPoolSession " + System.identityHashCode(this));
- if (state != COMMIT && state != ROLLBACK) {
+ if (state == RELEASE) {
+ return;
+ } else if (state != COMMIT && state != ROLLBACK) {
throw new SimpleXAResourceException("Attempting to release phase without commit or rollback");
}
+
state = RELEASE;
+
persistentStringPool.release();
persistentNodePool.release();
// TODO determine if release() should be called for the temp components.
Modified: trunk/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreResolver.java
===================================================================
--- trunk/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreResolver.java 2006-10-17 12:10:50 UTC (rev 105)
+++ trunk/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreResolver.java 2006-10-22 12:00:10 UTC (rev 106)
@@ -111,10 +111,13 @@
private final XAResolverSession xaResolverSession;
+ private final XAResource xaresource;
+
private boolean isSystemResolver;
private long systemModel;
+
//
// Constructors
//
@@ -145,6 +148,10 @@
this.statementStore = statementStore;
this.xaResolverSession = resolverSession;
this.isSystemResolver = true;
+ this.xaresource = new StatementStoreXAResource(
+ 10, // transaction timeout in seconds
+ resolverSession,
+ new SimpleXAResource[] { statementStore });
}
StatementStoreResolver(Resolver systemResolver,
@@ -170,6 +177,10 @@
this.statementStore = statementStore;
this.xaResolverSession = resolverSession;
this.isSystemResolver = false;
+ this.xaresource = new StatementStoreXAResource(
+ 10, // transaction timeout in seconds
+ resolverSession,
+ new SimpleXAResource[] { statementStore });
}
@@ -182,10 +193,6 @@
*/
public XAResource getXAResource()
{
- XAResource xaresource = new StatementStoreXAResource(
- 10, // transaction timeout in seconds
- (XAResolverSession) xaResolverSession,
- new SimpleXAResource[] { statementStore });
return xaresource;
}
Modified: trunk/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreResolverFactory.java
===================================================================
--- trunk/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreResolverFactory.java 2006-10-17 12:10:50 UTC (rev 105)
+++ trunk/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreResolverFactory.java 2006-10-22 12:00:10 UTC (rev 106)
@@ -205,12 +205,12 @@
rdfType,
systemModel,
modelTypeURI,
- allowWrites ?
- (XAResolverSession) resolverSessionFactory.newWritableResolverSession()
- :
- (XAResolverSession) resolverSessionFactory.newReadOnlyResolverSession(),
- allowWrites ? statementStore.newWritableStatementStore()
- : statementStore.newReadOnlyStatementStore());
+ allowWrites
+ ? (XAResolverSession) resolverSessionFactory.newWritableResolverSession()
+ : (XAResolverSession) resolverSessionFactory.newReadOnlyResolverSession(),
+ allowWrites
+ ? statementStore.newWritableStatementStore()
+ : statementStore.newReadOnlyStatementStore());
}
catch (ResolverSessionFactoryException er) {
throw new ResolverFactoryException(
Modified: trunk/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreXAResource.java
===================================================================
--- trunk/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreXAResource.java 2006-10-17 12:10:50 UTC (rev 105)
+++ trunk/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreXAResource.java 2006-10-22 12:00:10 UTC (rev 106)
@@ -86,7 +86,9 @@
private XAResolverSession session;
private boolean rollback;
private Xid xid;
- private static Xid preparing = null;
+ // Used to prevent multiple calls to prepare on the store layer.
+ // Set of session's that have been prepared.
+ private static Set preparing = new HashSet();
//
// Constructor
@@ -136,6 +138,11 @@
}
break;
case XAResource.TMJOIN:
+ if (!xid.equals(this.xid)) {
+ logger.error("Attempt to join with wrong transaction.");
+ throw new XAException(XAException.XAER_INVAL);
+ }
+ break;
default: // Currently fall-through.
rollback = true;
logger.warn("Unrecognised flags in start: " + System.identityHashCode(xid) + " flags=" + formatFlags(flags));
@@ -149,7 +156,7 @@
public int prepare(Xid xid) throws XAException
{
if (logger.isDebugEnabled()) {
- logger.debug("XAResource " + this + " Prepare " + System.identityHashCode(xid));
+ logger.debug("XAResource " + this + " Prepare " + System.identityHashCode(xid) + " With Session: " + System.identityHashCode(session));
}
if (rollback) {
@@ -160,16 +167,11 @@
logger.error("Attempting to prepare unknown transaction.");
throw new XAException(XAException.XAER_NOTA);
}
- synchronized(StatementStoreXAResource.class) {
- if (preparing != null) {
- if (preparing.equals(xid)) {
- return XA_OK;
- } else {
- logger.error("Attempting to prepare from different transaction. Multiple writers not supported");
- throw new XAException(XAException.XAER_NOTA);
- }
+ synchronized(preparing) {
+ if (preparing.contains(session)) {
+ return XA_OK;
} else {
- preparing = xid;
+ preparing.add(session);
}
}
@@ -177,6 +179,9 @@
session.prepare();
} catch (SimpleXAResourceException es) {
logger.warn("Attempt to prepare store failed", es);
+ synchronized(preparing) {
+ preparing.remove(session);
+ }
throw new XAException(XAException.XA_RBROLLBACK);
}
@@ -185,54 +190,48 @@
public void commit(Xid xid, boolean onePhase) throws XAException
{
- if (logger.isDebugEnabled()) {
- logger.debug("XAResource " + this + " Commit xid=" + System.identityHashCode(xid) + " onePhase=" + onePhase);
- }
- if (rollback) {
- logger.error("Attempting to commit in failed transaction");
- throw new XAException(XAException.XA_RBROLLBACK);
- }
- if (!xid.equals(this.xid)) {
- logger.error("Attempting to commit unknown transaction.");
- throw new XAException(XAException.XAER_NOTA);
- }
try {
- if (onePhase) {
- // Check return value is XA_OK.
- prepare(xid);
+ if (logger.isDebugEnabled()) {
+ logger.debug("XAResource " + this + " Commit xid=" + System.identityHashCode(xid) + " onePhase=" + onePhase + " session=" + System.identityHashCode(session));
}
- } catch (Throwable th) {
- this.rollback = true;
- logger.error("Attempt to prepare in onePhaseCommit failed.", th);
- throw new XAException(XAException.XA_RBROLLBACK);
- }
+ if (rollback) {
+ logger.error("Attempting to commit in failed transaction");
+ throw new XAException(XAException.XA_RBROLLBACK);
+ }
+ if (!xid.equals(this.xid)) {
+ logger.error("Attempting to commit unknown transaction.");
+ throw new XAException(XAException.XAER_NOTA);
+ }
+ try {
+ if (onePhase) {
+ // Check return value is XA_OK.
+ prepare(xid);
+ }
+ } catch (Throwable th) {
+ this.rollback = true;
+ logger.error("Attempt to prepare in onePhaseCommit failed.", th);
+ throw new XAException(XAException.XA_RBROLLBACK);
+ }
- synchronized(StatementStoreXAResource.class) {
- if (preparing != null) {
- if (!preparing.equals(xid)) {
- if (logger.isDebugEnabled()) {
- logger.debug("Committing different transaction to prepare.");
- }
- throw new XAException(XAException.XA_RBROLLBACK);
+ try {
+ session.commit();
+ } catch (Throwable th) {
+ // This is a serious problem since the database is now in an
+ // inconsistent state.
+ // Make sure the exception is logged.
+ logger.fatal("Failed to commit resource in transaction " + xid, th);
+ throw new XAException(XAException.XAER_RMERR);
+ }
+ } finally {
+ synchronized(preparing) {
+ if (preparing.contains(session)) {
+ preparing.remove(session);
} else {
- logger.debug("Possible race condition here");
- preparing = null;
+ logger.debug("Already committed in this transaction");
}
- } else {
- logger.debug("Already committed in this transaction");
- return;
}
}
- try {
- session.commit();
- } catch (Throwable th) {
- // This is a serious problem since the database is now in an
- // inconsistent state.
- // Make sure the exception is logged.
- logger.fatal("Failed to commit resource in transaction " + xid, th);
- throw new XAException(XAException.XAER_RMERR);
- }
}
public void end(Xid xid, int flags) throws XAException
More information about the Mulgara-svn
mailing list