[jboss-svn-commits] JBL Code SVN: r25171 - in labs/jbosstm/trunk/ArjunaCore: arjuna/classes/com/arjuna/ats/internal/arjuna and 5 other directories.
jboss-svn-commits at lists.jboss.org
jboss-svn-commits at lists.jboss.org
Mon Feb 9 14:13:30 EST 2009
Author: mark.little at jboss.com
Date: 2009-02-09 14:13:30 -0500 (Mon, 09 Feb 2009)
New Revision: 25171
Added:
labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/LogStore.java
labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/LogStoreSetup.java
labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreReactivationTest.java
labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreReactivationTest2.java
labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreRecoveryTest.java
labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreRecoveryTest2.java
labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreTest.java
labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreTest2.java
labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStressTest.java
labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStressTest2.java
Modified:
labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/recovery/RecoverAtomicAction.java
labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/Implementations.java
labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/FileSystemStore.java
labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/ShadowingStore.java
labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/recovery/TransactionStatusManagerItem.java
labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/resources/BasicRecord.java
labs/jbosstm/trunk/ArjunaCore/docs/user_guide/ProgrammersGuide.odt
Log:
https://jira.jboss.org/jira/browse/JBTM-397
Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/recovery/RecoverAtomicAction.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/recovery/RecoverAtomicAction.java 2009-02-09 19:12:13 UTC (rev 25170)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/recovery/RecoverAtomicAction.java 2009-02-09 19:13:30 UTC (rev 25171)
@@ -44,7 +44,7 @@
* @message com.arjuna.ats.arjuna.recovery.RecoverAtomicAction_1 [com.arjuna.ats.arjuna.recovery.RecoverAtomicAction_1] - RecoverAtomicAction.replayPhase2 recovering {0} ActionStatus is {1}
* @message com.arjuna.ats.arjuna.recovery.RecoverAtomicAction_2 [com.arjuna.ats.arjuna.recovery.RecoverAtomicAction_2] - RecoverAtomicAction.replayPhase2: Unexpected status: {0}
* @message com.arjuna.ats.arjuna.recovery.RecoverAtomicAction_3 [com.arjuna.ats.arjuna.recovery.RecoverAtomicAction_3] - RecoverAtomicAction.replayPhase2( {0} ) finished
- * @message com.arjuna.ats.arjuna.recovery.RecoverAtomicAction_4 [com.arjuna.ats.arjuna.recovery.RecoverAtomicAction_4] - RecoverAtomicAction: transaction {0} not activated, unable to replay phase 2 commit
+ * @message com.arjuna.ats.arjuna.recovery.RecoverAtomicAction_4 [com.arjuna.ats.arjuna.recovery.RecoverAtomicAction_4] - RecoverAtomicAction: transaction {0} not activated, unable to replay phase 2 commit. Check state has not already been completed.
*/
public class RecoverAtomicAction extends AtomicAction
Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/Implementations.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/Implementations.java 2009-02-09 19:12:13 UTC (rev 25170)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/Implementations.java 2009-02-09 19:13:30 UTC (rev 25171)
@@ -33,7 +33,6 @@
import com.arjuna.ats.arjuna.gandiva.inventory.Inventory;
-import com.arjuna.ats.internal.arjuna.objectstore.CacheStoreSetup;
import com.arjuna.ats.internal.arjuna.objectstore.*;
import com.arjuna.ats.internal.arjuna.gandiva.nameservice.JNSSetup;
@@ -76,7 +75,8 @@
Inventory.inventory().addToList(new JDBCActionStoreSetup());
Inventory.inventory().addToList(new CacheStoreSetup());
Inventory.inventory().addToList(new VolatileStoreSetup());
-
+ Inventory.inventory().addToList(new LogStoreSetup());
+
Inventory.inventory().addToList(new JNSSetup());
Inventory.inventory().addToList(new PNSSetup());
Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/FileSystemStore.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/FileSystemStore.java 2009-02-09 19:12:13 UTC (rev 25170)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/FileSystemStore.java 2009-02-09 19:13:30 UTC (rev 25171)
@@ -34,7 +34,6 @@
import com.arjuna.ats.arjuna.ArjunaNames;
import com.arjuna.ats.arjuna.objectstore.*;
import com.arjuna.ats.arjuna.common.*;
-import com.arjuna.common.util.propertyservice.PropertyManager;
import com.arjuna.ats.arjuna.logging.tsLogger;
import com.arjuna.ats.arjuna.logging.FacilityCode;
@@ -43,13 +42,16 @@
import com.arjuna.ats.arjuna.gandiva.ClassName;
import com.arjuna.ats.arjuna.gandiva.ObjectName;
import com.arjuna.ats.arjuna.utils.FileLock;
-import java.io.*;
+import com.arjuna.ats.arjuna.utils.Utility;
+
import java.util.Hashtable;
import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;
import java.lang.NumberFormatException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.FileNotFoundException;
import com.arjuna.common.util.logging.*;
@@ -73,6 +75,7 @@
* @message com.arjuna.ats.internal.arjuna.objectstore.FileSystemStore_6 [com.arjuna.ats.internal.arjuna.objectstore.FileSystemStore_6] - FileSystemStore::setupStore - error from unpack object store.
* @message com.arjuna.ats.internal.arjuna.objectstore.FileSystemStore_7 [com.arjuna.ats.internal.arjuna.objectstore.FileSystemStore_7] - FileSystemStore::allTypes - could not pack entry string.
* @message com.arjuna.ats.internal.arjuna.objectstore.FileSystemStore_8 [com.arjuna.ats.internal.arjuna.objectstore.FileSystemStore_8] - FileSystemStore::createHierarchy - null directory name.
+ * @message com.arjuna.ats.internal.arjuna.objectstore.FileSystemStore_20 [com.arjuna.ats.internal.arjuna.objectstore.FileSystemStore_20] - FileSystemStore.renameFromTo - from {0} not present. Possibly renamed by crash recovery.
*/
public abstract class FileSystemStore extends ObjectStoreImple
@@ -467,6 +470,160 @@
return fileLock.unlock();
}
+ /**
+ * Unlock and close the file. Note that if the unlock fails we set
+ * the return value to false to indicate an error but rely on the
+ * close to really do the unlock.
+ */
+
+ protected boolean closeAndUnlock (File fd, FileInputStream ifile, FileOutputStream ofile)
+ {
+ if (tsLogger.arjLogger.debugAllowed())
+ {
+ tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PRIVATE,
+ FacilityCode.FAC_OBJECT_STORE, "FileSystemStore.closeAndUnlock("+fd+", "+ifile+", "+ofile+")");
+ }
+
+ boolean closedOk = unlock(fd);
+
+ try
+ {
+ if (ifile != null)
+ ifile.close();
+ else
+ ofile.close();
+ }
+ catch (Exception e)
+ {
+ closedOk = false;
+ }
+
+ return closedOk;
+ }
+
+ protected File openAndLock (String fname, int lmode, boolean create) throws ObjectStoreException
+ {
+ if (tsLogger.arjLogger.debugAllowed())
+ {
+ tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PRIVATE,
+ FacilityCode.FAC_OBJECT_STORE, "FileSystemStore.openAndLock("+fname+", "+FileLock.modeString(lmode)+", "+create+")");
+ }
+
+ // File fd = (File) FdCache(fname);
+ File fd = null;
+
+ if (fd == null)
+ {
+ fd = new File(fname);
+
+ if (!fd.exists())
+ {
+ if (createHierarchy(fname))
+ {
+ if (!lock(fd, lmode, create))
+ {
+ return null;
+ }
+ else
+ return fd;
+ }
+ else
+ throw new ObjectStoreException("FileSystemStore.openAndLock failed to create hierarchy "+fname);
+ }
+
+ if (!lock(fd, lmode, create))
+ fd = null;
+ }
+
+ return fd;
+ }
+
+ /*
+ * Renaming on Unix works if the file to rename to already exists.
+ * However, on Windows if the file exists then rename fails! So, we
+ * need to delete the file to rename to before we can rename. But, we
+ * must ensure that we don't get into any race conditions, so we
+ * exclusively lock the file to be deleted first. Failure scenarios:
+ *
+ * (i) if we crash after deleting, but before we have had a chance to
+ * rename the file, then the lock file will exist and prevent anyone
+ * from deleting the shadow.
+ *
+ * (ii) if we crash after renaming and before the lock file is removed
+ * then subsequent lock attempts will fail, and the sys admin will
+ * have to resolve. But consistency will be maintained!
+ *
+ * When JDK 1.4 supports appears we will use the file-locking facility
+ * it provides.
+ *
+ * We have to use locks at deletion, but an implementation such as
+ * ShadowNoFileLockStore can still get away with no locking elsewhere
+ * to improve performance.
+ */
+
+ protected synchronized final boolean renameFromTo (File from, File to)
+ {
+ if (!isWindows)
+ return from.renameTo(to);
+ else
+ {
+ // FileLock fl = new FileLock(to);
+
+ if (!from.exists())
+ {
+ /*
+ * from is in the cache, but not on disk. So, either
+ *
+ * (i) two different users are using the same state and
+ * should have been using the OS_SHARED flag.
+ *
+ * or
+ *
+ * (ii) crash recovery has recovered the state.
+ *
+ * If (ii) we can't force OS_SHARED on all users for the
+ * minority case. So, assume (ii) and issue a warning.
+ */
+
+ removeFromCache(from.toString());
+
+ if (tsLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ tsLogger.arjLoggerI18N.warn("com.arjuna.ats.internal.arjuna.objectstore.FileSystemStore_20",
+ new Object[]{from});
+ }
+
+ return true;
+ }
+
+ /*
+ * Let let crash recovery deal with this!
+ */
+
+ // if (fl.lock(FileLock.F_WRLCK))
+ {
+ to.delete();
+
+ boolean res = from.renameTo(to);
+
+ // fl.unlock();
+
+ return true;
+ }
+ /*
+ else
+ {
+ if (tsLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ tsLogger.arjLoggerI18N.warn("com.arjuna.ats.internal.arjuna.objectstore.ShadowingStore_21",
+ new Object[]{to});
+ }
+
+ return false;
+ }*/
+ }
+ }
+
protected FileSystemStore (int ss)
{
super(ss);
@@ -534,13 +691,13 @@
*/
protected String genPathName (Uid objUid,
- String tName, int m) throws ObjectStoreException
+ String tName, int ostype) throws ObjectStoreException
{
if (tsLogger.arjLogger.debugAllowed())
{
tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PROTECTED,
FacilityCode.FAC_OBJECT_STORE,
- "FileSystemStore.genPathName("+objUid+", "+tName+", "+m+")");
+ "FileSystemStore.genPathName("+objUid+", "+tName+", "+ostype+")");
}
String storeName = locateStore(getStoreName());
@@ -879,5 +1036,7 @@
}
}
}
+
+ private static boolean isWindows = Utility.isWindows();
}
Added: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/LogStore.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/LogStore.java (rev 0)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/LogStore.java 2009-02-09 19:13:30 UTC (rev 25171)
@@ -0,0 +1,1733 @@
+/*
+ * Copyright (C) 1998, 1999, 2000-2009,
+ *
+ * Arjuna Solutions Limited,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: LogStore.java,v 1.4 2004/11/11 12:22:21 nmcl Exp $
+ */
+
+package com.arjuna.ats.internal.arjuna.objectstore;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.RandomAccessFile;
+import java.io.IOException;
+import java.io.SyncFailedException;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Stack;
+
+import com.arjuna.ats.arjuna.ArjunaNames;
+import com.arjuna.ats.arjuna.common.Environment;
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.arjuna.common.arjPropertyManager;
+import com.arjuna.ats.arjuna.objectstore.ObjectStore;
+import com.arjuna.ats.arjuna.objectstore.ObjectStoreType;
+import com.arjuna.ats.arjuna.state.*;
+import com.arjuna.ats.arjuna.utils.FileLock;
+import com.arjuna.ats.arjuna.gandiva.ClassName;
+import com.arjuna.ats.arjuna.gandiva.ObjectName;
+
+import com.arjuna.common.util.logging.*;
+
+import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;
+
+import com.arjuna.ats.arjuna.logging.tsLogger;
+import com.arjuna.ats.arjuna.logging.FacilityCode;
+import com.arjuna.ats.internal.arjuna.objectstore.LogInstance.TransactionData;
+
+/**
+ * This is the transaction log implementation. It is optimised for the typical
+ * mode of the coordinator: write-once and never read or update. Reads or
+ * updates occur only in the case of failures, which hopefully are rare; hence
+ * the reason we optimise for the non-failure case. This does mean that recovery
+ * may take longer than when using other log implementations.
+ *
+ * There are several implementations of this approach, some of which perform better
+ * on one operating system than another. We may put them in to the source eventually
+ * and make it clear for which OS combination they are best suited. However, this
+ * implementation works well on all operating systems we have tested so is a good
+ * default.
+ *
+ * @author Mark Little (mark at arjuna.com)
+ * @version $Id: LogStore.java,v 1.4 2004/11/11 12:22:21 nmcl Exp $
+ * @since JTS 1.0.
+ */
+
+/**
+ * Algorithm used: During normal execution of a transaction, we only ever write
+ * and then remove the log entry; we never read it. Therefore, optimise for that
+ * situation. The log continually builds up in size until a maximum capacity is
+ * reached and in which case, we switch to another log. Meanwhile, the recovery
+ * manager periodically runs through completed logs and removes those that are
+ * no longer needed, truncating those that require recovery (which cannot
+ * complete at this time). When writing the initial log entry, we write a
+ * redzone marker, followed by the entry size and then the actual entry. Since a
+ * log is never shared between VMs, we only need to synchronize between the
+ * threads within a given VM: the recovery manager never works on a log that is
+ * being used by another VM anyway. The end of a log is marked with a
+ * termination record. Obviously if a crash occurs, then no such record will
+ * have been written and in which case, the recovery manager determines that the
+ * log is no longer required via timeout heuristics.
+ *
+ * The implementation normally writes removal records to the end of the log
+ * when an entry is deleted. This can be disabled and in which case we end up in
+ * the same situation as if a failure occurred as the removal record was being written
+ * or a crash happened before remove_committed could succeed on any of the other
+ * file-based object store implementations: we potentially try to commit transactions
+ * that have terminated (either committed or rolled back). In which case we ...
+ *
+ * (i) call commit on a state that has already been committed and fail to do so. Will
+ * eventually move the log record elsewhere and the administrator can deal with it.
+ *
+ * (ii) call commit on a state that has already been rolled back and again fail to do so.
+ * Will eventually move the log record elsewhere as above.
+ *
+ * If we do not write removal records then we would end up in a situation of trying to
+ * commit every log instance multiple times. As such we always try to write records but
+ * do them either synchronously or asynchronously (periodically). Of course there's still
+ * the chance that a failure will cause problems in both sync and async cases, but we
+ * have reduced the probability as well as the number of such problem items. The periodicity
+ * of this is the same as pruning the log, i.e., the same thread does both jobs.
+ *
+ * By default we synchronously add the removal marker to the log, i.e., when remove_committed
+ * returns, the marker entry has been appended to the log.
+ *
+ * NOTE: there is a race where we terminate the log instance and yet transactions may
+ * still be using it. This happens with other object store implementations too. However, in
+ * this case we could end up with a log that should be deleted because all of the entries
+ * have gone. We try to fix this up through allObjUids. If recovery works correctly then
+ * these states will eventually get deleted.
+ *
+ * TODO
+ *
+ * When truncating logs we write a shadow and then overwrite the original with the shadow
+ * when finished. If there is a crash we could end up with the shadow as well as the
+ * original. Recovery could tidy this up for us - as long as we have the original then
+ * we can continue to recover - the shadow instance may be corrupted so best to ignore
+ * it and simply delete it. But we would need to ensure that we didn't delete a shadow that
+ * is actually still active.
+ *
+ * Also we do not use a primary and backup log approach. Whenever we need a new log instance we
+ * create one. This means that there could be many logs being used at the same time, which could
+ * be a problem for disk space (unlikely these days, but possible). If this approach gets to
+ * be an issue then we can limit the number of log instances created.
+ */
+
+/**
+ * Represents a specific log instance.
+ *
+ * @author mlittle
+ *
+ */
+
+class LogInstance
+{
+ public class TransactionData
+ {
+ TransactionData (final Uid tx, final long off, final LogInstance parent)
+ {
+ txId = tx;
+ offset = off;
+ container = parent;
+ }
+
+ public final Uid txId;
+ public final long offset;
+ public final LogInstance container;
+ }
+
+ public LogInstance(String tn, long size)
+ {
+ _logName = new Uid();
+ _typeName = tn;
+ _frozen = false;
+ _totalSize = size;
+ }
+
+ /*
+ * Once frozen we will not use the log again except for recovery and
+ * pruning.
+ *
+ * We could consider another algorithm that reuses the log once it has
+ * dropped below a threshold size. Probably not worth it at the moment.
+ */
+
+ public final boolean isFrozen()
+ {
+ return _frozen;
+ }
+
+ public final void freeze() // one way operation.
+ {
+ _frozen = true;
+ }
+
+ public final int numberOfUsers()
+ {
+ return _transactions.size();
+ }
+
+ public final Uid getName()
+ {
+ return _logName;
+ }
+
+ public final String getTypeName()
+ {
+ return _typeName;
+ }
+
+ public final InputObjectState allObjUids () throws ObjectStoreException
+ {
+ OutputObjectState state = new OutputObjectState();
+ Iterator<Uid> iter = _ids.keySet().iterator();
+
+ try
+ {
+ while (iter.hasNext())
+ {
+ iter.next().pack(state);
+ }
+
+ // don't forget to null terminate
+
+ Uid.nullUid().pack(state);
+ }
+ catch (final IOException ex)
+ {
+ throw new ObjectStoreException(ex);
+ }
+
+ return new InputObjectState(state);
+ }
+
+ public final boolean present(Uid id)
+ {
+ return _ids.containsKey(id);
+ }
+
+ public final TransactionData getTxId (Uid txId)
+ {
+ return new TransactionData(txId, _used, this);
+ }
+
+ public final TransactionData addTxId (Uid txId, long size)
+ {
+ TransactionData td = new TransactionData(txId, _used, this);
+
+ _transactions.add(td); // allow multiple entries in the same log
+ _ids.put(txId, txId);
+ _used += size;
+
+ return td;
+ }
+
+ public final long remaining()
+ {
+ return _totalSize - _used;
+ }
+
+ public final void resize (long size)
+ {
+ _totalSize = size;
+ }
+
+ public String toString()
+ {
+ return "LogInstance < " + _logName + ", " + _typeName + ", "
+ + numberOfUsers() + ", " + remaining() + " >";
+ }
+
+ private Uid _logName;
+ private String _typeName;
+ private boolean _frozen;
+ private Stack<TransactionData> _transactions = new Stack<TransactionData>();
+ private HashMap<Uid, Uid> _ids = new HashMap<Uid, Uid>();
+ private long _used = 0;
+ private long _totalSize;
+}
+
+/*
+ * Time based, but it would be good to have it triggered on the number of
+ * entries that need to be added.
+ */
+
+class LogPurger extends Thread
+{
+ private enum Status {ACTIVE, PASSIVE, TERMINATED};
+
+ class LogElement
+ {
+ public LogElement(final String t, final Uid u, final int s)
+ {
+ tn = t;
+ uid = u;
+ state = s;
+ }
+
+ public String tn;
+ public Uid uid;
+ public int state;
+ };
+
+ /*
+ * Purge every N seconds.
+ *
+ * TODO purge after number of logs > M
+ */
+
+ public static final long DEFAULT_PURGE_TIME = 100000; // 100 seconds
+
+ public LogPurger(LogStore instance)
+ {
+ this(instance, DEFAULT_PURGE_TIME);
+ }
+
+ public LogPurger(LogStore instance, long purgeTime)
+ {
+ _objStore = instance;
+ _purgeTime = purgeTime;
+ }
+
+ public void addRemovedState(final Uid u, final String tn, final int state)
+ {
+ synchronized (_entries)
+ {
+ _entries.put(u, new LogElement(tn, u, state));
+ }
+ }
+
+ public void purge()
+ {
+ try
+ {
+ _objStore.truncateLogs(true);
+ }
+ catch (final Exception ex)
+ {
+ }
+ }
+
+ public void writeRemovalEntries()
+ {
+ synchronized (_entries)
+ {
+ if (_entries.size() > 0)
+ {
+ Collection<LogElement> entries = _entries.values();
+ Iterator<LogElement> iter = entries.iterator();
+
+ while (iter.hasNext())
+ {
+ LogElement val = iter.next();
+
+ try
+ {
+ _objStore.removeState(val.uid, val.tn, val.state);
+ }
+ catch (final Exception ex)
+ {
+ // TODO log warning, but there's nothing else we can do.
+ }
+ }
+
+ _entries.clear();
+ }
+ }
+ }
+
+ /**
+ * Poke the thread into doing some work even if it normally
+ * would not.
+ */
+
+ public void trigger ()
+ {
+ synchronized (_lock)
+ {
+ if (_status == Status.PASSIVE)
+ _lock.notify();
+ }
+ }
+
+ public void run()
+ {
+ for (;;)
+ {
+ // TODO activate thread during read and get it to write deleted states
+
+ try
+ {
+ synchronized (_lock)
+ {
+ _status = Status.PASSIVE;
+
+ _lock.wait(_purgeTime);
+ }
+ }
+ catch (final Exception ex)
+ {
+ _status = Status.ACTIVE;
+ }
+
+ /*
+ * Write any asynchronous delete records.
+ */
+
+ System.err.println("**THREAD RUNNING");
+
+ writeRemovalEntries();
+
+ /*
+ * Now truncate any logs we've been working on.
+ */
+
+ try
+ {
+ _objStore.truncateLogs();
+ }
+ catch (final Exception ex)
+ {
+ }
+ }
+
+ // _status = Status.TERMINATED;
+ }
+
+ private HashMap<Uid, LogElement> _entries = new HashMap<Uid, LogElement>();
+ private long _purgeTime;
+ private LogStore _objStore;
+ private Status _status;
+ private Object _lock = new Object();
+}
+
+class PurgeShutdownHook extends Thread
+{
+ public PurgeShutdownHook(LogPurger purger)
+ {
+ _purger = purger;
+ }
+
+ public void run()
+ {
+ _purger.writeRemovalEntries(); // flush everything in the cache first.
+ _purger.purge();
+ }
+
+ private LogPurger _purger;
+}
+
+/*
+ * Derive it directly from FSStore for now, simply because we are unlikely to
+ * have many log instances in the store. However, if it becomes a problem, then
+ * we can simply derive from the HashedActionStore.
+ */
+
+public class LogStore extends FileSystemStore
+{
+ public static final long LOG_SIZE = 10 * 1024 * 1024; // default maximum log size in bytes
+
+ private static final String FILE_MODE = "rwd";
+
+ public int typeIs()
+ {
+ return ObjectStoreType.ACTIONLOG;
+ }
+
+ /**
+ * Normally returns the current state of the log entry. However, this is
+ * never called during normal (non-recovery) execution. Therefore, the
+ * overhead of having to scan all of the logs (if it's not one we're using)
+ * is minimal.
+ */
+
+ public int currentState(Uid objUid, String tName)
+ throws ObjectStoreException
+ {
+ InputObjectState ios = new InputObjectState();
+
+ /*
+ * TODO
+ *
+ * It's possible that the entry has been marked to be deleted but
+ * that the removal entry hasn't been written yet. We could check the
+ * async cache. However, since we really only care about this during
+ * recovery, it's not going to cause us problems anyway.
+ */
+
+ if (allObjUids(tName, ios, ObjectStore.OS_UNKNOWN))
+ {
+ Uid tempUid = new Uid(Uid.nullUid());
+
+ do
+ {
+ try
+ {
+ tempUid.unpack(ios);
+ }
+ catch (final Exception ex)
+ {
+ ex.printStackTrace();
+
+ return ObjectStore.OS_UNKNOWN;
+ }
+
+ if (tempUid.equals(objUid))
+ return ObjectStore.OS_COMMITTED;
+
+ } while (tempUid.notEquals(Uid.nullUid()));
+
+ return ObjectStore.OS_UNKNOWN;
+ }
+ else
+ return ObjectStore.OS_UNKNOWN;
+ }
+
+ /**
+ * Commit a previous write_state operation which was made with the SHADOW
+ * StateType argument. This is achieved by renaming the shadow and removing
+ * the hidden version.
+ */
+
+ public boolean commit_state(Uid objUid, String tName)
+ throws ObjectStoreException
+ {
+ return true;
+ }
+
+ public boolean hide_state(Uid u, String tn) throws ObjectStoreException
+ {
+ if (tsLogger.arjLogger.debugAllowed())
+ {
+ tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_OBJECT_STORE,
+ "LogStore.hide_state(" + u + ", " + tn + ")");
+ }
+
+ return false;
+ }
+
+ public boolean reveal_state(Uid u, String tn) throws ObjectStoreException
+ {
+ if (tsLogger.arjLogger.debugAllowed())
+ {
+ tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_OBJECT_STORE,
+ "LogStore.reveal_state(" + u + ", " + tn + ")");
+ }
+
+ return false;
+ }
+
+ public InputObjectState read_uncommitted(Uid u, String tn)
+ throws ObjectStoreException
+ {
+ if (tsLogger.arjLogger.debugAllowed())
+ {
+ tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_OBJECT_STORE,
+ "LogStore.read_uncommitted(" + u + ", " + tn + ")");
+ }
+
+ return null;
+ }
+
+ public boolean remove_uncommitted(Uid u, String tn)
+ throws ObjectStoreException
+ {
+ if (tsLogger.arjLogger.debugAllowed())
+ {
+ tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_OBJECT_STORE,
+ "LogStore.remove_uncommitted(" + u + ", " + tn + ")");
+ }
+
+ return false;
+ }
+
+ public boolean write_committed(Uid storeUid, String tName,
+ OutputObjectState state) throws ObjectStoreException
+ {
+ if (tsLogger.arjLogger.debugAllowed())
+ {
+ tsLogger.arjLogger
+ .debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC,
+ FacilityCode.FAC_OBJECT_STORE,
+ "LogStore.write_committed(" + storeUid + ", "
+ + tName + ")");
+ }
+
+ try
+ {
+ return super.write_committed(storeUid, tName, state);
+ }
+ catch (ObjectStoreException ex)
+ {
+ removeFromLog(storeUid);
+
+ throw ex;
+ }
+ }
+
+ public boolean write_uncommitted(Uid u, String tn, OutputObjectState s)
+ throws ObjectStoreException
+ {
+ if (tsLogger.arjLogger.debugAllowed())
+ {
+ tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_OBJECT_STORE,
+ "LogStore.write_uncommitted(" + u + ", " + tn + ", " + s
+ + ")");
+ }
+
+ return false;
+ }
+
+ public boolean allLogUids (String tName, InputObjectState state, int match) throws ObjectStoreException
+ {
+ return super.allObjUids(tName, state, match);
+ }
+
+ /**
+ * This is a recovery-only method and should not be called during normal
+ * execution. As such we need to load in all of the logs we can find that
+ * aren't already loaded (or activated).
+ */
+
+ public boolean allObjUids(String tName, InputObjectState state, int match)
+ throws ObjectStoreException
+ {
+ /*
+ * match will always be OS_COMMITTED since that's all we ever write for
+ * the logs.
+ */
+
+ // in case of asynchronous removals trigger the purger now.
+
+ _purger.trigger();
+
+ /*
+ * Get a list of logs. Load them in to memory if we aren't already
+ * working on them/it. But we can prune the entry once we're
+ * finished or the memory footprint will grow. We should do this
+ * for all frozen entries eventually too.
+ */
+
+ InputObjectState logs = new InputObjectState();
+ OutputObjectState objUids = new OutputObjectState();
+
+ /*
+ * We never call this method except during recovery. As such we shouldn't
+ * need to worry about optimizations such as checking whether or not the
+ * log is in current working memory.
+ */
+
+ if (!super.allObjUids(tName, logs, match))
+ return false;
+ else
+ {
+ /*
+ * Now we have all of the log names let's attach to each one
+ * and locate the committed instances (not deleted.)
+ */
+
+ Uid logName = new Uid(Uid.nullUid());
+
+ try
+ {
+ do
+ {
+ logName.unpack(logs);
+
+ if (logName.notEquals(Uid.nullUid()))
+ {
+ /*
+ * Could check to see if log is in current working memory.
+ */
+
+ /*
+ * TODO
+ *
+ * First purge the log if we can, but we need to know that
+ * we're not playing with an instance that is being manipulated
+ * from another VM instance.
+ */
+
+ ArrayList<InputObjectState> txs = scanLog(logName, tName);
+
+ if (txs.size() > 0)
+ {
+ for (int i = 0; i < txs.size(); i++)
+ {
+ txs.get(i).stateUid().pack(objUids);
+ }
+ }
+ }
+ } while (logName.notEquals(Uid.nullUid()));
+
+ // remember null terminator
+
+ Uid.nullUid().pack(objUids);
+
+ state.setBuffer(objUids.buffer());
+ }
+ catch (final IOException ex)
+ {
+ ex.printStackTrace();
+
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ public ClassName className()
+ {
+ return ArjunaNames.Implementation_ObjectStore_ActionLogStore();
+ }
+
+ public static ClassName name()
+ {
+ return ArjunaNames.Implementation_ObjectStore_ActionLogStore();
+ }
+
+ /**
+ * Have to return as a ShadowingStore because of inheritance.
+ */
+
+ public static FileSystemStore create()
+ {
+ return new LogStore("");
+ }
+
+ /**
+ * @message com.arjuna.ats.internal.arjuna.objectstore.LogStore_2
+ * [com.arjuna.ats.internal.arjuna.objectstore.LogStore_2] -
+ * LogStore.create caught: {0}
+ * @message com.arjuna.ats.internal.arjuna.objectstore.LogStore_3
+ * [com.arjuna.ats.internal.arjuna.objectstore.LogStore_3] - Could
+ * not parse {0} as log size: using default value.
+ */
+
+ public static FileSystemStore create(Object[] param)
+ {
+ if (param == null)
+ return null;
+
+ String location = (String) param[0];
+ Integer shareStatus = (Integer) param[1];
+ int ss = ObjectStore.OS_UNSHARED;
+
+ if (shareStatus != null)
+ {
+ try
+ {
+ if (shareStatus.intValue() == ObjectStore.OS_SHARED)
+ ss = ObjectStore.OS_SHARED;
+ }
+ catch (Exception e)
+ {
+ if (tsLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ tsLogger.arjLoggerI18N
+ .warn(
+ "com.arjuna.ats.internal.arjuna.objectstore.LogStore_2",
+ new Object[]
+ { e });
+ }
+ }
+ }
+
+ return new LogStore(location, ss);
+ }
+
+ public static FileSystemStore create(ObjectName param)
+ {
+ if (param == null)
+ return null;
+ else
+ return new LogStore(param);
+ }
+
+ protected LogStore(String locationOfStore)
+ {
+ this(locationOfStore, ObjectStore.OS_UNSHARED);
+ }
+
+ protected LogStore(String locationOfStore, int shareStatus)
+ {
+ super(shareStatus);
+
+ try
+ {
+ setupStore(locationOfStore);
+ }
+ catch (ObjectStoreException e)
+ {
+ if (tsLogger.arjLoggerI18N.isWarnEnabled())
+ tsLogger.arjLogger.warn(e.getMessage());
+
+ super.makeInvalid();
+
+ throw new com.arjuna.ats.arjuna.exceptions.FatalError(e.toString());
+ }
+ }
+
+ protected LogStore()
+ {
+ this(ObjectStore.OS_UNSHARED);
+ }
+
+ protected LogStore(int shareStatus)
+ {
+ super(shareStatus);
+ }
+
+ protected LogStore(ObjectName objName)
+ {
+ super(objName);
+ }
+
+ protected synchronized boolean setupStore(String location)
+ throws ObjectStoreException
+ {
+ if (!checkSync)
+ {
+ String syncOpt = arjPropertyManager.propertyManager
+ .getProperty(Environment.TRANSACTION_SYNC);
+
+ if (syncOpt != null)
+ {
+ if (syncOpt.compareTo("OFF") == 0)
+ syncOff();
+ else
+ syncOn();
+ }
+ else
+ syncOn();
+
+ checkSync = true;
+ }
+
+ if (_purger == null)
+ {
+ _purger = new LogPurger(this, _purgeTime);
+ _purger.setDaemon(true);
+
+ Runtime.getRuntime()
+ .addShutdownHook(new PurgeShutdownHook(_purger));
+
+ _purger.start();
+ }
+
+ return super.setupStore(location);
+ }
+
+ /**
+ * Unlock and close the file. Note that if the unlock fails we set the
+ * return value to false to indicate an error but rely on the close to
+ * really do the unlock.
+ */
+
+ protected boolean unlockAndClose(File fd, RandomAccessFile rf)
+ {
+ if (tsLogger.arjLogger.debugAllowed())
+ {
+ tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PRIVATE, FacilityCode.FAC_OBJECT_STORE,
+ "RandomAccessFile.unlockAndClose(" + fd + ", " + rf + ")");
+ }
+
+ boolean closedOk = unlock(fd);
+
+ try
+ {
+ rf.close();
+ }
+ catch (Exception e)
+ {
+ closedOk = false;
+ }
+
+ return closedOk;
+ }
+
+ /**
+ * write_state saves the ObjectState in a file named by the type and Uid of
+ * the ObjectState. If the second argument is SHADOW, then the file name is
+ * different so that a subsequent commit_state invocation will rename the
+ * file.
+ *
+ * We need to make sure that each entry is written to the next empty location
+ * in the log even if there's already an entry for this tx.
+ */
+
+ protected boolean write_state(Uid objUid, String tName,
+ OutputObjectState state, int ft) throws ObjectStoreException
+ {
+ if (tsLogger.arjLogger.debugAllowed())
+ {
+ tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PROTECTED,
+ FacilityCode.FAC_OBJECT_STORE,
+ "ShadowingStore.write_state(" + objUid + ", " + tName
+ + ", " + ObjectStore.stateTypeString(ft) + ")");
+ }
+
+ if (!storeValid())
+ return false;
+
+ String fname = null;
+ File fd = null;
+
+ if (tName != null)
+ {
+ int imageSize = (int) state.length();
+ byte[] uidString = objUid.stringForm().getBytes();
+ int buffSize = _redzone.length + uidString.length + imageSize + 8; // don't put in endOfLog since we keep overwriting that.
+ RandomAccessFile ofile = null;
+ java.nio.channels.FileLock lock = null;
+
+ if (imageSize > 0)
+ {
+ TransactionData theLogEntry = getLogName(objUid, tName, buffSize); // always adds entry to log
+ LogInstance theLog = theLogEntry.container;
+
+ if (theLog == null)
+ throw new ObjectStoreException();
+
+ fname = genPathName(theLog.getName(), tName, ft);
+ fd = openAndLock(fname, FileLock.F_WRLCK, true);
+
+ if (fd == null)
+ {
+ if (tsLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ tsLogger.arjLoggerI18N.warn(
+ "com.arjuna.ats.internal.arjuna.objectstore.ShadowingStore_18",
+ new Object[]
+ { fname });
+ }
+
+ return false;
+ }
+
+ boolean setLength = !fd.exists();
+
+ try
+ {
+ ofile = new RandomAccessFile(fd, FILE_MODE);
+
+ if (setLength)
+ {
+ ofile.setLength(_maxFileSize);
+ }
+ else
+ {
+ // may have to resize file if we keep updating this transaction info
+
+ if (theLog.remaining() < buffSize)
+ {
+ long size = ofile.length() + buffSize - theLog.remaining();
+
+ ofile.setLength(size);
+
+ theLog.resize(size);
+ }
+ }
+
+ java.nio.ByteBuffer buff = java.nio.ByteBuffer.allocate(buffSize);
+
+ buff.put(_redzone);
+ buff.putInt(uidString.length);
+ buff.put(uidString);
+ buff.putInt(imageSize);
+ buff.put(state.buffer());
+
+ synchronized (_lock)
+ {
+ ofile.seek(theLogEntry.offset);
+
+ ofile.write(buff.array());
+ }
+ }
+ catch (SyncFailedException e)
+ {
+ unlockAndClose(fd, ofile);
+
+ throw new ObjectStoreException(
+ "ShadowingStore::write_state() - write failed to sync for "
+ + fname);
+ }
+ catch (FileNotFoundException e)
+ {
+ unlockAndClose(fd, ofile);
+
+ e.printStackTrace();
+
+ throw new ObjectStoreException(
+ "ShadowingStore::write_state() - write failed to locate file "
+ + fname + ": " + e);
+ }
+ catch (IOException e)
+ {
+ unlockAndClose(fd, ofile);
+
+ e.printStackTrace();
+
+ throw new ObjectStoreException(
+ "ShadowingStore::write_state() - write failed for "
+ + fname + ": " + e);
+ }
+ finally
+ {
+ try
+ {
+ if (lock != null)
+ lock.release();
+ }
+ catch (IOException ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ if (!unlockAndClose(fd, ofile))
+ {
+ if (tsLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ tsLogger.arjLoggerI18N
+ .warn(
+ "com.arjuna.ats.internal.arjuna.objectstore.ShadowingStore_19",
+ new Object[]
+ { fname });
+ }
+ }
+
+ super.addToCache(fname);
+
+ return true;
+ }
+ else
+ throw new ObjectStoreException(
+ "ShadowStore::write_state - "
+ + tsLogger.log_mesg
+ .getString("com.arjuna.ats.internal.arjuna.objectstore.notypenameuid")
+ + objUid);
+ }
+
+ /**
+ * Shouldn't be called during normal execution only during recovery.
+ */
+
+ protected InputObjectState read_state(Uid u, String tn, int s)
+ throws ObjectStoreException
+ {
+ /*
+ * In case of asynchronous removals of state, let's trigger the purger
+ * thread to flush its cache now. Try to avoid false positives during
+ * recovery wherever possible!
+ */
+
+ _purger.trigger();
+
+ /*
+ * It's possible that recovery got hold of a state id while it was
+ * being deleted (marker written and pruning thread not yet active).
+ * In which case when it comes to do a read it's not going to find
+ * the state there any longer. Conversely it's possible that it could do
+ * a read on a state that is about to be deleted. Recovery should be
+ * able to cope with these edge cases.
+ */
+
+ TransactionData td = getLogName(u, tn, -1);
+
+ if (td == null)
+ throw new ObjectStoreException();
+
+ ArrayList<InputObjectState> states = scanLog(td.container.getName(), tn);
+
+ if ((states == null) || (states.size() == 0))
+ return null;
+
+ for (int i = 0; i < states.size(); i++)
+ {
+ if (states.get(i).stateUid().equals(u))
+ return states.get(i);
+ }
+
+ /*
+ * Not in the log, so probably removed by now.
+ */
+
+ return null;
+ }
+
+ /**
+ * Does nothing except indicate that this thread is finished with the log on
+ * behalf of this transaction.
+ */
+
+ protected boolean remove_state(Uid u, String tn, int s)
+ throws ObjectStoreException
+ {
+ // maybe write a removal entry into the log.
+
+ try
+ {
+ /*
+ * If we don't add a removal entry then recovery has to work a
+ * little harder to figure things out. But it has to cater for the
+ * situation where a removal record write fails anyway, so this
+ * shouldn't be a big deal. On the up side it improves performance
+ * by 30% for this implementation, which is a 40% improvement over
+ * the basic file-based log!
+ */
+
+ /*
+ * If we write a removal record as a separate entity to the original
+ * data item then we cannot ensure that they will go into the same
+ * log with a pre-set size for the log. Therefore, we have two
+ * options:
+ *
+ * (i) find the old entry in the log and mark it as deleted.
+ * (ii) increase the size of the log to accommodate the removal entry.
+ *
+ * We currently go for option (ii) as this is the quickest.
+ */
+
+ if (_synchronousRemoval)
+ {
+ OutputObjectState removalState = new OutputObjectState(u, tn);
+
+ removalState.packBytes(_removedState);
+
+ if (!write_state(u, tn, removalState, s))
+ throw new ObjectStoreException();
+ }
+ else
+ _purger.addRemovedState(u, tn, s);
+ }
+ catch (IOException ex)
+ {
+ throw new ObjectStoreException(ex.toString());
+ }
+ finally
+ {
+ removeFromLog(u);
+ }
+
+ return true;
+ }
+
+ protected boolean lock(File fd, int lmode, boolean create)
+ {
+ return true;
+ }
+
+ protected boolean unlock(File fd)
+ {
+ return true;
+ }
+
+ protected String genPathName (Uid objUid, String tName, int ft) throws ObjectStoreException
+ {
+ String fname = super.genPathName(objUid, tName, ft);
+
+ if (ft == ObjectStore.OS_UNCOMMITTED)
+ fname = fname + HIDDENCHAR;
+
+ return fname;
+ }
+
+ boolean removeState(Uid u, String tn, int s) throws ObjectStoreException
+ {
+ try
+ {
+ OutputObjectState removalState = new OutputObjectState(u, tn);
+
+ removalState.packBytes(_removedState);
+
+ if (!write_state(u, tn, removalState, s))
+ throw new ObjectStoreException();
+ }
+ catch (IOException ex)
+ {
+ throw new ObjectStoreException(ex.toString());
+ }
+
+ return true;
+ }
+
+ boolean truncateLogs () throws ObjectStoreException
+ {
+ return truncateLogs(false);
+ }
+
+ boolean truncateLogs (boolean force) throws ObjectStoreException
+ {
+ synchronized (_logNames)
+ {
+ Iterator<LogInstance> iter = _logNames.iterator();
+
+ /*
+ * Only do this for logs that are full to save time,
+ * except if we are terminating.
+ */
+
+ while (iter.hasNext())
+ {
+ boolean delete = false;
+ LogInstance log = null;
+
+ try
+ {
+ log = iter.next();
+
+ if (log.isFrozen() || force)
+ delete = truncateLog(log, force);
+ }
+ catch (final Exception ex)
+ {
+ // TODO log
+ }
+
+ if (delete)
+ iter.remove();
+ }
+ }
+
+ return true;
+ }
+
+ /*
+ * Return true if the log needs to be deleted.
+ */
+
+ private final boolean truncateLog(final LogInstance log, boolean force) throws ObjectStoreException
+ {
+ boolean delete = false;
+
+ synchronized (_lock)
+ {
+ File fd = new File(genPathName(log.getName(), log.getTypeName(), ObjectStore.OS_COMMITTED));
+
+ try
+ {
+ /*
+ * Create a list of ObjectState entries.
+ */
+
+ ArrayList<InputObjectState> objectStates = scanLog(log.getName(), log.getTypeName());
+
+ /*
+ * At this stage we should now have a list of unique
+ * entries. Write them back to the log. Do this
+ * atomically! If the list is empty then delete the
+ * file!
+ */
+
+ if ((objectStates != null) && (objectStates.size() > 0))
+ {
+ /*
+ * If we are terminating then we can truncate the log to the
+ * real size needed to contain the existing entries since we
+ * will not use it again within another VM except for
+ * recovery purposes.
+ */
+
+ String fname = genPathName(log.getName(), log.getTypeName(), ObjectStore.OS_UNCOMMITTED);
+ File fd2 = openAndLock(fname, FileLock.F_WRLCK, true);
+ RandomAccessFile oFile = new RandomAccessFile(fd2, FILE_MODE);
+ int size = 0;
+
+ oFile.setLength(_maxFileSize);
+
+ for (int i = 0; i < objectStates.size(); i++)
+ {
+ byte[] uidString = objectStates.get(i).stateUid().stringForm().getBytes();
+ int buffSize = _redzone.length + uidString.length + objectStates.get(i).buffer().length + 8;
+ java.nio.ByteBuffer buff = java.nio.ByteBuffer.allocate(buffSize);
+
+ size += buffSize;
+
+ try
+ {
+ buff.put(_redzone);
+ buff.putInt(uidString.length);
+ buff.put(uidString);
+ buff.putInt(objectStates.get(i).buffer().length);
+ buff.put(objectStates.get(i).buffer(),0, objectStates.get(i).buffer().length);
+ }
+ catch (final Exception ex)
+ {
+ ex.printStackTrace();
+
+ // TODO log
+
+ fd2.delete();
+
+ unlockAndClose(fd2, oFile);
+
+ throw new ObjectStoreException(ex.toString());
+ }
+ }
+
+ try
+ {
+ if (force)
+ {
+ oFile.setLength(size);
+
+ log.freeze();
+ }
+
+ fd2.renameTo(fd);
+ }
+ catch (final Exception ex)
+ {
+ ex.printStackTrace();
+
+ // TODO log
+
+ throw new ObjectStoreException(ex.toString());
+ }
+ finally
+ {
+ unlockAndClose(fd2, oFile);
+ }
+ }
+ else
+ {
+ /*
+ * Delete the log if there are no states in it. We could
+ * keep the file around and reuse it, but the advantage of
+ * this is small compared to having to cope with reusing old
+ * log instances.
+ */
+
+ fd.delete();
+
+ /*
+ * Remember to remove the information from the memory cache.
+ */
+
+ delete = true;
+ }
+ }
+ catch (final ObjectStoreException ex)
+ {
+ ex.printStackTrace();
+
+ throw ex;
+ }
+ catch (final Exception ex)
+ {
+ ex.printStackTrace();
+
+ throw new ObjectStoreException(ex.toString());
+ }
+ }
+
+ return delete;
+ }
+
+ private final ArrayList<InputObjectState> scanLog (final Uid logName, final String typeName) throws ObjectStoreException
+ {
+ /*
+ * Make sure no new entries can be created while we scan.
+ */
+
+ synchronized (_lock)
+ {
+ try
+ {
+ String fname = genPathName(logName, typeName, ObjectStore.OS_COMMITTED);
+ File fd = openAndLock(fname, FileLock.F_WRLCK, true);
+ RandomAccessFile iFile = new RandomAccessFile(fd, FILE_MODE);
+ // iFile.getChannel().lock();
+
+ try
+ {
+ /*
+ * Create a list of ObjectState entries.
+ */
+
+ ArrayList<InputObjectState> objectStates = new ArrayList<InputObjectState>();
+
+ iFile.seek(0); // make sure we're at the start
+
+ while (iFile.getFilePointer() < iFile.length())
+ {
+ byte[] buff = new byte[_redzone.length];
+
+ iFile.read(buff);
+
+ if (!redzoneProtected(buff))
+ {
+ // end
+
+ break;
+
+ /*
+ * TODO add an end-of-log entry and check for that. Currently just assume
+ * that no RZ means end, rather than corruption.
+ */
+ }
+ else
+ {
+ int uidSize = iFile.readInt();
+ byte[] uidString = new byte[uidSize];
+
+ iFile.read(uidString);
+
+ Uid txId = new Uid(new String(uidString));
+ int imageSize = iFile.readInt();
+ byte[] imageState = new byte[imageSize];
+
+ iFile.read(imageState);
+
+ try
+ {
+ InputObjectState state = new InputObjectState(
+ txId, "", imageState);
+
+ objectStates.add(state);
+ }
+ catch (final Exception ex)
+ {
+ ex.printStackTrace();
+
+ throw new ObjectStoreException(ex.toString());
+ }
+ }
+ }
+
+ unlockAndClose(fd, iFile);
+ iFile = null;
+
+ /*
+ * At this stage we now have a list of ObjectState entries.
+ * Now we need to go through and prune the list. This is
+ * complicated by the fact that there can be 1.. entries for
+ * a specific transaction since we continually update the
+ * log as we drive recovery. If an entry hasn't been deleted
+ * then we will keep the latest one we find.
+ */
+
+ /*
+ * First search for those entries that have been deleted.
+ */
+
+ ArrayList<InputObjectState> deletedLogs = new ArrayList<InputObjectState>();
+
+ for (int i = 0; i < objectStates.size(); i++)
+ {
+ InputObjectState curr = objectStates.get(i);
+
+ try
+ {
+ if (Arrays.equals(curr.unpackBytes(), _removedState))
+ {
+ deletedLogs.add(curr);
+ }
+ else
+ curr.reread(); // don't forget to reset the read pointer!
+ }
+ catch (final Exception ex)
+ {
+ // if not a delete record then the first entry won't
+ // be an the defined byte array.
+ }
+ }
+
+ if (deletedLogs.size() > 0)
+ {
+ /*
+ * make sure we remove them from the first list to save time.
+ */
+
+ objectStates.removeAll(deletedLogs);
+
+ deleteEntries(objectStates, deletedLogs);
+
+ /*
+ * At this stage we should only have entries that refer
+ * to in-flight transactions. Go through the list and
+ * remove N-1 references for each transaction id.
+ */
+
+ pruneEntries(objectStates);
+
+ /*
+ * Now return the list of committed entries.
+ */
+
+ return objectStates;
+ }
+ else
+ return objectStates;
+ }
+ finally
+ {
+ if (iFile != null)
+ unlockAndClose(fd, iFile);
+ }
+ }
+ catch (final ObjectStoreException ex)
+ {
+ ex.printStackTrace();
+
+ throw ex;
+ }
+ catch (final Exception ex)
+ {
+ ex.printStackTrace();
+
+ throw new ObjectStoreException(ex.toString());
+ }
+ }
+ }
+
+ private final boolean redzoneProtected(final byte[] buff)
+ {
+ for (int i = 0; i < _redzone.length; i++)
+ {
+ if (buff[i] != _redzone[i])
+ return false;
+ }
+
+ return true;
+ }
+
+ private final void deleteEntries(ArrayList<InputObjectState> allStates,
+ ArrayList<InputObjectState> deletedStates)
+ {
+ /*
+ * Look through the remaining states for entries that have been deleted.
+ */
+
+ for (int i = 0; i < deletedStates.size(); i++)
+ {
+ Uid txId = deletedStates.get(i).stateUid();
+
+ for (int j = 0; j < allStates.size(); j++)
+ {
+ if (allStates.get(j).stateUid().equals(txId))
+ allStates.remove(j);
+ }
+ }
+
+ deletedStates.clear();
+ }
+
+ private final void pruneEntries(ArrayList<InputObjectState> allStates)
+ {
+ /*
+ * The ArrayList is ordered with the earliest entries first.
+ */
+
+ for (int j = allStates.size() - 1; j >= 0; j--)
+ {
+ Uid txId = allStates.get(j).stateUid();
+
+ for (int i = 0; i < j; i++)
+ {
+ if (allStates.get(i).stateUid().equals(txId))
+ allStates.remove(i);
+ }
+ }
+ }
+
+ /*
+ * We maintain a list of log identifiers and the number of threads using
+ * them. If a log size goes over the maximum allowed, then we swap all
+ * threads to a new log with the exception of those that are currently using
+ * the old log.
+ *
+ * We always add a new entry to the log even if one already exists.
+ *
+ * Because normally we are writing to the log we pass in the size that we need to
+ * accommodate. However, during recovery we need to read the state yet still
+ * need the log name. So if we pass a size of -1 this signifies only to
+ * return the log data and not allocate space for a new instance.
+ */
+
+ private final TransactionData getLogName (Uid txid, String tName, long size)
+ throws ObjectStoreException
+ {
+ synchronized (_logNames)
+ {
+ Iterator<LogInstance> iter = _logNames.iterator();
+ LogInstance entry = null;
+
+ /*
+ * First check to see if the TxId is in an existing log. Always
+ * return the same log instance for the same txid so we can
+ * keep all data in the same location. This may mean that we have
+ * to extend the size of the log over time to accommodate situations
+ * where the log is modified but not deleted for a while, e.g., during
+ * recovery.
+ */
+
+ while (iter.hasNext())
+ {
+ entry = (LogInstance) iter.next();
+
+ if (entry.present(txid))
+ {
+ if (size == -1) // we are reading only
+ return entry.getTxId(txid);
+ else
+ return entry.addTxId(txid, size);
+ }
+ }
+
+ /*
+ * If we get here then this TxId isn't in one of the
+ * logs we are maintaining currently. So go back through
+ * the list of logs and find one that is small enough
+ * for us to use. The first one with room will do.
+ */
+
+ iter = _logNames.iterator();
+
+ while (iter.hasNext())
+ {
+ entry = (LogInstance) iter.next();
+
+ if (!entry.isFrozen())
+ {
+ if (entry.remaining() > size)
+ {
+ return entry.addTxId(txid, size);
+ }
+ else
+ {
+ /*
+ * TODO
+ *
+ * When can we remove the information about this
+ * log from memory? If we do it too soon then it's possible
+ * that delete entries will not go into the right log. If we
+ * leave it too late then the memory footprint increases. Prune
+ * the entry when we prune the log from disk?
+ */
+
+ entry.freeze();
+ }
+ }
+ }
+
+ // if we get here, then we need to create a new log
+
+ entry = new LogInstance(tName, _maxFileSize);
+ _logNames.add(entry);
+
+ return entry.addTxId(txid, size);
+ }
+ }
+
+ private final void removeFromLog(Uid txid)
+ {
+ if (_synchronousRemoval)
+ {
+ synchronized (_logNames)
+ {
+ Iterator<LogInstance> iter = _logNames.iterator();
+ LogInstance entry = null;
+
+ while (iter.hasNext())
+ {
+ entry = (LogInstance) iter.next();
+
+ if (entry.present(txid))
+ {
+ //entry.removeTxId(txid);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ private static boolean checkSync = false;
+
+ private static Object _lock = new Object();
+
+ private static ArrayList<LogInstance> _logNames = new ArrayList<LogInstance>();
+
+ private static long _maxFileSize = LOG_SIZE;
+ private static long _purgeTime = LogPurger.DEFAULT_PURGE_TIME;
+
+ private static LogPurger _purger;
+ private static boolean _synchronousRemoval = false;
+
+ private static final byte[] _redzone = { 0x2, 0x4, 0x6, 0x8 };
+
+ private static final byte[] _removedState = { 0xd, 0xe, 0xa, 0xd, 0xb, 0xe, 0xe, 0xf };
+
+ private static final char HIDDENCHAR = '~';
+
+ static
+ {
+ try
+ {
+ String removeMarkers = arjPropertyManager.propertyManager
+ .getProperty(Environment.TRANSACTION_LOG_SYNC_REMOVAL,
+ "TRUE");
+
+ if (removeMarkers.equalsIgnoreCase("true"))
+ _synchronousRemoval = true;
+
+ String purgeTime = arjPropertyManager.propertyManager.getProperty(Environment.TRANSACTION_LOG_PURGE_TIME);
+
+ if (purgeTime != null)
+ {
+ try
+ {
+ _purgeTime = Long.parseLong(purgeTime);
+ }
+ catch (final Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+
+ String logSize = arjPropertyManager.propertyManager.getProperty(Environment.TRANSACTION_LOG_SIZE);
+
+ if (logSize != null)
+ {
+ try
+ {
+ _maxFileSize = Long.parseLong(logSize);
+ }
+ catch (final Exception ex)
+ {
+ if (tsLogger.arjLoggerI18N.isWarnEnabled())
+ {
+ tsLogger.arjLoggerI18N
+ .warn(
+ "com.arjuna.ats.internal.arjuna.objectstore.LogStore_3",
+ new Object[]
+ { logSize });
+ }
+ }
+ }
+ }
+ catch (final Exception ex)
+ {
+ throw new ExceptionInInitializerError(ex.toString());
+ }
+ }
+}
Added: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/LogStoreSetup.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/LogStoreSetup.java (rev 0)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/LogStoreSetup.java 2009-02-09 19:13:30 UTC (rev 25171)
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 1998, 1999, 2000,
+ *
+ * Arjuna Solutions Limited,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: ActionStoreSetup.java,v 1.1 2003/06/19 10:50:32 nmcl Exp $
+ */
+
+package com.arjuna.ats.internal.arjuna.objectstore;
+
+import com.arjuna.ats.arjuna.ArjunaNames;
+import com.arjuna.ats.arjuna.gandiva.*;
+import com.arjuna.ats.arjuna.gandiva.inventory.*;
+
+public class LogStoreSetup implements InventoryElement
+{
+
+ public LogStoreSetup()
+ {
+ }
+
+ public synchronized Object createVoid()
+ {
+ return LogStore.create();
+ }
+
+ public synchronized Object createResources(Object[] param)
+ {
+ return LogStore.create(param);
+ }
+
+ public synchronized Object createClassName(ClassName className)
+ {
+ return null;
+ }
+
+ public synchronized Object createObjectName(ObjectName objectName)
+ {
+ return LogStore.create(objectName);
+ }
+
+ public synchronized Object createClassNameResources(ClassName className,
+ Object[] resources)
+ {
+ return null;
+ }
+
+ public synchronized Object createObjectNameResources(ObjectName objectName,
+ Object[] resources)
+ {
+ return null;
+ }
+
+ public ClassName className()
+ {
+ return ArjunaNames.Implementation_ObjectStore_ActionLogStore();
+ }
+
+}
Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/ShadowingStore.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/ShadowingStore.java 2009-02-09 19:12:13 UTC (rev 25170)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/ShadowingStore.java 2009-02-09 19:13:30 UTC (rev 25171)
@@ -32,7 +32,6 @@
package com.arjuna.ats.internal.arjuna.objectstore;
import com.arjuna.ats.arjuna.ArjunaNames;
-import com.arjuna.ats.arjuna.coordinator.*;
import com.arjuna.ats.arjuna.objectstore.ObjectStore;
import com.arjuna.ats.arjuna.objectstore.ObjectStoreType;
import com.arjuna.ats.arjuna.common.*;
@@ -854,165 +853,9 @@
FacilityCode.FAC_OBJECT_STORE, "ShadowingStore.ShadowingStore( "+objName+" )");
}
}
-
- /**
- * Unlock and close the file. Note that if the unlock fails we set
- * the return value to false to indicate an error but rely on the
- * close to really do the unlock.
- */
-private boolean closeAndUnlock (File fd, FileInputStream ifile, FileOutputStream ofile)
- {
- if (tsLogger.arjLogger.debugAllowed())
- {
- tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PRIVATE,
- FacilityCode.FAC_OBJECT_STORE, "ShadowingStore.closeAndUnlock("+fd+", "+ifile+", "+ofile+")");
- }
-
- boolean closedOk = unlock(fd);
-
- try
- {
- if (ifile != null)
- ifile.close();
- else
- ofile.close();
- }
- catch (Exception e)
- {
- closedOk = false;
- }
-
- return closedOk;
- }
-
-private File openAndLock (String fname, int lmode, boolean create) throws ObjectStoreException
- {
- if (tsLogger.arjLogger.debugAllowed())
- {
- tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PRIVATE,
- FacilityCode.FAC_OBJECT_STORE, "ShadowingStore.openAndLock("+fname+", "+FileLock.modeString(lmode)+", "+create+")");
- }
-
- // File fd = (File) FdCache(fname);
- File fd = null;
-
- if (fd == null)
- {
- fd = new File(fname);
-
- if (!fd.exists())
- {
- if (createHierarchy(fname))
- {
- if (!lock(fd, lmode, create))
- {
- return null;
- }
- else
- return fd;
- }
- else
- throw new ObjectStoreException("ShadowingStore.openAndLock failed to create hierarchy "+fname);
- }
-
- if (!lock(fd, lmode, create))
- fd = null;
- }
-
- return fd;
- }
-
- /*
- * Renaming on Unix works if the file to rename to already exists.
- * However, on Windows if the file exists then rename fails! So, we
- * need to delete the file to rename to before we can rename. But, we
- * must ensure that we don't get into any race conditions, so we
- * exclusively lock the file to be deleted first. Failure scenarios:
- *
- * (i) if we crash after deleting, but before we have had a chance to
- * rename the file, then the lock file will exist and prevent anyone
- * from deleting the shadow.
- *
- * (ii) if we crash after renaming and before the lock file is removed
- * then subsequent lock attempts will fail, and the sys admin will
- * have to resolve. But consistency will be maintained!
- *
- * When JDK 1.4 supports appears we will use the file-locking facility
- * it provides.
- *
- * We have to use locks at deletion, but an implementation such as
- * ShadowNoFileLockStore can still get away with no locking elsewhere
- * to improve performance.
- */
-
-private synchronized final boolean renameFromTo (File from, File to)
- {
- if (!isWindows)
- return from.renameTo(to);
- else
- {
- // FileLock fl = new FileLock(to);
-
- if (!from.exists())
- {
- /*
- * from is in the cache, but not on disk. So, either
- *
- * (i) two different users are using the same state and
- * should have been using the OS_SHARED flag.
- *
- * or
- *
- * (ii) crash recovery has recovered the state.
- *
- * If (ii) we can't force OS_SHARED on all users for the
- * minority case. So, assume (ii) and issue a warning.
- */
-
- super.removeFromCache(from.toString());
-
- if (tsLogger.arjLoggerI18N.isWarnEnabled())
- {
- tsLogger.arjLoggerI18N.warn("com.arjuna.ats.internal.arjuna.objectstore.ShadowingStore_20",
- new Object[]{from});
- }
-
- return true;
- }
-
- /*
- * Let let crash recovery deal with this!
- */
-
- // if (fl.lock(FileLock.F_WRLCK))
- {
- to.delete();
-
- boolean res = from.renameTo(to);
-
- // fl.unlock();
-
- return true;
- }
- /*
- else
- {
- if (tsLogger.arjLoggerI18N.isWarnEnabled())
- {
- tsLogger.arjLoggerI18N.warn("com.arjuna.ats.internal.arjuna.objectstore.ShadowingStore_21",
- new Object[]{to});
- }
-
- return false;
- }*/
- }
- }
-
public static final char HIDDINGCHAR = '#';
public static final char SHADOWCHAR = '!';
-
-private static boolean isWindows = Utility.isWindows();
}
Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/recovery/TransactionStatusManagerItem.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/recovery/TransactionStatusManagerItem.java 2009-02-09 19:12:13 UTC (rev 25170)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/recovery/TransactionStatusManagerItem.java 2009-02-09 19:13:30 UTC (rev 25171)
@@ -95,7 +95,7 @@
{
if ( _objectStore == null )
{
- _objectStore = TxControl.getStore();
+ _objectStore = TxControl.getRecoveryStore();
}
return _objectStore;
}
Added: labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreReactivationTest.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreReactivationTest.java (rev 0)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreReactivationTest.java 2009-02-09 19:13:30 UTC (rev 25171)
@@ -0,0 +1,67 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2005-2009,
+ * @author JBoss Inc.
+ */
+/*
+ * Copyright (C) 2004,
+ *
+ * Arjuna Technologies Ltd,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: Performance2.java 2342 2006-03-30 13:06:17Z $
+ */
+
+package com.hp.mwtests.ts.arjuna.objectstore;
+
+import com.arjuna.ats.arjuna.ArjunaNames;
+import com.arjuna.ats.arjuna.AtomicAction;
+import com.arjuna.ats.arjuna.common.Environment;
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.arjuna.coordinator.ActionStatus;
+import com.arjuna.ats.arjuna.recovery.RecoverAtomicAction;
+import com.hp.mwtests.ts.arjuna.resources.BasicRecord;
+
+public class LogStoreReactivationTest
+{
+
+ public static void main (String[] args)
+ {
+ System.setProperty(Environment.COMMIT_ONE_PHASE, "NO");
+ System.setProperty(Environment.OBJECTSTORE_TYPE, ArjunaNames.Implementation_ObjectStore_ActionLogStore().stringForm());
+ System.setProperty(Environment.TRANSACTION_LOG_SYNC_REMOVAL, "false");
+ System.setProperty(Environment.TRANSACTION_LOG_PURGE_TIME, "1000000"); // essentially infinite
+
+ AtomicAction A = new AtomicAction();
+ Uid txId = A.get_uid();
+
+ System.err.println("IMPORTANT: ignore warnings about USER_DEF_FIRST0 as they are expected due to BasicRecord usage!");
+
+ A.begin();
+
+ A.add(new BasicRecord());
+
+ A.commit();
+
+ RecoverAtomicAction rAA = new RecoverAtomicAction(txId, ActionStatus.COMMITTED);
+
+ rAA.replayPhase2();
+ }
+}
Added: labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreReactivationTest2.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreReactivationTest2.java (rev 0)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreReactivationTest2.java 2009-02-09 19:13:30 UTC (rev 25171)
@@ -0,0 +1,67 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2005-2009,
+ * @author JBoss Inc.
+ */
+/*
+ * Copyright (C) 2004,
+ *
+ * Arjuna Technologies Ltd,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: Performance2.java 2342 2006-03-30 13:06:17Z $
+ */
+
+package com.hp.mwtests.ts.arjuna.objectstore;
+
+import com.arjuna.ats.arjuna.ArjunaNames;
+import com.arjuna.ats.arjuna.AtomicAction;
+import com.arjuna.ats.arjuna.common.Environment;
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.arjuna.coordinator.ActionStatus;
+import com.arjuna.ats.arjuna.recovery.RecoverAtomicAction;
+import com.hp.mwtests.ts.arjuna.resources.BasicRecord;
+
+public class LogStoreReactivationTest2
+{
+
+ public static void main (String[] args)
+ {
+ System.setProperty(Environment.COMMIT_ONE_PHASE, "NO");
+ System.setProperty(Environment.OBJECTSTORE_TYPE, ArjunaNames.Implementation_ObjectStore_ActionLogStore().stringForm());
+ System.setProperty(Environment.TRANSACTION_LOG_SYNC_REMOVAL, "true");
+ System.setProperty(Environment.TRANSACTION_LOG_PURGE_TIME, "1000000"); // essentially infinite
+
+ AtomicAction A = new AtomicAction();
+ Uid txId = A.get_uid();
+
+ System.err.println("IMPORTANT: if there are warnings about USER_DEF_FIRST0 then the test has failed!");
+
+ A.begin();
+
+ A.add(new BasicRecord());
+
+ A.commit();
+
+ RecoverAtomicAction rAA = new RecoverAtomicAction(txId, ActionStatus.COMMITTED);
+
+ rAA.replayPhase2();
+ }
+}
Added: labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreRecoveryTest.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreRecoveryTest.java (rev 0)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreRecoveryTest.java 2009-02-09 19:13:30 UTC (rev 25171)
@@ -0,0 +1,171 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2005-2006,
+ * @author JBoss Inc.
+ */
+/*
+ * Copyright (C) 2004,
+ *
+ * Arjuna Technologies Ltd,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: AllObjUidsTest.java 2342 2006-03-30 13:06:17Z $
+ */
+
+package com.hp.mwtests.ts.arjuna.objectstore;
+
+import com.arjuna.ats.arjuna.ArjunaNames;
+import com.arjuna.ats.arjuna.objectstore.ObjectStore;
+import com.arjuna.ats.arjuna.state.*;
+import com.arjuna.ats.arjuna.common.Environment;
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.arjuna.coordinator.TxControl;
+
+public class LogStoreRecoveryTest
+{
+ public static void main (String[] args)
+ {
+ System.setProperty(Environment.OBJECTSTORE_TYPE, ArjunaNames.Implementation_ObjectStore_ActionLogStore().stringForm());
+ System.setProperty(Environment.TRANSACTION_LOG_PURGE_TIME, "10000");
+
+ ObjectStore objStore = TxControl.getStore();
+ final int numberOfTransactions = 1000;
+ final Uid[] ids = new Uid[numberOfTransactions];
+ final int fakeData = 0xdeedbaaf;
+ final String type = "/StateManager/BasicAction/TwoPhaseCoordinator/AtomicAction/LogStoreRecoveryTest";
+
+ for (int i = 0; i < numberOfTransactions; i++)
+ {
+ OutputObjectState dummyState = new OutputObjectState();
+
+ try
+ {
+ dummyState.packInt(fakeData);
+ ids[i] = new Uid();
+ objStore.write_committed(ids[i], type, dummyState);
+ }
+ catch (final Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+
+ /*
+ * Remove 50% of the entries, simulating a crash during
+ * normal execution.
+ *
+ * Q: why not just write 50% in the first place?
+ * A: because we will extend this test to allow the recovery
+ * system to run in between writing and removing.
+ */
+
+ for (int i = 0; i < numberOfTransactions / 2; i++)
+ {
+ try
+ {
+ objStore.remove_committed(ids[i], type);
+ }
+ catch (final Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+
+ try
+ {
+ /*
+ * Give the purger thread a chance to run and delete
+ * the entries we've "removed" (really only marked as
+ * being removable.)
+ */
+
+ Thread.sleep(12000);
+ }
+ catch (final Exception ex)
+ {
+ }
+
+ /*
+ * Now get a list of entries to work on.
+ */
+
+ InputObjectState ios = new InputObjectState();
+ boolean passed = true;
+
+ try
+ {
+ if (objStore.allObjUids(type, ios, ObjectStore.OS_UNKNOWN))
+ {
+ Uid id = new Uid(Uid.nullUid());
+ int numberOfEntries = 0;
+
+ do
+ {
+ try
+ {
+ id.unpack(ios);
+ }
+ catch (Exception ex)
+ {
+ id = Uid.nullUid();
+ }
+
+ if (id.notEquals(Uid.nullUid()))
+ {
+ numberOfEntries++;
+
+ boolean found = false;
+
+ for (int i = 0; i < ids.length; i++)
+ {
+ if (id.equals(ids[i]))
+ found = true;
+ }
+
+ if (passed && !found)
+ {
+ passed = false;
+
+ System.err.println("Found unexpected transaction!");
+ }
+ }
+ }
+ while (id.notEquals(Uid.nullUid()));
+
+ if ((numberOfEntries == numberOfTransactions / 2) && passed)
+ {
+ System.err.println("Would attempt recovery on "+numberOfEntries+" dead transactions.");
+ }
+ else
+ {
+ passed = false;
+
+ System.err.println("Expected "+(numberOfTransactions / 2)+" and got "+numberOfEntries);
+ }
+ }
+ }
+ catch (final Exception ex)
+ {
+ ex.printStackTrace();
+ }
+
+ System.err.println("Test "+((passed) ? "passed" : "failed"));
+ }
+}
Added: labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreRecoveryTest2.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreRecoveryTest2.java (rev 0)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreRecoveryTest2.java 2009-02-09 19:13:30 UTC (rev 25171)
@@ -0,0 +1,118 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2005-2009,
+ * @author JBoss Inc.
+ */
+/*
+ * Copyright (C) 2004,
+ *
+ * Arjuna Technologies Ltd,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: Performance2.java 2342 2006-03-30 13:06:17Z $
+ */
+
+package com.hp.mwtests.ts.arjuna.objectstore;
+
+import com.hp.mwtests.ts.arjuna.resources.*;
+
+import com.arjuna.ats.arjuna.ArjunaNames;
+import com.arjuna.ats.arjuna.AtomicAction;
+import com.arjuna.ats.arjuna.common.*;
+import com.arjuna.ats.arjuna.recovery.RecoveryManager;
+
+class TestWorker extends Thread
+{
+
+ public TestWorker (int iters)
+ {
+ _iters = iters;
+ }
+
+ public void run ()
+ {
+ for (int i = 0; i < _iters; i++)
+ {
+ try
+ {
+ AtomicAction A = new AtomicAction();
+
+ A.begin();
+
+ A.add(new BasicRecord());
+
+ A.commit();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+ Thread.yield();
+ }
+ }
+
+ private int _iters;
+}
+
+public class LogStoreRecoveryTest2
+{
+
+ public static void main (String[] args)
+ {
+ int threads = 10;
+ int work = 100;
+
+ System.setProperty(Environment.COMMIT_ONE_PHASE, "NO");
+ System.setProperty(Environment.OBJECTSTORE_TYPE, ArjunaNames.Implementation_ObjectStore_ActionLogStore().stringForm());
+ System.setProperty(Environment.TRANSACTION_LOG_SYNC_REMOVAL, "false");
+ System.setProperty(Environment.TRANSACTION_LOG_PURGE_TIME, "1000000"); // essentially infinite
+
+ TestWorker[] workers = new TestWorker[threads];
+
+ for (int i = 0; i < threads; i++)
+ {
+ workers[i] = new TestWorker(work);
+
+ workers[i].start();
+ }
+
+ for (int j = 0; j < threads; j++)
+ {
+ try
+ {
+ workers[j].join();
+ System.err.println("**terminated "+j);
+ }
+ catch (final Exception ex)
+ {
+ }
+ }
+
+ /*
+ * Now have a log that hasn't been deleted. Run recovery and see
+ * what happens!
+ */
+
+ RecoveryManager manager = RecoveryManager.manager(RecoveryManager.DIRECT_MANAGEMENT);
+
+ manager.scan();
+ }
+}
Added: labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreTest.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreTest.java (rev 0)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreTest.java 2009-02-09 19:13:30 UTC (rev 25171)
@@ -0,0 +1,131 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2005-2006,
+ * @author JBoss Inc.
+ */
+/*
+ * Copyright (C) 2004,
+ *
+ * Arjuna Technologies Ltd,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: AllObjUidsTest.java 2342 2006-03-30 13:06:17Z $
+ */
+
+package com.hp.mwtests.ts.arjuna.objectstore;
+
+import com.arjuna.ats.arjuna.ArjunaNames;
+import com.arjuna.ats.arjuna.objectstore.ObjectStore;
+import com.arjuna.ats.arjuna.state.*;
+import com.arjuna.ats.arjuna.common.Environment;
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.arjuna.coordinator.TxControl;
+
+public class LogStoreTest
+{
+ public static void main (String[] args)
+ {
+ System.setProperty(Environment.OBJECTSTORE_TYPE, ArjunaNames.Implementation_ObjectStore_ActionLogStore().stringForm());
+
+ ObjectStore objStore = TxControl.getStore();
+ final int numberOfTransactions = 1000;
+ final Uid[] ids = new Uid[numberOfTransactions];
+ final int fakeData = 0xdeedbaaf;
+ final String type = "/StateManager/BasicAction/TwoPhaseCoordinator/AtomicAction/Test";
+
+ for (int i = 0; i < numberOfTransactions; i++)
+ {
+ OutputObjectState dummyState = new OutputObjectState();
+
+ try
+ {
+ dummyState.packInt(fakeData);
+ ids[i] = new Uid();
+ objStore.write_committed(ids[i], type, dummyState);
+ }
+ catch (final Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+
+ InputObjectState ios = new InputObjectState();
+ boolean passed = false;
+
+ try
+ {
+ if (objStore.allObjUids(type, ios, ObjectStore.OS_UNKNOWN))
+ {
+ Uid id = new Uid(Uid.nullUid());
+ int numberOfEntries = 0;
+
+ do
+ {
+ try
+ {
+ id.unpack(ios);
+ }
+ catch (Exception ex)
+ {
+ id = Uid.nullUid();
+ }
+
+ if (id.notEquals(Uid.nullUid()))
+ {
+ passed = true;
+
+ System.err.println("Located transaction log "+id+" in object store.");
+
+ numberOfEntries++;
+
+ boolean found = false;
+
+ for (int i = 0; i < ids.length; i++)
+ {
+ if (id.equals(ids[i]))
+ found = true;
+ }
+
+ if (passed && !found)
+ {
+ passed = false;
+
+ System.err.println("Found unexpected transaction!");
+ }
+ }
+ }
+ while (id.notEquals(Uid.nullUid()));
+
+ if ((numberOfEntries != ids.length) && passed)
+ {
+ passed = false;
+
+ System.err.println("Expected "+ids.length+" and got "+numberOfEntries);
+ }
+ }
+ }
+ catch (final Exception ex)
+ {
+ ex.printStackTrace();
+ }
+
+ System.err.println("Test "+((passed) ? "passed" : "failed"));
+ }
+}
Added: labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreTest2.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreTest2.java (rev 0)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreTest2.java 2009-02-09 19:13:30 UTC (rev 25171)
@@ -0,0 +1,162 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2005-2006,
+ * @author JBoss Inc.
+ */
+/*
+ * Copyright (C) 2004,
+ *
+ * Arjuna Technologies Ltd,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: AllObjUidsTest.java 2342 2006-03-30 13:06:17Z $
+ */
+
+package com.hp.mwtests.ts.arjuna.objectstore;
+
+import com.arjuna.ats.arjuna.ArjunaNames;
+import com.arjuna.ats.arjuna.objectstore.ObjectStore;
+import com.arjuna.ats.arjuna.state.*;
+import com.arjuna.ats.arjuna.common.Environment;
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.arjuna.coordinator.TxControl;
+
+public class LogStoreTest2
+{
+ public static void main (String[] args)
+ {
+ System.setProperty(Environment.OBJECTSTORE_TYPE, ArjunaNames.Implementation_ObjectStore_ActionLogStore().stringForm());
+ System.setProperty(Environment.TRANSACTION_LOG_PURGE_TIME, "10000");
+
+ ObjectStore objStore = TxControl.getStore();
+ final int numberOfTransactions = 1000;
+ final Uid[] ids = new Uid[numberOfTransactions];
+ final int fakeData = 0xdeedbaaf;
+ final String type = "/StateManager/BasicAction/TwoPhaseCoordinator/AtomicAction/Test";
+
+ for (int i = 0; i < numberOfTransactions; i++)
+ {
+ OutputObjectState dummyState = new OutputObjectState();
+
+ try
+ {
+ dummyState.packInt(fakeData);
+ ids[i] = new Uid();
+ objStore.write_committed(ids[i], type, dummyState);
+ }
+ catch (final Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+
+ try
+ {
+ objStore.remove_committed(ids[0], type);
+ }
+ catch (final Exception ex)
+ {
+ ex.printStackTrace();
+ }
+
+ try
+ {
+ /*
+ * Give the purger thread a chance to run and delete
+ * the entry.
+ */
+
+ Thread.sleep(12000);
+ }
+ catch (final Exception ex)
+ {
+ }
+
+ InputObjectState ios = new InputObjectState();
+ boolean passed = false;
+
+ try
+ {
+ if (objStore.allObjUids(type, ios, ObjectStore.OS_UNKNOWN))
+ {
+ Uid id = new Uid(Uid.nullUid());
+ int numberOfEntries = 0;
+
+ do
+ {
+ try
+ {
+ id.unpack(ios);
+ }
+ catch (Exception ex)
+ {
+ id = Uid.nullUid();
+ }
+
+ if (id.notEquals(Uid.nullUid()))
+ {
+ passed = true;
+
+ numberOfEntries++;
+
+ boolean found = false;
+
+ for (int i = 0; i < ids.length; i++)
+ {
+ if (id.equals(ids[i]))
+ found = true;
+ }
+
+ if (passed && !found)
+ {
+ passed = false;
+
+ System.err.println("Found unexpected transaction!");
+ }
+ }
+ }
+ while (id.notEquals(Uid.nullUid()));
+
+ if ((numberOfEntries == ids.length -1) && passed)
+ {
+ if (objStore.currentState(ids[0], type) != ObjectStore.OS_UNKNOWN)
+ passed = false;
+ else
+ {
+ if (objStore.currentState(ids[1], type) != ObjectStore.OS_COMMITTED)
+ passed = false;
+ }
+ }
+ else
+ {
+ passed = false;
+
+ System.err.println("Expected "+ids.length+" and got "+numberOfEntries);
+ }
+ }
+ }
+ catch (final Exception ex)
+ {
+ ex.printStackTrace();
+ }
+
+ System.err.println("Test "+((passed) ? "passed" : "failed"));
+ }
+}
Added: labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStressTest.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStressTest.java (rev 0)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStressTest.java 2009-02-09 19:13:30 UTC (rev 25171)
@@ -0,0 +1,171 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2005-2009,
+ * @author JBoss Inc.
+ */
+/*
+ * Copyright (C) 2004,
+ *
+ * Arjuna Technologies Ltd,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: Performance2.java 2342 2006-03-30 13:06:17Z $
+ */
+
+package com.hp.mwtests.ts.arjuna.objectstore;
+
+import com.hp.mwtests.ts.arjuna.resources.*;
+
+import com.arjuna.ats.arjuna.ArjunaNames;
+import com.arjuna.ats.arjuna.AtomicAction;
+import com.arjuna.ats.arjuna.common.*;
+import com.arjuna.ats.arjuna.coordinator.TxControl;
+import com.arjuna.ats.arjuna.objectstore.ObjectStore;
+import com.arjuna.ats.arjuna.state.InputObjectState;
+
+class StressWorker extends Thread
+{
+
+ public StressWorker (int iters, int thread)
+ {
+ _iters = iters;
+ _thread = thread;
+ }
+
+ public void run ()
+ {
+ for (int i = 0; i < _iters; i++)
+ {
+ try
+ {
+ AtomicAction A = new AtomicAction();
+
+ A.begin();
+
+ A.add(new BasicRecord());
+
+ A.commit();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+ Thread.yield();
+ }
+ }
+
+ private int _iters;
+ private int _thread;
+}
+
+public class LogStressTest
+{
+
+ public static void main (String[] args)
+ {
+ int threads = 10;
+ int work = 100;
+
+ System.setProperty(Environment.COMMIT_ONE_PHASE, "NO");
+ System.setProperty(Environment.OBJECTSTORE_TYPE, ArjunaNames.Implementation_ObjectStore_ActionLogStore().stringForm());
+ System.setProperty(Environment.TRANSACTION_LOG_PURGE_TIME, "10000");
+
+ for (int i = 0; i < args.length; i++)
+ {
+ if (args[i].compareTo("-threads") == 0)
+ {
+ try
+ {
+ Integer v = new Integer(args[i + 1]);
+
+ threads = v.intValue();
+ }
+ catch (Exception e)
+ {
+ System.err.println(e);
+ }
+ }
+ if (args[i].compareTo("-work") == 0)
+ {
+ try
+ {
+ Integer v = new Integer(args[i + 1]);
+
+ work = v.intValue();
+ }
+ catch (Exception e)
+ {
+ System.err.println(e);
+ }
+ }
+ if (args[i].compareTo("-help") == 0)
+ {
+ System.out
+ .println("Usage: LogStressTest [-help] [-threads <number>] [-work <number>]");
+ System.exit(0);
+ }
+ }
+
+ StressWorker[] workers = new StressWorker[threads];
+
+ for (int i = 0; i < threads; i++)
+ {
+ workers[i] = new StressWorker(work, i);
+
+ workers[i].start();
+ }
+
+ for (int j = 0; j < threads; j++)
+ {
+ try
+ {
+ workers[j].join();
+ }
+ catch (final Exception ex)
+ {
+ }
+ }
+
+ InputObjectState ios = new InputObjectState();
+ boolean passed = false;
+
+ try
+ {
+ TxControl.getStore().allObjUids(new AtomicAction().type(), ios, ObjectStore.OS_UNKNOWN);
+
+ Uid tempUid = new Uid(Uid.nullUid());
+
+ tempUid.unpack(ios);
+
+ // there should be no entries left
+
+ if (tempUid.equals(Uid.nullUid()))
+ {
+ passed = true;
+ }
+ }
+ catch (final Exception ex)
+ {
+ }
+
+ System.err.println("Test "+((passed) ? "passed" : "failed"));
+ }
+}
Added: labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStressTest2.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStressTest2.java (rev 0)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStressTest2.java 2009-02-09 19:13:30 UTC (rev 25171)
@@ -0,0 +1,104 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * (C) 2005-2009,
+ * @author JBoss Inc.
+ */
+/*
+ * Copyright (C) 2004,
+ *
+ * Arjuna Technologies Ltd,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: Performance2.java 2342 2006-03-30 13:06:17Z $
+ */
+
+package com.hp.mwtests.ts.arjuna.objectstore;
+
+import com.arjuna.ats.arjuna.ArjunaNames;
+import com.arjuna.ats.arjuna.AtomicAction;
+import com.arjuna.ats.arjuna.common.*;
+import com.hp.mwtests.ts.arjuna.resources.BasicRecord;
+
+/*
+ * Run with the log store for N hours and make sure there are
+ * no logs left at the end.
+ */
+
+public class LogStressTest2
+{
+
+ public static void main (String[] args)
+ {
+ System.setProperty(Environment.COMMIT_ONE_PHASE, "NO");
+ System.setProperty(Environment.OBJECTSTORE_TYPE, ArjunaNames.Implementation_ObjectStore_ActionLogStore().stringForm());
+ System.setProperty(Environment.TRANSACTION_LOG_SIZE, "10000");
+
+ int timeLimit = 4; // hours
+
+ for (int i = 0; i < args.length; i++)
+ {
+ if (args[i].compareTo("-time") == 0)
+ {
+ try
+ {
+ Integer v = new Integer(args[i + 1]);
+
+ timeLimit = v.intValue();
+ }
+ catch (Exception e)
+ {
+ System.err.println(e);
+ }
+ }
+ if (args[i].compareTo("-help") == 0)
+ {
+ System.out
+ .println("Usage: LogStressTest2 [-help] [-time <hours>]");
+ System.exit(0);
+ }
+ }
+
+ System.err.println("WARNING: this test will run for "+timeLimit+" hours.");
+
+ final long stime = System.currentTimeMillis();
+ final long endTime = timeLimit * 60 * 60 * 1000;
+ long ftime;
+
+ do
+ {
+ try
+ {
+ AtomicAction A = new AtomicAction();
+
+ A.begin();
+
+ A.add(new BasicRecord());
+
+ A.commit();
+ }
+ catch (final Exception ex)
+ {
+ }
+
+ ftime = System.currentTimeMillis();
+
+ } while ((ftime - stime) < endTime);
+ }
+}
Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/resources/BasicRecord.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/resources/BasicRecord.java 2009-02-09 19:12:13 UTC (rev 25170)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/resources/BasicRecord.java 2009-02-09 19:13:30 UTC (rev 25171)
@@ -35,13 +35,8 @@
import com.arjuna.ats.arjuna.common.*;
import com.arjuna.ats.arjuna.state.*;
import com.arjuna.ats.arjuna.gandiva.ClassName;
-import com.arjuna.ats.arjuna.objectstore.ObjectStoreType;
import java.io.PrintWriter;
-import java.io.IOException;
-
-import com.arjuna.common.util.logging.*;
-
public class BasicRecord extends AbstractRecord
{
Modified: labs/jbosstm/trunk/ArjunaCore/docs/user_guide/ProgrammersGuide.odt
===================================================================
(Binary files differ)
More information about the jboss-svn-commits
mailing list