/** * Commits this transaction by performing the required upload and * delete operations. The method first moves all files to the temporary * remote location. If no errors occur, all files are moved to their * final location. * * <p>The method first writes a {@link TransactionRemoteFile} containing * all actions to be performed and then uploads this file. Then it uploads * new files (added by {@link #upload(File, RemoteFile) upload()} and moves * deleted files to a temporary location (deleted by {@link #delete(RemoteFile) delete()}. * * <p>If this was successful, the transaction file is deleted and the * temporary files. After deleting the transaction file, the transaction * is successfully committed. */ public void commit() throws StorageException { logger.log(Level.INFO, "Starting TX.commit() ..."); if (isEmpty()) { logger.log(Level.INFO, "- Empty transaction, not committing anything."); return; } File localTransactionFile = writeLocalTransactionFile(); TransactionRemoteFile remoteTransactionFile = uploadTransactionFile(localTransactionFile); commit(localTransactionFile, remoteTransactionFile); }
/** * This method adds unusedMultiChunks to the @{link RemoteTransaction} for deletion. * * @param unusedMultiChunks which are to be deleted because all references to them are gone. */ private void deleteUnusedRemoteMultiChunks(Map<MultiChunkId, MultiChunkEntry> unusedMultiChunks) throws StorageException { logger.log(Level.INFO, "- Deleting remote multichunks ..."); for (MultiChunkEntry multiChunkEntry : unusedMultiChunks.values()) { logger.log(Level.FINE, " + Deleting remote multichunk " + multiChunkEntry + " ..."); remoteTransaction.delete(new MultichunkRemoteFile(multiChunkEntry.getId())); } }
/** * Does exactly the same as the parameterless version, except it does not create and upload the transactionfile. Instead * it uses the files that are passed. Used for resuming existing transactions. Only call this function if resuming * cannot cause invalid states. */ public void commit(File localTransactionFile, TransactionRemoteFile remoteTransactionFile) throws StorageException { logger.log(Level.INFO, "- Starting to upload data in commit."); uploadAndMoveToTempLocation(); moveToFinalLocation(); deleteTransactionFile(localTransactionFile, remoteTransactionFile); deleteTempRemoteFiles(); }
/** * This method checks what the current cleanup number is, increments it by one and adds * a new cleanup file to the transaction, to signify to other clients that Cleanup has occurred. */ private void updateCleanupFileInTransaction() throws StorageException, IOException { if (remoteTransaction.isEmpty()) { // No need to bump numbers return; } // Find all existing cleanup files Map<String, CleanupRemoteFile> cleanupFiles = transferManager.list(CleanupRemoteFile.class); long lastRemoteCleanupNumber = getLastRemoteCleanupNumber(cleanupFiles); // Schedule any existing cleanup files for deletion for (CleanupRemoteFile cleanupRemoteFile : cleanupFiles.values()) { remoteTransaction.delete(cleanupRemoteFile); } // Upload a new cleanup file that indicates changes File newCleanupFile = config.getCache().createTempFile("cleanup"); long newCleanupNumber = lastRemoteCleanupNumber + 1; remoteTransaction.upload(newCleanupFile, new CleanupRemoteFile(newCleanupNumber)); localDatabase.writeCleanupNumber(newCleanupNumber); }
databaseVersion.setClient(config.getMachineName()); remoteTransaction = new RemoteTransaction(config, transferManager); try { if (transactionRemoteFileToResume == null) { remoteTransaction.commit(); remoteTransaction.commit(config.getTransactionFile(), transactionRemoteFileToResume); transactionRemoteFileToResume = null;
private void addLocalDatabaseToTransaction(RemoteTransaction remoteTransaction, File localDatabaseFile, DatabaseRemoteFile remoteDatabaseFile) throws InterruptedException, StorageException { logger.log(Level.INFO, "- Uploading " + localDatabaseFile + " to " + remoteDatabaseFile + " ..."); remoteTransaction.upload(localDatabaseFile, remoteDatabaseFile); }
private Collection<RemoteTransaction> attemptResumeTransactions(Collection<Long> versions) { try { Collection<RemoteTransaction> remoteTransactions = new ArrayList<>(); for (Long version : versions) { File transactionFile = config.getTransactionFile(version); // If a single transaction file is missing, we should restart if (!transactionFile.exists()) { return null; } TransactionTO transactionTO = TransactionTO.load(null, transactionFile); // Verify if all files needed are in cache. for (ActionTO action : transactionTO.getActions()) { if (action.getType() == ActionType.UPLOAD) { if (action.getStatus() == ActionStatus.UNSTARTED) { if (!action.getLocalTempLocation().exists()) { // Unstarted upload has no cached local copy, abort return null; } } } } remoteTransactions.add(new RemoteTransaction(config, transferManager, transactionTO)); } return remoteTransactions; } catch (Exception e) { logger.log(Level.WARNING, "Invalid transaction file. Cannot resume!"); return null; } }
/** * This method finishes the merging of remote files, by attempting to commit the {@link RemoteTransaction}. * If this fails, it will roll back the local database. */ private void finishMerging() throws Exception { updateCleanupFileInTransaction(); try { logger.log(Level.INFO, "Cleanup: COMMITTING TX ..."); remoteTransaction.commit(); localDatabase.commit(); } catch (StorageException e) { logger.log(Level.INFO, "Cleanup: FAILED TO COMMIT TX. Rolling back ..."); localDatabase.rollback(); throw e; } logger.log(Level.INFO, "Cleanup: SUCCESS COMMITTING TX."); }
TransactionStats stats = gatherTransactionStats(); int uploadFileIndex = 0;
/** * This methods adds the multichunks that are not yet present in the remote repo to the {@link RemoteTransaction} for * uploading. Multichunks are not uploaded if they are dirty. * * @param multiChunkEntries Collection of multiChunkEntries that are included in the new {@link DatabaseVersion} */ private void addMultiChunksToTransaction(RemoteTransaction remoteTransaction, Collection<MultiChunkEntry> multiChunksEntries) throws InterruptedException, StorageException { List<MultiChunkId> dirtyMultiChunkIds = localDatabase.getDirtyMultiChunkIds(); for (MultiChunkEntry multiChunkEntry : multiChunksEntries) { if (dirtyMultiChunkIds.contains(multiChunkEntry.getId())) { logger.log(Level.INFO, "- Ignoring multichunk (from dirty database, already uploaded), " + multiChunkEntry.getId() + " ..."); } else { File localMultiChunkFile = config.getCache().getEncryptedMultiChunkFile(multiChunkEntry.getId()); MultichunkRemoteFile remoteMultiChunkFile = new MultichunkRemoteFile(multiChunkEntry.getId()); logger.log(Level.INFO, "- Uploading multichunk {0} from {1} to {2} ...", new Object[] { multiChunkEntry.getId(), localMultiChunkFile, remoteMultiChunkFile }); remoteTransaction.upload(localMultiChunkFile, remoteMultiChunkFile); } } }
remoteTransaction = new RemoteTransaction(config, transferManager);