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,85 +50,16 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
} }
protected void expireMessages(long size) throws DbException { protected void expireMessages(long size) throws DbException {
messageLock.writeLock().lock(); contactLock.readLock().lock();
try { try {
neighbourLock.writeLock().lock(); messageLock.writeLock().lock();
try { try {
Txn txn = db.startTransaction(); messageStatusLock.writeLock().lock();
try {
for(MessageId m : db.getOldMessages(txn, size)) {
removeMessage(txn, m);
}
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
neighbourLock.writeLock().unlock();
}
} finally {
messageLock.writeLock().unlock();
}
}
public void close() throws DbException {
messageLock.writeLock().lock();
try {
neighbourLock.writeLock().lock();
try {
ratingLock.writeLock().lock();
try {
subscriptionLock.writeLock().lock();
try {
db.close();
} finally {
subscriptionLock.writeLock().unlock();
}
} finally {
ratingLock.writeLock().unlock();
}
} finally {
neighbourLock.writeLock().unlock();
}
} finally {
messageLock.writeLock().unlock();
}
}
public void addNeighbour(NeighbourId n) throws DbException {
if(LOG.isLoggable(Level.FINE)) LOG.fine("Adding neighbour " + n);
neighbourLock.writeLock().lock();
try {
Txn txn = db.startTransaction();
try {
db.addNeighbour(txn, n);
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
neighbourLock.writeLock().unlock();
}
}
public void addLocallyGeneratedMessage(Message m) throws DbException {
waitForPermissionToWrite();
messageLock.writeLock().lock();
try {
neighbourLock.writeLock().lock();
try {
subscriptionLock.readLock().lock();
try { try {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
if(db.containsSubscription(txn, m.getGroup())) { for(MessageId m : db.getOldMessages(txn, size)) {
boolean added = storeMessage(txn, m, null); removeMessage(txn, m);
assert added;
} else {
if(LOG.isLoggable(Level.FINE))
LOG.fine("Not subscribed");
} }
db.commitTransaction(txn); db.commitTransaction(txn);
} catch(DbException e) { } catch(DbException e) {
@@ -134,13 +67,102 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
throw e; throw e;
} }
} finally { } finally {
subscriptionLock.readLock().unlock(); messageStatusLock.writeLock().unlock();
} }
} finally { } finally {
neighbourLock.writeLock().unlock(); messageLock.writeLock().unlock();
} }
} finally { } finally {
messageLock.writeLock().unlock(); contactLock.readLock().unlock();
}
}
public void close() throws DbException {
contactLock.writeLock().lock();
try {
messageLock.writeLock().lock();
try {
messageStatusLock.writeLock().lock();
try {
ratingLock.writeLock().lock();
try {
subscriptionLock.writeLock().lock();
try {
db.close();
} finally {
subscriptionLock.writeLock().unlock();
}
} finally {
ratingLock.writeLock().unlock();
}
} finally {
messageStatusLock.writeLock().unlock();
}
} finally {
messageLock.writeLock().unlock();
}
} finally {
contactLock.writeLock().unlock();
}
}
public void addNeighbour(NeighbourId n) throws DbException {
if(LOG.isLoggable(Level.FINE)) LOG.fine("Adding neighbour " + n);
contactLock.writeLock().lock();
try {
messageStatusLock.writeLock().lock();
try {
Txn txn = db.startTransaction();
try {
db.addNeighbour(txn, n);
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
messageStatusLock.writeLock().unlock();
}
} finally {
contactLock.writeLock().unlock();
}
}
public void addLocallyGeneratedMessage(Message m) throws DbException {
waitForPermissionToWrite();
contactLock.readLock().lock();
try {
messageLock.writeLock().lock();
try {
messageStatusLock.writeLock().lock();
try {
subscriptionLock.readLock().lock();
try {
Txn txn = db.startTransaction();
try {
if(db.containsSubscription(txn, m.getGroup())) {
boolean added = storeMessage(txn, m, null);
assert added;
} else {
if(LOG.isLoggable(Level.FINE))
LOG.fine("Not subscribed");
}
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
subscriptionLock.readLock().unlock();
}
} finally {
messageStatusLock.writeLock().unlock();
}
} finally {
messageLock.writeLock().unlock();
}
} finally {
contactLock.readLock().unlock();
} }
} }
@@ -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,72 +268,89 @@ 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);
messageLock.writeLock().lock(); contactLock.readLock().lock();
try { try {
neighbourLock.writeLock().lock(); messageLock.writeLock().lock();
try { try {
subscriptionLock.writeLock().lock(); messageStatusLock.writeLock().lock();
try { try {
Txn txn = db.startTransaction(); subscriptionLock.writeLock().lock();
try { try {
db.removeSubscription(txn, g); Txn txn = db.startTransaction();
db.commitTransaction(txn); try {
} catch(DbException e) { db.removeSubscription(txn, g);
db.abortTransaction(txn); db.commitTransaction(txn);
throw e; } catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
subscriptionLock.writeLock().unlock();
} }
} finally { } finally {
subscriptionLock.writeLock().unlock(); messageStatusLock.writeLock().unlock();
} }
} finally { } finally {
neighbourLock.writeLock().unlock(); messageLock.writeLock().unlock();
} }
} finally { } finally {
messageLock.writeLock().unlock(); 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 { try {
Txn txn = db.startTransaction(); if(!containsNeighbour(n)) return;
messageStatusLock.writeLock().lock();
try { try {
int numAcks = 0; Txn txn = db.startTransaction();
for(BatchId ack : db.removeBatchesToAck(txn, n)) { try {
b.addAck(ack); int numAcks = 0;
numAcks++; for(BatchId ack : db.removeBatchesToAck(txn, n)) {
b.addAck(ack);
numAcks++;
}
if(LOG.isLoggable(Level.FINE))
LOG.fine("Added " + numAcks + " acks");
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
} }
if(LOG.isLoggable(Level.FINE)) } finally {
LOG.fine("Added " + numAcks + " acks"); messageStatusLock.writeLock().unlock();
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
} }
} finally { } finally {
neighbourLock.writeLock().unlock(); contactLock.readLock().unlock();
} }
// Add a list of subscriptions // Add a list of subscriptions
subscriptionLock.readLock().lock(); contactLock.readLock().lock();
try { try {
Txn txn = db.startTransaction(); if(!containsNeighbour(n)) return;
subscriptionLock.readLock().lock();
try { try {
int numSubs = 0; Txn txn = db.startTransaction();
for(GroupId g : db.getSubscriptions(txn)) { try {
b.addSubscription(g); int numSubs = 0;
numSubs++; for(GroupId g : db.getSubscriptions(txn)) {
b.addSubscription(g);
numSubs++;
}
if(LOG.isLoggable(Level.FINE))
LOG.fine("Added " + numSubs + " subscriptions");
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
} }
if(LOG.isLoggable(Level.FINE)) } finally {
LOG.fine("Added " + numSubs + " subscriptions"); subscriptionLock.readLock().unlock();
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
} }
} finally { } finally {
subscriptionLock.readLock().unlock(); 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();
@@ -309,55 +370,61 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
} }
private Batch fillBatch(NeighbourId n, long capacity) throws DbException { private Batch fillBatch(NeighbourId n, long capacity) throws DbException {
messageLock.readLock().lock(); contactLock.readLock().lock();
try { try {
Set<MessageId> sent; if(!containsNeighbour(n)) return null;
Batch b; messageLock.readLock().lock();
neighbourLock.readLock().lock();
try { try {
Txn txn = db.startTransaction(); Set<MessageId> sent;
Batch b;
messageStatusLock.readLock().lock();
try { try {
capacity = Math.min(capacity, Batch.CAPACITY); Txn txn = db.startTransaction();
Iterator<MessageId> it = try {
db.getSendableMessages(txn, n, capacity).iterator(); capacity = Math.min(capacity, Batch.CAPACITY);
if(!it.hasNext()) { Iterator<MessageId> it =
db.getSendableMessages(txn, n, capacity).iterator();
if(!it.hasNext()) {
db.commitTransaction(txn);
return null; // No more messages to send
}
sent = new HashSet<MessageId>();
b = batchProvider.get();
while(it.hasNext()) {
MessageId m = it.next();
b.addMessage(db.getMessage(txn, m));
sent.add(m);
}
b.seal();
db.commitTransaction(txn); db.commitTransaction(txn);
return null; // No more messages to send } catch(DbException e) {
db.abortTransaction(txn);
throw e;
} }
sent = new HashSet<MessageId>(); } finally {
b = batchProvider.get(); messageStatusLock.readLock().unlock();
while(it.hasNext()) {
MessageId m = it.next();
b.addMessage(db.getMessage(txn, m));
sent.add(m);
}
b.seal();
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
} }
} finally { // Record the contents of the batch
neighbourLock.readLock().unlock(); messageStatusLock.writeLock().lock();
}
// Record the contents of the batch
neighbourLock.writeLock().lock();
try {
Txn txn = db.startTransaction();
try { try {
assert !sent.isEmpty(); Txn txn = db.startTransaction();
db.addOutstandingBatch(txn, n, b.getId(), sent); try {
db.commitTransaction(txn); assert !sent.isEmpty();
return b; db.addOutstandingBatch(txn, n, b.getId(), sent);
} catch(DbException e) { db.commitTransaction(txn);
db.abortTransaction(txn); return b;
throw e; } catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
messageStatusLock.writeLock().unlock();
} }
} finally { } finally {
neighbourLock.writeLock().unlock(); messageLock.readLock().unlock();
} }
} finally { } finally {
messageLock.readLock().unlock(); contactLock.readLock().unlock();
} }
} }
@@ -366,133 +433,164 @@ 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
messageLock.readLock().lock(); contactLock.readLock().lock();
try { try {
neighbourLock.writeLock().lock(); if(!containsNeighbour(n)) return;
messageLock.readLock().lock();
try { try {
int acks = 0; messageStatusLock.writeLock().lock();
for(BatchId ack : b.getAcks()) {
acks++;
Txn txn = db.startTransaction();
try {
db.removeAckedBatch(txn, n, ack);
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
}
if(LOG.isLoggable(Level.FINE))
LOG.fine("Received " + acks + " acks");
} finally {
neighbourLock.writeLock().unlock();
}
} finally {
messageLock.readLock().unlock();
}
// Update the neighbour's subscriptions
neighbourLock.writeLock().lock();
try {
Txn txn = db.startTransaction();
try {
db.clearSubscriptions(txn, n);
int subs = 0;
for(GroupId g : b.getSubscriptions()) {
subs++;
db.addSubscription(txn, n, g);
}
if(LOG.isLoggable(Level.FINE))
LOG.fine("Received " + subs + " subscriptions");
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
neighbourLock.writeLock().unlock();
}
// Store the messages
int batches = 0;
for(Batch batch : b.getBatches()) {
batches++;
waitForPermissionToWrite();
messageLock.writeLock().lock();
try {
neighbourLock.writeLock().lock();
try { try {
subscriptionLock.readLock().lock(); int acks = 0;
try { for(BatchId ack : b.getAcks()) {
acks++;
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
int received = 0, stored = 0; db.removeAckedBatch(txn, n, ack);
for(Message m : batch.getMessages()) {
received++;
if(db.containsSubscription(txn, m.getGroup())) {
if(storeMessage(txn, m, n)) stored++;
}
}
if(LOG.isLoggable(Level.FINE))
LOG.fine("Received " + received
+ " messages, stored " + stored);
db.addBatchToAck(txn, n, batch.getId());
db.commitTransaction(txn); db.commitTransaction(txn);
} catch(DbException e) { } catch(DbException e) {
db.abortTransaction(txn); db.abortTransaction(txn);
throw e; throw e;
} }
} finally {
subscriptionLock.readLock().unlock();
} }
if(LOG.isLoggable(Level.FINE))
LOG.fine("Received " + acks + " acks");
} finally { } finally {
neighbourLock.writeLock().unlock(); messageStatusLock.writeLock().unlock();
} }
} finally { } finally {
messageLock.writeLock().unlock(); messageLock.readLock().unlock();
} }
} finally {
contactLock.readLock().unlock();
} }
if(LOG.isLoggable(Level.FINE)) // Update the neighbour's subscriptions
LOG.fine("Received " + batches + " batches"); contactLock.readLock().lock();
// Find any lost batches that need to be retransmitted
Set<BatchId> lost;
messageLock.readLock().lock();
try { try {
neighbourLock.writeLock().lock(); if(!containsNeighbour(n)) return;
messageStatusLock.writeLock().lock();
try { try {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
lost = db.addReceivedBundle(txn, n, b.getId()); db.clearSubscriptions(txn, n);
int subs = 0;
for(GroupId g : b.getSubscriptions()) {
subs++;
db.addSubscription(txn, n, g);
}
if(LOG.isLoggable(Level.FINE))
LOG.fine("Received " + subs + " subscriptions");
db.commitTransaction(txn); db.commitTransaction(txn);
} catch(DbException e) { } catch(DbException e) {
db.abortTransaction(txn); db.abortTransaction(txn);
throw e; throw e;
} }
} finally { } finally {
neighbourLock.writeLock().unlock(); messageStatusLock.writeLock().unlock();
} }
} finally { } finally {
messageLock.readLock().unlock(); contactLock.readLock().lock();
} }
for(BatchId batch : lost) { // Store the messages
int batches = 0;
for(Batch batch : b.getBatches()) {
batches++;
waitForPermissionToWrite();
contactLock.readLock().lock();
try {
if(!containsNeighbour(n)) return;
messageLock.writeLock().lock();
try {
messageStatusLock.writeLock().lock();
try {
subscriptionLock.readLock().lock();
try {
Txn txn = db.startTransaction();
try {
int received = 0, stored = 0;
for(Message m : batch.getMessages()) {
received++;
GroupId g = m.getGroup();
if(db.containsSubscription(txn, g)) {
if(storeMessage(txn, m, n)) stored++;
}
}
if(LOG.isLoggable(Level.FINE))
LOG.fine("Received " + received
+ " messages, stored " + stored);
db.addBatchToAck(txn, n, batch.getId());
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
subscriptionLock.readLock().unlock();
}
} finally {
messageStatusLock.writeLock().unlock();
}
} finally {
messageLock.writeLock().unlock();
}
} finally {
contactLock.readLock().unlock();
}
}
if(LOG.isLoggable(Level.FINE))
LOG.fine("Received " + batches + " batches");
// Find any lost batches that need to be retransmitted
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 {
if(LOG.isLoggable(Level.FINE)) lost = db.addReceivedBundle(txn, n, b.getId());
LOG.fine("Removing lost batch");
db.removeLostBatch(txn, n, batch);
db.commitTransaction(txn); db.commitTransaction(txn);
} catch(DbException e) { } catch(DbException e) {
db.abortTransaction(txn); db.abortTransaction(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) {
contactLock.readLock().lock();
try {
if(!containsNeighbour(n)) return;
messageLock.readLock().lock();
try {
messageStatusLock.writeLock().lock();
try {
Txn txn = db.startTransaction();
try {
if(LOG.isLoggable(Level.FINE))
LOG.fine("Removing lost batch");
db.removeLostBatch(txn, n, batch);
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
messageStatusLock.writeLock().unlock();
}
} finally {
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,13 +43,45 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
} }
protected void expireMessages(long size) throws DbException { protected void expireMessages(long size) throws DbException {
synchronized(messageLock) { synchronized(contactLock) {
synchronized(neighbourLock) { synchronized(messageLock) {
synchronized(messageStatusLock) {
Txn txn = db.startTransaction();
try {
for(MessageId m : db.getOldMessages(txn, size)) {
removeMessage(txn, m);
}
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
}
}
}
}
public void close() throws DbException {
synchronized(contactLock) {
synchronized(messageLock) {
synchronized(messageStatusLock) {
synchronized(ratingLock) {
synchronized(subscriptionLock) {
db.close();
}
}
}
}
}
}
public void addNeighbour(NeighbourId n) throws DbException {
if(LOG.isLoggable(Level.FINE)) LOG.fine("Adding neighbour " + n);
synchronized(contactLock) {
synchronized(messageStatusLock) {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
for(MessageId m : db.getOldMessages(txn, size)) { db.addNeighbour(txn, n);
removeMessage(txn, m);
}
db.commitTransaction(txn); db.commitTransaction(txn);
} catch(DbException e) { } catch(DbException e) {
db.abortTransaction(txn); db.abortTransaction(txn);
@@ -58,50 +91,26 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
} }
} }
public void close() throws DbException {
synchronized(messageLock) {
synchronized(neighbourLock) {
synchronized(ratingLock) {
synchronized(subscriptionLock) {
db.close();
}
}
}
}
}
public void addNeighbour(NeighbourId n) throws DbException {
if(LOG.isLoggable(Level.FINE)) LOG.fine("Adding neighbour " + n);
synchronized(neighbourLock) {
Txn txn = db.startTransaction();
try {
db.addNeighbour(txn, n);
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
}
}
public void addLocallyGeneratedMessage(Message m) throws DbException { public void addLocallyGeneratedMessage(Message m) throws DbException {
waitForPermissionToWrite(); waitForPermissionToWrite();
synchronized(messageLock) { synchronized(contactLock) {
synchronized(neighbourLock) { synchronized(messageLock) {
synchronized(subscriptionLock) { synchronized(messageStatusLock) {
Txn txn = db.startTransaction(); synchronized(subscriptionLock) {
try { Txn txn = db.startTransaction();
if(db.containsSubscription(txn, m.getGroup())) { try {
boolean added = storeMessage(txn, m, null); if(db.containsSubscription(txn, m.getGroup())) {
assert added; boolean added = storeMessage(txn, m, null);
} else { assert added;
if(LOG.isLoggable(Level.FINE)) } else {
LOG.fine("Not subscribed"); if(LOG.isLoggable(Level.FINE))
LOG.fine("Not subscribed");
}
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
} }
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
} }
} }
} }
@@ -173,16 +182,18 @@ 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(messageLock) { synchronized(contactLock) {
synchronized(neighbourLock) { synchronized(messageLock) {
synchronized(subscriptionLock) { synchronized(messageStatusLock) {
Txn txn = db.startTransaction(); synchronized(subscriptionLock) {
try { Txn txn = db.startTransaction();
db.removeSubscription(txn, g); try {
db.commitTransaction(txn); db.removeSubscription(txn, g);
} catch(DbException e) { db.commitTransaction(txn);
db.abortTransaction(txn); } catch(DbException e) {
throw e; db.abortTransaction(txn);
throw e;
}
} }
} }
} }
@@ -192,37 +203,43 @@ 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) {
Txn txn = db.startTransaction(); if(!containsNeighbour(n)) return;
try { synchronized(messageStatusLock) {
int numAcks = 0; Txn txn = db.startTransaction();
for(BatchId ack : db.removeBatchesToAck(txn, n)) { try {
b.addAck(ack); int numAcks = 0;
numAcks++; for(BatchId ack : db.removeBatchesToAck(txn, n)) {
b.addAck(ack);
numAcks++;
}
if(LOG.isLoggable(Level.FINE))
LOG.fine("Added " + numAcks + " acks");
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
} }
if(LOG.isLoggable(Level.FINE))
LOG.fine("Added " + numAcks + " acks");
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
} }
} }
// Add a list of subscriptions // Add a list of subscriptions
synchronized(subscriptionLock) { synchronized(contactLock) {
Txn txn = db.startTransaction(); if(!containsNeighbour(n)) return;
try { synchronized(subscriptionLock) {
int numSubs = 0; Txn txn = db.startTransaction();
for(GroupId g : db.getSubscriptions(txn)) { try {
b.addSubscription(g); int numSubs = 0;
numSubs++; for(GroupId g : db.getSubscriptions(txn)) {
b.addSubscription(g);
numSubs++;
}
if(LOG.isLoggable(Level.FINE))
LOG.fine("Added " + numSubs + " subscriptions");
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
} }
if(LOG.isLoggable(Level.FINE))
LOG.fine("Added " + numSubs + " subscriptions");
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
} }
} }
// Add as many messages as possible to the bundle // Add as many messages as possible to the bundle
@@ -243,30 +260,49 @@ 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(messageLock) { synchronized(contactLock) {
synchronized(neighbourLock) { if(!containsNeighbour(n)) return null;
synchronized(messageLock) {
synchronized(messageStatusLock) {
Txn txn = db.startTransaction();
try {
capacity = Math.min(capacity, Batch.CAPACITY);
Iterator<MessageId> it =
db.getSendableMessages(txn, n, capacity).iterator();
if(!it.hasNext()) {
db.commitTransaction(txn);
return null; // No more messages to send
}
Batch b = batchProvider.get();
Set<MessageId> sent = new HashSet<MessageId>();
while(it.hasNext()) {
MessageId m = it.next();
b.addMessage(db.getMessage(txn, m));
sent.add(m);
}
b.seal();
// Record the contents of the batch
assert !sent.isEmpty();
db.addOutstandingBatch(txn, n, b.getId(), sent);
db.commitTransaction(txn);
return b;
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
}
}
}
}
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(); Txn txn = db.startTransaction();
try { try {
capacity = Math.min(capacity, Batch.CAPACITY); db.removeNeighbour(txn, n);
Iterator<MessageId> it =
db.getSendableMessages(txn, n, capacity).iterator();
if(!it.hasNext()) {
db.commitTransaction(txn);
return null; // No more messages to send
}
Batch b = batchProvider.get();
Set<MessageId> sent = new HashSet<MessageId>();
while(it.hasNext()) {
MessageId m = it.next();
b.addMessage(db.getMessage(txn, m));
sent.add(m);
}
b.seal();
// Record the contents of the batch
assert !sent.isEmpty();
db.addOutstandingBatch(txn, n, b.getId(), sent);
db.commitTransaction(txn); db.commitTransaction(txn);
return b;
} catch(DbException e) { } catch(DbException e) {
db.abortTransaction(txn); db.abortTransaction(txn);
throw e; throw e;
@@ -278,42 +314,48 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
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(messageLock) { synchronized(contactLock) {
synchronized(neighbourLock) { if(!containsNeighbour(n)) return;
int acks = 0; synchronized(messageLock) {
for(BatchId ack : b.getAcks()) { synchronized(messageStatusLock) {
acks++; int acks = 0;
Txn txn = db.startTransaction(); for(BatchId ack : b.getAcks()) {
try { acks++;
db.removeAckedBatch(txn, n, ack); Txn txn = db.startTransaction();
db.commitTransaction(txn); try {
} catch(DbException e) { db.removeAckedBatch(txn, n, ack);
db.abortTransaction(txn); db.commitTransaction(txn);
throw e; } catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} }
if(LOG.isLoggable(Level.FINE))
LOG.fine("Received " + acks + " acks");
} }
if(LOG.isLoggable(Level.FINE))
LOG.fine("Received " + acks + " acks");
} }
} }
// Update the neighbour's subscriptions // Update the neighbour's subscriptions
synchronized(neighbourLock) { synchronized(contactLock) {
Txn txn = db.startTransaction(); if(!containsNeighbour(n)) return;
try { synchronized(messageStatusLock) {
db.clearSubscriptions(txn, n); Txn txn = db.startTransaction();
int subs = 0; try {
for(GroupId g : b.getSubscriptions()) { db.clearSubscriptions(txn, n);
subs++; int subs = 0;
db.addSubscription(txn, n, g); for(GroupId g : b.getSubscriptions()) {
subs++;
db.addSubscription(txn, n, g);
}
if(LOG.isLoggable(Level.FINE))
LOG.fine("Received " + subs + " subscriptions");
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
} }
if(LOG.isLoggable(Level.FINE))
LOG.fine("Received " + subs + " subscriptions");
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
} }
} }
// Store the messages // Store the messages
@@ -321,26 +363,30 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
for(Batch batch : b.getBatches()) { for(Batch batch : b.getBatches()) {
batches++; batches++;
waitForPermissionToWrite(); waitForPermissionToWrite();
synchronized(messageLock) { synchronized(contactLock) {
synchronized(neighbourLock) { if(!containsNeighbour(n)) return;
synchronized(subscriptionLock) { synchronized(messageLock) {
Txn txn = db.startTransaction(); synchronized(messageStatusLock) {
try { synchronized(subscriptionLock) {
int received = 0, stored = 0; Txn txn = db.startTransaction();
for(Message m : batch.getMessages()) { try {
received++; int received = 0, stored = 0;
if(db.containsSubscription(txn, m.getGroup())) { for(Message m : batch.getMessages()) {
if(storeMessage(txn, m, n)) stored++; received++;
GroupId g = m.getGroup();
if(db.containsSubscription(txn, g)) {
if(storeMessage(txn, m, n)) stored++;
}
} }
if(LOG.isLoggable(Level.FINE))
LOG.fine("Received " + received
+ " messages, stored " + stored);
db.addBatchToAck(txn, n, batch.getId());
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
} }
if(LOG.isLoggable(Level.FINE))
LOG.fine("Received " + received
+ " messages, stored " + stored);
db.addBatchToAck(txn, n, batch.getId());
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
} }
} }
} }
@@ -350,26 +396,13 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
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(messageLock) { synchronized(contactLock) {
synchronized(neighbourLock) { if(!containsNeighbour(n)) return;
Txn txn = db.startTransaction();
try {
lost = db.addReceivedBundle(txn, n, b.getId());
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
}
}
for(BatchId batch : lost) {
synchronized(messageLock) { synchronized(messageLock) {
synchronized(neighbourLock) { synchronized(messageStatusLock) {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
if(LOG.isLoggable(Level.FINE)) lost = db.addReceivedBundle(txn, n, b.getId());
LOG.fine("Removing lost batch");
db.removeLostBatch(txn, n, batch);
db.commitTransaction(txn); db.commitTransaction(txn);
} catch(DbException e) { } catch(DbException e) {
db.abortTransaction(txn); db.abortTransaction(txn);
@@ -378,6 +411,25 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
} }
} }
} }
for(BatchId batch : lost) {
synchronized(contactLock) {
if(!containsNeighbour(n)) return;
synchronized(messageLock) {
synchronized(messageStatusLock) {
Txn txn = db.startTransaction();
try {
if(LOG.isLoggable(Level.FINE))
LOG.fine("Removing lost batch");
db.removeLostBatch(txn, n, batch);
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
}
}
}
}
System.gc(); System.gc();
} }
} }