use of org.syncany.database.VectorClock in project syncany by syncany.
the class UpOperation method executeTransactions.
/**
* Transfers the given {@link DatabaseVersion} objects to the remote.
* Each {@link DatabaseVersion} will be transferred in its own {@link RemoteTransaction} object.
*
* This method resumes an interrupted sequence of earlier transactions.
* It expects the {@link DatabaseVersion} and {@link RemoteTransaction} files to be in the same order as they were originally generated.
* The first {@link DatabaseVersion} and {@link RemoteTransaction} objects should match the interrupted transaction.
*
* The assumption is that the given {@link RemoteTransaction} objects match the given {@link DatabaseVersion} objects.
* The given {@link TransactionRemoteFile} corresponds to the file on the remote from the interrupted transaction.
*
* @param databaseVersionQueue The {@link DatabaseVersion} objects to send to the remote.
* @param remoteTransactionsToResume {@link RemoteTransaction} objects that correspond to the given {@link DatabaseVersion} objects.
* @param transactionRemoteFileToResume The file on the remote that was used for the specific transaction that was interrupted.
*/
private int executeTransactions() throws Exception {
Iterator<RemoteTransaction> remoteTransactionsToResumeIterator = (resuming) ? remoteTransactionsToResume.iterator() : null;
// At this point, if a failure occurs from which we can resume, new transaction files will be written
// Delete any old transaction files
transferManager.clearPendingTransactions();
boolean detectedFailure = false;
Exception caughtFailure = null;
List<RemoteTransaction> remainingRemoteTransactions = new ArrayList<>();
List<DatabaseVersion> remainingDatabaseVersions = new ArrayList<>();
DatabaseVersion databaseVersion = databaseVersionQueue.take();
boolean noDatabaseVersions = databaseVersion.isEmpty();
// Add dirty data to first database
addDirtyData(databaseVersion);
//
while (!databaseVersion.isEmpty()) {
RemoteTransaction remoteTransaction = null;
if (!resuming) {
VectorClock newVectorClock = findNewVectorClock();
databaseVersion.setVectorClock(newVectorClock);
databaseVersion.setTimestamp(new Date());
databaseVersion.setClient(config.getMachineName());
remoteTransaction = new RemoteTransaction(config, transferManager);
// Add multichunks to transaction
logger.log(Level.INFO, "Uploading new multichunks ...");
// This call adds newly changed chunks to a "RemoteTransaction", so they can be uploaded later.
addMultiChunksToTransaction(remoteTransaction, databaseVersion.getMultiChunks());
} else {
remoteTransaction = remoteTransactionsToResumeIterator.next();
}
logger.log(Level.INFO, "Uploading database: " + databaseVersion);
// Create delta database and commit transaction
// The information about file changes is written to disk to locally "commit" the transaction. This
// enables Syncany to later resume the transaction if it is interrupted before completion.
writeAndAddDeltaDatabase(remoteTransaction, databaseVersion, resuming);
// This thread is to be run when the transaction is interrupted for connectivity reasons. It will serialize
// the transaction and metadata in memory such that the transaction can be resumed later.
Thread writeResumeFilesShutDownHook = createAndAddShutdownHook(remoteTransaction, databaseVersion);
// are confirmed to have been safely pushed to the remote, will the transaction be marked as complete.
if (!detectedFailure) {
boolean committingFailed = true;
try {
if (transactionRemoteFileToResume == null) {
remoteTransaction.commit();
} else {
remoteTransaction.commit(config.getTransactionFile(), transactionRemoteFileToResume);
transactionRemoteFileToResume = null;
}
logger.log(Level.INFO, "Persisting local SQL database (new database version {0}) ...", databaseVersion.getHeader().toString());
long newDatabaseVersionId = localDatabase.writeDatabaseVersion(databaseVersion);
logger.log(Level.INFO, "Removing DIRTY database versions from database ...");
localDatabase.removeDirtyDatabaseVersions(newDatabaseVersionId);
logger.log(Level.INFO, "Adding database version to result changes:" + databaseVersion);
addNewDatabaseChangesToResultChanges(databaseVersion, result.getChangeSet());
result.incrementTransactionsCompleted();
logger.log(Level.INFO, "Committing local database.");
localDatabase.commit();
committingFailed = false;
} catch (Exception e) {
detectedFailure = true;
caughtFailure = e;
} finally {
// The JVM has not shut down, so we can remove the shutdown hook.
// If it turns out that committing has failed, we run it explicitly.
removeShutdownHook(writeResumeFilesShutDownHook);
if (committingFailed) {
remainingRemoteTransactions.add(remoteTransaction);
remainingDatabaseVersions.add(databaseVersion);
}
}
} else {
remainingRemoteTransactions.add(remoteTransaction);
remainingDatabaseVersions.add(databaseVersion);
}
if (!noDatabaseVersions) {
logger.log(Level.FINE, "Waiting for new database version.");
databaseVersion = databaseVersionQueue.take();
logger.log(Level.FINE, "Took new database version: " + databaseVersion);
} else {
logger.log(Level.FINE, "Not waiting for new database version, last one has been taken.");
break;
}
}
if (detectedFailure) {
localDatabase.rollback();
serializeRemoteTransactionsAndMetadata(remainingRemoteTransactions, remainingDatabaseVersions);
throw caughtFailure;
}
return (int) result.getTransactionsCompleted();
}
use of org.syncany.database.VectorClock in project syncany by syncany.
the class XmlDatabaseDaoTest method testWriteAndReadVectorClock.
@Test
public void testWriteAndReadVectorClock() throws IOException {
// Prepare
MemoryDatabase newDatabase = new MemoryDatabase();
DatabaseVersion newDatabaseVersion = createDatabaseVersion();
// Create new vector clock
VectorClock vc = new VectorClock();
vc.setClock("UserA", 14234234L);
vc.setClock("UserB", 9433431232432L);
vc.setClock("UserC", 1926402374L);
newDatabaseVersion.setVectorClock(vc);
// Add database version
newDatabase.addDatabaseVersion(newDatabaseVersion);
// Write database to disk, read it again, and compare them
MemoryDatabase loadedDatabase = writeReadAndCompareDatabase(newDatabase);
// Check VC
DatabaseVersion loadedDatabaseVersionSelectedByVectorClock = loadedDatabase.getDatabaseVersion(vc);
DatabaseVersion loadedDatabaseVersionSelectedFirst = loadedDatabase.getDatabaseVersions().get(0);
assertEquals("Vector clocks do not match (selected by vector clock)", vc, loadedDatabaseVersionSelectedByVectorClock.getVectorClock());
assertEquals("Vector clocks do not match (selected first)", vc, loadedDatabaseVersionSelectedFirst.getVectorClock());
assertEquals("Database versions do not match.", loadedDatabaseVersionSelectedByVectorClock, loadedDatabaseVersionSelectedFirst);
}
use of org.syncany.database.VectorClock in project syncany by syncany.
the class XmlDatabaseDaoTest method createDatabaseVersion.
private DatabaseVersion createDatabaseVersion(DatabaseVersion basedOnDatabaseVersion) {
VectorClock vectorClock = (basedOnDatabaseVersion != null) ? basedOnDatabaseVersion.getVectorClock().clone() : new VectorClock();
vectorClock.incrementClock("someclient");
DatabaseVersion databaseVersion = new DatabaseVersion();
databaseVersion.setClient("someclient");
databaseVersion.setTimestamp(new Date());
databaseVersion.setVectorClock(vectorClock);
return databaseVersion;
}
Aggregations