Changed copy on write to occur during lookup, rather than after lookup.

Much carnage, but it's way faster.


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/WCM-DEV2/root@3172 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Britt Park
2006-06-21 13:07:42 +00:00
parent 8cb3db78e3
commit 1318a5204b
14 changed files with 134 additions and 280 deletions

View File

@@ -58,13 +58,6 @@ interface AVMNode
*/ */
public void setVersionID(int version); public void setVersionID(int version);
/**
* Perform a COW if required.
* @param lPath The lookup path.
* @return A 'copied' version of this node.
*/
public AVMNode copyOnWrite(Lookup lPath);
/** /**
* Possibly copy ourselves. * Possibly copy ourselves.
* @param lPath The Lookup for this node. * @param lPath The Lookup for this node.

View File

@@ -148,47 +148,6 @@ abstract class AVMNodeImpl implements AVMNode, Serializable
return (AVMNode)query.uniqueResult(); return (AVMNode)query.uniqueResult();
} }
/**
* Perform a copy on write on this node and recursively
* up to the repository root. This is a template method
* which farms out work to possiblyCopy().
* @param lPath The Lookup.
*/
public AVMNode copyOnWrite(Lookup lPath)
{
// Call the subclass's copy on write logic.
AVMNode newMe = possiblyCopy(lPath);
// No copying needed, so short circuit.
if (newMe == null)
{
return this;
}
String myName = lPath.getName();
lPath.upCurrentNode();
Repository repos = lPath.getRepository();
newMe.setVersionID(repos.getNextVersionID());
// Get our parent directory if we have one.
DirectoryNode parent = null;
if (!getIsRoot())
{
parent = (DirectoryNode)lPath.getCurrentNode();
}
if (parent != null)
{
// Recursive invocation.
DirectoryNode newParent =
(DirectoryNode)parent.copyOnWrite(lPath);
newParent.putChild(myName, newMe);
}
else // Null parent means root of repository.
{
repos.setNewRoot((DirectoryNode)newMe);
}
newMe.setRepository(repos);
newMe.setIsNew(true);
return newMe;
}
/** /**
* Set the owning repository for this. * Set the owning repository for this.
* @param repo The owning repository. * @param repo The owning repository.

View File

@@ -92,6 +92,7 @@ public class AVMServiceTest extends AVMServiceTestBase
// assertEquals("foo", listing.get(1).getName()); // assertEquals("foo", listing.get(1).getName());
// /layer/b should contain foo, bar, and baz. // /layer/b should contain foo, bar, and baz.
listing = fService.getDirectoryListing(-1, "main:/layer/b"); listing = fService.getDirectoryListing(-1, "main:/layer/b");
System.out.println(recursiveList("main", -1, true));
assertEquals(3, listing.size()); assertEquals(3, listing.size());
// assertEquals("bar", listing.get(0).getName()); // assertEquals("bar", listing.get(0).getName());
// assertEquals("baz", listing.get(1).getName()); // assertEquals("baz", listing.get(1).getName());
@@ -1114,6 +1115,7 @@ public class AVMServiceTest extends AVMServiceTestBase
catch (Exception e) catch (Exception e)
{ {
e.printStackTrace(System.err); e.printStackTrace(System.err);
fail();
} }
} }

View File

@@ -50,7 +50,7 @@ public class AVMServiceTestBase extends TestCase
@Override @Override
protected void setUp() throws Exception protected void setUp() throws Exception
{ {
HibernateHelper.GetSessionFactory().getStatistics().setStatisticsEnabled(true); // HibernateHelper.GetSessionFactory().getStatistics().setStatisticsEnabled(true);
AVMServiceImpl service = new AVMServiceImpl(); AVMServiceImpl service = new AVMServiceImpl();
service.setStorage("build/test-results/storage"); service.setStorage("build/test-results/storage");
service.init(true); service.init(true);
@@ -66,9 +66,9 @@ public class AVMServiceTestBase extends TestCase
{ {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
System.out.println("Timing: " + (now - fStartTime) + "ms"); System.out.println("Timing: " + (now - fStartTime) + "ms");
Statistics stats = HibernateHelper.GetSessionFactory().getStatistics(); // Statistics stats = HibernateHelper.GetSessionFactory().getStatistics();
stats.logSummary(); // stats.logSummary();
stats.clear(); // stats.clear();
HibernateHelper.Reset(); HibernateHelper.Reset();
} }

View File

@@ -46,7 +46,7 @@ public class AVMStressTest extends AVMServiceTestBase
System.out.println("Load time: " + (System.currentTimeMillis() - start)); System.out.println("Load time: " + (System.currentTimeMillis() - start));
List<AVMTester> testers = new ArrayList<AVMTester>(); List<AVMTester> testers = new ArrayList<AVMTester>();
List<Thread> threads = new ArrayList<Thread>(); List<Thread> threads = new ArrayList<Thread>();
for (int i = 0; i < 1; i++) for (int i = 0; i < 8; i++)
{ {
AVMTester tester AVMTester tester
= new AVMTester(400, // create file. = new AVMTester(400, // create file.
@@ -74,12 +74,12 @@ public class AVMStressTest extends AVMServiceTestBase
thread.start(); thread.start();
} }
int exited = 0; int exited = 0;
while (exited != 1) while (exited != 8)
{ {
try try
{ {
Thread.sleep(2000); Thread.sleep(2000);
for (int i = 0; i < 1; i++) for (int i = 0; i < 8; i++)
{ {
if (threads.get(i) == null) if (threads.get(i) == null)
{ {

View File

@@ -56,28 +56,11 @@ interface DirectoryNode extends AVMNode
*/ */
public AVMNodeDescriptor lookupChild(AVMNodeDescriptor mine, String name); public AVMNodeDescriptor lookupChild(AVMNodeDescriptor mine, String name);
/**
* Add a child node. Fails if child already exists.
* Copy is possible.
* @param name The name to give the child.
* @param child The child to add.
* @param The lookup path.
*/
public boolean addChild(String name, AVMNode child, Lookup lPath);
/**
* Remove a child node. Fails if child does not exist.
* Copy is possible.
* @param name The name of the child to remove.
* @param lPath The lookup path.
*/
public boolean removeChild(String name, Lookup lPath);
/** /**
* Remove a child directly. No copy is possible. * Remove a child directly. No copy is possible.
* @param name The name of the child to remove. * @param name The name of the child to remove.
*/ */
public void rawRemoveChild(String name); public void removeChild(String name);
/** /**
* Get a directory listing. * Get a directory listing.

View File

@@ -282,44 +282,6 @@ class LayeredDirectoryNodeImpl extends DirectoryNodeImpl implements LayeredDirec
} }
} }
/**
* Add a child to this directory and possibly COW.
* @param name The name of the child to add.
* @param child The child to add.
* @param lPath The Lookup.
* @return Whether the child was successfully added.
*/
public boolean addChild(String name, AVMNode child, Lookup lPath)
{
if (getChild(name) != null)
{
return false;
}
if (getDeleted(name) == null)
{
try
{
Lookup lookup = SuperRepository.GetInstance().lookupDirectory(-1, getUnderlying(lPath));
DirectoryNode dir = (DirectoryNode)lookup.getCurrentNode();
if (dir.lookupChild(lookup, name, -1) != null)
{
return false;
}
}
catch (AVMException re)
{
if (re instanceof AVMCycleException)
{
throw re;
}
// Do nothing.
}
}
DirectoryNode toModify = (DirectoryNode)copyOnWrite(lPath);
toModify.putChild(name, child);
child.setRepository(lPath.getRepository());
return true;
}
/** /**
* Does this node directly contain the indicated node. * Does this node directly contain the indicated node.
@@ -506,7 +468,7 @@ class LayeredDirectoryNodeImpl extends DirectoryNodeImpl implements LayeredDirec
* @param name The name of the child to remove. * @param name The name of the child to remove.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void rawRemoveChild(String name) public void removeChild(String name)
{ {
ChildEntry entry = getChild(name); ChildEntry entry = getChild(name);
if (entry != null) if (entry != null)
@@ -518,48 +480,6 @@ class LayeredDirectoryNodeImpl extends DirectoryNodeImpl implements LayeredDirec
SuperRepository.GetInstance().getSession().save(dc); SuperRepository.GetInstance().getSession().save(dc);
} }
/**
* Remove a child by name. Possibly COW.
* @param name The name of the child to remove.
* @param lPath The Lookup.
* @return Whether the child was successfully removed.
*/
@SuppressWarnings("unchecked")
public boolean removeChild(String name, Lookup lPath)
{
// Can't delete something that is already deleted.
if (getDeleted(name) != null)
{
return false;
}
ChildEntry entry = getChild(name);
if (entry == null)
{
// See if the name is seen via indirection.
try
{
Lookup lookup = SuperRepository.GetInstance().lookupDirectory(-1, getUnderlying(lPath));
DirectoryNode dir = (DirectoryNode)lookup.getCurrentNode();
if (dir.lookupChild(lookup, name, -1) == null)
{
return false;
}
}
catch (AVMException re)
{
if (re instanceof AVMCycleException)
{
throw re;
}
return false;
}
}
LayeredDirectoryNode toModify =
(LayeredDirectoryNode)copyOnWrite(lPath);
toModify.rawRemoveChild(name);
return true;
}
/** /**
* Get the type of this node. * Get the type of this node.
* @return The type of this node. * @return The type of this node.
@@ -596,8 +516,7 @@ class LayeredDirectoryNodeImpl extends DirectoryNodeImpl implements LayeredDirec
public void turnPrimary(Lookup lPath) public void turnPrimary(Lookup lPath)
{ {
String path = lPath.getCurrentIndirection(); String path = lPath.getCurrentIndirection();
LayeredDirectoryNode toModify = (LayeredDirectoryNode)copyOnWrite(lPath); rawSetPrimary(path);
toModify.rawSetPrimary(path);
} }
/** /**
@@ -606,8 +525,7 @@ class LayeredDirectoryNodeImpl extends DirectoryNodeImpl implements LayeredDirec
*/ */
public void retarget(Lookup lPath, String target) public void retarget(Lookup lPath, String target)
{ {
LayeredDirectoryNode toModify = (LayeredDirectoryNode)copyOnWrite(lPath); rawSetPrimary(target);
toModify.rawSetPrimary(target);
} }
/** /**
@@ -617,8 +535,7 @@ class LayeredDirectoryNodeImpl extends DirectoryNodeImpl implements LayeredDirec
*/ */
public void uncover(Lookup lPath, String name) public void uncover(Lookup lPath, String name)
{ {
LayeredDirectoryNodeImpl toModify = (LayeredDirectoryNodeImpl)copyOnWrite(lPath); DeletedChild dc = getDeleted(name);
DeletedChild dc = toModify.getDeleted(name);
if (dc != null) if (dc != null)
{ {
SuperRepository.GetInstance().getSession().delete(dc); SuperRepository.GetInstance().getSession().delete(dc);

View File

@@ -96,8 +96,10 @@ class Lookup
* Add a new node to the lookup. * Add a new node to the lookup.
* @param node The node to add. * @param node The node to add.
* @param name The name of the node in the path. * @param name The name of the node in the path.
* @param write Whether this is in the context of
* a write operation.
*/ */
public void add(AVMNode node, String name) public void add(AVMNode node, String name, boolean write)
{ {
LookupComponent comp = new LookupComponent(); LookupComponent comp = new LookupComponent();
comp.setName(name); comp.setName(name);
@@ -109,6 +111,8 @@ class Lookup
} }
else else
{ {
// TODO The isDirectlyContained should be eliminated in favor of
// a cumulative state.
if (fPosition >= 0 && (!((DirectoryNode)fComponents.get(fPosition).getNode()).directlyContains(node) || if (fPosition >= 0 && (!((DirectoryNode)fComponents.get(fPosition).getNode()).directlyContains(node) ||
!isDirectlyContained())) !isDirectlyContained()))
{ {
@@ -127,20 +131,7 @@ class Lookup
} }
else else
{ {
String parentIndirection = fComponents.get(fPosition).getIndirection(); comp.setIndirection(computeIndirection(name));
if (parentIndirection == null)
{
System.out.println("Oink!");
}
if (parentIndirection.endsWith("/")) // This currently is impossible because
// root dirs are always plain.
{
comp.setIndirection(parentIndirection + name);
}
else
{
comp.setIndirection(parentIndirection + "/" + name);
}
} }
fLayeredYet = true; fLayeredYet = true;
// Record the first layer seen. // Record the first layer seen.
@@ -151,10 +142,58 @@ class Lookup
} }
fLowestLayerIndex = fPosition + 1; fLowestLayerIndex = fPosition + 1;
} }
// In a write context a plain directory contained in a layer will
// be copied so we will need to compute an indirection path.
else if (fLayeredYet && write)
{
comp.setIndirection(computeIndirection(name));
}
comp.setLowestLayerIndex(fLowestLayerIndex); comp.setLowestLayerIndex(fLowestLayerIndex);
comp.setLayered(fLayeredYet); comp.setLayered(fLayeredYet);
fComponents.add(comp); fComponents.add(comp);
fPosition++; fPosition++;
// If we are in a write context do copy on write.
if (write)
{
// Possibly copy.
node = node.possiblyCopy(this);
if (node == null)
{
return;
}
// Node was copied.
fComponents.get(fPosition).setNode(node);
if (fPosition == 0)
{
// Inform the repository of a new root.
fRepository.setNewRoot((DirectoryNode)node);
return;
}
// Not the root. Check if we are the top layer and insert this into it's parent.
if (fPosition == fTopLayerIndex)
{
fTopLayer = (LayeredDirectoryNode)node;
}
((DirectoryNode)fComponents.get(fPosition - 1).getNode()).putChild(name, node);
}
}
/**
* A helper for keeping track of indirection.
* @param name The name of the being added node.
* @return The indirection for the being added node.
*/
private String computeIndirection(String name)
{
String parentIndirection = fComponents.get(fPosition).getIndirection();
if (parentIndirection.endsWith("/"))
{
return parentIndirection + name;
}
else
{
return parentIndirection + "/" + name;
}
} }
/** /**

View File

@@ -72,27 +72,6 @@ class PlainDirectoryNodeImpl extends DirectoryNodeImpl implements PlainDirectory
} }
} }
/**
* Add a child to this directory, possibly doing a copy.
* @param name The name of the child.
* @param child The child node.
* @param lPath The lookup path to this directory.
* @return Success or failure.
*/
public boolean addChild(String name, AVMNode child, Lookup lPath)
{
// No, if a child with the given name exists. Note that uniqueness
// of names is built into the AVM, as opposed to being configurable.
if (getChild(name) != null)
{
return false;
}
DirectoryNode toModify = (DirectoryNode)copyOnWrite(lPath);
toModify.putChild(name, child);
child.setRepository(lPath.getRepository());
return true;
}
/** /**
* Does this directory directly contain the given node. * Does this directory directly contain the given node.
* @param node The node to check. * @param node The node to check.
@@ -187,7 +166,7 @@ class PlainDirectoryNodeImpl extends DirectoryNodeImpl implements PlainDirectory
* @param name The name of the child to remove. * @param name The name of the child to remove.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void rawRemoveChild(String name) public void removeChild(String name)
{ {
ChildEntry entry = getChild(name); ChildEntry entry = getChild(name);
if (entry != null) if (entry != null)
@@ -196,24 +175,6 @@ class PlainDirectoryNodeImpl extends DirectoryNodeImpl implements PlainDirectory
} }
} }
/**
* Remove a child. Possibly copy.
* @param name The name of the child to remove.
* @param lPath The lookup path.
* @return Success or failure.
*/
public boolean removeChild(String name, Lookup lPath)
{
// Can't remove it if it's not there.
if (getChild(name) == null)
{
return false;
}
DirectoryNode toModify = (DirectoryNode)copyOnWrite(lPath);
toModify.rawRemoveChild(name);
return true;
}
/** /**
* Put a new child node into this directory. No copy. * Put a new child node into this directory. No copy.
* @param name The name of the child. * @param name The name of the child.
@@ -294,9 +255,7 @@ class PlainDirectoryNodeImpl extends DirectoryNodeImpl implements PlainDirectory
*/ */
public void turnPrimary(Lookup lPath) public void turnPrimary(Lookup lPath)
{ {
LayeredDirectoryNode toModify = (LayeredDirectoryNode)copyOnWrite(lPath); assert false : "Should never happen.";
Lookup lookup = SuperRepository.GetInstance().lookup(-1, lPath.getRepresentedPath());
toModify.rawSetPrimary(lookup.getCurrentIndirection());
} }
/** /**
@@ -306,8 +265,7 @@ class PlainDirectoryNodeImpl extends DirectoryNodeImpl implements PlainDirectory
*/ */
public void retarget(Lookup lPath, String target) public void retarget(Lookup lPath, String target)
{ {
LayeredDirectoryNode toModify = (LayeredDirectoryNode)copyOnWrite(lPath); assert false : "Should never happen.";
toModify.rawSetPrimary(target);
} }
/** /**

View File

@@ -156,17 +156,19 @@ interface Repository
* Lookup a node. * Lookup a node.
* @param version The version to look under. * @param version The version to look under.
* @param path The path to the node. * @param path The path to the node.
* @param write Whether this is in a write context.
* @return A Lookup object. * @return A Lookup object.
*/ */
public Lookup lookup(int version, String path); public Lookup lookup(int version, String path, boolean write);
/** /**
* Lookup a directory. * Lookup a directory.
* @param version The version to look under. * @param version The version to look under.
* @param path The path to the directory. * @param path The path to the directory.
* @param write Whether this is in a write context.
* @return A Lookup object. * @return A Lookup object.
*/ */
public Lookup lookupDirectory(int version, String path); public Lookup lookupDirectory(int version, String path, boolean write);
/** /**
* For a layered node, get its indirection. * For a layered node, get its indirection.

View File

@@ -147,7 +147,7 @@ class RepositoryImpl implements Repository, Serializable
*/ */
public void createDirectory(String path, String name) public void createDirectory(String path, String name)
{ {
Lookup lPath = lookupDirectory(-1, path); Lookup lPath = lookupDirectory(-1, path, true);
// lPath.acquireLocks(); // lPath.acquireLocks();
DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode(); DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode();
if (dir.lookupChild(lPath, name, -1) != null) if (dir.lookupChild(lPath, name, -1) != null)
@@ -168,7 +168,7 @@ class RepositoryImpl implements Repository, Serializable
newDir = new PlainDirectoryNodeImpl(this); newDir = new PlainDirectoryNodeImpl(this);
} }
newDir.setVersionID(getNextVersionID()); newDir.setVersionID(getNextVersionID());
dir.addChild(name, newDir, lPath); dir.putChild(name, newDir);
} }
/** /**
@@ -180,7 +180,7 @@ class RepositoryImpl implements Repository, Serializable
public void createLayeredDirectory(String srcPath, String dstPath, public void createLayeredDirectory(String srcPath, String dstPath,
String name) String name)
{ {
Lookup lPath = lookupDirectory(-1, dstPath); Lookup lPath = lookupDirectory(-1, dstPath, true);
// lPath.acquireLocks(); // lPath.acquireLocks();
DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode(); DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode();
if (dir.lookupChild(lPath, name, -1) != null) if (dir.lookupChild(lPath, name, -1) != null)
@@ -202,7 +202,7 @@ class RepositoryImpl implements Repository, Serializable
// Otherwise we issue a brand new layer id. // Otherwise we issue a brand new layer id.
newDir.setLayerID(fSuper.issueLayerID()); newDir.setLayerID(fSuper.issueLayerID());
} }
dir.addChild(name, newDir, lPath); dir.putChild(name, newDir);
newDir.setVersionID(getNextVersionID()); newDir.setVersionID(getNextVersionID());
} }
@@ -215,7 +215,7 @@ class RepositoryImpl implements Repository, Serializable
*/ */
public OutputStream createFile(String path, String name) public OutputStream createFile(String path, String name)
{ {
Lookup lPath = lookupDirectory(-1, path); Lookup lPath = lookupDirectory(-1, path, true);
// lPath.acquireLocks(); // lPath.acquireLocks();
DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode(); DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode();
if (dir.lookupChild(lPath, name, -1) != null) if (dir.lookupChild(lPath, name, -1) != null)
@@ -224,7 +224,7 @@ class RepositoryImpl implements Repository, Serializable
} }
PlainFileNodeImpl file = new PlainFileNodeImpl(this); PlainFileNodeImpl file = new PlainFileNodeImpl(this);
file.setVersionID(getNextVersionID()); file.setVersionID(getNextVersionID());
dir.addChild(name, file, lPath); dir.putChild(name, file);
return file.getContentForWrite().getOutputStream(); return file.getContentForWrite().getOutputStream();
} }
@@ -236,7 +236,7 @@ class RepositoryImpl implements Repository, Serializable
*/ */
public void createLayeredFile(String srcPath, String dstPath, String name) public void createLayeredFile(String srcPath, String dstPath, String name)
{ {
Lookup lPath = lookupDirectory(-1, dstPath); Lookup lPath = lookupDirectory(-1, dstPath, true);
// lPath.acquireLocks(); // lPath.acquireLocks();
DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode(); DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode();
if (dir.lookupChild(lPath, name, -1) != null) if (dir.lookupChild(lPath, name, -1) != null)
@@ -246,7 +246,7 @@ class RepositoryImpl implements Repository, Serializable
// TODO Reexamine decision to not check validity of srcPath. // TODO Reexamine decision to not check validity of srcPath.
LayeredFileNodeImpl newFile = LayeredFileNodeImpl newFile =
new LayeredFileNodeImpl(srcPath, this); new LayeredFileNodeImpl(srcPath, this);
dir.addChild(name, newFile, lPath); dir.putChild(name, newFile);
newFile.setVersionID(getNextVersionID()); newFile.setVersionID(getNextVersionID());
} }
@@ -258,7 +258,7 @@ class RepositoryImpl implements Repository, Serializable
*/ */
public InputStream getInputStream(int version, String path) public InputStream getInputStream(int version, String path)
{ {
Lookup lPath = lookup(version, path); Lookup lPath = lookup(version, path, false);
AVMNode node = lPath.getCurrentNode(); AVMNode node = lPath.getCurrentNode();
if (node.getType() != AVMNodeType.PLAIN_FILE && if (node.getType() != AVMNodeType.PLAIN_FILE &&
node.getType() != AVMNodeType.LAYERED_FILE) node.getType() != AVMNodeType.LAYERED_FILE)
@@ -278,7 +278,7 @@ class RepositoryImpl implements Repository, Serializable
*/ */
public Map<String, AVMNodeDescriptor> getListing(int version, String path) public Map<String, AVMNodeDescriptor> getListing(int version, String path)
{ {
Lookup lPath = lookupDirectory(version, path); Lookup lPath = lookupDirectory(version, path, false);
DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode(); DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode();
Map<String, AVMNode> listing = dir.getListing(lPath); Map<String, AVMNode> listing = dir.getListing(lPath);
Map<String, AVMNodeDescriptor> results = new TreeMap<String, AVMNodeDescriptor>(); Map<String, AVMNodeDescriptor> results = new TreeMap<String, AVMNodeDescriptor>();
@@ -298,7 +298,7 @@ class RepositoryImpl implements Repository, Serializable
*/ */
public OutputStream getOutputStream(String path) public OutputStream getOutputStream(String path)
{ {
Lookup lPath = lookup(-1, path); Lookup lPath = lookup(-1, path, true);
// lPath.acquireLocks(); // lPath.acquireLocks();
AVMNode node = lPath.getCurrentNode(); AVMNode node = lPath.getCurrentNode();
if (node.getType() != AVMNodeType.PLAIN_FILE && if (node.getType() != AVMNodeType.PLAIN_FILE &&
@@ -307,9 +307,8 @@ class RepositoryImpl implements Repository, Serializable
throw new AVMWrongTypeException("Not a file: " + path); throw new AVMWrongTypeException("Not a file: " + path);
} }
FileNode file = (FileNode)node; FileNode file = (FileNode)node;
file = (FileNode)file.copyOnWrite(lPath);
FileContent content = file.getContentForWrite(); FileContent content = file.getContentForWrite();
return content.getOutputStream(); // TODO Do we really need fSuper? return content.getOutputStream();
} }
/** /**
@@ -326,7 +325,7 @@ class RepositoryImpl implements Repository, Serializable
{ {
throw new AVMException("Access denied: " + path); throw new AVMException("Access denied: " + path);
} }
Lookup lPath = lookup(version, path); Lookup lPath = lookup(version, path, write);
// if (write) // if (write)
// { // {
// lPath.acquireLocks(); // lPath.acquireLocks();
@@ -341,7 +340,6 @@ class RepositoryImpl implements Repository, Serializable
FileContent content = null; FileContent content = null;
if (write) if (write)
{ {
file = (FileNode)file.copyOnWrite(lPath);
content = file.getContentForWrite(); content = file.getContentForWrite();
} }
else else
@@ -359,14 +357,14 @@ class RepositoryImpl implements Repository, Serializable
public void removeNode(String path, String name) public void removeNode(String path, String name)
{ {
// TODO Are we double checking for existence? // TODO Are we double checking for existence?
Lookup lPath = lookupDirectory(-1, path); Lookup lPath = lookupDirectory(-1, path, true);
// lPath.acquireLocks(); // lPath.acquireLocks();
DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode(); DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode();
if (dir.lookupChild(lPath, name, -1) == null) if (dir.lookupChild(lPath, name, -1) == null)
{ {
throw new AVMNotFoundException("Does not exist: " + name); throw new AVMNotFoundException("Does not exist: " + name);
} }
dir.removeChild(name, lPath); dir.removeChild(name);
} }
/** /**
@@ -376,7 +374,7 @@ class RepositoryImpl implements Repository, Serializable
*/ */
public void uncover(String dirPath, String name) public void uncover(String dirPath, String name)
{ {
Lookup lPath = lookup(-1, dirPath); Lookup lPath = lookup(-1, dirPath, true);
// lPath.acquireLocks(); // lPath.acquireLocks();
AVMNode node = lPath.getCurrentNode(); AVMNode node = lPath.getCurrentNode();
if (node.getType() != AVMNodeType.LAYERED_DIRECTORY) if (node.getType() != AVMNodeType.LAYERED_DIRECTORY)
@@ -475,9 +473,10 @@ class RepositoryImpl implements Repository, Serializable
* Lookup up a path. * Lookup up a path.
* @param version The version to look in. * @param version The version to look in.
* @param path The path to look up. * @param path The path to look up.
* @param write Whether this is in the context of a write.
* @return A Lookup object. * @return A Lookup object.
*/ */
public Lookup lookup(int version, String path) public Lookup lookup(int version, String path, boolean write)
{ {
// Make up a Lookup to hold the results. // Make up a Lookup to hold the results.
Lookup result = new Lookup(this, fName); Lookup result = new Lookup(this, fName);
@@ -507,7 +506,8 @@ class RepositoryImpl implements Repository, Serializable
} }
// fSuper.getSession().lock(dir, LockMode.READ); // fSuper.getSession().lock(dir, LockMode.READ);
// Add an entry for the root. // Add an entry for the root.
result.add(dir, ""); result.add(dir, "", write);
dir = (DirectoryNode)result.getCurrentNode();
if (pathElements.length == 0) if (pathElements.length == 0)
{ {
return result; return result;
@@ -527,9 +527,9 @@ class RepositoryImpl implements Repository, Serializable
{ {
throw new AVMWrongTypeException("Not a directory: " + pathElements[i]); throw new AVMWrongTypeException("Not a directory: " + pathElements[i]);
} }
dir = (DirectoryNode)child;
// fSuper.getSession().lock(dir, LockMode.READ); // fSuper.getSession().lock(dir, LockMode.READ);
result.add(dir, pathElements[i]); result.add(child, pathElements[i], write);
dir = (DirectoryNode)result.getCurrentNode();
} }
// Now look up the last element. // Now look up the last element.
AVMNode child = dir.lookupChild(result, pathElements[pathElements.length - 1], version); AVMNode child = dir.lookupChild(result, pathElements[pathElements.length - 1], version);
@@ -538,7 +538,7 @@ class RepositoryImpl implements Repository, Serializable
throw new AVMNotFoundException("Not found: " + pathElements[pathElements.length - 1]); throw new AVMNotFoundException("Not found: " + pathElements[pathElements.length - 1]);
} }
// fSuper.getSession().lock(child, LockMode.READ); // fSuper.getSession().lock(child, LockMode.READ);
result.add(child, pathElements[pathElements.length - 1]); result.add(child, pathElements[pathElements.length - 1], write);
return result; return result;
} }
@@ -573,13 +573,14 @@ class RepositoryImpl implements Repository, Serializable
* Lookup a node and insist that it is a directory. * Lookup a node and insist that it is a directory.
* @param version The version to look under. * @param version The version to look under.
* @param path The path to the directory. * @param path The path to the directory.
* @param write Whether this is in a write context.
* @return A Lookup object. * @return A Lookup object.
*/ */
public Lookup lookupDirectory(int version, String path) public Lookup lookupDirectory(int version, String path, boolean write)
{ {
// Just do a regular lookup and assert that the last element // Just do a regular lookup and assert that the last element
// is a directory. // is a directory.
Lookup lPath = lookup(version, path); Lookup lPath = lookup(version, path, write);
if (lPath.getCurrentNode().getType() != AVMNodeType.PLAIN_DIRECTORY && if (lPath.getCurrentNode().getType() != AVMNodeType.PLAIN_DIRECTORY &&
lPath.getCurrentNode().getType() != AVMNodeType.LAYERED_DIRECTORY) lPath.getCurrentNode().getType() != AVMNodeType.LAYERED_DIRECTORY)
{ {
@@ -596,7 +597,7 @@ class RepositoryImpl implements Repository, Serializable
*/ */
public String getIndirectionPath(int version, String path) public String getIndirectionPath(int version, String path)
{ {
Lookup lPath = lookup(version, path); Lookup lPath = lookup(version, path, false);
AVMNode node = lPath.getCurrentNode(); AVMNode node = lPath.getCurrentNode();
if (node.getType() == AVMNodeType.LAYERED_DIRECTORY) if (node.getType() == AVMNodeType.LAYERED_DIRECTORY)
{ {
@@ -615,7 +616,7 @@ class RepositoryImpl implements Repository, Serializable
*/ */
public void makePrimary(String path) public void makePrimary(String path)
{ {
Lookup lPath = lookupDirectory(-1, path); Lookup lPath = lookupDirectory(-1, path, true);
// lPath.acquireLocks(); // lPath.acquireLocks();
DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode(); DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode();
if (!lPath.isLayered()) if (!lPath.isLayered())
@@ -632,7 +633,7 @@ class RepositoryImpl implements Repository, Serializable
*/ */
public void retargetLayeredDirectory(String path, String target) public void retargetLayeredDirectory(String path, String target)
{ {
Lookup lPath = lookupDirectory(-1, path); Lookup lPath = lookupDirectory(-1, path, true);
// lPath.acquireLocks(); // lPath.acquireLocks();
DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode(); DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode();
if (!lPath.isLayered()) if (!lPath.isLayered())

View File

@@ -32,7 +32,7 @@ public class SimultaneousLoadTest extends AVMServiceTestBase
{ {
try try
{ {
int n = 16; int n = 4;
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
{ {
fService.createDirectory("main:/", "d" + i); fService.createDirectory("main:/", "d" + i);

View File

@@ -201,13 +201,13 @@ class SuperRepository
String [] pathParts = SplitPath(srcPath); String [] pathParts = SplitPath(srcPath);
Repository srcRepo = getRepositoryByName(pathParts[0], false); Repository srcRepo = getRepositoryByName(pathParts[0], false);
// fSession.get().lock(srcRepo, LockMode.READ); // fSession.get().lock(srcRepo, LockMode.READ);
Lookup sPath = srcRepo.lookup(version, pathParts[1]); Lookup sPath = srcRepo.lookup(version, pathParts[1], false);
// Lookup the destination directory. // Lookup the destination directory.
fLookupCount.set(1); fLookupCount.set(1);
pathParts = SplitPath(dstPath); pathParts = SplitPath(dstPath);
Repository dstRepo = getRepositoryByName(pathParts[0], true); Repository dstRepo = getRepositoryByName(pathParts[0], true);
// fSession.get().lock(dstRepo, LockMode.UPGRADE); // fSession.get().lock(dstRepo, LockMode.UPGRADE);
Lookup dPath = dstRepo.lookupDirectory(-1, pathParts[1]); Lookup dPath = dstRepo.lookupDirectory(-1, pathParts[1], true);
// dPath.acquireLocks(); // dPath.acquireLocks();
DirectoryNode dirNode = (DirectoryNode)dPath.getCurrentNode(); DirectoryNode dirNode = (DirectoryNode)dPath.getCurrentNode();
AVMNode srcNode = sPath.getCurrentNode(); AVMNode srcNode = sPath.getCurrentNode();
@@ -235,7 +235,7 @@ class SuperRepository
} }
dstNode.setVersionID(dstRepo.getNextVersionID()); dstNode.setVersionID(dstRepo.getNextVersionID());
dstNode.setAncestor(srcNode); dstNode.setAncestor(srcNode);
dirNode.addChild(name, dstNode, dPath); dirNode.putChild(name, dstNode);
} }
/** /**
@@ -287,7 +287,7 @@ class SuperRepository
String [] pathParts = SplitPath(srcPath); String [] pathParts = SplitPath(srcPath);
Repository srcRepo = getRepositoryByName(pathParts[0], true); Repository srcRepo = getRepositoryByName(pathParts[0], true);
// fSession.get().lock(srcRepo, LockMode.UPGRADE); // fSession.get().lock(srcRepo, LockMode.UPGRADE);
Lookup sPath = srcRepo.lookupDirectory(-1, pathParts[1]); Lookup sPath = srcRepo.lookupDirectory(-1, pathParts[1], true);
// sPath.acquireLocks(); // sPath.acquireLocks();
DirectoryNode srcDir = (DirectoryNode)sPath.getCurrentNode(); DirectoryNode srcDir = (DirectoryNode)sPath.getCurrentNode();
AVMNode srcNode = srcDir.lookupChild(sPath, srcName, -1); AVMNode srcNode = srcDir.lookupChild(sPath, srcName, -1);
@@ -299,7 +299,7 @@ class SuperRepository
pathParts = SplitPath(dstPath); pathParts = SplitPath(dstPath);
Repository dstRepo = getRepositoryByName(pathParts[0], true); Repository dstRepo = getRepositoryByName(pathParts[0], true);
// fSession.get().lock(dstRepo, LockMode.UPGRADE); // fSession.get().lock(dstRepo, LockMode.UPGRADE);
Lookup dPath = dstRepo.lookupDirectory(-1, pathParts[1]); Lookup dPath = dstRepo.lookupDirectory(-1, pathParts[1], true);
// dPath.acquireLocks(); // dPath.acquireLocks();
DirectoryNode dstDir = (DirectoryNode)dPath.getCurrentNode(); DirectoryNode dstDir = (DirectoryNode)dPath.getCurrentNode();
AVMNode dstNode = dstDir.lookupChild(dPath, dstName, -1); AVMNode dstNode = dstDir.lookupChild(dPath, dstName, -1);
@@ -385,14 +385,9 @@ class SuperRepository
{ {
dstNode = new PlainFileNodeImpl((PlainFileNode)srcNode, dstRepo); dstNode = new PlainFileNodeImpl((PlainFileNode)srcNode, dstRepo);
} }
srcDir.removeChild(srcName, sPath); srcDir.removeChild(srcName);
fLookupCount.set(1);
pathParts = SplitPath(dstPath);
dPath = dstRepo.lookup(-1, pathParts[1]);
// dPath.acquireLocks();
dstDir = (DirectoryNode)dPath.getCurrentNode();
dstNode.setVersionID(dstRepo.getNextVersionID()); dstNode.setVersionID(dstRepo.getNextVersionID());
dstDir.addChild(dstName, dstNode, dPath); dstDir.putChild(dstName, dstNode);
dstNode.setAncestor(srcNode); dstNode.setAncestor(srcNode);
} }
@@ -674,8 +669,8 @@ class SuperRepository
private Repository getRepositoryByName(String name, boolean write) private Repository getRepositoryByName(String name, boolean write)
{ {
return (Repository)fSession.get().get(RepositoryImpl.class, return (Repository)fSession.get().get(RepositoryImpl.class,
name, name /* ,
write ? LockMode.UPGRADE : LockMode.READ); write ? LockMode.UPGRADE : LockMode.READ */);
} }
/** /**
@@ -711,7 +706,7 @@ class SuperRepository
String [] pathParts = SplitPath(path); String [] pathParts = SplitPath(path);
Repository rep = getRepositoryByName(pathParts[0], false); Repository rep = getRepositoryByName(pathParts[0], false);
// fSession.get().lock(rep, LockMode.READ); // fSession.get().lock(rep, LockMode.READ);
return rep.lookup(version, pathParts[1]); return rep.lookup(version, pathParts[1], false);
} }
/** /**
@@ -746,14 +741,14 @@ class SuperRepository
public Lookup lookupDirectory(int version, String path) public Lookup lookupDirectory(int version, String path)
{ {
fLookupCount.set(fLookupCount.get() + 1); fLookupCount.set(fLookupCount.get() + 1);
if (fLookupCount.get() > 10) if (fLookupCount.get() > 50)
{ {
throw new AVMCycleException("Cycle in lookup."); throw new AVMCycleException("Cycle in lookup.");
} }
String [] pathParts = SplitPath(path); String [] pathParts = SplitPath(path);
Repository rep = getRepositoryByName(pathParts[0], false); Repository rep = getRepositoryByName(pathParts[0], false);
// fSession.get().lock(rep, LockMode.READ); // fSession.get().lock(rep, LockMode.READ);
return rep.lookupDirectory(version, pathParts[1]); return rep.lookupDirectory(version, pathParts[1], false);
} }
/** /**

View File

@@ -27,6 +27,7 @@ import org.hibernate.SessionFactory;
import org.hibernate.StaleStateException; import org.hibernate.StaleStateException;
import org.hibernate.Transaction; import org.hibernate.Transaction;
import org.hibernate.exception.GenericJDBCException; import org.hibernate.exception.GenericJDBCException;
import org.hibernate.exception.LockAcquisitionException;
/** /**
* Helper for DAOs. * Helper for DAOs.
@@ -106,26 +107,30 @@ public class HibernateTxn
} }
// If we've lost a race or we've deadlocked, retry. // If we've lost a race or we've deadlocked, retry.
if (t instanceof StaleStateException || if (t instanceof StaleStateException ||
t instanceof GenericJDBCException) t instanceof GenericJDBCException ||
t instanceof LockAcquisitionException)
{ {
if (t instanceof StaleStateException) if (t instanceof StaleStateException)
{ {
System.err.println("Lost Race"); System.err.println("Lost Race");
continue;
} }
System.err.println("Deadlock"); else
try
{ {
long interval; System.err.println("Deadlock");
synchronized (fRandom) try
{ {
interval = fRandom.nextInt(1000); long interval;
synchronized (fRandom)
{
interval = fRandom.nextInt(1000);
}
Thread.sleep(interval);
continue;
}
catch (InterruptedException ie)
{
// Do nothing.
} }
Thread.sleep(interval);
}
catch (InterruptedException ie)
{
// Do nothing.
} }
continue; continue;
} }