/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.activemq.usage; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.net.URI; import java.util.Random; import org.apache.activemq.broker.BrokerService; import org.apache.activemq.broker.TransportConnector; import org.apache.activemq.store.PersistenceAdapter; import org.apache.activemq.util.StoreUtil; import org.apache.activemq.util.Wait; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * This test is for AMQ-5393 and will check that schedulePeriodForDiskLimitCheck * properly schedules a task that will update disk limits if the amount of usable disk space drops * because another process uses up disk space. * */ public class PeriodicDiskUsageLimitTest { protected static final Logger LOG = LoggerFactory .getLogger(PeriodicDiskUsageLimitTest.class); @Rule public TemporaryFolder dataFileDir = new TemporaryFolder(new File("target")); File testfile; private BrokerService broker; private PersistenceAdapter adapter; private TempUsage tempUsage; private StoreUsage storeUsage; protected URI brokerConnectURI; @Before public void setUpBroker() throws Exception { broker = new BrokerService(); broker.setUseJmx(false); broker.setPersistent(true); testfile = dataFileDir.newFile(); broker.setDataDirectoryFile(dataFileDir.getRoot()); broker.setDeleteAllMessagesOnStartup(true); adapter = broker.getPersistenceAdapter(); TransportConnector connector = broker.addConnector("tcp://0.0.0.0:0"); connector.setName("tcp"); brokerConnectURI = broker.getConnectorByName("tcp").getConnectUri(); FileUtils.deleteQuietly(testfile); FileUtils.forceMkdir(adapter.getDirectory()); FileUtils.forceMkdir(broker.getTempDataStore().getDirectory()); final SystemUsage systemUsage = broker.getSystemUsage(); tempUsage = systemUsage.getTempUsage(); storeUsage = systemUsage.getStoreUsage(); } protected void startBroker() throws Exception { broker.start(); broker.waitUntilStarted(); } @After public void stopBroker() throws Exception { broker.stop(); broker.waitUntilStopped(); FileUtils.deleteQuietly(testfile); } /** * This test will show that if a file is written to take away free space, and * if the usage limit is now less than the store size plus remaining free space, then * the usage limits will adjust lower. */ @Test(timeout=90000) public void testDiskUsageAdjustLower() throws Exception { //set the limit to max space so that if a file is added to eat up free space then //the broker should adjust the usage limit..set time to 2 seconds for testing setLimitMaxSpace(); broker.setSchedulePeriodForDiskUsageCheck(2000); startBroker(); assertRampDown(); } /** * This test will show that if a file is written to take away free space, and * if the usage limit is now less than the store size plus remaining free space, then * the usage limits will adjust lower. Then test that size regrows when file is deleted. */ @Test(timeout=90000) public void testDiskUsageAdjustLowerAndHigherUsingPercent() throws Exception { //set the limit to max space so that if a file is added to eat up free space then //the broker should adjust the usage limit..add 5% above free space tempUsage.setPercentLimit(getFreePercentage(broker.getTempDataStore().getDirectory()) + 5); storeUsage.setPercentLimit(getFreePercentage(adapter.getDirectory()) + 5); //set threshold to 1 megabyte broker.setDiskUsageCheckRegrowThreshold(1024 * 1024); broker.setSchedulePeriodForDiskUsageCheck(2000); startBroker(); assertRampDown(); //get the limits and then delete the test file to free up space final long storeLimit = broker.getSystemUsage().getStoreUsage().getLimit(); final long tmpLimit = broker.getSystemUsage().getTempUsage().getLimit(); FileUtils.deleteQuietly(testfile); //regrow assertTrue("Store Usage should ramp up.", Wait.waitFor(new Wait.Condition() { @Override public boolean isSatisified() throws Exception { return broker.getSystemUsage().getStoreUsage().getLimit() > storeLimit; } }, 15000)); //regrow assertTrue("Temp Usage should ramp up.", Wait.waitFor(new Wait.Condition() { @Override public boolean isSatisified() throws Exception { return broker.getSystemUsage().getTempUsage().getLimit() > tmpLimit; } }, 15000)); } /** * This test shows that the usage limits will not change if the * schedulePeriodForDiskLimitCheck property is not set because no task will run */ @Test(timeout=60000) public void testDiskLimitCheckNotSet() throws Exception { setLimitMaxSpace(); startBroker(); long originalDisk = broker.getSystemUsage().getStoreUsage().getLimit(); long originalTmp = broker.getSystemUsage().getTempUsage().getLimit(); //write a 5 meg file to the file system writeTestFile(5 * 1024 * 1024); Thread.sleep(5000); //assert that the usage limits have not changed because a task should not have run assertEquals(originalDisk, broker.getSystemUsage().getStoreUsage().getLimit()); assertEquals(originalTmp, broker.getSystemUsage().getTempUsage().getLimit()); } /** * This test shows that the usage limits will not change if the * schedulePeriodForDiskLimitCheck property is not set because no task will run */ @Test(timeout=60000) public void testDiskLimitCheckNotSetUsingPercent() throws Exception { tempUsage.setPercentLimit(getFreePercentage(broker.getTempDataStore().getDirectory()) + 5); storeUsage.setPercentLimit(getFreePercentage(adapter.getDirectory()) + 5); startBroker(); long originalDisk = broker.getSystemUsage().getStoreUsage().getLimit(); long originalTmp = broker.getSystemUsage().getTempUsage().getLimit(); //write a 5 meg file to the file system writeTestFile(5 * 1024 * 1024); Thread.sleep(5000); //assert that the usage limits have not changed because a task should not have run assertEquals(originalDisk, broker.getSystemUsage().getStoreUsage().getLimit()); assertEquals(originalTmp, broker.getSystemUsage().getTempUsage().getLimit()); } /** * This test will show that if a file is written to take away free space, but * if the limit is greater than the store size and the remaining free space, then * the usage limits will not adjust. */ @Test(timeout=60000) public void testDiskUsageStaySame() throws Exception { //set a limit lower than max available space and set the period to 2 seconds tempUsage.setLimit(10000000); storeUsage.setLimit(100000000); broker.setSchedulePeriodForDiskUsageCheck(2000); startBroker(); long originalDisk = broker.getSystemUsage().getStoreUsage().getLimit(); long originalTmp = broker.getSystemUsage().getTempUsage().getLimit(); //write a 2 meg file to the file system writeTestFile(2 * 1024 * 1024); Thread.sleep(5000); //Assert that the usage limits have not changed because writing a 2 meg file //did not decrease the the free space below the already set limit assertEquals(originalDisk, broker.getSystemUsage().getStoreUsage().getLimit()); assertEquals(originalTmp, broker.getSystemUsage().getTempUsage().getLimit()); } /** * This test will show that if a file is written to take away free space, but * if the limit is greater than the store size and the remaining free space, then * the usage limits will not adjust. */ @Test(timeout=60000) public void testDiskUsageStaySameUsingPercent() throws Exception { //set a limit lower than max available space and set the period to 5 seconds //only run if at least 4 percent disk space free int tempFreePercent = getFreePercentage(broker.getTempDataStore().getDirectory()); int freePercent = getFreePercentage(adapter.getDirectory()); if (freePercent >= 4 && tempFreePercent >= 4) { tempUsage.setPercentLimit(freePercent / 2); storeUsage.setPercentLimit(tempFreePercent / 2); broker.setSchedulePeriodForDiskUsageCheck(2000); startBroker(); long originalDisk = broker.getSystemUsage().getStoreUsage().getLimit(); long originalTmp = broker.getSystemUsage().getTempUsage().getLimit(); //write a 5 meg file to the file system writeTestFile(5 * 1024 * 1024); Thread.sleep(5000); //Assert that the usage limits have not changed because writing a 2 meg file //did not decrease the the free space below the already set limit assertEquals(originalDisk, broker.getSystemUsage().getStoreUsage().getLimit()); assertEquals(originalTmp, broker.getSystemUsage().getTempUsage().getLimit()); } LOG.info("Not running b/c there is less that 4% disk space, freePrecent:" + freePercent); } protected void assertRampDown() throws Exception { //Try a couple of times because other processes could write/delete from disk assertTrue("Store Usage should ramp down", Wait.waitFor(new Wait.Condition() { @Override public boolean isSatisified() throws Exception { FileUtils.deleteQuietly(testfile); final long originalDisk = broker.getSystemUsage().getStoreUsage().getLimit(); final long originalTmp = broker.getSystemUsage().getTempUsage().getLimit(); //write a 10 meg file to the file system writeTestFile(10 * 1024 * 1024); //Assert that the usage limits have been decreased because some free space was used //up by a file boolean storeUsageRampDown = Wait.waitFor(new Wait.Condition() { @Override public boolean isSatisified() throws Exception { return broker.getSystemUsage().getStoreUsage().getLimit() < originalDisk; } }, 12000); boolean tempUsageRampDown = false; if (storeUsageRampDown) { tempUsageRampDown = Wait.waitFor(new Wait.Condition() { @Override public boolean isSatisified() throws Exception { return broker.getSystemUsage().getTempUsage().getLimit() < originalTmp; } }, 12000); } return storeUsageRampDown && tempUsageRampDown; } }, 60000)); } protected void setLimitMaxSpace() { //Configure store limits to be the max usable space on startup tempUsage.setLimit(broker.getTempDataStore().getDirectory().getUsableSpace()); storeUsage.setLimit(adapter.getDirectory().getUsableSpace()); } protected void writeTestFile(int size) throws IOException { final byte[] data = new byte[size]; final Random rng = new Random(); rng.nextBytes(data); try(FileOutputStream stream = new FileOutputStream(testfile)) { IOUtils.write(data, stream); } } protected int getFreePercentage(File directory) { File storeDir = StoreUtil.findParentDirectory(directory); return (int) (((double)storeDir.getUsableSpace() / storeDir.getTotalSpace()) * 100); } }