Don't block when free disk space is critically low.

If it's not possible to free up any additional space, exit rather than
hanging. In future we should warn the user before exiting.
This commit is contained in:
akwizgran
2011-09-27 17:05:51 +01:00
parent 90e32ac906
commit 6ed8d89e59

View File

@@ -86,10 +86,8 @@ DatabaseCleaner.Callback {
private final List<DatabaseListener> listeners = private final List<DatabaseListener> listeners =
new ArrayList<DatabaseListener>(); // Locking: self new ArrayList<DatabaseListener>(); // Locking: self
private final Object spaceLock = new Object(); private final Object spaceLock = new Object();
private final Object writeLock = new Object();
private long bytesStoredSinceLastCheck = 0L; // Locking: spaceLock private long bytesStoredSinceLastCheck = 0L; // Locking: spaceLock
private long timeOfLastCheck = 0L; // Locking: spaceLock private long timeOfLastCheck = 0L; // Locking: spaceLock
private volatile boolean writesAllowed = true;
@Inject @Inject
DatabaseComponentImpl(Database<T> db, DatabaseCleaner cleaner) { DatabaseComponentImpl(Database<T> db, DatabaseCleaner cleaner) {
@@ -162,7 +160,6 @@ DatabaseCleaner.Callback {
public void addLocalGroupMessage(Message m) throws DbException { public void addLocalGroupMessage(Message m) throws DbException {
boolean added = false; boolean added = false;
waitForPermissionToWrite();
contactLock.readLock().lock(); contactLock.readLock().lock();
try { try {
messageLock.writeLock().lock(); messageLock.writeLock().lock();
@@ -201,23 +198,6 @@ DatabaseCleaner.Callback {
if(added) callListeners(Event.MESSAGES_ADDED); if(added) callListeners(Event.MESSAGES_ADDED);
} }
/**
* Blocks until messages are allowed to be stored in the database. The
* storage of messages is not allowed while the amount of free storage
* space available to the database is less than CRITICAL_FREE_SPACE.
*/
private void waitForPermissionToWrite() {
synchronized(writeLock) {
while(!writesAllowed) {
if(LOG.isLoggable(Level.FINE))
LOG.fine("Waiting for permission to write");
try {
writeLock.wait();
} catch(InterruptedException ignored) {}
}
}
}
/** /**
* If the given message is already in the database, marks it as seen by the * If the given message is already in the database, marks it as seen by the
* sender and returns false. Otherwise stores the message, updates the * sender and returns false. Otherwise stores the message, updates the
@@ -307,7 +287,6 @@ DatabaseCleaner.Callback {
public void addLocalPrivateMessage(Message m, ContactId c) public void addLocalPrivateMessage(Message m, ContactId c)
throws DbException { throws DbException {
boolean added = false; boolean added = false;
waitForPermissionToWrite();
contactLock.readLock().lock(); contactLock.readLock().lock();
try { try {
if(!containsContact(c)) throw new NoSuchContactException(); if(!containsContact(c)) throw new NoSuchContactException();
@@ -901,7 +880,6 @@ DatabaseCleaner.Callback {
public void receiveBatch(ContactId c, Batch b) throws DbException { public void receiveBatch(ContactId c, Batch b) throws DbException {
boolean anyAdded = false; boolean anyAdded = false;
waitForPermissionToWrite();
contactLock.readLock().lock(); contactLock.readLock().lock();
try { try {
if(!containsContact(c)) throw new NoSuchContactException(); if(!containsContact(c)) throw new NoSuchContactException();
@@ -1356,27 +1334,25 @@ DatabaseCleaner.Callback {
public void checkFreeSpaceAndClean() throws DbException { public void checkFreeSpaceAndClean() throws DbException {
long freeSpace = db.getFreeSpace(); long freeSpace = db.getFreeSpace();
while(freeSpace < MIN_FREE_SPACE) { while(freeSpace < MIN_FREE_SPACE) {
// If disk space is critical, disable the storage of new messages boolean expired = expireMessages(BYTES_PER_SWEEP);
if(freeSpace < CRITICAL_FREE_SPACE) { if(freeSpace < CRITICAL_FREE_SPACE && !expired) {
if(LOG.isLoggable(Level.FINE)) LOG.fine("Critical cleanup"); // FIXME: Work out what to do here - the amount of free space
writesAllowed = false; // is critically low and there are no messages left to expire
} else { System.err.println("Disk space is critical - shutting down");
if(LOG.isLoggable(Level.FINE)) LOG.fine("Normal cleanup"); System.exit(1);
} }
expireMessages(BYTES_PER_SWEEP);
Thread.yield(); Thread.yield();
freeSpace = db.getFreeSpace(); freeSpace = db.getFreeSpace();
// If disk space is no longer critical, re-enable writes
if(freeSpace >= CRITICAL_FREE_SPACE && !writesAllowed) {
writesAllowed = true;
synchronized(writeLock) {
writeLock.notifyAll();
}
}
} }
} }
private void expireMessages(int size) throws DbException { /**
* Removes the oldest messages from the database, with a total size less
* than or equal to the given size, and returns true if any messages were
* removed.
*/
private boolean expireMessages(int size) throws DbException {
Collection<MessageId> old;
contactLock.readLock().lock(); contactLock.readLock().lock();
try { try {
messageLock.writeLock().lock(); messageLock.writeLock().lock();
@@ -1385,9 +1361,8 @@ DatabaseCleaner.Callback {
try { try {
T txn = db.startTransaction(); T txn = db.startTransaction();
try { try {
for(MessageId m : db.getOldMessages(txn, size)) { old = db.getOldMessages(txn, size);
removeMessage(txn, m); for(MessageId m : old) removeMessage(txn, m);
}
db.commitTransaction(txn); db.commitTransaction(txn);
} catch(DbException e) { } catch(DbException e) {
db.abortTransaction(txn); db.abortTransaction(txn);
@@ -1402,6 +1377,7 @@ DatabaseCleaner.Callback {
} finally { } finally {
contactLock.readLock().unlock(); contactLock.readLock().unlock();
} }
return old.isEmpty();
} }
/** /**