Skip to content

Commit

Permalink
Keep track of transactions and threads associated with locks.
Browse files Browse the repository at this point in the history
This is a debug feature so that the transaction/thread that uses
a specific lock can be seen from the MBean or found in a heap dump.

Transactions already tracked the thread they are bound to, so the
lock will now bind to a transaction and the chain is complete.

From the shell it is possible to do:
 dbinfo -g Locking
  • Loading branch information
martinfurmanski committed Mar 26, 2015
1 parent 99798ef commit 1af370d
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 15 deletions.
Expand Up @@ -19,6 +19,8 @@
*/
package org.neo4j.kernel.impl.locking;

import javax.transaction.Transaction;

import org.neo4j.helpers.Service;
import org.neo4j.kernel.impl.util.concurrent.WaitStrategy;
import org.neo4j.kernel.lifecycle.Lifecycle;
Expand Down Expand Up @@ -107,6 +109,9 @@ public interface Client extends AutoCloseable
/** Release all locks. */
void releaseAll();

/** Associate with transaction (for debug). */
void setTx( Transaction tx );

/** Releases all locks, using the client after calling this is undefined. */
@Override
void close();
Expand Down
Expand Up @@ -19,6 +19,8 @@
*/
package org.neo4j.kernel.impl.locking;

import javax.transaction.Transaction;

public class NoOpClient implements Locks.Client
{
@Override
Expand Down Expand Up @@ -68,6 +70,11 @@ public void releaseAll()
{
}

@Override
public void setTx( Transaction tx )
{
}

@Override
public void close()
{
Expand Down
Expand Up @@ -22,12 +22,14 @@
import java.util.HashMap;
import java.util.Map;

import javax.transaction.Transaction;

import org.neo4j.kernel.impl.locking.Locks;

public class CommunityLockClient implements Locks.Client
{
private final LockManagerImpl manager;
private final LockTransaction lockTransaction = new LockTransaction();
private Transaction tx = LockTransaction.NO_TRANSACTION;

private final Map<Locks.ResourceType, Map<Long, LockResource>> sharedLocks = new HashMap<>();
private final Map<Locks.ResourceType, Map<Long, LockResource>> exclusiveLocks = new HashMap<>();
Expand All @@ -51,7 +53,7 @@ public void acquireShared( Locks.ResourceType resourceType, long... resourceIds
}

resource = new LockResource( resourceType, resourceId );
manager.getReadLock( resource, lockTransaction );
manager.getReadLock( resource, tx );
localLocks.put(resourceId, resource);
}
}
Expand All @@ -70,7 +72,7 @@ public void acquireExclusive( Locks.ResourceType resourceType, long... resourceI
}

resource = new LockResource( resourceType, resourceId );
manager.getWriteLock( resource, lockTransaction );
manager.getWriteLock( resource, tx );
localLocks.put(resourceId, resource);
}
}
Expand All @@ -89,7 +91,7 @@ public boolean tryExclusiveLock( Locks.ResourceType resourceType, long... resour
}

resource = new LockResource( resourceType, resourceId );
if(manager.tryWriteLock( resource, lockTransaction ))
if(manager.tryWriteLock( resource, tx ))
{
localLocks.put(resourceId, resource);
}
Expand All @@ -115,7 +117,7 @@ public boolean trySharedLock( Locks.ResourceType resourceType, long... resourceI
}

resource = new LockResource( resourceType, resourceId );
if(manager.tryReadLock( resource, lockTransaction ))
if(manager.tryReadLock( resource, tx ))
{
localLocks.put(resourceId, resource);
}
Expand All @@ -140,7 +142,7 @@ public void releaseShared( Locks.ResourceType resourceType, long... resourceIds
}
localLocks.remove( resourceId );

manager.releaseReadLock( new LockResource( resourceType, resourceId ), lockTransaction );
manager.releaseReadLock( new LockResource( resourceType, resourceId ), tx );
}
}

Expand All @@ -157,7 +159,7 @@ public void releaseExclusive( Locks.ResourceType resourceType, long... resourceI
}
localLocks.remove( resourceId );

manager.releaseWriteLock( new LockResource( resourceType, resourceId ), lockTransaction );
manager.releaseWriteLock( new LockResource( resourceType, resourceId ), tx );
}
}

Expand All @@ -168,7 +170,7 @@ public void releaseAllShared()
{
for ( LockResource resource : map.values() )
{
manager.releaseReadLock( resource, lockTransaction );
manager.releaseReadLock( resource, tx );
}
}
sharedLocks.clear();
Expand All @@ -181,7 +183,7 @@ public void releaseAllExclusive()
{
for ( LockResource resource : map.values() )
{
manager.releaseWriteLock( resource, lockTransaction );
manager.releaseWriteLock( resource, tx );
}
}
exclusiveLocks.clear();
Expand All @@ -194,10 +196,17 @@ public void releaseAll()
releaseAllShared();
}

@Override
public void setTx( Transaction tx )
{
this.tx = tx;
}

@Override
public void close()
{
releaseAll();
setTx( LockTransaction.NO_TRANSACTION );
}

private Map<Long, LockResource> localShared( Locks.ResourceType resourceType )
Expand Down
Expand Up @@ -41,6 +41,8 @@ public class LockTransaction implements Transaction

private final long id = IDS.getAndIncrement();

static final Transaction NO_TRANSACTION = new LockTransaction();

