Search in sources :

Example 21 with DatabaseVersionHeader

use of org.syncany.database.DatabaseVersionHeader in project syncany by syncany.

the class DatabaseFileReader method next.

/**
	 * Loads the winner's database branch into the memory in a {@link MemoryDatabase} object, by using
	 * the already downloaded list of remote database files.
	 *
	 * <p>Because database files can contain multiple {@link DatabaseVersion}s per client, a range for which
	 * to load the database versions must be determined.
	 *
	 * <p><b>Example 1:</b><br />
	 * <pre>
	 *  db-A-0001   (A1)     Already known             Not loaded
	 *  db-A-0005   (A2)     Already known             Not loaded
	 *              (A3)     Already known             Not loaded
	 *              (A4)     Part of winner's branch   Loaded
	 *              (A5)     Purge database version    Ignored (only DEFAULT)
	 *  db-B-0001   (A5,B1)  Part of winner's branch   Loaded
	 *  db-A-0006   (A6,B1)  Part of winner's branch   Loaded
	 * </pre>
	 *
	 * <p>In example 1, only (A4)-(A5) must be loaded from db-A-0005, and not all four database versions.
	 *
	 * <p><b>Other example:</b><br />
	 * <pre>
	 *  db-A-0005   (A1)     Part of winner's branch   Loaded
	 *  db-A-0005   (A2)     Part of winner's branch   Loaded
	 *  db-B-0001   (A2,B1)  Part of winner's branch   Loaded
	 *  db-A-0005   (A3,B1)  Part of winner's branch   Loaded
	 *  db-A-0005   (A4,B1)  Part of winner's branch   Loaded
	 *  db-A-0005   (A5,B1)  Purge database version    Ignored (only DEFAULT)
	 * </pre>
	 *
	 * <p>In example 2, (A1)-(A5,B1) [except (A2,B1)] are contained in db-A-0005 (after merging!), so
	 * db-A-0005 must be processed twice; each time loading separate parts of the file. In this case:
	 * First load (A1)-(A2) from db-A-0005, then load (A2,B1) from db-B-0001, then load (A3,B1)-(A4,B1)
	 * from db-A-0005, and ignore (A5,B1).
	 * @param databaseFileList
	 * @param ignoredMostRecentPurgeVersions
	 *
	 * @return Returns a loaded memory database containing all metadata from the winner's branch
	 */
@Override
public MemoryDatabase next() {
    MemoryDatabase winnerBranchDatabase = new MemoryDatabase();
    String rangeClientName = null;
    VectorClock rangeVersionFrom = null;
    VectorClock rangeVersionTo = null;
    while (branchIndex < winnersApplyBranchList.size() && winnerBranchDatabase.getFileHistories().size() < MAX_FILES) {
        DatabaseVersionHeader currentDatabaseVersionHeader = winnersApplyBranchList.get(branchIndex);
        DatabaseVersionHeader nextDatabaseVersionHeader = (branchIndex + 1 < winnersApplyBranchList.size()) ? winnersApplyBranchList.get(branchIndex + 1) : null;
        // First of range for this client
        if (rangeClientName == null) {
            rangeClientName = currentDatabaseVersionHeader.getClient();
            rangeVersionFrom = currentDatabaseVersionHeader.getVectorClock();
            rangeVersionTo = currentDatabaseVersionHeader.getVectorClock();
        } else // Still in range for this client
        {
            rangeVersionTo = currentDatabaseVersionHeader.getVectorClock();
        }
        // Now load this stuff from the database file (or not)
        //   - If the database file exists, load the range and reset it
        //   - If not, only force a load if this is the range end
        File databaseVersionFile = databaseVersionLocations.get(currentDatabaseVersionHeader);
        if (databaseVersionFile == null) {
            throw new RuntimeException("Could not find file corresponding to " + currentDatabaseVersionHeader + ", while it is in the winners branch.");
        }
        boolean lastDatabaseVersionHeader = nextDatabaseVersionHeader == null;
        boolean nextDatabaseVersionInSameFile = lastDatabaseVersionHeader || databaseVersionFile.equals(databaseVersionLocations.get(nextDatabaseVersionHeader));
        boolean rangeEnds = lastDatabaseVersionHeader || !nextDatabaseVersionInSameFile;
        if (rangeEnds) {
            try {
                databaseSerializer.load(winnerBranchDatabase, databaseVersionFile, rangeVersionFrom, rangeVersionTo, DatabaseReadType.FULL);
            } catch (IOException e) {
                throw new RuntimeException(e.getMessage(), e);
            }
            rangeClientName = null;
        }
        branchIndex++;
    }
    return winnerBranchDatabase;
}
Also used : VectorClock(org.syncany.database.VectorClock) MemoryDatabase(org.syncany.database.MemoryDatabase) IOException(java.io.IOException) DatabaseVersionHeader(org.syncany.database.DatabaseVersionHeader) File(java.io.File)

