Added the ability to remove neighbours from the database (untested).

This commit is contained in:
akwizgran
2011-06-29 12:54:00 +01:00
parent 6960f64982
commit ed0174a91b
6 changed files with 689 additions and 425 deletions

View File

@@ -50,6 +50,9 @@ public interface DatabaseComponent {
*/ */
void receiveBundle(NeighbourId n, Bundle b) throws DbException; void receiveBundle(NeighbourId n, Bundle b) throws DbException;
/** Removes a neighbour (and all associated state) from the database. */
void removeNeighbour(NeighbourId n) throws DbException;
/** Records the user's rating for the given author. */ /** Records the user's rating for the given author. */
void setRating(AuthorId a, Rating r) throws DbException; void setRating(AuthorId a, Rating r) throws DbException;

View File

@@ -19,6 +19,16 @@ import net.sf.briar.api.protocol.MessageId;
* obtained by calling startTransaction(). Every transaction must be * obtained by calling startTransaction(). Every transaction must be
* terminated by calling either abortTransaction() or commitTransaction(), * terminated by calling either abortTransaction() or commitTransaction(),
* even if an exception is thrown. * even if an exception is thrown.
*
* Locking is provided by the DatabaseComponent implementation. To prevent
* deadlock, locks must be acquired in the following order:
* <ul>
* <li> contacts
* <li> messages
* <li> messageStatuses
* <li> ratings
* <li> subscriptions
* </ul>
*/ */
interface Database<T> { interface Database<T> {
@@ -49,63 +59,80 @@ interface Database<T> {
/** /**
* Records a received batch as needing to be acknowledged. * Records a received batch as needing to be acknowledged.
* Locking: neighbours write. * <p>
* Locking: contacts read, messageStatuses write.
*/ */
void addBatchToAck(T txn, NeighbourId n, BatchId b) throws DbException; void addBatchToAck(T txn, NeighbourId n, BatchId b) throws DbException;
/** /**
* Returns false if the given message is already in the database. Otherwise * Returns false if the given message is already in the database. Otherwise
* stores the message and returns true. * stores the message and returns true.
* <p>
* Locking: messages write. * Locking: messages write.
*/ */
boolean addMessage(T txn, Message m) throws DbException; boolean addMessage(T txn, Message m) throws DbException;
/** /**
* Adds a new neighbour to the database. * Adds a new neighbour to the database.
* Locking: neighbours write. * <p>
* Locking: contacts write, messageStatuses write.
*/ */
void addNeighbour(T txn, NeighbourId n) throws DbException; void addNeighbour(T txn, NeighbourId n) throws DbException;
/** /**
* Records a sent batch as needing to be acknowledged. * Records a sent batch as needing to be acknowledged.
* Locking: neighbours write, messages read. * <p>
* Locking: contacts read, messages read, messageStatuses write.
*/ */
void addOutstandingBatch(T txn, NeighbourId n, BatchId b, Set<MessageId> sent) throws DbException; void addOutstandingBatch(T txn, NeighbourId n, BatchId b, Set<MessageId> sent) throws DbException;
/** /**
* Records a received bundle. This should be called after processing the * Records a received bundle. This should be called after processing the
* bundle's contents, and may result in outstanding messages becoming * bundle's contents, and may result in outstanding messages becoming
* eligible for retransmittion. * eligible for retransmission.
* Locking: neighbours write, messages read. * <p>
* Locking: contacts read, messages read, messageStatuses write.
*/ */
Set<BatchId> addReceivedBundle(T txn, NeighbourId n, BundleId b) throws DbException; Set<BatchId> addReceivedBundle(T txn, NeighbourId n, BundleId b) throws DbException;
/** /**
* Subscribes to the given group. * Subscribes to the given group.
* <p>
* Locking: subscriptions write. * Locking: subscriptions write.
*/ */
void addSubscription(T txn, GroupId g) throws DbException; void addSubscription(T txn, GroupId g) throws DbException;
/** /**
* Records a neighbour's subscription to a group. * Records a neighbour's subscription to a group.
* Locking: neighbours write. * <p>
* Locking: contacts read, messageStatuses write.
*/ */
void addSubscription(T txn, NeighbourId n, GroupId g) throws DbException; void addSubscription(T txn, NeighbourId n, GroupId g) throws DbException;
/** /**
* Removes all recorded subscriptions for the given neighbour. * Removes all recorded subscriptions for the given neighbour.
* Locking: neighbours write. * <p>
* Locking: contacts read, messageStatuses write.
*/ */
void clearSubscriptions(T txn, NeighbourId n) throws DbException; void clearSubscriptions(T txn, NeighbourId n) throws DbException;
/** /**
* Returns true iff the database contains the given message. * Returns true iff the database contains the given message.
* <p>
* Locking: messages read. * Locking: messages read.
*/ */
boolean containsMessage(T txn, MessageId m) throws DbException; boolean containsMessage(T txn, MessageId m) throws DbException;
/**
* Returns true iff the database contains the given neighbour.
* <p>
* Locking: contacts read.
*/
boolean containsNeighbour(T txn, NeighbourId n) throws DbException;
/** /**
* Returns true iff the user is subscribed to the given group. * Returns true iff the user is subscribed to the given group.
* <p>
* Locking: subscriptions read. * Locking: subscriptions read.
*/ */
boolean containsSubscription(T txn, GroupId g) throws DbException; boolean containsSubscription(T txn, GroupId g) throws DbException;
@@ -114,18 +141,21 @@ interface Database<T> {
* Returns the amount of free storage space available to the database, in * Returns the amount of free storage space available to the database, in
* bytes. This is based on the minimum of the space available on the device * bytes. This is based on the minimum of the space available on the device
* where the database is stored and the database's configured size. * where the database is stored and the database's configured size.
* <p>
* Locking: messages read. * Locking: messages read.
*/ */
long getFreeSpace() throws DbException; long getFreeSpace() throws DbException;
/** /**
* Returns the message identified by the given ID. * Returns the message identified by the given ID.
* <p>
* Locking: messages read. * Locking: messages read.
*/ */
Message getMessage(T txn, MessageId m) throws DbException; Message getMessage(T txn, MessageId m) throws DbException;
/** /**
* Returns the IDs of all messages signed by the given author. * Returns the IDs of all messages signed by the given author.
* <p>
* Locking: messages read. * Locking: messages read.
*/ */
Iterable<MessageId> getMessagesByAuthor(T txn, AuthorId a) throws DbException; Iterable<MessageId> getMessagesByAuthor(T txn, AuthorId a) throws DbException;
@@ -133,31 +163,36 @@ interface Database<T> {
/** /**
* Returns the IDs of all children of the message identified by the given * Returns the IDs of all children of the message identified by the given
* ID that are present in the database. * ID that are present in the database.
* <p>
* Locking: messages read. * Locking: messages read.
*/ */
Iterable<MessageId> getMessagesByParent(T txn, MessageId m) throws DbException; Iterable<MessageId> getMessagesByParent(T txn, MessageId m) throws DbException;
/** /**
* Returns the IDs of all neighbours * Returns the IDs of all neighbours.
* Locking: neighbours read. * <p>
* Locking: contacts read, messageStatuses read.
*/ */
Set<NeighbourId> getNeighbours(T txn) throws DbException; Set<NeighbourId> getNeighbours(T txn) throws DbException;
/** /**
* Returns the IDs of the oldest messages in the database, with a total * Returns the IDs of the oldest messages in the database, with a total
* size less than or equal to the given size. * size less than or equal to the given size.
* <p>
* Locking: messages read. * Locking: messages read.
*/ */
Iterable<MessageId> getOldMessages(T txn, long size) throws DbException; Iterable<MessageId> getOldMessages(T txn, long size) throws DbException;
/** /**
* Returns the parent of the given message. * Returns the parent of the given message.
* <p>
* Locking: messages read. * Locking: messages read.
*/ */
MessageId getParent(T txn, MessageId m) throws DbException; MessageId getParent(T txn, MessageId m) throws DbException;
/** /**
* Returns the user's rating for the given author. * Returns the user's rating for the given author.
* <p>
* Locking: ratings read. * Locking: ratings read.
*/ */
Rating getRating(T txn, AuthorId a) throws DbException; Rating getRating(T txn, AuthorId a) throws DbException;
@@ -166,6 +201,7 @@ interface Database<T> {
* Returns the sendability score of the given message. Messages with * Returns the sendability score of the given message. Messages with
* sendability scores greater than zero are eligible to be sent to * sendability scores greater than zero are eligible to be sent to
* neighbours. * neighbours.
* <p>
* Locking: messages read. * Locking: messages read.
*/ */
int getSendability(T txn, MessageId m) throws DbException; int getSendability(T txn, MessageId m) throws DbException;
@@ -173,12 +209,14 @@ interface Database<T> {
/** /**
* Returns the IDs of some messages that are eligible to be sent to the * Returns the IDs of some messages that are eligible to be sent to the
* given neighbour, with a total size less than or equal to the given size. * given neighbour, with a total size less than or equal to the given size.
* Locking: neighbours read, messages read. * <p>
* Locking: contacts read, messages read, messageStatuses read.
*/ */
Iterable<MessageId> getSendableMessages(T txn, NeighbourId n, long capacity) throws DbException; Iterable<MessageId> getSendableMessages(T txn, NeighbourId n, long capacity) throws DbException;
/** /**
* Returns the groups to which the user subscribes. * Returns the groups to which the user subscribes.
* <p>
* Locking: subscriptions read. * Locking: subscriptions read.
*/ */
Set<GroupId> getSubscriptions(T txn) throws DbException; Set<GroupId> getSubscriptions(T txn) throws DbException;
@@ -187,14 +225,16 @@ interface Database<T> {
* Removes an outstanding batch that has been acknowledged. Any messages in * Removes an outstanding batch that has been acknowledged. Any messages in
* the batch that are still considered outstanding (Status.SENT) with * the batch that are still considered outstanding (Status.SENT) with
* respect to the given neighbour are now considered seen (Status.SEEN). * respect to the given neighbour are now considered seen (Status.SEEN).
* Locking: neighbours write, messages read. * <p>
* Locking: contacts read, messages read, messageStatuses write.
*/ */
void removeAckedBatch(T txn, NeighbourId n, BatchId b) throws DbException; void removeAckedBatch(T txn, NeighbourId n, BatchId b) throws DbException;
/** /**
* Removes and returns the IDs of any batches received from the given * Removes and returns the IDs of any batches received from the given
* neighbour that need to be acknowledged. * neighbour that need to be acknowledged.
* Locking: neighbours write. * <p>
* Locking: contacts read, messageStatuses write.
*/ */
Set<BatchId> removeBatchesToAck(T txn, NeighbourId n) throws DbException; Set<BatchId> removeBatchesToAck(T txn, NeighbourId n) throws DbException;
@@ -202,38 +242,52 @@ interface Database<T> {
* Removes an outstanding batch that has been lost. Any messages in the * Removes an outstanding batch that has been lost. Any messages in the
* batch that are still considered outstanding (Status.SENT) with respect * batch that are still considered outstanding (Status.SENT) with respect
* to the given neighbour are now considered unsent (Status.NEW). * to the given neighbour are now considered unsent (Status.NEW).
* Locking: neighbours write, messages read. * <p>
* Locking: contacts read, messages read, messageStatuses write.
*/ */
void removeLostBatch(T txn, NeighbourId n, BatchId b) throws DbException; void removeLostBatch(T txn, NeighbourId n, BatchId b) throws DbException;
/** /**
* Removes a message from the database. * Removes a message (and all associated state) from the database.
* Locking: neighbours write, messages write. * <p>
* Locking: contacts read, messages write, messageStatuses write.
*/ */
void removeMessage(T txn, MessageId m) throws DbException; void removeMessage(T txn, MessageId m) throws DbException;
/**
* Removes a neighbour (and all associated state) from the database.
* <p>
* Locking: contacts write, messageStatuses write.
*/
void removeNeighbour(T txn, NeighbourId n) throws DbException;
/** /**
* Unsubscribes from the given group. Any messages belonging to the group * Unsubscribes from the given group. Any messages belonging to the group
* are deleted from the database. * are deleted from the database.
* Locking: subscriptions write, neighbours write, messages write. * <p>
* Locking: contacts read, subscriptions write, messages write,
* messageStatuses write.
*/ */
void removeSubscription(T txn, GroupId g) throws DbException; void removeSubscription(T txn, GroupId g) throws DbException;
/** /**
* Records the user's rating for the given author. * Records the user's rating for the given author.
* <p>
* Locking: ratings write. * Locking: ratings write.
*/ */
Rating setRating(T txn, AuthorId a, Rating r) throws DbException; Rating setRating(T txn, AuthorId a, Rating r) throws DbException;
/** /**
* Records the sendability score of the given message. * Records the sendability score of the given message.
* <p>
* Locking: messages write. * Locking: messages write.
*/ */
void setSendability(T txn, MessageId m, int sendability) throws DbException; void setSendability(T txn, MessageId m, int sendability) throws DbException;
/** /**
* Sets the status of the given message with respect to the given neighbour. * Sets the status of the given message with respect to the given neighbour.
* Locking: neighbours write, messages read * <p>
* Locking: contacts read, messages read, messageStatuses write.
*/ */
void setStatus(T txn, NeighbourId n, MessageId m, Status s) throws DbException; void setStatus(T txn, NeighbourId n, MessageId m, Status s) throws DbException;
} }

View File

@@ -74,7 +74,20 @@ abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent {
} }
} }
// Locking: messages write, neighbours write // Locking: contacts read
protected boolean containsNeighbour(NeighbourId n) throws DbException {
Txn txn = db.startTransaction();
try {
boolean contains = db.containsNeighbour(txn, n);
db.commitTransaction(txn);
return contains;
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
}
// Locking: contacts read, messages write, messageStatuses write
protected void removeMessage(Txn txn, MessageId id) throws DbException { protected void removeMessage(Txn txn, MessageId id) throws DbException {
Integer sendability = db.getSendability(txn, id); Integer sendability = db.getSendability(txn, id);
assert sendability != null; assert sendability != null;
@@ -127,7 +140,7 @@ abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent {
new Thread(cleaner).start(); new Thread(cleaner).start();
} }
// Locking: messages write, neighbours write // Locking: contacts read, messages write, messageStatuses write
protected boolean storeMessage(Txn txn, Message m, NeighbourId sender) protected boolean storeMessage(Txn txn, Message m, NeighbourId sender)
throws DbException { throws DbException {
boolean added = db.addMessage(txn, m); boolean added = db.addMessage(txn, m);

View File

@@ -650,6 +650,33 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public boolean containsNeighbour(Connection txn, NeighbourId n)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT COUNT(neighbourId) FROM neighbours"
+ " WHERE neighbourId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, n.getInt());
rs = ps.executeQuery();
boolean found = rs.next();
assert found;
int count = rs.getInt(1);
assert count <= 1;
boolean more = rs.next();
assert !more;
rs.close();
ps.close();
return count > 0;
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
tryToClose(txn);
throw new DbException(e);
}
}
public boolean containsSubscription(Connection txn, GroupId g) public boolean containsSubscription(Connection txn, GroupId g)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
@@ -1070,6 +1097,23 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public void removeNeighbour(Connection txn, NeighbourId n)
throws DbException {
PreparedStatement ps = null;
try {
String sql = "DELETE FROM neighbours WHERE neighbourId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, n.getInt());
int rowsAffected = ps.executeUpdate();
assert rowsAffected == 1;
ps.close();
} catch(SQLException e) {
tryToClose(ps);
tryToClose(txn);
throw new DbException(e);
}
}
public void removeSubscription(Connection txn, GroupId g) public void removeSubscription(Connection txn, GroupId g)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;

View File

@@ -32,9 +32,11 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
* implementation can allow writers to starve. * implementation can allow writers to starve.
*/ */
private final ReentrantReadWriteLock contactLock =
new ReentrantReadWriteLock(true);
private final ReentrantReadWriteLock messageLock = private final ReentrantReadWriteLock messageLock =
new ReentrantReadWriteLock(true); new ReentrantReadWriteLock(true);
private final ReentrantReadWriteLock neighbourLock = private final ReentrantReadWriteLock messageStatusLock =
new ReentrantReadWriteLock(true); new ReentrantReadWriteLock(true);
private final ReentrantReadWriteLock ratingLock = private final ReentrantReadWriteLock ratingLock =
new ReentrantReadWriteLock(true); new ReentrantReadWriteLock(true);
@@ -48,9 +50,11 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
} }
protected void expireMessages(long size) throws DbException { protected void expireMessages(long size) throws DbException {
contactLock.readLock().lock();
try {
messageLock.writeLock().lock(); messageLock.writeLock().lock();
try { try {
neighbourLock.writeLock().lock(); messageStatusLock.writeLock().lock();
try { try {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
@@ -63,17 +67,22 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
throw e; throw e;
} }
} finally { } finally {
neighbourLock.writeLock().unlock(); messageStatusLock.writeLock().unlock();
} }
} finally { } finally {
messageLock.writeLock().unlock(); messageLock.writeLock().unlock();
} }
} finally {
contactLock.readLock().unlock();
}
} }
public void close() throws DbException { public void close() throws DbException {
contactLock.writeLock().lock();
try {
messageLock.writeLock().lock(); messageLock.writeLock().lock();
try { try {
neighbourLock.writeLock().lock(); messageStatusLock.writeLock().lock();
try { try {
ratingLock.writeLock().lock(); ratingLock.writeLock().lock();
try { try {
@@ -87,16 +96,21 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
ratingLock.writeLock().unlock(); ratingLock.writeLock().unlock();
} }
} finally { } finally {
neighbourLock.writeLock().unlock(); messageStatusLock.writeLock().unlock();
} }
} finally { } finally {
messageLock.writeLock().unlock(); messageLock.writeLock().unlock();
} }
} finally {
contactLock.writeLock().unlock();
}
} }
public void addNeighbour(NeighbourId n) throws DbException { public void addNeighbour(NeighbourId n) throws DbException {
if(LOG.isLoggable(Level.FINE)) LOG.fine("Adding neighbour " + n); if(LOG.isLoggable(Level.FINE)) LOG.fine("Adding neighbour " + n);
neighbourLock.writeLock().lock(); contactLock.writeLock().lock();
try {
messageStatusLock.writeLock().lock();
try { try {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
@@ -107,15 +121,20 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
throw e; throw e;
} }
} finally { } finally {
neighbourLock.writeLock().unlock(); messageStatusLock.writeLock().unlock();
}
} finally {
contactLock.writeLock().unlock();
} }
} }
public void addLocallyGeneratedMessage(Message m) throws DbException { public void addLocallyGeneratedMessage(Message m) throws DbException {
waitForPermissionToWrite(); waitForPermissionToWrite();
contactLock.readLock().lock();
try {
messageLock.writeLock().lock(); messageLock.writeLock().lock();
try { try {
neighbourLock.writeLock().lock(); messageStatusLock.writeLock().lock();
try { try {
subscriptionLock.readLock().lock(); subscriptionLock.readLock().lock();
try { try {
@@ -137,11 +156,14 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
subscriptionLock.readLock().unlock(); subscriptionLock.readLock().unlock();
} }
} finally { } finally {
neighbourLock.writeLock().unlock(); messageStatusLock.writeLock().unlock();
} }
} finally { } finally {
messageLock.writeLock().unlock(); messageLock.writeLock().unlock();
} }
} finally {
contactLock.readLock().unlock();
}
} }
public Rating getRating(AuthorId a) throws DbException { public Rating getRating(AuthorId a) throws DbException {
@@ -161,6 +183,28 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
} }
} }
public void removeNeighbour(NeighbourId n) throws DbException {
if(LOG.isLoggable(Level.FINE)) LOG.fine("Removing neighbour " + n);
contactLock.writeLock().lock();
try {
messageStatusLock.writeLock().lock();
try {
Txn txn = db.startTransaction();
try {
db.removeNeighbour(txn, n);
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
messageStatusLock.writeLock().unlock();
}
} finally {
contactLock.writeLock().unlock();
}
}
public void setRating(AuthorId a, Rating r) throws DbException { public void setRating(AuthorId a, Rating r) throws DbException {
messageLock.writeLock().lock(); messageLock.writeLock().lock();
try { try {
@@ -224,9 +268,11 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
public void unsubscribe(GroupId g) throws DbException { public void unsubscribe(GroupId g) throws DbException {
if(LOG.isLoggable(Level.FINE)) LOG.fine("Unsubscribing from " + g); if(LOG.isLoggable(Level.FINE)) LOG.fine("Unsubscribing from " + g);
contactLock.readLock().lock();
try {
messageLock.writeLock().lock(); messageLock.writeLock().lock();
try { try {
neighbourLock.writeLock().lock(); messageStatusLock.writeLock().lock();
try { try {
subscriptionLock.writeLock().lock(); subscriptionLock.writeLock().lock();
try { try {
@@ -242,17 +288,23 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
subscriptionLock.writeLock().unlock(); subscriptionLock.writeLock().unlock();
} }
} finally { } finally {
neighbourLock.writeLock().unlock(); messageStatusLock.writeLock().unlock();
} }
} finally { } finally {
messageLock.writeLock().unlock(); messageLock.writeLock().unlock();
} }
} finally {
contactLock.readLock().unlock();
}
} }
public void generateBundle(NeighbourId n, Bundle b) throws DbException { public void generateBundle(NeighbourId n, Bundle b) throws DbException {
if(LOG.isLoggable(Level.FINE)) LOG.fine("Generating bundle for " + n); if(LOG.isLoggable(Level.FINE)) LOG.fine("Generating bundle for " + n);
// Ack all batches received from the neighbour // Ack all batches received from the neighbour
neighbourLock.writeLock().lock(); contactLock.readLock().lock();
try {
if(!containsNeighbour(n)) return;
messageStatusLock.writeLock().lock();
try { try {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
@@ -269,9 +321,15 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
throw e; throw e;
} }
} finally { } finally {
neighbourLock.writeLock().unlock(); messageStatusLock.writeLock().unlock();
}
} finally {
contactLock.readLock().unlock();
} }
// Add a list of subscriptions // Add a list of subscriptions
contactLock.readLock().lock();
try {
if(!containsNeighbour(n)) return;
subscriptionLock.readLock().lock(); subscriptionLock.readLock().lock();
try { try {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
@@ -291,6 +349,9 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
} finally { } finally {
subscriptionLock.readLock().unlock(); subscriptionLock.readLock().unlock();
} }
} finally {
contactLock.readLock().unlock();
}
// Add as many messages as possible to the bundle // Add as many messages as possible to the bundle
long capacity = b.getCapacity(); long capacity = b.getCapacity();
while(true) { while(true) {
@@ -309,11 +370,14 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
} }
private Batch fillBatch(NeighbourId n, long capacity) throws DbException { private Batch fillBatch(NeighbourId n, long capacity) throws DbException {
contactLock.readLock().lock();
try {
if(!containsNeighbour(n)) return null;
messageLock.readLock().lock(); messageLock.readLock().lock();
try { try {
Set<MessageId> sent; Set<MessageId> sent;
Batch b; Batch b;
neighbourLock.readLock().lock(); messageStatusLock.readLock().lock();
try { try {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
@@ -338,10 +402,10 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
throw e; throw e;
} }
} finally { } finally {
neighbourLock.readLock().unlock(); messageStatusLock.readLock().unlock();
} }
// Record the contents of the batch // Record the contents of the batch
neighbourLock.writeLock().lock(); messageStatusLock.writeLock().lock();
try { try {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
@@ -354,11 +418,14 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
throw e; throw e;
} }
} finally { } finally {
neighbourLock.writeLock().unlock(); messageStatusLock.writeLock().unlock();
} }
} finally { } finally {
messageLock.readLock().unlock(); messageLock.readLock().unlock();
} }
} finally {
contactLock.readLock().unlock();
}
} }
public void receiveBundle(NeighbourId n, Bundle b) throws DbException { public void receiveBundle(NeighbourId n, Bundle b) throws DbException {
@@ -366,9 +433,12 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
LOG.fine("Received bundle from " + n + ", " LOG.fine("Received bundle from " + n + ", "
+ b.getSize() + " bytes"); + b.getSize() + " bytes");
// Mark all messages in acked batches as seen // Mark all messages in acked batches as seen
contactLock.readLock().lock();
try {
if(!containsNeighbour(n)) return;
messageLock.readLock().lock(); messageLock.readLock().lock();
try { try {
neighbourLock.writeLock().lock(); messageStatusLock.writeLock().lock();
try { try {
int acks = 0; int acks = 0;
for(BatchId ack : b.getAcks()) { for(BatchId ack : b.getAcks()) {
@@ -385,13 +455,19 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
if(LOG.isLoggable(Level.FINE)) if(LOG.isLoggable(Level.FINE))
LOG.fine("Received " + acks + " acks"); LOG.fine("Received " + acks + " acks");
} finally { } finally {
neighbourLock.writeLock().unlock(); messageStatusLock.writeLock().unlock();
} }
} finally { } finally {
messageLock.readLock().unlock(); messageLock.readLock().unlock();
} }
} finally {
contactLock.readLock().unlock();
}
// Update the neighbour's subscriptions // Update the neighbour's subscriptions
neighbourLock.writeLock().lock(); contactLock.readLock().lock();
try {
if(!containsNeighbour(n)) return;
messageStatusLock.writeLock().lock();
try { try {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
@@ -409,16 +485,22 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
throw e; throw e;
} }
} finally { } finally {
neighbourLock.writeLock().unlock(); messageStatusLock.writeLock().unlock();
}
} finally {
contactLock.readLock().lock();
} }
// Store the messages // Store the messages
int batches = 0; int batches = 0;
for(Batch batch : b.getBatches()) { for(Batch batch : b.getBatches()) {
batches++; batches++;
waitForPermissionToWrite(); waitForPermissionToWrite();
contactLock.readLock().lock();
try {
if(!containsNeighbour(n)) return;
messageLock.writeLock().lock(); messageLock.writeLock().lock();
try { try {
neighbourLock.writeLock().lock(); messageStatusLock.writeLock().lock();
try { try {
subscriptionLock.readLock().lock(); subscriptionLock.readLock().lock();
try { try {
@@ -427,7 +509,8 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
int received = 0, stored = 0; int received = 0, stored = 0;
for(Message m : batch.getMessages()) { for(Message m : batch.getMessages()) {
received++; received++;
if(db.containsSubscription(txn, m.getGroup())) { GroupId g = m.getGroup();
if(db.containsSubscription(txn, g)) {
if(storeMessage(txn, m, n)) stored++; if(storeMessage(txn, m, n)) stored++;
} }
} }
@@ -444,19 +527,25 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
subscriptionLock.readLock().unlock(); subscriptionLock.readLock().unlock();
} }
} finally { } finally {
neighbourLock.writeLock().unlock(); messageStatusLock.writeLock().unlock();
} }
} finally { } finally {
messageLock.writeLock().unlock(); messageLock.writeLock().unlock();
} }
} finally {
contactLock.readLock().unlock();
}
} }
if(LOG.isLoggable(Level.FINE)) if(LOG.isLoggable(Level.FINE))
LOG.fine("Received " + batches + " batches"); LOG.fine("Received " + batches + " batches");
// Find any lost batches that need to be retransmitted // Find any lost batches that need to be retransmitted
Set<BatchId> lost; Set<BatchId> lost;
contactLock.readLock().lock();
try {
if(!containsNeighbour(n)) return;
messageLock.readLock().lock(); messageLock.readLock().lock();
try { try {
neighbourLock.writeLock().lock(); messageStatusLock.writeLock().lock();
try { try {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
@@ -467,15 +556,21 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
throw e; throw e;
} }
} finally { } finally {
neighbourLock.writeLock().unlock(); messageStatusLock.writeLock().unlock();
} }
} finally { } finally {
messageLock.readLock().unlock(); messageLock.readLock().unlock();
} }
} finally {
contactLock.readLock().unlock();
}
for(BatchId batch : lost) { for(BatchId batch : lost) {
contactLock.readLock().lock();
try {
if(!containsNeighbour(n)) return;
messageLock.readLock().lock(); messageLock.readLock().lock();
try { try {
neighbourLock.writeLock().lock(); messageStatusLock.writeLock().lock();
try { try {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
@@ -488,11 +583,14 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
throw e; throw e;
} }
} finally { } finally {
neighbourLock.writeLock().unlock(); messageStatusLock.writeLock().unlock();
} }
} finally { } finally {
messageLock.readLock().unlock(); messageLock.readLock().unlock();
} }
} finally {
contactLock.readLock().unlock();
}
} }
System.gc(); System.gc();
} }

