use of org.syncany.database.DatabaseVersionHeader in project syncany by syncany.
the class DatabaseVersionDaoTest method testNonEmptyDatabaseVersionHeaders.
@Test
public void testNonEmptyDatabaseVersionHeaders() throws Exception {
// Setup
Config testConfig = TestConfigUtil.createTestLocalConfig();
Connection databaseConnection = testConfig.createDatabaseConnection();
// Run
TestSqlUtil.runSqlFromResource(databaseConnection, "test.insert.set1.sql");
ChunkSqlDao chunkDao = new ChunkSqlDao(databaseConnection);
MultiChunkSqlDao multiChunkDao = new MultiChunkSqlDao(databaseConnection);
FileVersionSqlDao fileVersionDao = new FileVersionSqlDao(databaseConnection);
FileHistorySqlDao fileHistoryDao = new FileHistorySqlDao(databaseConnection, fileVersionDao);
FileContentSqlDao fileContentDao = new FileContentSqlDao(databaseConnection);
DatabaseVersionSqlDao databaseVersionDao = new DatabaseVersionSqlDao(databaseConnection, chunkDao, fileContentDao, fileVersionDao, fileHistoryDao, multiChunkDao);
List<DatabaseVersionHeader> databaseVersionHeaders = databaseVersionDao.getNonEmptyDatabaseVersionHeaders();
Collection<String> databaseVersionHeaderStrings = databaseVersionHeaders.stream().map(dbv -> dbv.toString()).collect(Collectors.toCollection(ArrayList::new));
// Test
assertNotNull(databaseVersionHeaders);
assertEquals(6, databaseVersionHeaders.size());
assertTrue(CollectionUtil.containsExactly(Arrays.asList(new String[] { "A/(A1)/T=1388589969000", "A/(A2)/T=1388676369000", "A/(A3)/T=1388762769000", "A/(A4)/T=1388849289000", "A/(A5)/T=1388935689000", "B/(B1)/T=1388849289000" }), databaseVersionHeaderStrings));
TestSqlUtil.runSql("DELETE FROM fileversion WHERE databaseversion_id = 0", databaseConnection);
// Tear down
databaseConnection.close();
TestConfigUtil.deleteTestLocalConfigAndData(testConfig);
}
use of org.syncany.database.DatabaseVersionHeader in project syncany by syncany.
the class DownOperation method persistMuddyMultiChunks.
/**
* Identifies and persists 'muddy' multichunks to the local database. Muddy multichunks are multichunks
* that have been referenced by DIRTY database versions and might be reused in future database versions when
* the other client cleans up its mess (performs another 'up').
*/
private void persistMuddyMultiChunks(Entry<String, DatabaseBranch> winnersBranch, DatabaseBranches allStitchedBranches, Map<DatabaseVersionHeader, File> databaseVersionLocations) throws StorageException, IOException, SQLException {
// Find dirty database versions (from other clients!) and load them from files
Map<DatabaseVersionHeader, Collection<MultiChunkEntry>> muddyMultiChunksPerDatabaseVersion = new HashMap<>();
Set<DatabaseVersionHeader> winnersDatabaseVersionHeaders = Sets.newHashSet(winnersBranch.getValue().getAll());
for (String otherClientName : allStitchedBranches.getClients()) {
boolean isLocalMachine = config.getMachineName().equals(otherClientName);
if (!isLocalMachine) {
DatabaseBranch otherClientBranch = allStitchedBranches.getBranch(otherClientName);
Set<DatabaseVersionHeader> otherClientDatabaseVersionHeaders = Sets.newHashSet(otherClientBranch.getAll());
SetView<DatabaseVersionHeader> otherMuddyDatabaseVersionHeaders = Sets.difference(otherClientDatabaseVersionHeaders, winnersDatabaseVersionHeaders);
boolean hasMuddyDatabaseVersionHeaders = otherMuddyDatabaseVersionHeaders.size() > 0;
if (hasMuddyDatabaseVersionHeaders) {
logger.log(Level.INFO, "DIRTY database version headers of " + otherClientName + ": " + otherMuddyDatabaseVersionHeaders);
for (DatabaseVersionHeader muddyDatabaseVersionHeader : otherMuddyDatabaseVersionHeaders) {
MemoryDatabase muddyMultiChunksDatabase = new MemoryDatabase();
File localFileForMuddyDatabaseVersion = databaseVersionLocations.get(muddyDatabaseVersionHeader);
VectorClock fromVersion = muddyDatabaseVersionHeader.getVectorClock();
VectorClock toVersion = muddyDatabaseVersionHeader.getVectorClock();
logger.log(Level.INFO, " - Loading " + muddyDatabaseVersionHeader + " from file " + localFileForMuddyDatabaseVersion);
databaseSerializer.load(muddyMultiChunksDatabase, localFileForMuddyDatabaseVersion, fromVersion, toVersion, DatabaseReadType.FULL);
boolean hasMuddyMultiChunks = muddyMultiChunksDatabase.getMultiChunks().size() > 0;
if (hasMuddyMultiChunks) {
muddyMultiChunksPerDatabaseVersion.put(muddyDatabaseVersionHeader, muddyMultiChunksDatabase.getMultiChunks());
}
}
}
}
}
// Add muddy multichunks to 'multichunks_muddy' database table
boolean hasMuddyMultiChunks = muddyMultiChunksPerDatabaseVersion.size() > 0;
if (hasMuddyMultiChunks) {
localDatabase.writeMuddyMultiChunks(muddyMultiChunksPerDatabaseVersion);
}
}
use of org.syncany.database.DatabaseVersionHeader in project syncany by syncany.
the class DownOperation method populateDatabaseBranches.
/**
* This methods takes a Map containing DatabaseVersions (headers only) and loads these headers into {@link DatabaseBranches}.
* In addition, the local branch is added to this. The resulting DatabaseBranches will contain all headers exactly once,
* for the client that created that version.
*
* @param localBranch {@link DatabaseBranch} containing the locally known headers.
* @param remoteDatabaseHeaders Map from {@link DatabaseRemoteFile}s (important for client names) to the {@link DatabaseVersion}s that are
* contained in these files.
*
* @return DatabaseBranches filled with all the headers that originated from either of the parameters.
*/
private DatabaseBranches populateDatabaseBranches(DatabaseBranch localBranch, SortedMap<DatabaseRemoteFile, List<DatabaseVersion>> remoteDatabaseHeaders) {
DatabaseBranches allBranches = new DatabaseBranches();
allBranches.put(config.getMachineName(), localBranch.clone());
for (DatabaseRemoteFile remoteDatabaseFile : remoteDatabaseHeaders.keySet()) {
// Populate branches
DatabaseBranch remoteClientBranch = allBranches.getBranch(remoteDatabaseFile.getClientName(), true);
for (DatabaseVersion remoteDatabaseVersion : remoteDatabaseHeaders.get(remoteDatabaseFile)) {
DatabaseVersionHeader header = remoteDatabaseVersion.getHeader();
remoteClientBranch.add(header);
}
}
logger.log(Level.INFO, "Populated unknown branches: " + allBranches);
return allBranches;
}
use of org.syncany.database.DatabaseVersionHeader in project syncany by syncany.
the class DatabaseReconciliator method findWinnersNameAndBranch.
/**
* Algorithm to find the winner's database branch (client name and branch).
* The winner's branch is used to determine the local file system actions.
*
* <p>Basic algorithm: Sort all databaseversions by vectorclocks, tiebreaking with timestamps and machinenames.
* Iterate over this list, adding databaseversions to the winning branch if they are not simultaneous with the
* winning branch up until this point.
*
* <p><b>Illustration:</b><br />
* Suppose the following branches exist.
* Naming: <em>created-by / vector clock / local time</em>.
*
* <pre>
* A B C
* --|-------------------------------------------------
* 0 | A/(A1)/T=10 B/(A3,B1)/T=20 C/(A1,C1)/T=14
* 1 | A/(A2)/T=13 C/(A1,C2)/T=15
* 2 | A/(A3)/T=19
* 3 | A/(A4)/T=23
* </pre>
*
* <b>Sorted Database versions:</b>
* <ol>
* <li>A[0]:A/(A1)/T=10</li>
* <li>A[1]:A/(A2)/T=13</li>
* <li>C[0]:C/(A1,C1)/T=14</li>
* <li>C[1]:C/(A1,C2)/T=15</li>
* <li>A[2]:A/(A3)/T=19</li>
* <li>B[0]:B/(A3,B1)/T=20</li>
* <li>A[3]:A/(A4)/T=23</li>
* </ol>
*
* <b>Iterating through the list:</b>
* <ol>
* <li>A[0] is the first version. Add it.</li>
* <li>A[1] > A[0]. Add it.</li>
* <li>C[0] is simultaneous with A[1]. Ignore it.</li>
* <li>C[1] is simultaneous with A[1]. Ignore it.</li>
* <li>A[2] > A[1]. Add it.</li>
* <li>B[0] > A[2]. Add it.</li>
* <li>A[3] is simultaneous with B[0]. Ignore it.</li>
* </ol>
*
* <b>Winning branch:</b>
* <ol>
* <li>A[0]:A/(A1)/T=10</li>
* <li>A[1]:A/(A2)/T=13</li>
* <li>A[2]:A/(A3)/T=19</li>
* <li>B[0]:B/(A3,B1)/T=20</li>
* </ol>
*
* Last version matches last version of B. Hence B wins.
*
* @param allStitchedBranches All branches of all machines (including local)
* @return Returns the name and the branch of the winning machine
*/
private Entry<String, DatabaseBranch> findWinnersNameAndBranch(DatabaseBranches allBranches) {
List<DatabaseVersionHeader> databaseVersionHeaders = sortBranches(allBranches);
if (databaseVersionHeaders.size() == 0) {
return null;
}
// Determine winning branch
DatabaseBranch winnersBranch = new DatabaseBranch();
DatabaseVersionHeaderComparator databaseVersionHeaderComparator = new DatabaseVersionHeaderComparator(false);
for (DatabaseVersionHeader potentialWinner : databaseVersionHeaders) {
boolean emptyWinnerBranch = winnersBranch.size() == 0;
boolean potentialWinnerWins = !emptyWinnerBranch && databaseVersionHeaderComparator.compare(potentialWinner, winnersBranch.getLast()) > 0;
if (emptyWinnerBranch || potentialWinnerWins) {
logger.log(Level.INFO, "Adding database version to winning branch: " + potentialWinner);
winnersBranch.add(potentialWinner);
} else {
logger.log(Level.INFO, "Ignoring databaseVersion: " + potentialWinner);
}
}
// Determine client name for winning branch
DatabaseVersionHeader winningLastDatabaseVersionHeader = winnersBranch.getLast();
for (String currentClient : allBranches.getClients()) {
DatabaseBranch currentBranch = allBranches.getBranch(currentClient);
DatabaseVersionHeader currentBranchLastDatabaseVersionHeader = currentBranch.getLast();
if (winningLastDatabaseVersionHeader.equals(currentBranchLastDatabaseVersionHeader)) {
return new AbstractMap.SimpleEntry<String, DatabaseBranch>(currentClient, winnersBranch);
}
}
return null;
}
use of org.syncany.database.DatabaseVersionHeader in project syncany by syncany.
the class MultiChunkSqlDao method writeMuddyMultiChunks.
public void writeMuddyMultiChunks(Map<DatabaseVersionHeader, Collection<MultiChunkEntry>> muddyMultiChunksPerDatabaseVersion) throws SQLException {
PreparedStatement preparedStatement = getStatement("multichunk_muddy.insert.muddy.writeMuddyMultiChunks.sql");
for (DatabaseVersionHeader muddyDatabaseVersionHeader : muddyMultiChunksPerDatabaseVersion.keySet()) {
Collection<MultiChunkEntry> muddyMultiChunks = muddyMultiChunksPerDatabaseVersion.get(muddyDatabaseVersionHeader);
for (MultiChunkEntry muddyMultiChunk : muddyMultiChunks) {
String multiChunkIdStr = muddyMultiChunk.getId().toString();
String clientName = muddyDatabaseVersionHeader.getClient();
Long clientVersion = muddyDatabaseVersionHeader.getVectorClock().getClock(clientName);
preparedStatement.setString(1, multiChunkIdStr);
preparedStatement.setString(2, clientName);
preparedStatement.setLong(3, clientVersion);
preparedStatement.addBatch();
}
}
preparedStatement.executeBatch();
preparedStatement.close();
}
Aggregations