diff --git a/briar-core/src/org/briarproject/db/Database.java b/briar-core/src/org/briarproject/db/Database.java
index 934156308b6b53ef99322206b16f0f642ce41bad..30c824038342911a87942e5344dbc43ddd22879a 100644
--- a/briar-core/src/org/briarproject/db/Database.java
+++ b/briar-core/src/org/briarproject/db/Database.java
@@ -30,27 +30,23 @@ import java.util.Map;
  * obtained by calling {@link #startTransaction()}. Every transaction must be
  * terminated by calling either {@link #abortTransaction(T)} or
  * {@link #commitTransaction(T)}, even if an exception is thrown.
- * <p>
- * Read-write locking is provided by the DatabaseComponent implementation.
  */
 interface Database<T> {
 
 	/**
 	 * Opens the database and returns true if the database already existed.
-	 * <p>
-	 * Locking: write.
 	 */
 	boolean open() throws DbException;
 
 	/**
 	 * Prevents new transactions from starting, waits for all current
 	 * transactions to finish, and closes the database.
-	 * <p>
-	 * Locking: write.
 	 */
 	void close() throws DbException, IOException;
 
-	/** Starts a new transaction and returns an object representing it. */
+	/**
+	 * Starts a new transaction and returns an object representing it.
+	 */
 	T startTransaction() throws DbException;
 
 	/**
@@ -65,59 +61,39 @@ interface Database<T> {
 	 */
 	void commitTransaction(T txn) throws DbException;
 
-	/**
-	 * Returns the number of transactions started since the transaction count
-	 * was last reset.
-	 */
-	int getTransactionCount();
-
-	/**  Resets the transaction count. */
-	void resetTransactionCount();
-
 	/**
 	 * Stores a contact associated with the given local and remote pseudonyms,
 	 * and returns an ID for the contact.
-	 * <p>
-	 * Locking: write.
 	 */
 	ContactId addContact(T txn, Author remote, AuthorId local)
 			throws DbException;
 
 	/**
 	 * Stores a group.
-	 * <p>
-	 * Locking: write.
 	 */
 	void addGroup(T txn, Group g) throws DbException;
 
 	/**
 	 * Stores a local pseudonym.
-	 * <p>
-	 * Locking: write.
 	 */
 	void addLocalAuthor(T txn, LocalAuthor a) throws DbException;
 
 	/**
 	 * Stores a message.
-	 * <p>
-	 * Locking: write.
 	 */
 	void addMessage(T txn, Message m, Validity validity, boolean shared)
 			throws DbException;
 
 	/**
 	 * Records that a message has been offered by the given contact.
-	 * <p>
-	 * Locking: write.
 	 */
 	void addOfferedMessage(T txn, ContactId c, MessageId m) throws DbException;
 
 	/**
 	 * Initialises the status of the given message with respect to the given
 	 * contact.
-	 * <p>
-	 * Locking: write.
-	 * @param ack whether the message needs to be acknowledged.
+	 *
+	 * @param ack  whether the message needs to be acknowledged.
 	 * @param seen whether the contact has seen the message.
 	 */
 	void addStatus(T txn, ContactId c, MessageId m, boolean ack, boolean seen)
@@ -125,76 +101,56 @@ interface Database<T> {
 
 	/**
 	 * Stores a transport.
-	 * <p>
-	 * Locking: write.
 	 */
 	void addTransport(T txn, TransportId t, int maxLatency)
 			throws DbException;
 
 	/**
 	 * Stores transport keys for a newly added contact.
-	 * <p>
-	 * Locking: write.
 	 */
 	void addTransportKeys(T txn, ContactId c, TransportKeys k)
 			throws DbException;
 
 	/**
 	 * Makes a group visible to the given contact.
-	 * <p>
-	 * Locking: write.
 	 */
 	void addVisibility(T txn, ContactId c, GroupId g) throws DbException;
 
 	/**
 	 * Returns true if the database contains the given contact for the given
 	 * local pseudonym.
-	 * <p>
-	 * Locking: read.
 	 */
 	boolean containsContact(T txn, AuthorId remote, AuthorId local)
 			throws DbException;
 
 	/**
 	 * Returns true if the database contains the given contact.
-	 * <p>
-	 * Locking: read.
 	 */
 	boolean containsContact(T txn, ContactId c) throws DbException;
 
 	/**
 	 * Returns true if the database contains the given group.
-	 * <p>
-	 * Locking: read.
 	 */
 	boolean containsGroup(T txn, GroupId g) throws DbException;
 
 	/**
 	 * Returns true if the database contains the given local pseudonym.
-	 * <p>
-	 * Locking: read.
 	 */
 	boolean containsLocalAuthor(T txn, AuthorId a) throws DbException;
 
 	/**
 	 * Returns true if the database contains the given message.
-	 * <p>
-	 * Locking: read.
 	 */
 	boolean containsMessage(T txn, MessageId m) throws DbException;
 
 	/**
 	 * Returns true if the database contains the given transport.
-	 * <p>
-	 * Locking: read.
 	 */
 	boolean containsTransport(T txn, TransportId t) throws DbException;
 
 	/**
 	 * Returns true if the database contains the given group and the group is
 	 * visible to the given contact.
-	 * <p>
-	 * Locking: read.
 	 */
 	boolean containsVisibleGroup(T txn, ContactId c, GroupId g)
 			throws DbException;
@@ -202,16 +158,12 @@ interface Database<T> {
 	/**
 	 * Returns true if the database contains the given message and the message
 	 * is visible to the given contact.
-	 * <p>
-	 * Locking: read.
 	 */
 	boolean containsVisibleMessage(T txn, ContactId c, MessageId m)
 			throws DbException;
 
 	/**
 	 * Returns the number of messages offered by the given contact.
-	 * <p>
-	 * Locking: read.
 	 */
 	int countOfferedMessages(T txn, ContactId c) throws DbException;
 
@@ -234,36 +186,26 @@ interface Database<T> {
 
 	/**
 	 * Returns the contact with the given ID.
-	 * <p>
-	 * Locking: read.
 	 */
 	Contact getContact(T txn, ContactId c) throws DbException;
 
 	/**
 	 * Returns the IDs of all contacts.
-	 * <p>
-	 * Locking: read.
 	 */
 	Collection<ContactId> getContactIds(T txn) throws DbException;
 
 	/**
 	 * Returns all contacts.
-	 * <p>
-	 * Locking: read.
 	 */
 	Collection<Contact> getContacts(T txn) throws DbException;
 
 	/**
 	 * Returns all contacts associated with the given local pseudonym.
-	 * <p>
-	 * Locking: read.
 	 */
 	Collection<ContactId> getContacts(T txn, AuthorId a) throws DbException;
 
 	/**
 	 * Returns the unique ID for this device.
-	 * <p>
-	 * Locking: read.
 	 */
 	DeviceId getDeviceId(T txn) throws DbException;
 
@@ -276,59 +218,43 @@ interface Database<T> {
 
 	/**
 	 * Returns the group with the given ID.
-	 * <p>
-	 * Locking: read.
 	 */
 	Group getGroup(T txn, GroupId g) throws DbException;
 
 	/**
 	 * Returns the metadata for the given group.
-	 * <p>
-	 * Locking: read.
 	 */
 	Metadata getGroupMetadata(T txn, GroupId g) throws DbException;
 
 	/**
 	 * Returns all groups belonging to the given client.
-	 * <p>
-	 * Locking: read.
 	 */
 	Collection<Group> getGroups(T txn, ClientId c) throws DbException;
 
 	/**
 	 * Returns the local pseudonym with the given ID.
-	 * <p>
-	 * Locking: read.
 	 */
 	LocalAuthor getLocalAuthor(T txn, AuthorId a) throws DbException;
 
 	/**
 	 * Returns all local pseudonyms.
-	 * <p>
-	 * Locking: read.
 	 */
 	Collection<LocalAuthor> getLocalAuthors(T txn) throws DbException;
 
 	/**
 	 * Returns the metadata for all messages in the given group.
-	 * <p>
-	 * Locking: read.
 	 */
 	Map<MessageId, Metadata> getMessageMetadata(T txn, GroupId g)
 			throws DbException;
 
 	/**
 	 * Returns the metadata for the given message.
-	 * <p>
-	 * Locking: read.
 	 */
 	Metadata getMessageMetadata(T txn, MessageId m) throws DbException;
 
 	/**
 	 * Returns the status of all messages in the given group with respect
 	 * to the given contact.
-	 * <p>
-	 * Locking: read
 	 */
 	Collection<MessageStatus> getMessageStatus(T txn, ContactId c, GroupId g)
 			throws DbException;
@@ -336,8 +262,6 @@ interface Database<T> {
 	/**
 	 * Returns the status of the given message with respect to the given
 	 * contact.
-	 * <p>
-	 * Locking: read
 	 */
 	MessageStatus getMessageStatus(T txn, ContactId c, MessageId m)
 			throws DbException;
@@ -345,8 +269,6 @@ interface Database<T> {
 	/**
 	 * Returns the IDs of some messages received from the given contact that
 	 * need to be acknowledged, up to the given number of messages.
-	 * <p>
-	 * Locking: read.
 	 */
 	Collection<MessageId> getMessagesToAck(T txn, ContactId c, int maxMessages)
 			throws DbException;
@@ -354,8 +276,6 @@ interface Database<T> {
 	/**
 	 * Returns the IDs of some messages that are eligible to be offered to the
 	 * given contact, up to the given number of messages.
-	 * <p>
-	 * Locking: read.
 	 */
 	Collection<MessageId> getMessagesToOffer(T txn, ContactId c,
 			int maxMessages) throws DbException;
@@ -363,8 +283,6 @@ interface Database<T> {
 	/**
 	 * Returns the IDs of some messages that are eligible to be sent to the
 	 * given contact, up to the given total length.
-	 * <p>
-	 * Locking: read.
 	 */
 	Collection<MessageId> getMessagesToSend(T txn, ContactId c, int maxLength)
 			throws DbException;
@@ -372,8 +290,6 @@ interface Database<T> {
 	/**
 	 * Returns the IDs of some messages that are eligible to be requested from
 	 * the given contact, up to the given number of messages.
-	 * <p>
-	 * Locking: read.
 	 */
 	Collection<MessageId> getMessagesToRequest(T txn, ContactId c,
 			int maxMessages) throws DbException;
@@ -381,16 +297,12 @@ interface Database<T> {
 	/**
 	 * Returns the IDs of any messages that need to be validated by the given
 	 * client.
-	 * <p>
-	 * Locking: read.
 	 */
 	Collection<MessageId> getMessagesToValidate(T txn, ClientId c)
 			throws DbException;
 
 	/**
 	 * Returns the message with the given ID, in serialised form.
-	 * <p>
-	 * Locking: read.
 	 */
 	byte[] getRawMessage(T txn, MessageId m) throws DbException;
 
@@ -398,46 +310,34 @@ interface Database<T> {
 	 * Returns the IDs of some messages that are eligible to be sent to the
 	 * given contact and have been requested by the contact, up to the given
 	 * total length.
-	 * <p>
-	 * Locking: read.
 	 */
 	Collection<MessageId> getRequestedMessagesToSend(T txn, ContactId c,
 			int maxLength) throws DbException;
 
 	/**
 	 * Returns all settings in the given namespace.
-	 * <p>
-	 * Locking: read.
 	 */
 	Settings getSettings(T txn, String namespace) throws DbException;
 
 	/**
 	 * Returns all transport keys for the given transport.
-	 * <p>
-	 * Locking: read.
 	 */
 	Map<ContactId, TransportKeys> getTransportKeys(T txn, TransportId t)
 			throws DbException;
 
 	/**
 	 * Returns the maximum latencies in milliseconds of all transports.
-	 * <p>
-	 * Locking: read.
 	 */
 	Map<TransportId, Integer> getTransportLatencies(T txn) throws DbException;
 
 	/**
 	 * Returns the IDs of all contacts to which the given group is visible.
-	 * <p>
-	 * Locking: read.
 	 */
 	Collection<ContactId> getVisibility(T txn, GroupId g) throws DbException;
 
 	/**
 	 * Increments the outgoing stream counter for the given contact and
 	 * transport in the given rotation period.
-	 * <p>
-	 * Locking: write.
 	 */
 	void incrementStreamCounter(T txn, ContactId c, TransportId t,
 			long rotationPeriod) throws DbException;
@@ -445,8 +345,6 @@ interface Database<T> {
 	/**
 	 * Marks the given messages as not needing to be acknowledged to the
 	 * given contact.
-	 * <p>
-	 * Locking: write.
 	 */
 	void lowerAckFlag(T txn, ContactId c, Collection<MessageId> acked)
 			throws DbException;
@@ -454,8 +352,6 @@ interface Database<T> {
 	/**
 	 * Marks the given messages as not having been requested by the given
 	 * contact.
-	 * <p>
-	 * Locking: write.
 	 */
 	void lowerRequestedFlag(T txn, ContactId c, Collection<MessageId> requested)
 			throws DbException;
@@ -463,8 +359,6 @@ interface Database<T> {
 	/*
 	 * Merges the given metadata with the existing metadata for the given
 	 * group.
-	 * <p>
-	 * Locking: write.
 	 */
 	void mergeGroupMetadata(T txn, GroupId g, Metadata meta)
 			throws DbException;
@@ -472,8 +366,6 @@ interface Database<T> {
 	/*
 	 * Merges the given metadata with the existing metadata for the given
 	 * message.
-	 * <p>
-	 * Locking: write.
 	 */
 	void mergeMessageMetadata(T txn, MessageId m, Metadata meta)
 			throws DbException;
@@ -481,65 +373,47 @@ interface Database<T> {
 	/**
 	 * Merges the given settings with the existing settings in the given
 	 * namespace.
-	 * <p>
-	 * Locking: write.
 	 */
 	void mergeSettings(T txn, Settings s, String namespace) throws DbException;
 
 	/**
 	 * Marks a message as needing to be acknowledged to the given contact.
-	 * <p>
-	 * Locking: write.
 	 */
 	void raiseAckFlag(T txn, ContactId c, MessageId m) throws DbException;
 
 	/**
 	 * Marks a message as having been requested by the given contact.
-	 * <p>
-	 * Locking: write.
 	 */
 	void raiseRequestedFlag(T txn, ContactId c, MessageId m) throws DbException;
 
 	/**
 	 * Marks a message as having been seen by the given contact.
-	 * <p>
-	 * Locking: write.
 	 */
 	void raiseSeenFlag(T txn, ContactId c, MessageId m) throws DbException;
 
 	/**
 	 * Removes a contact from the database.
-	 * <p>
-	 * Locking: write.
 	 */
 	void removeContact(T txn, ContactId c) throws DbException;
 
 	/**
 	 * Removes a group (and all associated state) from the database.
-	 * <p>
-	 * Locking: write.
 	 */
 	void removeGroup(T txn, GroupId g) throws DbException;
 
 	/**
 	 * Removes a local pseudonym (and all associated state) from the database.
-	 * <p>
-	 * Locking: write.
 	 */
 	void removeLocalAuthor(T txn, AuthorId a) throws DbException;
 
 	/**
 	 * Removes a message (and all associated state) from the database.
-	 * <p>
-	 * Locking: write.
 	 */
 	void removeMessage(T txn, MessageId m) throws DbException;
 
 	/**
 	 * Removes an offered message that was offered by the given contact, or
 	 * returns false if there is no such message.
-	 * <p>
-	 * Locking: write.
 	 */
 	boolean removeOfferedMessage(T txn, ContactId c, MessageId m)
 			throws DbException;
@@ -547,70 +421,52 @@ interface Database<T> {
 	/**
 	 * Removes the given offered messages that were offered by the given
 	 * contact.
-	 * <p>
-	 * Locking: write.
 	 */
 	void removeOfferedMessages(T txn, ContactId c,
 			Collection<MessageId> requested) throws DbException;
 
 	/**
 	 * Removes a transport (and all associated state) from the database.
-	 * <p>
-	 * Locking: write.
 	 */
 	void removeTransport(T txn, TransportId t) throws DbException;
 
 	/**
 	 * Makes a group invisible to the given contact.
-	 * <p>
-	 * Locking: write.
 	 */
 	void removeVisibility(T txn, ContactId c, GroupId g) throws DbException;
 
 	/**
 	 * Resets the transmission count and expiry time of the given message with
 	 * respect to the given contact.
-	 * <p>
-	 * Locking: write.
 	 */
 	void resetExpiryTime(T txn, ContactId c, MessageId m) throws DbException;
 
 	/**
 	 * Sets the status of the given contact.
-	 * <p>
-	 * Locking: write.
 	 */
 	void setContactStatus(T txn, ContactId c, StorageStatus s)
 			throws DbException;
 
 	/**
 	 * Sets the status of the given local pseudonym.
-	 * <p>
-	 * Locking: write.
 	 */
 	void setLocalAuthorStatus(T txn, AuthorId a, StorageStatus s)
 			throws DbException;
 
 	/**
 	 * Marks the given message as shared or unshared.
-	 * <p>
-	 * Locking: write.
 	 */
 	void setMessageShared(T txn, MessageId m, boolean shared)
 			throws DbException;
 
 	/**
 	 * Marks the given message as valid or invalid.
-	 * <p>
-	 * Locking: write.
 	 */
 	void setMessageValid(T txn, MessageId m, boolean valid) throws DbException;
 
 	/**
 	 * Sets the reordering window for the given contact and transport in the
 	 * given rotation period.
-	 * <p>
-	 * Locking: write.
 	 */
 	void setReorderingWindow(T txn, ContactId c, TransportId t,
 			long rotationPeriod, long base, byte[] bitmap) throws DbException;
@@ -619,16 +475,12 @@ interface Database<T> {
 	 * Updates the transmission count and expiry time of the given message
 	 * with respect to the given contact, using the latency of the transport
 	 * over which it was sent.
-	 * <p>
-	 * Locking: write.
 	 */
 	void updateExpiryTime(T txn, ContactId c, MessageId m, int maxLatency)
 			throws DbException;
 
 	/**
 	 * Stores the given transport keys, deleting any keys they have replaced.
-	 * <p>
-	 * Locking: write.
 	 */
 	void updateTransportKeys(T txn, Map<ContactId, TransportKeys> keys)
 			throws DbException;
diff --git a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java
index 0a974ed8edb9095e56ea94fabdf635df4258f617..dcf88660af85e9858c356f3fca910d7e790e0b96 100644
--- a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java
+++ b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java
@@ -55,7 +55,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.Logger;
 
 import javax.inject.Inject;
@@ -79,12 +79,9 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 	private final Database<T> db;
 	private final EventBus eventBus;
 	private final ShutdownManager shutdown;
+	private final AtomicBoolean closed = new AtomicBoolean(false);
 
-	private final ReentrantReadWriteLock lock =
-			new ReentrantReadWriteLock(true);
-
-	private boolean open = false; // Locking: lock.writeLock
-	private int shutdownHandle = -1; // Locking: lock.writeLock
+	private volatile int shutdownHandle = -1;
 
 	@Inject
 	DatabaseComponentImpl(Database<T> db, EventBus eventBus,
@@ -97,9 +94,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 	public boolean open() throws DbException {
 		Runnable shutdownHook = new Runnable() {
 			public void run() {
-				lock.writeLock().lock();
 				try {
-					shutdownHandle = -1;
 					close();
 				} catch (DbException e) {
 					if (LOG.isLoggable(WARNING))
@@ -107,118 +102,81 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 				} catch (IOException e) {
 					if (LOG.isLoggable(WARNING))
 						LOG.log(WARNING, e.toString(), e);
-				} finally {
-					lock.writeLock().unlock();
 				}
 			}
 		};
-		lock.writeLock().lock();
-		try {
-			if (open) throw new IllegalStateException();
-			open = true;
-			boolean reopened = db.open();
-			shutdownHandle = shutdown.addShutdownHook(shutdownHook);
-			return reopened;
-		} finally {
-			lock.writeLock().unlock();
-		}
+		boolean reopened = db.open();
+		shutdownHandle = shutdown.addShutdownHook(shutdownHook);
+		return reopened;
 	}
 
 	public void close() throws DbException, IOException {
-		lock.writeLock().lock();
-		try {
-			if (!open) return;
-			open = false;
-			if (shutdownHandle != -1)
-				shutdown.removeShutdownHook(shutdownHandle);
-			db.close();
-		} finally {
-			lock.writeLock().unlock();
-		}
+		if (closed.getAndSet(true)) return;
+		shutdown.removeShutdownHook(shutdownHandle);
+		db.close();
 	}
 
 	public ContactId addContact(Author remote, AuthorId local)
 			throws DbException {
-		lock.writeLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsLocalAuthor(txn, local))
-					throw new NoSuchLocalAuthorException();
-				if (db.containsContact(txn, remote.getId(), local))
-					throw new ContactExistsException();
-				ContactId c = db.addContact(txn, remote, local);
-				db.commitTransaction(txn);
-				return c;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
+			if (!db.containsLocalAuthor(txn, local))
+				throw new NoSuchLocalAuthorException();
+			if (db.containsContact(txn, remote.getId(), local))
+				throw new ContactExistsException();
+			ContactId c = db.addContact(txn, remote, local);
+			db.commitTransaction(txn);
+			return c;
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public void addGroup(Group g) throws DbException {
 		boolean added = false;
-		lock.writeLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsGroup(txn, g.getId())) {
-					db.addGroup(txn, g);
-					added = true;
-				}
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
+			if (!db.containsGroup(txn, g.getId())) {
+				db.addGroup(txn, g);
+				added = true;
 			}
-		} finally {
-			lock.writeLock().unlock();
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 		if (added) eventBus.broadcast(new GroupAddedEvent(g));
 	}
 
 	public void addLocalAuthor(LocalAuthor a) throws DbException {
-		lock.writeLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsLocalAuthor(txn, a.getId())) {
-					db.addLocalAuthor(txn, a);
-				}
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
+			if (!db.containsLocalAuthor(txn, a.getId()))
+				db.addLocalAuthor(txn, a);
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public void addLocalMessage(Message m, ClientId c, Metadata meta,
 			boolean shared) throws DbException {
 		boolean added = false;
-		lock.writeLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsGroup(txn, m.getGroupId()))
-					throw new NoSuchGroupException();
-				if (!db.containsMessage(txn, m.getId())) {
-					addMessage(txn, m, VALID, shared, null);
-					added = true;
-				}
-				db.mergeMessageMetadata(txn, m.getId(), meta);
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
+			if (!db.containsGroup(txn, m.getGroupId()))
+				throw new NoSuchGroupException();
+			if (!db.containsMessage(txn, m.getId())) {
+				addMessage(txn, m, VALID, shared, null);
+				added = true;
 			}
-		} finally {
-			lock.writeLock().unlock();
+			db.mergeMessageMetadata(txn, m.getId(), meta);
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 		if (added) {
 			eventBus.broadcast(new MessageAddedEvent(m, null));
@@ -229,8 +187,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 
 	/**
 	 * Stores a message and initialises its status with respect to each contact.
-	 * <p>
-	 * Locking: write.
+	 *
 	 * @param sender null for a locally generated message.
 	 */
 	private void addMessage(T txn, Message m, Validity validity, boolean shared,
@@ -253,43 +210,33 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 
 	public void addTransport(TransportId t, int maxLatency) throws DbException {
 		boolean added = false;
-		lock.writeLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsTransport(txn, t)) {
-					db.addTransport(txn, t, maxLatency);
-					added = true;
-				}
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
+			if (!db.containsTransport(txn, t)) {
+				db.addTransport(txn, t, maxLatency);
+				added = true;
 			}
-		} finally {
-			lock.writeLock().unlock();
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 		if (added) eventBus.broadcast(new TransportAddedEvent(t, maxLatency));
 	}
 
 	public void addTransportKeys(ContactId c, TransportKeys k)
 			throws DbException {
-		lock.writeLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				if (!db.containsTransport(txn, k.getTransportId()))
-					throw new NoSuchTransportException();
-				db.addTransportKeys(txn, c, k);
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
+			if (!db.containsContact(txn, c))
+				throw new NoSuchContactException();
+			if (!db.containsTransport(txn, k.getTransportId()))
+				throw new NoSuchTransportException();
+			db.addTransportKeys(txn, c, k);
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
@@ -329,21 +276,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 
 	public Ack generateAck(ContactId c, int maxMessages) throws DbException {
 		Collection<MessageId> ids;
-		lock.writeLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				ids = db.getMessagesToAck(txn, c, maxMessages);
-				if (!ids.isEmpty()) db.lowerAckFlag(txn, c, ids);
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
+			if (!db.containsContact(txn, c))
+				throw new NoSuchContactException();
+			ids = db.getMessagesToAck(txn, c, maxMessages);
+			if (!ids.isEmpty()) db.lowerAckFlag(txn, c, ids);
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 		if (ids.isEmpty()) return null;
 		return new Ack(ids);
@@ -353,25 +295,20 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 			int maxLatency) throws DbException {
 		Collection<MessageId> ids;
 		List<byte[]> messages = new ArrayList<byte[]>();
-		lock.writeLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				ids = db.getMessagesToSend(txn, c, maxLength);
-				for (MessageId m : ids) {
-					messages.add(db.getRawMessage(txn, m));
-					db.updateExpiryTime(txn, c, m, maxLatency);
-				}
-				if (!ids.isEmpty()) db.lowerRequestedFlag(txn, c, ids);
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
+			if (!db.containsContact(txn, c))
+				throw new NoSuchContactException();
+			ids = db.getMessagesToSend(txn, c, maxLength);
+			for (MessageId m : ids) {
+				messages.add(db.getRawMessage(txn, m));
+				db.updateExpiryTime(txn, c, m, maxLatency);
 			}
-		} finally {
-			lock.writeLock().unlock();
+			if (!ids.isEmpty()) db.lowerRequestedFlag(txn, c, ids);
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 		if (messages.isEmpty()) return null;
 		if (!ids.isEmpty()) eventBus.broadcast(new MessagesSentEvent(c, ids));
@@ -381,22 +318,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 	public Offer generateOffer(ContactId c, int maxMessages, int maxLatency)
 			throws DbException {
 		Collection<MessageId> ids;
-		lock.writeLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				ids = db.getMessagesToOffer(txn, c, maxMessages);
-				for (MessageId m : ids)
-					db.updateExpiryTime(txn, c, m, maxLatency);
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
+			if (!db.containsContact(txn, c))
+				throw new NoSuchContactException();
+			ids = db.getMessagesToOffer(txn, c, maxMessages);
+			for (MessageId m : ids) db.updateExpiryTime(txn, c, m, maxLatency);
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 		if (ids.isEmpty()) return null;
 		return new Offer(ids);
@@ -405,21 +336,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 	public Request generateRequest(ContactId c, int maxMessages)
 			throws DbException {
 		Collection<MessageId> ids;
-		lock.writeLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				ids = db.getMessagesToRequest(txn, c, maxMessages);
-				if (!ids.isEmpty()) db.removeOfferedMessages(txn, c, ids);
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
+			if (!db.containsContact(txn, c))
+				throw new NoSuchContactException();
+			ids = db.getMessagesToRequest(txn, c, maxMessages);
+			if (!ids.isEmpty()) db.removeOfferedMessages(txn, c, ids);
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 		if (ids.isEmpty()) return null;
 		return new Request(ids);
@@ -429,25 +355,20 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 			int maxLatency) throws DbException {
 		Collection<MessageId> ids;
 		List<byte[]> messages = new ArrayList<byte[]>();
-		lock.writeLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				ids = db.getRequestedMessagesToSend(txn, c, maxLength);
-				for (MessageId m : ids) {
-					messages.add(db.getRawMessage(txn, m));
-					db.updateExpiryTime(txn, c, m, maxLatency);
-				}
-				if (!ids.isEmpty()) db.lowerRequestedFlag(txn, c, ids);
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
+			if (!db.containsContact(txn, c))
+				throw new NoSuchContactException();
+			ids = db.getRequestedMessagesToSend(txn, c, maxLength);
+			for (MessageId m : ids) {
+				messages.add(db.getRawMessage(txn, m));
+				db.updateExpiryTime(txn, c, m, maxLatency);
 			}
-		} finally {
-			lock.writeLock().unlock();
+			if (!ids.isEmpty()) db.lowerRequestedFlag(txn, c, ids);
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 		if (messages.isEmpty()) return null;
 		if (!ids.isEmpty()) eventBus.broadcast(new MessagesSentEvent(c, ids));
@@ -455,518 +376,384 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 	}
 
 	public Contact getContact(ContactId c) throws DbException {
-		lock.readLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				Contact contact = db.getContact(txn, c);
-				db.commitTransaction(txn);
-				return contact;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.readLock().unlock();
+			if (!db.containsContact(txn, c))
+				throw new NoSuchContactException();
+			Contact contact = db.getContact(txn, c);
+			db.commitTransaction(txn);
+			return contact;
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public Collection<Contact> getContacts() throws DbException {
-		lock.readLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				Collection<Contact> contacts = db.getContacts(txn);
-				db.commitTransaction(txn);
-				return contacts;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.readLock().unlock();
+			Collection<Contact> contacts = db.getContacts(txn);
+			db.commitTransaction(txn);
+			return contacts;
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public Collection<ContactId> getContacts(AuthorId a) throws DbException {
-		lock.readLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsLocalAuthor(txn, a))
-					throw new NoSuchLocalAuthorException();
-				Collection<ContactId> contacts = db.getContacts(txn, a);
-				db.commitTransaction(txn);
-				return contacts;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.readLock().unlock();
+			if (!db.containsLocalAuthor(txn, a))
+				throw new NoSuchLocalAuthorException();
+			Collection<ContactId> contacts = db.getContacts(txn, a);
+			db.commitTransaction(txn);
+			return contacts;
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public DeviceId getDeviceId() throws DbException {
-		lock.readLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				DeviceId id = db.getDeviceId(txn);
-				db.commitTransaction(txn);
-				return id;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.readLock().unlock();
+			DeviceId id = db.getDeviceId(txn);
+			db.commitTransaction(txn);
+			return id;
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public Group getGroup(GroupId g) throws DbException {
-		lock.readLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsGroup(txn, g))
-					throw new NoSuchGroupException();
-				Group group = db.getGroup(txn, g);
-				db.commitTransaction(txn);
-				return group;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.readLock().unlock();
+			if (!db.containsGroup(txn, g))
+				throw new NoSuchGroupException();
+			Group group = db.getGroup(txn, g);
+			db.commitTransaction(txn);
+			return group;
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public Metadata getGroupMetadata(GroupId g) throws DbException {
-		lock.readLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsGroup(txn, g))
-					throw new NoSuchGroupException();
-				Metadata metadata = db.getGroupMetadata(txn, g);
-				db.commitTransaction(txn);
-				return metadata;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.readLock().unlock();
+			if (!db.containsGroup(txn, g))
+				throw new NoSuchGroupException();
+			Metadata metadata = db.getGroupMetadata(txn, g);
+			db.commitTransaction(txn);
+			return metadata;
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public Collection<Group> getGroups(ClientId c) throws DbException {
-		lock.readLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				Collection<Group> groups = db.getGroups(txn, c);
-				db.commitTransaction(txn);
-				return groups;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.readLock().unlock();
+			Collection<Group> groups = db.getGroups(txn, c);
+			db.commitTransaction(txn);
+			return groups;
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public LocalAuthor getLocalAuthor(AuthorId a) throws DbException {
-		lock.readLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsLocalAuthor(txn, a))
-					throw new NoSuchLocalAuthorException();
-				LocalAuthor localAuthor = db.getLocalAuthor(txn, a);
-				db.commitTransaction(txn);
-				return localAuthor;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.readLock().unlock();
+			if (!db.containsLocalAuthor(txn, a))
+				throw new NoSuchLocalAuthorException();
+			LocalAuthor localAuthor = db.getLocalAuthor(txn, a);
+			db.commitTransaction(txn);
+			return localAuthor;
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public Collection<LocalAuthor> getLocalAuthors() throws DbException {
-		lock.readLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				Collection<LocalAuthor> authors = db.getLocalAuthors(txn);
-				db.commitTransaction(txn);
-				return authors;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.readLock().unlock();
+			Collection<LocalAuthor> authors = db.getLocalAuthors(txn);
+			db.commitTransaction(txn);
+			return authors;
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public Collection<MessageId> getMessagesToValidate(ClientId c)
 			throws DbException {
-		lock.readLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				Collection<MessageId> ids = db.getMessagesToValidate(txn, c);
-				db.commitTransaction(txn);
-				return ids;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.readLock().unlock();
+			Collection<MessageId> ids = db.getMessagesToValidate(txn, c);
+			db.commitTransaction(txn);
+			return ids;
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public byte[] getRawMessage(MessageId m) throws DbException {
-		lock.readLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsMessage(txn, m))
-					throw new NoSuchMessageException();
-				byte[] raw = db.getRawMessage(txn, m);
-				db.commitTransaction(txn);
-				return raw;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.readLock().unlock();
+			if (!db.containsMessage(txn, m))
+				throw new NoSuchMessageException();
+			byte[] raw = db.getRawMessage(txn, m);
+			db.commitTransaction(txn);
+			return raw;
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public Map<MessageId, Metadata> getMessageMetadata(GroupId g)
 			throws DbException {
-		lock.readLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsGroup(txn, g))
-					throw new NoSuchGroupException();
-				Map<MessageId, Metadata> metadata =
-						db.getMessageMetadata(txn, g);
-				db.commitTransaction(txn);
-				return metadata;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.readLock().unlock();
+			if (!db.containsGroup(txn, g))
+				throw new NoSuchGroupException();
+			Map<MessageId, Metadata> metadata = db.getMessageMetadata(txn, g);
+			db.commitTransaction(txn);
+			return metadata;
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public Metadata getMessageMetadata(MessageId m) throws DbException {
-		lock.readLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsMessage(txn, m))
-					throw new NoSuchMessageException();
-				Metadata metadata = db.getMessageMetadata(txn, m);
-				db.commitTransaction(txn);
-				return metadata;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.readLock().unlock();
+			if (!db.containsMessage(txn, m))
+				throw new NoSuchMessageException();
+			Metadata metadata = db.getMessageMetadata(txn, m);
+			db.commitTransaction(txn);
+			return metadata;
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public Collection<MessageStatus> getMessageStatus(ContactId c, GroupId g)
 			throws DbException {
-		lock.readLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				if (!db.containsGroup(txn, g))
-					throw new NoSuchGroupException();
-				Collection<MessageStatus> statuses =
-						db.getMessageStatus(txn, c, g);
-				db.commitTransaction(txn);
-				return statuses;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.readLock().unlock();
+			if (!db.containsContact(txn, c))
+				throw new NoSuchContactException();
+			if (!db.containsGroup(txn, g))
+				throw new NoSuchGroupException();
+			Collection<MessageStatus> statuses = db.getMessageStatus(txn, c, g);
+			db.commitTransaction(txn);
+			return statuses;
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public MessageStatus getMessageStatus(ContactId c, MessageId m)
 			throws DbException {
-		lock.readLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				if (!db.containsMessage(txn, m))
-					throw new NoSuchMessageException();
-				MessageStatus status = db.getMessageStatus(txn, c, m);
-				db.commitTransaction(txn);
-				return status;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.readLock().unlock();
+			if (!db.containsContact(txn, c))
+				throw new NoSuchContactException();
+			if (!db.containsMessage(txn, m))
+				throw new NoSuchMessageException();
+			MessageStatus status = db.getMessageStatus(txn, c, m);
+			db.commitTransaction(txn);
+			return status;
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public Settings getSettings(String namespace) throws DbException {
-		lock.readLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				Settings s = db.getSettings(txn, namespace);
-				db.commitTransaction(txn);
-				return s;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.readLock().unlock();
+			Settings s = db.getSettings(txn, namespace);
+			db.commitTransaction(txn);
+			return s;
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public Map<ContactId, TransportKeys> getTransportKeys(TransportId t)
 			throws DbException {
-		lock.readLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsTransport(txn, t))
-					throw new NoSuchTransportException();
-				Map<ContactId, TransportKeys> keys =
-						db.getTransportKeys(txn, t);
-				db.commitTransaction(txn);
-				return keys;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.readLock().unlock();
+			if (!db.containsTransport(txn, t))
+				throw new NoSuchTransportException();
+			Map<ContactId, TransportKeys> keys = db.getTransportKeys(txn, t);
+			db.commitTransaction(txn);
+			return keys;
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public Map<TransportId, Integer> getTransportLatencies()
 			throws DbException {
-		lock.readLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				Map<TransportId, Integer> latencies =
-						db.getTransportLatencies(txn);
-				db.commitTransaction(txn);
-				return latencies;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.readLock().unlock();
+			Map<TransportId, Integer> latencies = db.getTransportLatencies(txn);
+			db.commitTransaction(txn);
+			return latencies;
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public Collection<ContactId> getVisibility(GroupId g) throws DbException {
-		lock.readLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsGroup(txn, g))
-					throw new NoSuchGroupException();
-				Collection<ContactId> visible = db.getVisibility(txn, g);
-				db.commitTransaction(txn);
-				return visible;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.readLock().unlock();
+			if (!db.containsGroup(txn, g))
+				throw new NoSuchGroupException();
+			Collection<ContactId> visible = db.getVisibility(txn, g);
+			db.commitTransaction(txn);
+			return visible;
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public void incrementStreamCounter(ContactId c, TransportId t,
 			long rotationPeriod) throws DbException {
-		lock.writeLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				if (!db.containsTransport(txn, t))
-					throw new NoSuchTransportException();
-				db.incrementStreamCounter(txn, c, t, rotationPeriod);
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
+			if (!db.containsContact(txn, c))
+				throw new NoSuchContactException();
+			if (!db.containsTransport(txn, t))
+				throw new NoSuchTransportException();
+			db.incrementStreamCounter(txn, c, t, rotationPeriod);
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public boolean isVisibleToContact(ContactId c, GroupId g)
 			throws DbException {
-		lock.readLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				if (!db.containsGroup(txn, g))
-					throw new NoSuchGroupException();
-				boolean visible = db.containsVisibleGroup(txn, c, g);
-				db.commitTransaction(txn);
-				return visible;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.readLock().unlock();
+			if (!db.containsContact(txn, c))
+				throw new NoSuchContactException();
+			if (!db.containsGroup(txn, g))
+				throw new NoSuchGroupException();
+			boolean visible = db.containsVisibleGroup(txn, c, g);
+			db.commitTransaction(txn);
+			return visible;
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public void mergeGroupMetadata(GroupId g, Metadata meta)
 			throws DbException {
-		lock.writeLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsGroup(txn, g))
-					throw new NoSuchGroupException();
-				db.mergeGroupMetadata(txn, g, meta);
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
+			if (!db.containsGroup(txn, g))
+				throw new NoSuchGroupException();
+			db.mergeGroupMetadata(txn, g, meta);
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public void mergeMessageMetadata(MessageId m, Metadata meta)
 			throws DbException {
-		lock.writeLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsMessage(txn, m))
-					throw new NoSuchMessageException();
-				db.mergeMessageMetadata(txn, m, meta);
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
+			if (!db.containsMessage(txn, m))
+				throw new NoSuchMessageException();
+			db.mergeMessageMetadata(txn, m, meta);
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public void mergeSettings(Settings s, String namespace) throws DbException {
 		boolean changed = false;
-		lock.writeLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				Settings old = db.getSettings(txn, namespace);
-				Settings merged = new Settings();
-				merged.putAll(old);
-				merged.putAll(s);
-				if (!merged.equals(old)) {
-					db.mergeSettings(txn, s, namespace);
-					changed = true;
-				}
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
+			Settings old = db.getSettings(txn, namespace);
+			Settings merged = new Settings();
+			merged.putAll(old);
+			merged.putAll(s);
+			if (!merged.equals(old)) {
+				db.mergeSettings(txn, s, namespace);
+				changed = true;
 			}
-		} finally {
-			lock.writeLock().unlock();
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 		if (changed) eventBus.broadcast(new SettingsUpdatedEvent(namespace));
 	}
 
 	public void receiveAck(ContactId c, Ack a) throws DbException {
 		Collection<MessageId> acked = new ArrayList<MessageId>();
-		lock.writeLock().lock();
-		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				for (MessageId m : a.getMessageIds()) {
-					if (db.containsVisibleMessage(txn, c, m)) {
-						db.raiseSeenFlag(txn, c, m);
-						acked.add(m);
-					}
+		T txn = db.startTransaction();
+		try {
+			if (!db.containsContact(txn, c))
+				throw new NoSuchContactException();
+			for (MessageId m : a.getMessageIds()) {
+				if (db.containsVisibleMessage(txn, c, m)) {
+					db.raiseSeenFlag(txn, c, m);
+					acked.add(m);
 				}
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
 			}
-		} finally {
-			lock.writeLock().unlock();
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 		eventBus.broadcast(new MessagesAckedEvent(c, acked));
 	}
 
 	public void receiveMessage(ContactId c, Message m) throws DbException {
 		boolean duplicate, visible;
-		lock.writeLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				duplicate = db.containsMessage(txn, m.getId());
-				visible = db.containsVisibleGroup(txn, c, m.getGroupId());
-				if (visible) {
-					if (!duplicate) addMessage(txn, m, UNKNOWN, false, c);
-					db.raiseAckFlag(txn, c, m.getId());
-				}
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
+			if (!db.containsContact(txn, c))
+				throw new NoSuchContactException();
+			duplicate = db.containsMessage(txn, m.getId());
+			visible = db.containsVisibleGroup(txn, c, m.getGroupId());
+			if (visible) {
+				if (!duplicate) addMessage(txn, m, UNKNOWN, false, c);
+				db.raiseAckFlag(txn, c, m.getId());
 			}
-		} finally {
-			lock.writeLock().unlock();
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 		if (visible) {
 			if (!duplicate) eventBus.broadcast(new MessageAddedEvent(m, c));
@@ -976,31 +763,26 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 
 	public void receiveOffer(ContactId c, Offer o) throws DbException {
 		boolean ack = false, request = false;
-		lock.writeLock().lock();
-		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				int count = db.countOfferedMessages(txn, c);
-				for (MessageId m : o.getMessageIds()) {
-					if (db.containsVisibleMessage(txn, c, m)) {
-						db.raiseSeenFlag(txn, c, m);
-						db.raiseAckFlag(txn, c, m);
-						ack = true;
-					} else if (count < MAX_OFFERED_MESSAGES) {
-						db.addOfferedMessage(txn, c, m);
-						request = true;
-						count++;
-					}
+		T txn = db.startTransaction();
+		try {
+			if (!db.containsContact(txn, c))
+				throw new NoSuchContactException();
+			int count = db.countOfferedMessages(txn, c);
+			for (MessageId m : o.getMessageIds()) {
+				if (db.containsVisibleMessage(txn, c, m)) {
+					db.raiseSeenFlag(txn, c, m);
+					db.raiseAckFlag(txn, c, m);
+					ack = true;
+				} else if (count < MAX_OFFERED_MESSAGES) {
+					db.addOfferedMessage(txn, c, m);
+					request = true;
+					count++;
 				}
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
 			}
-		} finally {
-			lock.writeLock().unlock();
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 		if (ack) eventBus.broadcast(new MessageToAckEvent(c));
 		if (request) eventBus.broadcast(new MessageToRequestEvent(c));
@@ -1008,239 +790,184 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 
 	public void receiveRequest(ContactId c, Request r) throws DbException {
 		boolean requested = false;
-		lock.writeLock().lock();
-		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				for (MessageId m : r.getMessageIds()) {
-					if (db.containsVisibleMessage(txn, c, m)) {
-						db.raiseRequestedFlag(txn, c, m);
-						db.resetExpiryTime(txn, c, m);
-						requested = true;
-					}
+		T txn = db.startTransaction();
+		try {
+			if (!db.containsContact(txn, c))
+				throw new NoSuchContactException();
+			for (MessageId m : r.getMessageIds()) {
+				if (db.containsVisibleMessage(txn, c, m)) {
+					db.raiseRequestedFlag(txn, c, m);
+					db.resetExpiryTime(txn, c, m);
+					requested = true;
 				}
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
 			}
-		} finally {
-			lock.writeLock().unlock();
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 		if (requested) eventBus.broadcast(new MessageRequestedEvent(c));
 	}
 
 	public void removeContact(ContactId c) throws DbException {
-		lock.writeLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				db.removeContact(txn, c);
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
+			if (!db.containsContact(txn, c))
+				throw new NoSuchContactException();
+			db.removeContact(txn, c);
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public void removeGroup(Group g) throws DbException {
 		Collection<ContactId> affected;
-		lock.writeLock().lock();
-		try {
-			T txn = db.startTransaction();
-			try {
-				GroupId id = g.getId();
-				if (!db.containsGroup(txn, id))
-					throw new NoSuchGroupException();
-				affected = db.getVisibility(txn, id);
-				db.removeGroup(txn, id);
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
+		T txn = db.startTransaction();
+		try {
+			GroupId id = g.getId();
+			if (!db.containsGroup(txn, id))
+				throw new NoSuchGroupException();
+			affected = db.getVisibility(txn, id);
+			db.removeGroup(txn, id);
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 		eventBus.broadcast(new GroupRemovedEvent(g));
 		eventBus.broadcast(new GroupVisibilityUpdatedEvent(affected));
 	}
 
 	public void removeLocalAuthor(AuthorId a) throws DbException {
-		lock.writeLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsLocalAuthor(txn, a))
-					throw new NoSuchLocalAuthorException();
-				db.removeLocalAuthor(txn, a);
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
+			if (!db.containsLocalAuthor(txn, a))
+				throw new NoSuchLocalAuthorException();
+			db.removeLocalAuthor(txn, a);
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public void removeTransport(TransportId t) throws DbException {
-		lock.writeLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsTransport(txn, t))
-					throw new NoSuchTransportException();
-				db.removeTransport(txn, t);
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
+			if (!db.containsTransport(txn, t))
+				throw new NoSuchTransportException();
+			db.removeTransport(txn, t);
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 		eventBus.broadcast(new TransportRemovedEvent(t));
 	}
 
 	public void setContactStatus(ContactId c, StorageStatus s)
 			throws DbException {
-		lock.writeLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				db.setContactStatus(txn, c, s);
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
+			if (!db.containsContact(txn, c))
+				throw new NoSuchContactException();
+			db.setContactStatus(txn, c, s);
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public void setLocalAuthorStatus(AuthorId a, StorageStatus s)
 			throws DbException {
-		lock.writeLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsLocalAuthor(txn, a))
-					throw new NoSuchLocalAuthorException();
-				db.setLocalAuthorStatus(txn, a, s);
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
+			if (!db.containsLocalAuthor(txn, a))
+				throw new NoSuchLocalAuthorException();
+			db.setLocalAuthorStatus(txn, a, s);
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public void setMessageShared(Message m, boolean shared)
 			throws DbException {
-		lock.writeLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsMessage(txn, m.getId()))
-					throw new NoSuchMessageException();
-				db.setMessageShared(txn, m.getId(), shared);
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
+			if (!db.containsMessage(txn, m.getId()))
+				throw new NoSuchMessageException();
+			db.setMessageShared(txn, m.getId(), shared);
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 		if (shared) eventBus.broadcast(new MessageSharedEvent(m));
 	}
 
 	public void setMessageValid(Message m, ClientId c, boolean valid)
 			throws DbException {
-		lock.writeLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsMessage(txn, m.getId()))
-					throw new NoSuchMessageException();
-				db.setMessageValid(txn, m.getId(), valid);
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
+			if (!db.containsMessage(txn, m.getId()))
+				throw new NoSuchMessageException();
+			db.setMessageValid(txn, m.getId(), valid);
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 		eventBus.broadcast(new MessageValidatedEvent(m, c, false, valid));
 	}
 
 	public void setReorderingWindow(ContactId c, TransportId t,
 			long rotationPeriod, long base, byte[] bitmap) throws DbException {
-		lock.writeLock().lock();
+		T txn = db.startTransaction();
 		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				if (!db.containsTransport(txn, t))
-					throw new NoSuchTransportException();
-				db.setReorderingWindow(txn, c, t, rotationPeriod, base, bitmap);
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
+			if (!db.containsContact(txn, c))
+				throw new NoSuchContactException();
+			if (!db.containsTransport(txn, t))
+				throw new NoSuchTransportException();
+			db.setReorderingWindow(txn, c, t, rotationPeriod, base, bitmap);
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 
 	public void setVisibility(GroupId g, Collection<ContactId> visible)
 			throws DbException {
 		Collection<ContactId> affected = new ArrayList<ContactId>();
-		lock.writeLock().lock();
-		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsGroup(txn, g))
-					throw new NoSuchGroupException();
-				// Use HashSets for O(1) lookups, O(n) overall running time
-				Collection<ContactId> now = new HashSet<ContactId>(visible);
-				Collection<ContactId> before = db.getVisibility(txn, g);
-				before = new HashSet<ContactId>(before);
-				// Set the group's visibility for each current contact
-				for (ContactId c : db.getContactIds(txn)) {
-					boolean wasBefore = before.contains(c);
-					boolean isNow = now.contains(c);
-					if (!wasBefore && isNow) {
-						db.addVisibility(txn, c, g);
-						affected.add(c);
-					} else if (wasBefore && !isNow) {
-						db.removeVisibility(txn, c, g);
-						affected.add(c);
-					}
+		T txn = db.startTransaction();
+		try {
+			if (!db.containsGroup(txn, g))
+				throw new NoSuchGroupException();
+			// Use HashSets for O(1) lookups, O(n) overall running time
+			Collection<ContactId> now = new HashSet<ContactId>(visible);
+			Collection<ContactId> before = db.getVisibility(txn, g);
+			before = new HashSet<ContactId>(before);
+			// Set the group's visibility for each current contact
+			for (ContactId c : db.getContactIds(txn)) {
+				boolean wasBefore = before.contains(c);
+				boolean isNow = now.contains(c);
+				if (!wasBefore && isNow) {
+					db.addVisibility(txn, c, g);
+					affected.add(c);
+				} else if (wasBefore && !isNow) {
+					db.removeVisibility(txn, c, g);
+					affected.add(c);
 				}
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
 			}
-		} finally {
-			lock.writeLock().unlock();
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 		if (!affected.isEmpty())
 			eventBus.broadcast(new GroupVisibilityUpdatedEvent(affected));
@@ -1249,24 +976,19 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 	public void setVisibleToContact(ContactId c, GroupId g, boolean visible)
 			throws DbException {
 		boolean wasVisible = false;
-		lock.writeLock().lock();
-		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				if (!db.containsGroup(txn, g))
-					throw new NoSuchGroupException();
-				wasVisible = db.containsVisibleGroup(txn, c, g);
-				if (visible && !wasVisible) db.addVisibility(txn, c, g);
-				else if (!visible && wasVisible) db.removeVisibility(txn, c, g);
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
+		T txn = db.startTransaction();
+		try {
+			if (!db.containsContact(txn, c))
+				throw new NoSuchContactException();
+			if (!db.containsGroup(txn, g))
+				throw new NoSuchGroupException();
+			wasVisible = db.containsVisibleGroup(txn, c, g);
+			if (visible && !wasVisible) db.addVisibility(txn, c, g);
+			else if (!visible && wasVisible) db.removeVisibility(txn, c, g);
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 		if (visible != wasVisible) {
 			eventBus.broadcast(new GroupVisibilityUpdatedEvent(
@@ -1276,28 +998,23 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 
 	public void updateTransportKeys(Map<ContactId, TransportKeys> keys)
 			throws DbException {
-		lock.writeLock().lock();
-		try {
-			T txn = db.startTransaction();
-			try {
-				Map<ContactId, TransportKeys> filtered =
-						new HashMap<ContactId, TransportKeys>();
-				for (Entry<ContactId, TransportKeys> e : keys.entrySet()) {
-					ContactId c = e.getKey();
-					TransportKeys k = e.getValue();
-					if (db.containsContact(txn, c)
-							&& db.containsTransport(txn, k.getTransportId())) {
-						filtered.put(c, k);
-					}
+		T txn = db.startTransaction();
+		try {
+			Map<ContactId, TransportKeys> filtered =
+					new HashMap<ContactId, TransportKeys>();
+			for (Entry<ContactId, TransportKeys> e : keys.entrySet()) {
+				ContactId c = e.getKey();
+				TransportKeys k = e.getValue();
+				if (db.containsContact(txn, c)
+						&& db.containsTransport(txn, k.getTransportId())) {
+					filtered.put(c, k);
 				}
-				db.updateTransportKeys(txn, filtered);
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
 			}
-		} finally {
-			lock.writeLock().unlock();
+			db.updateTransportKeys(txn, filtered);
+			db.commitTransaction(txn);
+		} catch (DbException e) {
+			db.abortTransaction(txn);
+			throw e;
 		}
 	}
 }
diff --git a/briar-core/src/org/briarproject/db/JdbcDatabase.java b/briar-core/src/org/briarproject/db/JdbcDatabase.java
index ac583594bd27844440519e3c9256cab770b06409..b1b536d7fab33ac640f8d394519023d6783f3f8b 100644
--- a/briar-core/src/org/briarproject/db/JdbcDatabase.java
+++ b/briar-core/src/org/briarproject/db/JdbcDatabase.java
@@ -41,7 +41,6 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
@@ -228,8 +227,6 @@ abstract class JdbcDatabase implements Database<Connection> {
 	private final LinkedList<Connection> connections =
 			new LinkedList<Connection>(); // Locking: connectionsLock
 
-	private final AtomicInteger transactionCount = new AtomicInteger(0);
-
 	private int openConnections = 0; // Locking: connectionsLock
 	private boolean closed = false; // Locking: connectionsLock
 
@@ -369,7 +366,6 @@ abstract class JdbcDatabase implements Database<Connection> {
 		} catch (SQLException e) {
 			throw new DbException(e);
 		}
-		transactionCount.incrementAndGet();
 		return txn;
 	}
 
@@ -418,14 +414,6 @@ abstract class JdbcDatabase implements Database<Connection> {
 		}
 	}
 
-	public int getTransactionCount() {
-		return transactionCount.get();
-	}
-
-	public void resetTransactionCount() {
-		transactionCount.set(0);
-	}
-
 	protected void closeAllConnections() throws SQLException {
 		boolean interrupted = false;
 		connectionsLock.lock();