Search in sources :

Example 26 with VectorClock

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();
}
Also used : RemoteTransaction(org.syncany.plugins.transfer.RemoteTransaction) VectorClock(org.syncany.database.VectorClock) ArrayList(java.util.ArrayList) StorageException(org.syncany.plugins.transfer.StorageException) SQLException(java.sql.SQLException) IOException(java.io.IOException) Date(java.util.Date) DatabaseVersion(org.syncany.database.DatabaseVersion)

Example 27 with VectorClock

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);
}
Also used : VectorClock(org.syncany.database.VectorClock) MemoryDatabase(org.syncany.database.MemoryDatabase) DatabaseVersion(org.syncany.database.DatabaseVersion) Test(org.junit.Test)

Example 28 with VectorClock

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;
}
Also used : VectorClock(org.syncany.database.VectorClock) Date(java.util.Date) DatabaseVersion(org.syncany.database.DatabaseVersion)

Aggregations

VectorClock (org.syncany.database.VectorClock)28 Test (org.junit.Test)14 IOException (java.io.IOException)5 Date (java.util.Date)5 DatabaseVersion (org.syncany.database.DatabaseVersion)5 DatabaseVersionHeader (org.syncany.database.DatabaseVersionHeader)5 PreparedStatement (java.sql.PreparedStatement)3 ResultSet (java.sql.ResultSet)3 SQLException (java.sql.SQLException)3 MemoryDatabase (org.syncany.database.MemoryDatabase)3 File (java.io.File)2 Matcher (java.util.regex.Matcher)2 PartialFileHistory (org.syncany.database.PartialFileHistory)2 UnsupportedEncodingException (java.io.UnsupportedEncodingException)1 ArrayList (java.util.ArrayList)1 Collection (java.util.Collection)1 HashMap (java.util.HashMap)1 Map (java.util.Map)1 Before (org.junit.Before)1 ChunkEntry (org.syncany.database.ChunkEntry)1