Example 22 with DatabaseVersionHeader

use of org.syncany.database.DatabaseVersionHeader in project syncany by syncany.

the class DatabaseReconciliator method findWinnerBranch.

/**
	 * Implements the core synchronization algorithm as described {@link DatabaseReconciliator in the class description}.
	 * 
	 * @param localMachineName Client name of the local machine (required for branch stitching)
	 * @param localBranch Local branch, created from the local database
	 * @param unknownRemoteBranches Newly downloaded unknown remote branches (incomplete branches; will be stitched)
	 * @return Returns the branch of the winning client
	 */
public Map.Entry<String, DatabaseBranch> findWinnerBranch(DatabaseBranches allBranches) throws Exception {
    Entry<String, DatabaseBranch> winnersNameAndBranch = findWinnersNameAndBranch(allBranches);
    if (winnersNameAndBranch != null) {
        String winnersName = winnersNameAndBranch.getKey();
        DatabaseBranch winnersBranch = winnersNameAndBranch.getValue();
        if (logger.isLoggable(Level.INFO)) {
            logger.log(Level.INFO, "- Winner is " + winnersName + " with branch: ");
            for (DatabaseVersionHeader databaseVersionHeader : winnersBranch.getAll()) {
                logger.log(Level.INFO, "  + " + databaseVersionHeader);
            }
        }
        return winnersNameAndBranch;
    } else {
        return null;
    }
}
Also used : DatabaseVersionHeader(org.syncany.database.DatabaseVersionHeader)

Example 23 with DatabaseVersionHeader

use of org.syncany.database.DatabaseVersionHeader in project syncany by syncany.

the class DownOperation method purgeConflictingLocalBranch.

/**
	 * Marks locally conflicting database versions as <tt>DIRTY</tt> and removes remote databases that
	 * correspond to those database versions. This method uses the {@link DatabaseReconciliator}
	 * to determine whether there is a local purge branch.
	 */
private void purgeConflictingLocalBranch(DatabaseBranch localBranch, Entry<String, DatabaseBranch> winnersBranch) throws Exception {
    DatabaseBranch localPurgeBranch = databaseReconciliator.findLosersPruneBranch(localBranch, winnersBranch.getValue());
    logger.log(Level.INFO, "- Database versions to REMOVE locally: " + localPurgeBranch);
    if (localPurgeBranch.size() == 0) {
        logger.log(Level.INFO, "  + Nothing to purge locally. No conflicts. Only updates. Nice!");
    } else {
        // Load dirty database (if existent)
        logger.log(Level.INFO, "  + Marking databases as DIRTY locally ...");
        for (DatabaseVersionHeader databaseVersionHeader : localPurgeBranch.getAll()) {
            logger.log(Level.INFO, "    * MASTER->DIRTY: " + databaseVersionHeader);
            localDatabase.markDatabaseVersionDirty(databaseVersionHeader.getVectorClock());
            boolean isOwnDatabaseVersionHeader = config.getMachineName().equals(databaseVersionHeader.getClient());
            if (isOwnDatabaseVersionHeader) {
                String remoteFileToPruneClientName = config.getMachineName();
                long remoteFileToPruneVersion = databaseVersionHeader.getVectorClock().getClock(config.getMachineName());
                DatabaseRemoteFile remoteFileToPrune = new DatabaseRemoteFile(remoteFileToPruneClientName, remoteFileToPruneVersion);
                logger.log(Level.INFO, "    * Deleting own remote database file " + remoteFileToPrune + " ...");
                transferManager.delete(remoteFileToPrune);
            } else {
                logger.log(Level.INFO, "    * NOT deleting any database file remotely (not our database!)");
            }
            result.getDirtyDatabasesCreated().add(databaseVersionHeader);
        }
    }
}
Also used : DatabaseRemoteFile(org.syncany.plugins.transfer.files.DatabaseRemoteFile) DatabaseVersionHeader(org.syncany.database.DatabaseVersionHeader)

