Skip to content
Snippets Groups Projects
Commit 287afffe authored by buczekp's avatar buczekp
Browse files

[LMS-2180] use rsync when copying files locally; don't use --append when using...

[LMS-2180] use rsync when copying files locally; don't use --append when using rsync (failed to override files in destination that are bigger than source files)

SVN: 20651
parent 0c585dfa
No related branches found
No related tags found
No related merge requests found
Showing
with 139 additions and 12 deletions
...@@ -35,6 +35,9 @@ public interface IDataSetFileOperationsExecutor ...@@ -35,6 +35,9 @@ public interface IDataSetFileOperationsExecutor
void copyDataSetToDestination(File dataSet, File destination); void copyDataSetToDestination(File dataSet, File destination);
// uses rsync --delete for performance both locally and remotely
void syncDataSetWithDestination(File dataSet, File destination);
void retrieveDataSetFromDestination(File dataSet, File destination); void retrieveDataSetFromDestination(File dataSet, File destination);
void renameTo(File newFile, File oldFile); void renameTo(File newFile, File oldFile);
......
...@@ -32,6 +32,7 @@ import ch.systemsx.cisd.common.exceptions.Status; ...@@ -32,6 +32,7 @@ import ch.systemsx.cisd.common.exceptions.Status;
import ch.systemsx.cisd.common.filesystem.BooleanStatus; import ch.systemsx.cisd.common.filesystem.BooleanStatus;
import ch.systemsx.cisd.common.filesystem.FileUtilities; import ch.systemsx.cisd.common.filesystem.FileUtilities;
import ch.systemsx.cisd.common.filesystem.IFileOperations; import ch.systemsx.cisd.common.filesystem.IFileOperations;
import ch.systemsx.cisd.common.filesystem.IPathCopier;
import ch.systemsx.cisd.common.logging.LogCategory; import ch.systemsx.cisd.common.logging.LogCategory;
import ch.systemsx.cisd.common.logging.LogFactory; import ch.systemsx.cisd.common.logging.LogFactory;
...@@ -40,11 +41,21 @@ public final class LocalDataSetFileOperationsExcecutor implements IDataSetFileOp ...@@ -40,11 +41,21 @@ public final class LocalDataSetFileOperationsExcecutor implements IDataSetFileOp
final static Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, final static Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION,
LocalDataSetFileOperationsExcecutor.class); LocalDataSetFileOperationsExcecutor.class);
private final IPathCopier copier;
private final String rsyncModuleNameOrNull;
private final String rsyncPasswordFileOrNull;
private final IFileOperations fileOperations; private final IFileOperations fileOperations;
public LocalDataSetFileOperationsExcecutor(IFileOperations fileOperations) public LocalDataSetFileOperationsExcecutor(IFileOperations fileOperations, IPathCopier copier,
String rsyncModuleNameOrNull, String rsyncPasswordFileOrNull)
{ {
this.fileOperations = fileOperations; this.fileOperations = fileOperations;
this.copier = copier;
this.rsyncModuleNameOrNull = rsyncModuleNameOrNull;
this.rsyncPasswordFileOrNull = rsyncPasswordFileOrNull;
} }
public BooleanStatus checkSame(File dataSet, File destination) public BooleanStatus checkSame(File dataSet, File destination)
...@@ -63,7 +74,6 @@ public final class LocalDataSetFileOperationsExcecutor implements IDataSetFileOp ...@@ -63,7 +74,6 @@ public final class LocalDataSetFileOperationsExcecutor implements IDataSetFileOp
if (destination.isDirectory()) if (destination.isDirectory())
{ {
FileFilter nullFilter = null; FileFilter nullFilter = null;
// TODO ignore symlinks?
List<File> storeFiles = FileUtilities.listFiles(dataSet, nullFilter, true); List<File> storeFiles = FileUtilities.listFiles(dataSet, nullFilter, true);
List<File> destFiles = FileUtilities.listFiles(destination, nullFilter, true); List<File> destFiles = FileUtilities.listFiles(destination, nullFilter, true);
...@@ -154,6 +164,19 @@ public final class LocalDataSetFileOperationsExcecutor implements IDataSetFileOp ...@@ -154,6 +164,19 @@ public final class LocalDataSetFileOperationsExcecutor implements IDataSetFileOp
} }
} }
public void syncDataSetWithDestination(File dataSet, File destination)
{
// rsync --delete is more effective then deletion of destination directory & copy all
String host = null; // local
Status result =
copier.copyToRemote(dataSet, destination, host, rsyncModuleNameOrNull,
rsyncPasswordFileOrNull);
if (result.isError())
{
throw new ExceptionWithStatus(result);
}
}
public void retrieveDataSetFromDestination(File dataSet, File destination) public void retrieveDataSetFromDestination(File dataSet, File destination)
{ {
try try
......
...@@ -128,6 +128,11 @@ public final class RemoteDataSetFileOperationsExecutor implements IDataSetFileOp ...@@ -128,6 +128,11 @@ public final class RemoteDataSetFileOperationsExecutor implements IDataSetFileOp
} }
} }
public void syncDataSetWithDestination(File dataSet, File destination)
{
copyDataSetToDestination(dataSet, destination);
}
public void retrieveDataSetFromDestination(File dataSet, File destination) public void retrieveDataSetFromDestination(File dataSet, File destination)
{ {
Status result = Status result =
......
...@@ -92,9 +92,16 @@ public class DataSetFileOperationsManager ...@@ -92,9 +92,16 @@ public class DataSetFileOperationsManager
if (hostOrNull == null) if (hostOrNull == null)
{ {
File sshExecutable = null; // don't use ssh locally
File rsyncExecutable = Copier.getExecutable(properties, RSYNC_EXEC);
IPathCopier copier = pathCopierFactory.create(rsyncExecutable, sshExecutable);
copier.check();
String rsyncModule = hostAwareFile.tryGetRsyncModule();
String rsyncPasswordFile = properties.getProperty(RSYNC_PASSWORD_FILE_KEY);
this.executor = this.executor =
new LocalDataSetFileOperationsExcecutor( new LocalDataSetFileOperationsExcecutor(
FileOperations.getMonitoredInstanceForCurrentThread()); FileOperations.getMonitoredInstanceForCurrentThread(), copier,
rsyncModule, rsyncPasswordFile);
} else } else
{ {
File sshExecutable = Copier.getExecutable(properties, SSH_EXEC); File sshExecutable = Copier.getExecutable(properties, SSH_EXEC);
...@@ -124,10 +131,18 @@ public class DataSetFileOperationsManager ...@@ -124,10 +131,18 @@ public class DataSetFileOperationsManager
try try
{ {
File destinationFolder = new File(destination, dataset.getDataSetLocation()); File destinationFolder = new File(destination, dataset.getDataSetLocation());
createFolderIfNotExists(destinationFolder.getParentFile()); if (createFolderIfNotExists(destinationFolder.getParentFile())
operationLog.info("Copy dataset '" + dataset.getDatasetCode() + "' from '" || destinationExists(destinationFolder).isSuccess() == false)
+ originalData.getPath() + "' to '" + destinationFolder.getParentFile()); {
executor.copyDataSetToDestination(originalData, destinationFolder.getParentFile()); operationLog.info("Copy dataset '" + dataset.getDatasetCode() + "' from '"
+ originalData.getPath() + "' to '" + destinationFolder.getParentFile());
executor.copyDataSetToDestination(originalData, destinationFolder.getParentFile());
} else
{
operationLog.info("Update dataset '" + dataset.getDatasetCode() + "' from '"
+ originalData.getPath() + "' to '" + destinationFolder.getParentFile());
executor.syncDataSetWithDestination(originalData, destinationFolder.getParentFile());
}
return Status.OK; return Status.OK;
} catch (ExceptionWithStatus ex) } catch (ExceptionWithStatus ex)
{ {
......
/*
* Copyright 2010 ETH Zuerich, CISD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.systemsx.cisd.openbis.dss.generic.server.plugins.standard;
import java.io.File;
import java.io.Serializable;
import ch.systemsx.cisd.common.filesystem.IPathCopier;
import ch.systemsx.cisd.common.filesystem.rsync.RsyncCopier;
/**
* {@link IPathCopierFactory} that is more reliable than {@link RsyncCopierFactory} when it comes to
* deciding which files to transfer. {@link IPathCopier} created by {@link RsyncCopierFactory} uses
* "--append" flag causing files that are bigger in destination than in source to be ignored.
* {@link IPathCopier} created by this factory is supposed to ignore only those files that have same
* sizes and modification times.
*/
public final class RsyncArchiveCopierFactory implements Serializable, IPathCopierFactory
{
private static final long serialVersionUID = 1L;
public IPathCopier create(File rsyncExecutable, File sshExecutableOrNull)
{
// TODO 2011-04-05, Piotr Buczek: should we use --no-whole-file?
return new RsyncCopier(rsyncExecutable, sshExecutableOrNull, "--archive", "--delete",
"--inplace");
}
}
\ No newline at end of file
...@@ -52,12 +52,12 @@ public class RsyncArchiver extends AbstractArchiverProcessingPlugin ...@@ -52,12 +52,12 @@ public class RsyncArchiver extends AbstractArchiverProcessingPlugin
public RsyncArchiver(Properties properties, File storeRoot) public RsyncArchiver(Properties properties, File storeRoot)
{ {
this(properties, storeRoot, new RsyncCopierFactory(), new SshCommandExecutorFactory()); this(properties, storeRoot, new RsyncArchiveCopierFactory(),
new SshCommandExecutorFactory());
} }
@Private @Private
RsyncArchiver(Properties properties, File storeRoot, RsyncArchiver(Properties properties, File storeRoot, IPathCopierFactory pathCopierFactory,
IPathCopierFactory pathCopierFactory,
ISshCommandExecutorFactory sshCommandExecutorFactory) ISshCommandExecutorFactory sshCommandExecutorFactory)
{ {
super(properties, storeRoot, null, null); super(properties, storeRoot, null, null);
...@@ -131,7 +131,8 @@ public class RsyncArchiver extends AbstractArchiverProcessingPlugin ...@@ -131,7 +131,8 @@ public class RsyncArchiver extends AbstractArchiverProcessingPlugin
if (fileOperationsManager == null) if (fileOperationsManager == null)
{ {
this.fileOperationsManager = this.fileOperationsManager =
new DataSetFileOperationsManager(properties, pathCopierFactory, sshCommandExecutorFactory); new DataSetFileOperationsManager(properties, pathCopierFactory,
sshCommandExecutorFactory);
} }
} }
......
...@@ -249,6 +249,18 @@ public class DataSetFileOperationsManagerTest extends AbstractFileSystemTestCase ...@@ -249,6 +249,18 @@ public class DataSetFileOperationsManagerTest extends AbstractFileSystemTestCase
/* /*
* archive 2nd time (could happen on crash of DSS, but shouldn't hurt) * archive 2nd time (could happen on crash of DSS, but shouldn't hurt)
*/ */
context.checking(new Expectations()
{
{
/*
* use rsync
*/
one(copier).copyToRemote(ds1Location,
ds1ArchivedLocationFile.getParentFile().getAbsoluteFile(), null, null,
null);
will(returnValue(Status.OK));
}
});
status = dataSetCopier.copyToDestination(ds1Location, ds1); status = dataSetCopier.copyToDestination(ds1Location, ds1);
assertSuccessful(status); assertSuccessful(status);
// check that data set is now in archive // check that data set is now in archive
...@@ -482,6 +494,9 @@ public class DataSetFileOperationsManagerTest extends AbstractFileSystemTestCase ...@@ -482,6 +494,9 @@ public class DataSetFileOperationsManagerTest extends AbstractFileSystemTestCase
{ {
final Properties properties = new Properties(); final Properties properties = new Properties();
properties.setProperty(DataSetFileOperationsManager.DESTINATION_KEY, destination.getPath()); properties.setProperty(DataSetFileOperationsManager.DESTINATION_KEY, destination.getPath());
properties.setProperty(DataSetFileOperationsManager.RSYNC_EXEC + "-executable",
rsyncExec.getPath());
prepareLocalCreateAndCheckCopier();
return properties; return properties;
} }
...@@ -521,6 +536,8 @@ public class DataSetFileOperationsManagerTest extends AbstractFileSystemTestCase ...@@ -521,6 +536,8 @@ public class DataSetFileOperationsManagerTest extends AbstractFileSystemTestCase
one(sshExecutor).exists(ds2ArchivedLocationFile.getParentFile().getPath(), one(sshExecutor).exists(ds2ArchivedLocationFile.getParentFile().getPath(),
SSH_TIMEOUT_MILLIS); SSH_TIMEOUT_MILLIS);
will(returnValue(BooleanStatus.createTrue())); will(returnValue(BooleanStatus.createTrue()));
one(sshExecutor).exists(ds2ArchivedLocationFile.getPath(), SSH_TIMEOUT_MILLIS);
will(returnValue(BooleanStatus.createTrue()));
one(copier).copyToRemote(ds2Location, ds2ArchivedLocationFile.getParentFile(), one(copier).copyToRemote(ds2Location, ds2ArchivedLocationFile.getParentFile(),
HOST, null, null); HOST, null, null);
...@@ -851,6 +868,19 @@ public class DataSetFileOperationsManagerTest extends AbstractFileSystemTestCase ...@@ -851,6 +868,19 @@ public class DataSetFileOperationsManagerTest extends AbstractFileSystemTestCase
// context.assertIsSatisfied(); // context.assertIsSatisfied();
// } // }
private void prepareLocalCreateAndCheckCopier()
{
context.checking(new Expectations()
{
{
one(copierFactory).create(rsyncExec, null);
will(returnValue(copier));
one(copier).check();
}
});
}
private void prepareRemoteCreateAndCheckCopier(final String hostOrNull, private void prepareRemoteCreateAndCheckCopier(final String hostOrNull,
final String rsyncModuleOrNull, final boolean checkingResult) final String rsyncModuleOrNull, final boolean checkingResult)
{ {
......
...@@ -99,9 +99,17 @@ class LocalAndRemoteCopier implements Serializable, IPostRegistrationDatasetHand ...@@ -99,9 +99,17 @@ class LocalAndRemoteCopier implements Serializable, IPostRegistrationDatasetHand
destination = hostAwareFile.getFile(); destination = hostAwareFile.getFile();
if (hostOrNull == null) if (hostOrNull == null)
{ {
File sshExecutable = null; // don't use ssh locally
File rsyncExecutable = Copier.getExecutable(properties, DataSetCopier.RSYNC_EXEC);
IPathCopier copier = pathCopierFactory.create(rsyncExecutable, sshExecutable);
copier.check();
String rsyncModule = hostAwareFile.tryGetRsyncModule();
String rsyncPasswordFile =
properties.getProperty(DataSetCopier.RSYNC_PASSWORD_FILE_KEY);
executor = executor =
new LocalDataSetFileOperationsExcecutor( new LocalDataSetFileOperationsExcecutor(
FileOperations.getMonitoredInstanceForCurrentThread()); FileOperations.getMonitoredInstanceForCurrentThread(), copier,
rsyncModule, rsyncPasswordFile);
} else } else
{ {
File sshExecutable = Copier.getExecutable(properties, DataSetCopier.SSH_EXEC); File sshExecutable = Copier.getExecutable(properties, DataSetCopier.SSH_EXEC);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment