From 287afffe80c098bc6ef7c1cc411b0e21e7b97421 Mon Sep 17 00:00:00 2001 From: buczekp <buczekp> Date: Tue, 5 Apr 2011 09:27:19 +0000 Subject: [PATCH] [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 --- .../IDataSetFileOperationsExecutor.java | 3 ++ .../LocalDataSetFileOperationsExcecutor.java | 27 +++++++++++- .../RemoteDataSetFileOperationsExecutor.java | 5 +++ .../DataSetFileOperationsManager.java | 25 ++++++++--- .../standard/RsyncArchiveCopierFactory.java | 42 +++++++++++++++++++ .../plugins/standard/RsyncArchiver.java | 9 ++-- .../DataSetFileOperationsManagerTest.java | 30 +++++++++++++ .../server/plugins/LocalAndRemoteCopier.java | 10 ++++- 8 files changed, 139 insertions(+), 12 deletions(-) create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/RsyncArchiveCopierFactory.java diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IDataSetFileOperationsExecutor.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IDataSetFileOperationsExecutor.java index b206ffc123c..1a8a89b2060 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IDataSetFileOperationsExecutor.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IDataSetFileOperationsExecutor.java @@ -35,6 +35,9 @@ public interface IDataSetFileOperationsExecutor 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 renameTo(File newFile, File oldFile); diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/LocalDataSetFileOperationsExcecutor.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/LocalDataSetFileOperationsExcecutor.java index e8b7082926f..223a13b93cd 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/LocalDataSetFileOperationsExcecutor.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/LocalDataSetFileOperationsExcecutor.java @@ -32,6 +32,7 @@ import ch.systemsx.cisd.common.exceptions.Status; import ch.systemsx.cisd.common.filesystem.BooleanStatus; import ch.systemsx.cisd.common.filesystem.FileUtilities; 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.LogFactory; @@ -40,11 +41,21 @@ public final class LocalDataSetFileOperationsExcecutor implements IDataSetFileOp final static Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, LocalDataSetFileOperationsExcecutor.class); + private final IPathCopier copier; + + private final String rsyncModuleNameOrNull; + + private final String rsyncPasswordFileOrNull; + private final IFileOperations fileOperations; - public LocalDataSetFileOperationsExcecutor(IFileOperations fileOperations) + public LocalDataSetFileOperationsExcecutor(IFileOperations fileOperations, IPathCopier copier, + String rsyncModuleNameOrNull, String rsyncPasswordFileOrNull) { this.fileOperations = fileOperations; + this.copier = copier; + this.rsyncModuleNameOrNull = rsyncModuleNameOrNull; + this.rsyncPasswordFileOrNull = rsyncPasswordFileOrNull; } public BooleanStatus checkSame(File dataSet, File destination) @@ -63,7 +74,6 @@ public final class LocalDataSetFileOperationsExcecutor implements IDataSetFileOp if (destination.isDirectory()) { FileFilter nullFilter = null; - // TODO ignore symlinks? List<File> storeFiles = FileUtilities.listFiles(dataSet, nullFilter, true); List<File> destFiles = FileUtilities.listFiles(destination, nullFilter, true); @@ -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) { try diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/RemoteDataSetFileOperationsExecutor.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/RemoteDataSetFileOperationsExecutor.java index c3932d0b4f9..72180f09bce 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/RemoteDataSetFileOperationsExecutor.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/RemoteDataSetFileOperationsExecutor.java @@ -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) { Status result = diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetFileOperationsManager.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetFileOperationsManager.java index 8745915c786..770dc5cedc4 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetFileOperationsManager.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetFileOperationsManager.java @@ -92,9 +92,16 @@ public class DataSetFileOperationsManager 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 = new LocalDataSetFileOperationsExcecutor( - FileOperations.getMonitoredInstanceForCurrentThread()); + FileOperations.getMonitoredInstanceForCurrentThread(), copier, + rsyncModule, rsyncPasswordFile); } else { File sshExecutable = Copier.getExecutable(properties, SSH_EXEC); @@ -124,10 +131,18 @@ public class DataSetFileOperationsManager try { File destinationFolder = new File(destination, dataset.getDataSetLocation()); - createFolderIfNotExists(destinationFolder.getParentFile()); - operationLog.info("Copy dataset '" + dataset.getDatasetCode() + "' from '" - + originalData.getPath() + "' to '" + destinationFolder.getParentFile()); - executor.copyDataSetToDestination(originalData, destinationFolder.getParentFile()); + if (createFolderIfNotExists(destinationFolder.getParentFile()) + || destinationExists(destinationFolder).isSuccess() == false) + { + 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; } catch (ExceptionWithStatus ex) { diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/RsyncArchiveCopierFactory.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/RsyncArchiveCopierFactory.java new file mode 100644 index 00000000000..a0898ecf996 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/RsyncArchiveCopierFactory.java @@ -0,0 +1,42 @@ +/* + * 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 diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/RsyncArchiver.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/RsyncArchiver.java index 770df7c92be..934f3470106 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/RsyncArchiver.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/RsyncArchiver.java @@ -52,12 +52,12 @@ public class RsyncArchiver extends AbstractArchiverProcessingPlugin public RsyncArchiver(Properties properties, File storeRoot) { - this(properties, storeRoot, new RsyncCopierFactory(), new SshCommandExecutorFactory()); + this(properties, storeRoot, new RsyncArchiveCopierFactory(), + new SshCommandExecutorFactory()); } @Private - RsyncArchiver(Properties properties, File storeRoot, - IPathCopierFactory pathCopierFactory, + RsyncArchiver(Properties properties, File storeRoot, IPathCopierFactory pathCopierFactory, ISshCommandExecutorFactory sshCommandExecutorFactory) { super(properties, storeRoot, null, null); @@ -131,7 +131,8 @@ public class RsyncArchiver extends AbstractArchiverProcessingPlugin if (fileOperationsManager == null) { this.fileOperationsManager = - new DataSetFileOperationsManager(properties, pathCopierFactory, sshCommandExecutorFactory); + new DataSetFileOperationsManager(properties, pathCopierFactory, + sshCommandExecutorFactory); } } diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetFileOperationsManagerTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetFileOperationsManagerTest.java index b4cdae4e5d0..112a24a9665 100644 --- a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetFileOperationsManagerTest.java +++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetFileOperationsManagerTest.java @@ -249,6 +249,18 @@ public class DataSetFileOperationsManagerTest extends AbstractFileSystemTestCase /* * 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); assertSuccessful(status); // check that data set is now in archive @@ -482,6 +494,9 @@ public class DataSetFileOperationsManagerTest extends AbstractFileSystemTestCase { final Properties properties = new Properties(); properties.setProperty(DataSetFileOperationsManager.DESTINATION_KEY, destination.getPath()); + properties.setProperty(DataSetFileOperationsManager.RSYNC_EXEC + "-executable", + rsyncExec.getPath()); + prepareLocalCreateAndCheckCopier(); return properties; } @@ -521,6 +536,8 @@ public class DataSetFileOperationsManagerTest extends AbstractFileSystemTestCase one(sshExecutor).exists(ds2ArchivedLocationFile.getParentFile().getPath(), SSH_TIMEOUT_MILLIS); will(returnValue(BooleanStatus.createTrue())); + one(sshExecutor).exists(ds2ArchivedLocationFile.getPath(), SSH_TIMEOUT_MILLIS); + will(returnValue(BooleanStatus.createTrue())); one(copier).copyToRemote(ds2Location, ds2ArchivedLocationFile.getParentFile(), HOST, null, null); @@ -851,6 +868,19 @@ public class DataSetFileOperationsManagerTest extends AbstractFileSystemTestCase // 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, final String rsyncModuleOrNull, final boolean checkingResult) { diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/dss/phosphonetx/server/plugins/LocalAndRemoteCopier.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/dss/phosphonetx/server/plugins/LocalAndRemoteCopier.java index bb1c99fecbc..4630408b2cf 100644 --- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/dss/phosphonetx/server/plugins/LocalAndRemoteCopier.java +++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/dss/phosphonetx/server/plugins/LocalAndRemoteCopier.java @@ -99,9 +99,17 @@ class LocalAndRemoteCopier implements Serializable, IPostRegistrationDatasetHand destination = hostAwareFile.getFile(); 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 = new LocalDataSetFileOperationsExcecutor( - FileOperations.getMonitoredInstanceForCurrentThread()); + FileOperations.getMonitoredInstanceForCurrentThread(), copier, + rsyncModule, rsyncPasswordFile); } else { File sshExecutable = Copier.getExecutable(properties, DataSetCopier.SSH_EXEC); -- GitLab