Example 24 with DatabaseVersionHeader

use of org.syncany.database.DatabaseVersionHeader in project syncany by syncany.

the class DatabaseReconciliatorTest method testTwoWinningVersionsWithSameTimestamp.

@Test
public void testTwoWinningVersionsWithSameTimestamp() throws Exception {
    /* Scenario: Three clients, to conflicting DBVs with the same timestamp
		 *           --> A should win over B (alphabetical order)
		 */
    Logging.init();
    /// Input data ///
    String localMachineName = "C";
    DatabaseVersionHeader currentLocalVersion = TestDatabaseUtil.createFromString("A/(A2)/T=1376074225230");
    DatabaseBranches allBranches = new DatabaseBranches();
    allBranches.put("A", TestDatabaseUtil.createBranch(new String[] { "A/(A1)/T=1376074225169", "A/(A2)/T=1376074225230", "A/(A3)/T=1376074225256", // Conflicts with A -> A3,B1; also: SAME timestamp!
    "A/(A4)/T=9999999999999" }));
    allBranches.put("B", TestDatabaseUtil.createBranch(new String[] { // Conflicts with B -> A4; also: SAME timestamp!
    "B/(A3,B1)/T=9999999999999" }));
    allBranches.put("C", TestDatabaseUtil.createBranch(new String[] { "A/(A1)/T=1376074225169", "A/(A2)/T=1376074225230" }));
    /// Expected results ///
    TestResult expectedTestResult = new TestResult();
    expectedTestResult.winnersLastDatabaseVersionHeader = TestDatabaseUtil.createMapWithMachineKey(new String[] { "A", "A/(A4)/T=9999999999999" }).firstEntry();
    /// Perform test ///
    testFromMachinePerspective(localMachineName, currentLocalVersion, allBranches, expectedTestResult);
}
Also used : DatabaseVersionHeader(org.syncany.database.DatabaseVersionHeader) DatabaseBranches(org.syncany.operations.down.DatabaseBranches) Test(org.junit.Test)

Example 25 with DatabaseVersionHeader

use of org.syncany.database.DatabaseVersionHeader in project syncany by syncany.

the class DatabaseReconciliatorTest method testStitchBranchesIssue226CompleteBranchesWithDatabaseVersionHeaders.