@Override
public void commit() throws HeuristicMixedException, HeuristicRollbackException, RollbackException, SecurityException, SystemException
{
Expand Down
Expand Up @@ -76,11 +76,11 @@ class TransactionImpl implements Transaction
{
this.txManager = txManager;
this.logger = logger;
this.owner = Thread.currentThread();
this.state = stateFactory.create( this );
globalId = xidGlobalId;
eventIdentifier = txManager.getNextEventIdentifier();
this.forceMode = forceMode;
owner = Thread.currentThread();
}

/**
Expand Down Expand Up @@ -111,8 +111,8 @@ private String getStatusAsString()
@Override
public String toString()
{
return String.format( "Transaction(%d, owner:\"%s\")[%s,Resources=%d]",
eventIdentifier, owner.getName(), getStatusAsString(), resourceList.size() );
return String.format( "Transaction[eventId=%d,thread=%d:'%s',status=%s,resourceCount=%d]",
eventIdentifier, owner.getId(), owner.getName(), getStatusAsString(), resourceList.size() );
}

@Override
Expand Down
Expand Up @@ -52,8 +52,13 @@ public void setDependencies( Locks lockManager,

public TransactionState create( Transaction tx )
{
return new WritableTransactionState( locks.newClient(), nodeManager,
Locks.Client lockClient = locks.newClient();
lockClient.setTx( tx );

TransactionState transactionState = new WritableTransactionState( lockClient, nodeManager,
txHook, txIdGenerator );

return transactionState;
}

public static TransactionStateFactory noStateFactory( Logging logging )
Expand Down
Expand Up @@ -29,6 +29,8 @@
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import static org.neo4j.kernel.impl.transaction.xaframework.LogPruneStrategies.NO_PRUNING;
import static org.neo4j.kernel.impl.transaction.xaframework.RecoveryVerifier.ALWAYS_VALID;
import static org.neo4j.kernel.impl.util.StringLogger.DEV_NULL;
Expand Down Expand Up @@ -105,7 +107,10 @@ public void shouldNotSetTmNotOKForFailureInCommitted() throws Throwable
TxIdGenerator txIdGenerator = mock( TxIdGenerator.class );
doThrow( RuntimeException.class ).when( txIdGenerator )
.committed( any( XaDataSource.class ), anyInt(), anyLong(), any( Integer.class ) );
stateFactory.setDependencies( mock( Locks.class ),
Locks mockLocks = mock( Locks.class );
Locks.Client mockLock = mock( Locks.Client.class );
when( mockLocks.newClient() ).thenReturn( mockLock );
stateFactory.setDependencies( mockLocks,
mock( NodeManager.class ), mock( RemoteTxHook.class ), txIdGenerator );
XaDataSourceManager xaDataSourceManager = life.add( new XaDataSourceManager( DEV_NULL ) );
KernelHealth kernelHealth = new KernelHealth( panicGenerator, logging );
Expand Down
Expand Up @@ -24,6 +24,8 @@
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import javax.transaction.Transaction;

import org.neo4j.com.Response;
import org.neo4j.kernel.AvailabilityGuard;
import org.neo4j.kernel.DeadlockDetectedException;
Expand Down Expand Up @@ -55,6 +57,7 @@ class SlaveLocksClient implements Locks.Client
// Using atomic ints to avoid creating garbage through boxing.
private final Map<Locks.ResourceType, Map<Long, AtomicInteger>> sharedLocks;
private final Map<Locks.ResourceType, Map<Long, AtomicInteger>> exclusiveLocks;
private Transaction tx;

public SlaveLocksClient(
Master master,
Expand Down Expand Up @@ -238,12 +241,25 @@ public void releaseAll()
client.releaseAll();
}

@Override
public void setTx( Transaction tx )
{
this.tx = tx;
}

@Override
public void close()
{
sharedLocks.clear();
exclusiveLocks.clear();
client.close();
tx = null;
}

@Override
public String toString()
{
return String.format( "SlaveLocksClient[tx=%s,target=%s]", tx, client );
}

private boolean getReadLockOnMaster( Locks.ResourceType resourceType, long ... resourceId )
Expand Down
Expand Up @@ -21,6 +21,8 @@

import java.util.concurrent.ConcurrentMap;

import javax.transaction.Transaction;

import org.neo4j.collection.primitive.Primitive;
import org.neo4j.collection.primitive.PrimitiveIntIterator;
import org.neo4j.collection.primitive.PrimitiveLongIntMap;
Expand Down Expand Up @@ -64,6 +66,8 @@ public class ForsetiClient implements Locks.Client
/** For exclusive locks, we only need a single re-usable one per client. */
private final ExclusiveLock myExclusiveLock = new ExclusiveLock(this);

private Transaction tx;

public ForsetiClient( int id,
ConcurrentMap[] lockMaps,
WaitStrategy[] waitStrategies,
Expand Down Expand Up @@ -465,6 +469,8 @@ public void releaseAll()
public void close()
{
releaseAll();
setTx( null );

clientPool.release( this );
}

Expand Down Expand Up @@ -513,7 +519,7 @@ public int hashCode()
@Override
public String toString()
{
return String.format( "ForsetiClient[%d]", myId );
return String.format( "ForsetiClient[id=%d,tx=%s]", myId, tx );
}

/** Release a lock from the global pool. */
Expand Down Expand Up @@ -664,6 +670,11 @@ public int id()
return myId;
}

public void setTx( Transaction tx )
{
this.tx = tx;
}

// Visitors used for bulk ops on the lock maps (such as releasing all locks)

private class ReleaseSharedLocksVisitor implements PrimitiveLongVisitor
Expand Down

0 comments on commit 1af370d

Please sign in to comment.