[ https://issues.apache.org/jira/browse/ARTEMIS-4973?focusedWorklogId=929185&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-929185 ]
ASF GitHub Bot logged work on ARTEMIS-4973: ------------------------------------------- Author: ASF GitHub Bot Created on: 07/Aug/24 17:19 Start Date: 07/Aug/24 17:19 Worklog Time Spent: 10m Work Description: gemmellr commented on code in PR #5128: URL: https://github.com/apache/activemq-artemis/pull/5128#discussion_r1707484372 ########## artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingStoreImpl.java: ########## @@ -228,11 +228,58 @@ private void configureSizeMetric() { size.setMax(maxSize, maxSize, maxMessages, maxMessages); } + private boolean validateNewSettings(final AddressSettings addressSettings) { + + Long newPageLimitBytes = addressSettings.getPageLimitBytes(); + + if (newPageLimitBytes != null && newPageLimitBytes < 0) { + newPageLimitBytes = null; + } + + int newPageSize = storageManager.getAllowedPageSize(addressSettings.getPageSizeBytes()); + + if (newPageLimitBytes != null && newPageSize > 0) { + + if (newPageSize > newPageLimitBytes) { + ActiveMQServerLogger.LOGGER.pageSettingsFailedApply(storeName, addressSettings, "pageLimitBytes is smaller than pageSize. It should allow at least one page file"); + return false; + } Review Comment: I think this should probably just throw personally. ########## artemis-server/src/main/java/org/apache/activemq/artemis/core/paging/impl/PagingStoreImpl.java: ########## @@ -228,11 +228,58 @@ private void configureSizeMetric() { size.setMax(maxSize, maxSize, maxMessages, maxMessages); } + private boolean validateNewSettings(final AddressSettings addressSettings) { + + Long newPageLimitBytes = addressSettings.getPageLimitBytes(); + + if (newPageLimitBytes != null && newPageLimitBytes < 0) { + newPageLimitBytes = null; + } + + int newPageSize = storageManager.getAllowedPageSize(addressSettings.getPageSizeBytes()); + + if (newPageLimitBytes != null && newPageSize > 0) { + + if (newPageSize > newPageLimitBytes) { + ActiveMQServerLogger.LOGGER.pageSettingsFailedApply(storeName, addressSettings, "pageLimitBytes is smaller than pageSize. It should allow at least one page file"); + return false; + } + + long newEstimatedMaxPages = newPageLimitBytes / newPageSize; + + long existingNumberOfPages; + if (!running) { + try { + checkFileFactory(); + List<String> files = this.fileFactory.listFiles("page"); + existingNumberOfPages = files.size(); + } catch (Exception e) { + ActiveMQServerLogger.LOGGER.pageSettingsFailedApply(storeName, addressSettings, "failed to get existing page files with exception: " + e); + return false; + } + } else { + existingNumberOfPages = this.numberOfPages; + } + + if (existingNumberOfPages > newEstimatedMaxPages) { + ActiveMQServerLogger.LOGGER.pageSettingsFailedApply(storeName, addressSettings, "estimated max pages " + newEstimatedMaxPages + " (pageLimitBytes / pageSize) is less than current number of pages " + existingNumberOfPages); + return false; + } Review Comment: Per my other comments, I now question this entirely (not the log message, but the return false). If someone wants to set a limit, that is lower than the current usage, its not clear to me that is a reason not to set it. They can consume those thigns to get back below it, and untilt hen it would act as if at the limit and stop more being added, which is as close to the desired behaviour as its possible to get. Failing to set anything might just mean usage keeps increasing beyond what it already is, i.e the exact opposite of what they want to happen. ########## tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/paging/PagingSendTest.java: ########## @@ -288,6 +291,103 @@ public void testPagingDoesNotDuplicateBatchMessagesAfterPagingStarted() throws E } } + @Test + public void testPageLimitBytesValidation() throws Exception { + + try (ClientSessionFactory sf = createSessionFactory(locator)) { + ClientSession session = sf.createSession(false, false); + + SimpleString queueAddr = SimpleString.of("FOO"); + session.createQueue(QueueConfiguration.of(queueAddr)); + + int size = 1048576; + AddressSettings addressSettings = new AddressSettings(); + addressSettings.setPageFullMessagePolicy(PageFullMessagePolicy.FAIL); + addressSettings.setPageSizeBytes(size); + addressSettings.setPageLimitBytes(Long.valueOf(size)); + addressSettings.setMaxSizeBytes(size); Review Comment: Setting the address full policy to PAGE explicitly would make this more understandable, and less brittle should the default change ########## tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/paging/PagingSendTest.java: ########## @@ -288,6 +291,103 @@ public void testPagingDoesNotDuplicateBatchMessagesAfterPagingStarted() throws E } } + @Test + public void testPageLimitBytesValidation() throws Exception { + + try (ClientSessionFactory sf = createSessionFactory(locator)) { + ClientSession session = sf.createSession(false, false); + + SimpleString queueAddr = SimpleString.of("FOO"); + session.createQueue(QueueConfiguration.of(queueAddr)); + + int size = 1048576; + AddressSettings addressSettings = new AddressSettings(); + addressSettings.setPageFullMessagePolicy(PageFullMessagePolicy.FAIL); + addressSettings.setPageSizeBytes(size); + addressSettings.setPageLimitBytes(Long.valueOf(size)); + addressSettings.setMaxSizeBytes(size); + + server.getAddressSettingsRepository().addMatch("FOO", addressSettings); + + int totalMessages = 15; + int messageSize = 90000; + sendMessageBatch(totalMessages, messageSize, session, queueAddr); + + Queue queue = server.locateQueue(queueAddr); + + // Give time Queue.deliverAsync to deliver messages + assertTrue(waitForMessages(queue, totalMessages, 10000)); + + PagingStore queuePagingStore = queue.getPagingStore(); + assertTrue(queuePagingStore != null && queuePagingStore.isPaging()); + + // set page size bytes to be larger than pageLimitBytes + addressSettings.setPageSizeBytes(size * 2); + server.getAddressSettingsRepository().addMatch("FOO", addressSettings); + + // check the original pageSizeBytes is not changed + assertEquals(size, queuePagingStore.getPageSizeBytes()); + + // send a messages should be allowed because the page file still have space + sendMessageBatch(1, messageSize, session, queueAddr); + assertTrue(waitForMessages(queue, totalMessages + 1, 10000)); + } + } + + @Test + public void testPageLimitBytesValidationOnRestart() throws Exception { + + try (ClientSessionFactory sf = createSessionFactory(locator)) { + ClientSession session = sf.createSession(false, false); + + SimpleString queueAddr = SimpleString.of("FOO"); + session.createQueue(QueueConfiguration.of(queueAddr)); + + int size = 1024 * 50; + AddressSettings addressSettings = new AddressSettings(); + addressSettings.setPageFullMessagePolicy(PageFullMessagePolicy.FAIL); + addressSettings.setPageSizeBytes(size); + addressSettings.setPageLimitBytes(Long.valueOf(size * 10)); + addressSettings.setMaxSizeBytes(size); + + server.getAddressSettingsRepository().addMatch("FOO", addressSettings); + + int totalMessages = 30; + int messageSize = 1024 * 10; + sendMessageBatch(totalMessages, messageSize, session, queueAddr); + + Queue queue = server.locateQueue(queueAddr); + + // Give time Queue.deliverAsync to deliver messages + assertTrue(waitForMessages(queue, totalMessages, 10000)); + + PagingStore queuePagingStore = queue.getPagingStore(); + assertTrue(queuePagingStore != null && queuePagingStore.isPaging()); + + long existingPages = queuePagingStore.getNumberOfPages(); + assertTrue(existingPages > 4); + + // restart the server and the invalid settings are not applied too. + server.stop(true); + waitForServerToStop(server); + + addressSettings.setPageLimitBytes(Long.valueOf(size * 4)); + server.getAddressSettingsRepository().addMatch("FOO", addressSettings); + + server.start(); + waitForServerToStart(server); + + queue = server.locateQueue(queueAddr); + queuePagingStore = queue.getPagingStore(); + + // check settings not applied + assertEquals(0, queuePagingStore.getPageSizeBytes()); + assertNull(queuePagingStore.getPageFullMessagePolicy()); + assertNull(queuePagingStore.getPageLimitBytes()); + assertEquals(0, queuePagingStore.getMaxSize()); Review Comment: Its not clear to me what the behavioural state of the address would actually be in this case? Will it allow more paging? Seems like a test should validate the expected behaviour rather than just the return here. If it does allow more paging as it seems it might, then that seems questionable since a limit was attempted to be imposed, and the reason it failed is seemingly because it was already beyond it? That seems off. Why not allow it but log it is beyond limit already, rather than reject it and then permit _yet more_ paging beyond the desired limit as a result? Issue Time Tracking ------------------- Worklog Id: (was: 929185) Time Spent: 2h 40m (was: 2.5h) > pageSizeBytes/pageLimitBytes combination can cause Address full > --------------------------------------------------------------- > > Key: ARTEMIS-4973 > URL: https://issues.apache.org/jira/browse/ARTEMIS-4973 > Project: ActiveMQ Artemis > Issue Type: Bug > Components: Broker > Affects Versions: 2.36.0 > Reporter: Howard Gao > Assignee: Howard Gao > Priority: Major > Time Spent: 2h 40m > Remaining Estimate: 0h > > There is an edge case where adjusting pageSizeBytes can cause "Address is > full" errors, even though the address is not full. > Do we need to enforce that pageSizeBytes <= pageLimitBytes? > Reproducer steps: > Step 1: configure pageSizeBytes == pageLimitBytes == 1mb: > $ cat my.broker.properties > addressSettings."FOO".pageSizeBytes=1048576 > addressSettings."FOO".pageLimitBytes=1048576 > addressSettings."FOO".maxSizeBytes=1048576 > addressSettings."FOO".pageFullMessagePolicy=FAIL > addressConfigurations."FOO".routingTypes=MULTICAST > addressConfigurations."FOO".queueConfigs."FOO".name=FOO > addressConfigurations."FOO".queueConfigs."FOO".address=FOO > addressConfigurations."FOO".queueConfigs."FOO".routingType=MULTICAST > Step 2: run broker > bin/artemis run --properties my.broker.properties > Step 3: produce 15 messages > $ bin/artemis producer --user admin --password admin --destination > topic://FOO --message-count 15 --message-size 100000 --protocol amqp > Step 4: observe paging started on the destination (but the page size is > 328kb, can hold more messages) > INFO [org.apache.activemq.artemis.core.server] AMQ222038: Starting paging on > address 'FOO'; size=1107003 bytes (11 messages); maxSize=1048576 bytes (-1 > messages); globalSize=1107003 bytes (11 messages); globalMaxSize=1073741824 > bytes (-1 messages); > Step 5: stop broker, increase page size > cat my.broker.properties > addressSettings."FOO".pageSizeBytes=4048576 > ... > Step 6: run broker, observe logs show paging warning > 2024-06-25 15:23:47,135 WARN [org.apache.activemq.artemis.core.server] > AMQ224123: Address FOO has more pages than allowed. System currently has 1 > pages, while the estimated max number of pages is 0 based on the > page-limit-bytes (1048576) / page-size (4048576) > Step 7: try to produce a message, address full > WARN [org.apache.activemq.artemis.protocol.amqp.broker.AMQPSessionCallback] > AMQ229102: Address "FOO" is full. -- This message was sent by Atlassian Jira (v8.20.10#820010) --------------------------------------------------------------------- To unsubscribe, e-mail: issues-unsubscr...@activemq.apache.org For additional commands, e-mail: issues-h...@activemq.apache.org For further information, visit: https://activemq.apache.org/contact