diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/IDataSetMover.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/IDataSetMover.java new file mode 100644 index 0000000000000000000000000000000000000000..50118fc9daeaae0fd2f341bbddb667e09f442af0 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/IDataSetMover.java @@ -0,0 +1,35 @@ +/* + * Copyright 2011 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.etlserver.plugins; + +import java.io.File; + + +/** + * Strategy of moving a data set to another share. + * + * @author Franz-Josef Elmer + */ +public interface IDataSetMover +{ + /** + * Moves the specified data set to the specified share. The data set is folder in the store + * its name is the data set code. The destination folder is <code>share</code>. Its name is + * the share id. Share id and size will be updated on openBIS. + */ + public void moveDataSetToAnotherShare(File dataSetDirInStore, File share); +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/ISegmentedStoreBalancer.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/ISegmentedStoreBalancer.java index 731c6a61c473f1a6446199ff90cb12dd9ecbf03a..faaef689d9c58c3e23520e50005564da5ca65e4b 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/ISegmentedStoreBalancer.java +++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/ISegmentedStoreBalancer.java @@ -23,12 +23,18 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService; import ch.systemsx.cisd.openbis.dss.generic.shared.utils.Share; /** - * + * Strategy of balancing data sets between shares in segmented store. * * @author Franz-Josef Elmer */ public interface ISegmentedStoreBalancer { + /** + * Balances sizes of specified shares by moving data sets between them using specified data set mover. + * + * @param service + * + */ public void balanceStore(List<Share> shares, IEncapsulatedOpenBISService service, - ISimpleLogger logger); + IDataSetMover dataSetMover, ISimpleLogger logger); } diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/NonBalancer.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/NonBalancer.java index f983dfad13d47de0931571d62d5f1b30c067550f..fd16a31c2271bc4fa9548f63e0a10d408edb1b1b 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/NonBalancer.java +++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/NonBalancer.java @@ -39,21 +39,26 @@ public class NonBalancer implements ISegmentedStoreBalancer private static final int N = 3; public void balanceStore(List<Share> shares, IEncapsulatedOpenBISService service, - ISimpleLogger logger) + IDataSetMover dataSetMover, ISimpleLogger logger) { logger.log(INFO, "Data Store Shares:"); for (Share share : shares) { List<SimpleDataSetInformationDTO> dataSets = share.getDataSetsOrderedBySize(); - logger.log(INFO, " Share " + share.getShareId() + " (free space: " - + FileUtils.byteCountToDisplaySize(share.calculateFreeSpace()) + ") has " - + dataSets.size() + " data sets occupying " - + FileUtilities.byteCountToDisplaySize(share.getTotalSizeOfDataSets()) + "."); + logger.log( + INFO, + " Share " + share.getShareId() + " (free space: " + + FileUtils.byteCountToDisplaySize(share.calculateFreeSpace()) + + ") has " + dataSets.size() + " data sets occupying " + + FileUtilities.byteCountToDisplaySize(share.getTotalSizeOfDataSets()) + + "."); for (int i = 0, n = Math.min(N, dataSets.size()); i < n; i++) { SimpleDataSetInformationDTO dataSet = dataSets.get(i); - logger.log(INFO, " " + dataSet.getDataSetCode() + " " - + FileUtilities.byteCountToDisplaySize(dataSet.getDataSetSize())); + logger.log( + INFO, + " " + dataSet.getDataSetCode() + " " + + FileUtilities.byteCountToDisplaySize(dataSet.getDataSetSize())); } if (dataSets.size() > N) { diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/SegmentedStoreBalancingTask.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/SegmentedStoreBalancingTask.java index 9353a079e24f8e3f27ff9d68dc1a48fd23ea01ee..1d496ba5c2723f417e42e9d5a663bffc3e3a822f 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/SegmentedStoreBalancingTask.java +++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/SegmentedStoreBalancingTask.java @@ -22,6 +22,7 @@ import java.util.Properties; import org.apache.log4j.Logger; +import ch.rinn.restrictions.Private; import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel; import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException; import ch.systemsx.cisd.common.filesystem.IFreeSpaceProvider; @@ -42,38 +43,48 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.utils.SegmentedStoreUtils; import ch.systemsx.cisd.openbis.dss.generic.shared.utils.Share; /** - * + * Maintenance task which tries to balance a segmented store. * * @author Franz-Josef Elmer */ public class SegmentedStoreBalancingTask implements IMaintenanceTask { - private static final String BALANCER_SECTION_NAME = "balancer"; - private static final String CLASS_PROPERTY_NAME = "class"; + @Private static final String BALANCER_SECTION_NAME = "balancer"; + @Private static final String CLASS_PROPERTY_NAME = "class"; private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, SegmentedStoreBalancingTask.class); private final IEncapsulatedOpenBISService service; + private final IDataSetMover dataSetMover; private final IFreeSpaceProvider freeSpaceProvider; private final ISimpleLogger operationLogger; private File storeRoot; private String dataStoreCode; - private ISegmentedStoreBalancer balancer; + @Private ISegmentedStoreBalancer balancer; public SegmentedStoreBalancingTask() { this(ServiceProvider.getOpenBISService(), new SimpleFreeSpaceProvider(), - new Log4jSimpleLogger(operationLog)); + new IDataSetMover() + { + + public void moveDataSetToAnotherShare(File dataSetDirInStore, File share) + { + SegmentedStoreUtils.moveDataSetToAnotherShare(dataSetDirInStore, share, + ServiceProvider.getOpenBISService()); + } + }, new Log4jSimpleLogger(operationLog)); } - SegmentedStoreBalancingTask(IEncapsulatedOpenBISService service, - IFreeSpaceProvider freeSpaceProvider, ISimpleLogger logger) + SegmentedStoreBalancingTask(final IEncapsulatedOpenBISService service, + IFreeSpaceProvider freeSpaceProvider, IDataSetMover dataSetMover, ISimpleLogger logger) { LogInitializer.init(); this.freeSpaceProvider = freeSpaceProvider; this.service = service; + this.dataSetMover = dataSetMover; operationLogger = logger; } @@ -126,7 +137,7 @@ public class SegmentedStoreBalancingTask implements IMaintenanceTask List<Share> shares = SegmentedStoreUtils.getDataSetsPerShare(storeRoot, dataStoreCode, freeSpaceProvider, service, operationLogger); - balancer.balanceStore(shares, service, operationLogger); + balancer.balanceStore(shares, service, dataSetMover, operationLogger); operationLog.info("Segmented store balancing finished."); } diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/SimpleBalancer.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/SimpleBalancer.java index 0cae99a78d5a311904ff4127939f041bcac30665..9141324724868bfb6d8171b9863751b588546711 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/SimpleBalancer.java +++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/SimpleBalancer.java @@ -26,22 +26,26 @@ import java.util.List; import java.util.Properties; import org.apache.commons.io.FileUtils; -import org.apache.commons.lang.time.StopWatch; +import ch.rinn.restrictions.Private; import ch.systemsx.cisd.common.logging.ISimpleLogger; +import ch.systemsx.cisd.common.utilities.ITimeProvider; import ch.systemsx.cisd.common.utilities.PropertyUtils; +import ch.systemsx.cisd.common.utilities.SystemTimeProvider; import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService; -import ch.systemsx.cisd.openbis.dss.generic.shared.utils.SegmentedStoreUtils; import ch.systemsx.cisd.openbis.dss.generic.shared.utils.Share; import ch.systemsx.cisd.openbis.generic.shared.dto.SimpleDataSetInformationDTO; /** + * Simple balancer which moves data sets from full shares to the share with initial most free space + * until it is full. * - * * @author Franz-Josef Elmer */ public class SimpleBalancer implements ISegmentedStoreBalancer { + @Private static final String MINIMUM_FREE_SPACE_KEY = "minimum-free-space-in-MB"; + private static final class ShareState { private final Share share; @@ -88,16 +92,24 @@ public class SimpleBalancer implements ISegmentedStoreBalancer } private final long minimumFreeSpace; + private final ITimeProvider timeProvider; public SimpleBalancer(Properties properties) { + this(properties, SystemTimeProvider.SYSTEM_TIME_PROVIDER); + } + + SimpleBalancer(Properties properties, ITimeProvider timeProvider) + { + this.timeProvider = timeProvider; minimumFreeSpace = - FileUtils.ONE_MB - * PropertyUtils.getLong(properties, "minimum-free-space-in-MB", 1024); + FileUtils.ONE_MB + * PropertyUtils.getLong(properties, MINIMUM_FREE_SPACE_KEY, 1024); + } public void balanceStore(List<Share> shares, IEncapsulatedOpenBISService service, - ISimpleLogger logger) + IDataSetMover dataSetMover, ISimpleLogger logger) { List<ShareState> shareStates = getSortedShares(shares); ShareState shareWithMostFree = shareStates.get(shareStates.size() - 1); @@ -119,7 +131,7 @@ public class SimpleBalancer implements ISegmentedStoreBalancer long dataSetSize = dataSets.get(i).getDataSetSize(); if (shareWithMostFree.getFreeSpace() - dataSetSize > minimumFreeSpace) { - copy(fullShare, 0, shareWithMostFree, service, logger); + copy(fullShare, 0, shareWithMostFree, dataSetMover, logger); } } } @@ -137,11 +149,11 @@ public class SimpleBalancer implements ISegmentedStoreBalancer } freeSpaceAboveMinimum += dataSets.get(i).getDataSetSize(); } - return -1; + return freeSpaceAboveMinimum > 0 ? dataSets.size() : -1; } private void copy(ShareState from, int dataSetIndex, ShareState to, - IEncapsulatedOpenBISService service, ISimpleLogger logger) + IDataSetMover mover, ISimpleLogger logger) { Share fromShare = from.getShare(); Share toShare = to.getShare(); @@ -149,17 +161,15 @@ public class SimpleBalancer implements ISegmentedStoreBalancer fromShare.getDataSetsOrderedBySize().get(dataSetIndex); File dataSetDirInStore = new File(fromShare.getShare(), dataSet.getDataSetLocation()); String commonMessage = - "data set " + dataSet.getDataSetCode() + " from share " + fromShare.getShareId() + "Moving data set " + dataSet.getDataSetCode() + " from share " + fromShare.getShareId() + " to share " + toShare.getShareId(); - logger.log(INFO, "Move " + commonMessage + " ..."); - StopWatch stopWatch = new StopWatch(); - stopWatch.start(); - SegmentedStoreUtils.moveDataSetToAnotherShare(dataSetDirInStore, toShare.getShare(), - service); + logger.log(INFO, commonMessage + " ..."); + long t0 = timeProvider.getTimeInMilliseconds(); + mover.moveDataSetToAnotherShare(dataSetDirInStore, toShare.getShare()); from.removeDataSet(dataSetIndex); to.addDataSet(dataSet); - stopWatch.stop(); - logger.log(INFO, "Moving " + commonMessage + " took " + stopWatch.toString()); + logger.log(INFO, commonMessage + " took " + + ((timeProvider.getTimeInMilliseconds() - t0 + 500) / 1000) + " seconds."); } private List<ShareState> getFullShares(List<ShareState> shareStates) diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/Share.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/Share.java index becaf8bffab3c4b33e2d8a6ba9976ca72bdf1b33..7ced2d3554982ab96f89064a10794da7d2a6c703 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/Share.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/Share.java @@ -59,7 +59,7 @@ public final class Share private long size; - Share(File share, IFreeSpaceProvider freeSpaceProvider) + public Share(File share, IFreeSpaceProvider freeSpaceProvider) { this.share = share; this.freeSpaceProvider = freeSpaceProvider; @@ -96,7 +96,7 @@ public final class Share } } - void addDataSet(SimpleDataSetInformationDTO dataSet) + public void addDataSet(SimpleDataSetInformationDTO dataSet) { dataSets.add(dataSet); size += dataSet.getDataSetSize(); diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/plugins/SegmentedStoreBalancingTaskTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/plugins/SegmentedStoreBalancingTaskTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5416e4dd82342eabd251394950cf9735d2490666 --- /dev/null +++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/plugins/SegmentedStoreBalancingTaskTest.java @@ -0,0 +1,155 @@ +/* + * Copyright 2011 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.etlserver.plugins; + +import static ch.systemsx.cisd.etlserver.plugins.SegmentedStoreBalancingTask.BALANCER_SECTION_NAME; +import static ch.systemsx.cisd.etlserver.plugins.SegmentedStoreBalancingTask.CLASS_PROPERTY_NAME; + +import java.io.File; +import java.util.Arrays; +import java.util.List; +import java.util.Properties; + +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import ch.rinn.restrictions.Friend; +import ch.systemsx.cisd.base.tests.AbstractFileSystemTestCase; +import ch.systemsx.cisd.common.filesystem.IFreeSpaceProvider; +import ch.systemsx.cisd.common.logging.ISimpleLogger; +import ch.systemsx.cisd.common.logging.LogLevel; +import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService; +import ch.systemsx.cisd.openbis.dss.generic.shared.utils.DssPropertyParametersUtil; +import ch.systemsx.cisd.openbis.dss.generic.shared.utils.Share; +import ch.systemsx.cisd.openbis.generic.shared.dto.SimpleDataSetInformationDTO; + +/** + * + * + * @author Franz-Josef Elmer + */ +@Friend(toClasses=SegmentedStoreBalancingTask.class) +public class SegmentedStoreBalancingTaskTest extends AbstractFileSystemTestCase +{ + public static final class Balancer implements ISegmentedStoreBalancer + { + private final Properties properties; + private List<Share> shares; + private IEncapsulatedOpenBISService service; + private IDataSetMover dataSetMover; + private ISimpleLogger logger; + + public Balancer(Properties properties) + { + this.properties = properties; + } + + public void balanceStore(List<Share> list, IEncapsulatedOpenBISService openBisService, + IDataSetMover mover, ISimpleLogger simpleLogger) + { + shares = list; + service = openBisService; + dataSetMover = mover; + logger = simpleLogger; + } + } + + private Mockery context; + private IEncapsulatedOpenBISService service; + private IFreeSpaceProvider spaceProvider; + private IDataSetMover dataSetMover; + private ISimpleLogger logger; + private SegmentedStoreBalancingTask balancerTask; + private File storeRoot; + + @BeforeMethod + public void beforeMethod() + { + context = new Mockery(); + service = context.mock(IEncapsulatedOpenBISService.class); + spaceProvider = context.mock(IFreeSpaceProvider.class); + dataSetMover = context.mock(IDataSetMover.class); + logger = context.mock(ISimpleLogger.class); + balancerTask = new SegmentedStoreBalancingTask(service, spaceProvider, dataSetMover, logger); + storeRoot = new File(workingDirectory, "store"); + storeRoot.mkdirs(); + } + + @AfterMethod + public void afterMethod() + { + // The following line of code should also be called at the end of each test method. + // Otherwise one do not known which test failed. + context.assertIsSatisfied(); + } + + @Test + public void testExecute() + { + Properties properties = new Properties(); + properties.setProperty(DssPropertyParametersUtil.DSS_CODE_KEY, "data-store-1"); + properties.setProperty(DssPropertyParametersUtil.STOREROOT_DIR_KEY, storeRoot.getPath()); + properties.setProperty(BALANCER_SECTION_NAME + "." + CLASS_PROPERTY_NAME, + SegmentedStoreBalancingTaskTest.Balancer.class.getName()); + balancerTask.setUp("mock-balancer", properties); + context.checking(new Expectations() + { + { + one(service).listDataSets(); + SimpleDataSetInformationDTO ds = new SimpleDataSetInformationDTO(); + ds.setDataStoreCode("other data store"); + will(returnValue(Arrays.asList(ds))); + } + }); + + balancerTask.execute(); + + Balancer balancer = (SegmentedStoreBalancingTaskTest.Balancer) balancerTask.balancer; + assertEquals("{class=" + balancer.getClass().getName() + "}", + balancer.properties.toString()); + assertEquals("[]", balancer.shares.toString()); + assertSame(service, balancer.service); + assertSame(dataSetMover, balancer.dataSetMover); + assertSame(logger, balancer.logger); + context.assertIsSatisfied(); + } + + @Test + public void testDefaultBalancer() + { + Properties properties = new Properties(); + properties.setProperty(DssPropertyParametersUtil.DSS_CODE_KEY, "data-store-1"); + properties.setProperty(DssPropertyParametersUtil.STOREROOT_DIR_KEY, storeRoot.getPath()); + balancerTask.setUp("mock-balancer", properties); + context.checking(new Expectations() + { + { + one(service).listDataSets(); + will(returnValue(Arrays.asList())); + + one(logger).log(LogLevel.INFO, "Data Store Shares:"); + } + }); + + balancerTask.execute(); + + context.assertIsSatisfied(); + } +} diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/plugins/SimpleBalancerTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/plugins/SimpleBalancerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9d782b231d21726d5056b73ccc327f84603bd007 --- /dev/null +++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/plugins/SimpleBalancerTest.java @@ -0,0 +1,186 @@ +/* + * Copyright 2011 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.etlserver.plugins; + + +import static org.apache.commons.io.FileUtils.ONE_MB; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.Set; + +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import ch.rinn.restrictions.Friend; +import ch.systemsx.cisd.base.tests.AbstractFileSystemTestCase; +import ch.systemsx.cisd.common.filesystem.HostAwareFile; +import ch.systemsx.cisd.common.filesystem.IFreeSpaceProvider; +import ch.systemsx.cisd.common.logging.ISimpleLogger; +import ch.systemsx.cisd.common.logging.LogLevel; +import ch.systemsx.cisd.common.utilities.ITimeProvider; +import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService; +import ch.systemsx.cisd.openbis.dss.generic.shared.utils.Share; +import ch.systemsx.cisd.openbis.generic.shared.dto.SimpleDataSetInformationDTO; + +/** + * + * + * @author Franz-Josef Elmer + */ +@Friend(toClasses=SimpleBalancer.class) +public class SimpleBalancerTest extends AbstractFileSystemTestCase +{ + private static final String STORE_PATH = "01/02/03/"; + + private static final class MockSpaceProvider implements IFreeSpaceProvider + { + private final Map<File, List<Long>> freeSpace = new HashMap<File, List<Long>>(); + + void addFreeSpaceExpectationFor(Share share, long valueInKb) + { + List<Long> list = freeSpace.get(share.getShare()); + if (list == null) + { + list = new ArrayList<Long>(); + freeSpace.put(share.getShare(), list); + } + list.add(valueInKb); + } + + @SuppressWarnings("null") + public long freeSpaceKb(HostAwareFile path) throws IOException + { + File file = path.getFile(); + List<Long> list = freeSpace.get(file); + assertTrue("Unknown file " + file, list != null); + assertFalse("Unexpected invocation for file " + file, list.isEmpty()); + return list.remove(0); + } + + void assertIsSatiesfied() + { + Set<Entry<File, List<Long>>> entrySet = freeSpace.entrySet(); + for (Entry<File, List<Long>> entry : entrySet) + { + assertEquals("Unfullfilled free space assertions for " + entry.getKey().getPath(), + 0, entry.getValue().size()); + } + } + } + + private MockSpaceProvider spaceProvider; + private Mockery context; + private IEncapsulatedOpenBISService service; + private IDataSetMover dataSetMover; + private ISimpleLogger logger; + private SimpleBalancer balancer; + private File store; + + @BeforeMethod + public void beforeMethod() + { + spaceProvider = new MockSpaceProvider(); + context = new Mockery(); + service = context.mock(IEncapsulatedOpenBISService.class); + dataSetMover = context.mock(IDataSetMover.class); + logger = context.mock(ISimpleLogger.class); + final ITimeProvider timeProvider = context.mock(ITimeProvider.class); + context.checking(new Expectations() + { + { + allowing(timeProvider).getTimeInMilliseconds(); + will(returnValue(0L)); + } + }); + Properties properties = new Properties(); + properties.setProperty(SimpleBalancer.MINIMUM_FREE_SPACE_KEY, "2"); + balancer = new SimpleBalancer(properties, timeProvider); + store = new File(workingDirectory, "store"); + } + + @AfterMethod + public void afterMethod() + { + // The following line of code should also be called at the end of each test method. + // Otherwise one do not known which test failed. + context.assertIsSatisfied(); + } + + @Test + public void test() + { + final Share share1 = new Share(new File(store, "1"), spaceProvider); + share1.addDataSet(dataSet("ds1", 2000)); + share1.addDataSet(dataSet("ds2", ONE_MB)); + share1.addDataSet(dataSet("ds3", ONE_MB + 100)); + spaceProvider.addFreeSpaceExpectationFor(share1, 100l); + spaceProvider.addFreeSpaceExpectationFor(share1, 1100l); + spaceProvider.addFreeSpaceExpectationFor(share1, 2100l); + final Share share2 = new Share(new File(store, "2"), spaceProvider); + share2.addDataSet(dataSet("ds4", 2 * ONE_MB)); + spaceProvider.addFreeSpaceExpectationFor(share2, 500l); + final Share share3 = new Share(new File(store, "3"), spaceProvider); + spaceProvider.addFreeSpaceExpectationFor(share3, 4 * 1024l); + spaceProvider.addFreeSpaceExpectationFor(share3, 3 * 1024l); + spaceProvider.addFreeSpaceExpectationFor(share3, 1024l); + final Share share4 = new Share(new File(store, "4"), spaceProvider); + spaceProvider.addFreeSpaceExpectationFor(share4, 3 * 1024l); + context.checking(new Expectations() + { + { + one(logger).log(LogLevel.INFO, "Moving data set ds3 from share 1 to share 3 ..."); + one(dataSetMover).moveDataSetToAnotherShare(new File(share1.getShare(), STORE_PATH + "ds3"), share3.getShare()); + one(logger).log(LogLevel.INFO, "Moving data set ds3 from share 1 to share 3 took 0 seconds."); + + one(logger).log(LogLevel.INFO, "Moving data set ds2 from share 1 to share 3 ..."); + one(dataSetMover).moveDataSetToAnotherShare(new File(share1.getShare(), STORE_PATH + "ds2"), share3.getShare()); + one(logger).log(LogLevel.INFO, "Moving data set ds2 from share 1 to share 3 took 0 seconds."); + } + }); + + balancer.balanceStore(Arrays.asList(share1, share2, share3, share4), service, dataSetMover, logger); + + assertEquals(1, share1.getDataSetsOrderedBySize().size()); + assertEquals(1, share2.getDataSetsOrderedBySize().size()); + assertEquals("ds3", share3.getDataSetsOrderedBySize().get(0).getDataSetCode()); + assertEquals("ds2", share3.getDataSetsOrderedBySize().get(1).getDataSetCode()); + assertEquals(2, share3.getDataSetsOrderedBySize().size()); + assertEquals(0, share4.getDataSetsOrderedBySize().size()); + spaceProvider.assertIsSatiesfied(); + context.assertIsSatisfied(); + } + + private SimpleDataSetInformationDTO dataSet(String code, long size) + { + SimpleDataSetInformationDTO dataSet = new SimpleDataSetInformationDTO(); + dataSet.setDataSetCode(code); + dataSet.setDataSetLocation(STORE_PATH + code); + dataSet.setDataSetSize(size); + return dataSet; + } +}