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;
}
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;
}
}
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);
}
}
}
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);
}
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);
}
Aggregations