@Test
public void testStitchBranchesIssue226CompleteBranchesWithDatabaseVersionHeaders() throws Exception {
    Logging.init();
    /// Input data ///
    String localMachineName = "T";
    DatabaseVersionHeader currentLocalVersion = null;
    DatabaseBranches allBranches = new DatabaseBranches();
    // T
    allBranches.put("T", TestDatabaseUtil.createBranch(new String[] { "T/(T2,d5,k4,t7)/T=697013", // <<< Conflicts with k/(T2,d5,k28,t10)/T=772169
    "T/(T3,d5,k24,t8)/T=760721", "T/(T3,d6,k44,t10)/T=822389", "T/(T4,d6,k44,t10)/T=824100" }));
    // d
    allBranches.put("d", TestDatabaseUtil.createBranch(new String[] { "d/(d1,t1)/T=684310", "d/(d2,t4)/T=687747", "d/(d3,k1,t4)/T=689077", "d/(d4,k1,t5)/T=692428", "d/(d5,k4,t7)/T=696655", "d/(T2,d6,k43,t10)/T=820561" }));
    // k
    allBranches.put("k", TestDatabaseUtil.createBranch(new String[] { "k/(d2,k1,t4)/T=688323", "k/(d4,k3,t5)/T=693134", "k/(d4,k4,t7)/T=696251", "k/(T2,d5,k5,t8)/T=701066", "k/(T2,d5,k6,t8)/T=703688", "k/(T2,d5,k7,t8)/T=707177", "k/(T2,d5,k8,t8)/T=709571", "k/(T2,d5,k9,t8)/T=712900", "k/(T2,d5,k10,t8)/T=716399", "k/(T2,d5,k11,t8)/T=719119", "k/(T2,d5,k12,t8)/T=722554", "k/(T2,d5,k13,t8)/T=724848", "k/(T2,d5,k14,t8)/T=728286", "k/(T2,d5,k15,t8)/T=731538", "k/(T2,d5,k16,t8)/T=734832", "k/(T2,d5,k17,t8)/T=738089", "k/(T2,d5,k18,t8)/T=740541", "k/(T2,d5,k19,t8)/T=743906", "k/(T2,d5,k20,t8)/T=747192", "k/(T2,d5,k21,t8)/T=750445", "k/(T2,d5,k22,t8)/T=752883", "k/(T2,d5,k23,t8)/T=756264", "k/(T2,d5,k24,t8)/T=759640", "k/(T2,d5,k25,t10)/T=762872", "k/(T2,d5,k26,t10)/T=765293", "k/(T2,d5,k27,t10)/T=768795", // <<< Conflicts with T/(T3,d5,k24,t8)/T=760721
    "k/(T2,d5,k28,t10)/T=772169", "k/(T2,d5,k29,t10)/T=774593", "k/(T2,d5,k30,t10)/T=777935", "k/(T2,d5,k31,t10)/T=781320", "k/(T2,d5,k32,t10)/T=784670", "k/(T2,d5,k33,t10)/T=787138", "k/(T2,d5,k34,t10)/T=790501", "k/(T2,d5,k35,t10)/T=793760", "k/(T2,d5,k36,t10)/T=797117", "k/(T2,d5,k37,t10)/T=799638", "k/(T2,d5,k38,t10)/T=803046", "k/(T2,d5,k39,t10)/T=806357", "k/(T2,d5,k40,t10)/T=808699", "k/(T2,d5,k41,t10)/T=812166", "k/(T2,d5,k42,t10)/T=815182", "k/(T2,d5,k43,t10)/T=818604", "k/(T2,d6,k44,t10)/T=821185" }));
    // t
    allBranches.put("t", TestDatabaseUtil.createBranch(new String[] { "t/(d1,t3)/T=685357", "t/(d1,t4)/T=686957", "t/(d3,k1,t5)/T=690944", "t/(d4,k3,t6)/T=693534", "t/(d4,k3,t7)/T=696048", "t/(T2,d5,k4,t8)/T=700373", "t/(T2,d5,k24,t9)/T=760625", "t/(T2,d5,k24,t10)/T=762172" }));
    /// Expected results ///
    TestResult expectedTestResult = new TestResult();
    expectedTestResult.winnersLastDatabaseVersionHeader = TestDatabaseUtil.createMapWithMachineKey(new String[] { "T", "T/(T4,d6,k44,t10)/T=824100" }).firstEntry();
    /// Perform test ///
    testFromMachinePerspective(localMachineName, currentLocalVersion, allBranches, expectedTestResult);
}
Also used : DatabaseVersionHeader(org.syncany.database.DatabaseVersionHeader) DatabaseBranches(org.syncany.operations.down.DatabaseBranches) Test(org.junit.Test)

Aggregations

DatabaseVersionHeader (org.syncany.database.DatabaseVersionHeader)33 Test (org.junit.Test)17 DatabaseBranches (org.syncany.operations.down.DatabaseBranches)14 File (java.io.File)4 Date (java.util.Date)4 VectorClock (org.syncany.database.VectorClock)4 PreparedStatement (java.sql.PreparedStatement)3 DatabaseVersion (org.syncany.database.DatabaseVersion)3 MultiChunkEntry (org.syncany.database.MultiChunkEntry)3 DatabaseBranch (org.syncany.operations.down.DatabaseBranch)3 DatabaseRemoteFile (org.syncany.plugins.transfer.files.DatabaseRemoteFile)3 IOException (java.io.IOException)2 Connection (java.sql.Connection)2 ResultSet (java.sql.ResultSet)2 SQLException (java.sql.SQLException)2 Config (org.syncany.config.Config)2 ChunkEntry (org.syncany.database.ChunkEntry)2 FileContent (org.syncany.database.FileContent)2 MemoryDatabase (org.syncany.database.MemoryDatabase)2 MultiChunkId (org.syncany.database.MultiChunkEntry.MultiChunkId)2