View File

@@ -30,8 +30,9 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
* interface to find out which calls require which locks. * interface to find out which calls require which locks.
*/ */
private final Object contactLock = new Object();
private final Object messageLock = new Object(); private final Object messageLock = new Object();
private final Object neighbourLock = new Object(); private final Object messageStatusLock = new Object();
private final Object ratingLock = new Object(); private final Object ratingLock = new Object();
private final Object subscriptionLock = new Object(); private final Object subscriptionLock = new Object();
@@ -42,8 +43,9 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
} }
protected void expireMessages(long size) throws DbException { protected void expireMessages(long size) throws DbException {
synchronized(contactLock) {
synchronized(messageLock) { synchronized(messageLock) {
synchronized(neighbourLock) { synchronized(messageStatusLock) {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
for(MessageId m : db.getOldMessages(txn, size)) { for(MessageId m : db.getOldMessages(txn, size)) {
@@ -57,10 +59,12 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
} }
} }
} }
}
public void close() throws DbException { public void close() throws DbException {
synchronized(contactLock) {
synchronized(messageLock) { synchronized(messageLock) {
synchronized(neighbourLock) { synchronized(messageStatusLock) {
synchronized(ratingLock) { synchronized(ratingLock) {
synchronized(subscriptionLock) { synchronized(subscriptionLock) {
db.close(); db.close();
@@ -69,10 +73,12 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
} }
} }
} }
}
public void addNeighbour(NeighbourId n) throws DbException { public void addNeighbour(NeighbourId n) throws DbException {
if(LOG.isLoggable(Level.FINE)) LOG.fine("Adding neighbour " + n); if(LOG.isLoggable(Level.FINE)) LOG.fine("Adding neighbour " + n);
synchronized(neighbourLock) { synchronized(contactLock) {
synchronized(messageStatusLock) {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
db.addNeighbour(txn, n); db.addNeighbour(txn, n);
@@ -83,11 +89,13 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
} }
} }
} }
}
public void addLocallyGeneratedMessage(Message m) throws DbException { public void addLocallyGeneratedMessage(Message m) throws DbException {
waitForPermissionToWrite(); waitForPermissionToWrite();
synchronized(contactLock) {
synchronized(messageLock) { synchronized(messageLock) {
synchronized(neighbourLock) { synchronized(messageStatusLock) {
synchronized(subscriptionLock) { synchronized(subscriptionLock) {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
@@ -107,6 +115,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
} }
} }
} }
}
public Rating getRating(AuthorId a) throws DbException { public Rating getRating(AuthorId a) throws DbException {
synchronized(ratingLock) { synchronized(ratingLock) {
@@ -173,8 +182,9 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
public void unsubscribe(GroupId g) throws DbException { public void unsubscribe(GroupId g) throws DbException {
if(LOG.isLoggable(Level.FINE)) LOG.fine("Unsubscribing from " + g); if(LOG.isLoggable(Level.FINE)) LOG.fine("Unsubscribing from " + g);
synchronized(contactLock) {
synchronized(messageLock) { synchronized(messageLock) {
synchronized(neighbourLock) { synchronized(messageStatusLock) {
synchronized(subscriptionLock) { synchronized(subscriptionLock) {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
@@ -188,11 +198,14 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
} }
} }
} }
}
public void generateBundle(NeighbourId n, Bundle b) throws DbException { public void generateBundle(NeighbourId n, Bundle b) throws DbException {
if(LOG.isLoggable(Level.FINE)) LOG.fine("Generating bundle for " + n); if(LOG.isLoggable(Level.FINE)) LOG.fine("Generating bundle for " + n);
// Ack all batches received from the neighbour // Ack all batches received from the neighbour
synchronized(neighbourLock) { synchronized(contactLock) {
if(!containsNeighbour(n)) return;
synchronized(messageStatusLock) {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
int numAcks = 0; int numAcks = 0;
@@ -208,7 +221,10 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
throw e; throw e;
} }
} }
}
// Add a list of subscriptions // Add a list of subscriptions
synchronized(contactLock) {
if(!containsNeighbour(n)) return;
synchronized(subscriptionLock) { synchronized(subscriptionLock) {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
@@ -225,6 +241,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
throw e; throw e;
} }
} }
}
// Add as many messages as possible to the bundle // Add as many messages as possible to the bundle
long capacity = b.getCapacity(); long capacity = b.getCapacity();
while(true) { while(true) {
@@ -243,8 +260,10 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
} }
private Batch fillBatch(NeighbourId n, long capacity) throws DbException { private Batch fillBatch(NeighbourId n, long capacity) throws DbException {
synchronized(contactLock) {
if(!containsNeighbour(n)) return null;
synchronized(messageLock) { synchronized(messageLock) {
synchronized(neighbourLock) { synchronized(messageStatusLock) {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
capacity = Math.min(capacity, Batch.CAPACITY); capacity = Math.min(capacity, Batch.CAPACITY);
@@ -274,14 +293,33 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
} }
} }
} }
}
public void removeNeighbour(NeighbourId n) throws DbException {
if(LOG.isLoggable(Level.FINE)) LOG.fine("Removing neighbour " + n);
synchronized(contactLock) {
synchronized(messageStatusLock) {
Txn txn = db.startTransaction();
try {
db.removeNeighbour(txn, n);
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
}
}
}
public void receiveBundle(NeighbourId n, Bundle b) throws DbException { public void receiveBundle(NeighbourId n, Bundle b) throws DbException {
if(LOG.isLoggable(Level.FINE)) if(LOG.isLoggable(Level.FINE))
LOG.fine("Received bundle from " + n + ", " LOG.fine("Received bundle from " + n + ", "
+ b.getSize() + " bytes"); + b.getSize() + " bytes");
// Mark all messages in acked batches as seen // Mark all messages in acked batches as seen
synchronized(contactLock) {
if(!containsNeighbour(n)) return;
synchronized(messageLock) { synchronized(messageLock) {
synchronized(neighbourLock) { synchronized(messageStatusLock) {
int acks = 0; int acks = 0;
for(BatchId ack : b.getAcks()) { for(BatchId ack : b.getAcks()) {
acks++; acks++;
@@ -298,8 +336,11 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
LOG.fine("Received " + acks + " acks"); LOG.fine("Received " + acks + " acks");
} }
} }
}
// Update the neighbour's subscriptions // Update the neighbour's subscriptions
synchronized(neighbourLock) { synchronized(contactLock) {
if(!containsNeighbour(n)) return;
synchronized(messageStatusLock) {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
db.clearSubscriptions(txn, n); db.clearSubscriptions(txn, n);
@@ -316,20 +357,24 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
throw e; throw e;
} }
} }
}
// Store the messages // Store the messages
int batches = 0; int batches = 0;
for(Batch batch : b.getBatches()) { for(Batch batch : b.getBatches()) {
batches++; batches++;
waitForPermissionToWrite(); waitForPermissionToWrite();
synchronized(contactLock) {
if(!containsNeighbour(n)) return;
synchronized(messageLock) { synchronized(messageLock) {
synchronized(neighbourLock) { synchronized(messageStatusLock) {
synchronized(subscriptionLock) { synchronized(subscriptionLock) {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
int received = 0, stored = 0; int received = 0, stored = 0;
for(Message m : batch.getMessages()) { for(Message m : batch.getMessages()) {
received++; received++;
if(db.containsSubscription(txn, m.getGroup())) { GroupId g = m.getGroup();
if(db.containsSubscription(txn, g)) {
if(storeMessage(txn, m, n)) stored++; if(storeMessage(txn, m, n)) stored++;
} }
} }
@@ -346,12 +391,15 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
} }
} }
} }
}
if(LOG.isLoggable(Level.FINE)) if(LOG.isLoggable(Level.FINE))
LOG.fine("Received " + batches + " batches"); LOG.fine("Received " + batches + " batches");
// Find any lost batches that need to be retransmitted // Find any lost batches that need to be retransmitted
Set<BatchId> lost; Set<BatchId> lost;
synchronized(contactLock) {
if(!containsNeighbour(n)) return;
synchronized(messageLock) { synchronized(messageLock) {
synchronized(neighbourLock) { synchronized(messageStatusLock) {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
lost = db.addReceivedBundle(txn, n, b.getId()); lost = db.addReceivedBundle(txn, n, b.getId());
@@ -362,9 +410,12 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
} }
} }
} }
}
for(BatchId batch : lost) { for(BatchId batch : lost) {
synchronized(contactLock) {
if(!containsNeighbour(n)) return;
synchronized(messageLock) { synchronized(messageLock) {
synchronized(neighbourLock) { synchronized(messageStatusLock) {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
if(LOG.isLoggable(Level.FINE)) if(LOG.isLoggable(Level.FINE))
@@ -378,6 +429,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
} }
} }
} }
}
System.gc(); System.gc();
} }
} }