mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-17 05:09:53 +01:00
Added the ability to remove neighbours from the database (untested).
This commit is contained in:
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user