From 2ac8a7c3d43c13e0b3b0fb9a5787993565b164e0 Mon Sep 17 00:00:00 2001 From: "diego.salvi" Date: Fri, 27 Aug 2021 16:04:53 +0200 Subject: [PATCH] Separate data and index memory #755 --- .../src/main/java/herddb/core/DBManager.java | 44 ++++++++++++++++--- .../main/java/herddb/core/MemoryManager.java | 39 +++++++++++++--- .../herddb/index/brin/BRINIndexManager.java | 2 +- .../herddb/server/ServerConfiguration.java | 17 ++++++- .../cluster/follower/ChangeRoleTest.java | 7 ++- .../herddb/index/BLinkKeyToPageIndexTest.java | 2 +- .../java/herddb/index/blink/BlinkBench.java | 2 +- .../herddb/index/blink/BlinkRandomBench.java | 2 +- 8 files changed, 97 insertions(+), 18 deletions(-) diff --git a/herddb-core/src/main/java/herddb/core/DBManager.java b/herddb-core/src/main/java/herddb/core/DBManager.java index ccb366be2..6d801a9cb 100644 --- a/herddb-core/src/main/java/herddb/core/DBManager.java +++ b/herddb-core/src/main/java/herddb/core/DBManager.java @@ -154,8 +154,10 @@ public class DBManager implements AutoCloseable, MetadataChangeListener { private long maxMemoryReference = ServerConfiguration.PROPERTY_MEMORY_LIMIT_REFERENCE_DEFAULT; private long maxLogicalPageSize = ServerConfiguration.PROPERTY_MAX_LOGICAL_PAGE_SIZE_DEFAULT; private long maxDataUsedMemory = ServerConfiguration.PROPERTY_MAX_DATA_MEMORY_DEFAULT; + private long maxIndexUsedMemory = ServerConfiguration.PROPERTY_MAX_INDEX_MEMORY_DEFAULT; private long maxPKUsedMemory = ServerConfiguration.PROPERTY_MAX_PK_MEMORY_DEFAULT; private double maxDataUsedMemoryPercentage = ServerConfiguration.PROPERTY_MAX_DATA_MEMORY_PERCENTAGE_DEFAULT; + private double maxIndexUsedMemoryPercentage = ServerConfiguration.PROPERTY_MAX_INDEX_MEMORY_PERCENTAGE_DEFAULT; private double maxPKUsedMemoryPercentage = ServerConfiguration.PROPERTY_MAX_PK_MEMORY_PERCENTAGE_DEFAULT; private boolean clearAtBoot = false; @@ -259,6 +261,10 @@ public Thread newThread(final Runnable r) { ServerConfiguration.PROPERTY_MAX_DATA_MEMORY, ServerConfiguration.PROPERTY_MAX_DATA_MEMORY_DEFAULT); + this.maxIndexUsedMemory = configuration.getLong( + ServerConfiguration.PROPERTY_MAX_INDEX_MEMORY, + ServerConfiguration.PROPERTY_MAX_INDEX_MEMORY_DEFAULT); + this.maxPKUsedMemory = configuration.getLong( ServerConfiguration.PROPERTY_MAX_PK_MEMORY, ServerConfiguration.PROPERTY_MAX_PK_MEMORY_DEFAULT); @@ -267,15 +273,23 @@ public Thread newThread(final Runnable r) { ServerConfiguration.PROPERTY_MAX_DATA_MEMORY_PERCENTAGE, ServerConfiguration.PROPERTY_MAX_DATA_MEMORY_PERCENTAGE_DEFAULT); - if (maxDataUsedMemoryPercentage <= 0) { + if (maxDataUsedMemoryPercentage <= 0.0D) { maxDataUsedMemoryPercentage = ServerConfiguration.PROPERTY_MAX_DATA_MEMORY_PERCENTAGE_DEFAULT; } + this.maxIndexUsedMemoryPercentage = configuration.getDouble( + ServerConfiguration.PROPERTY_MAX_INDEX_MEMORY_PERCENTAGE, + ServerConfiguration.PROPERTY_MAX_INDEX_MEMORY_PERCENTAGE_DEFAULT); + + if (maxIndexUsedMemoryPercentage <= 0.0D) { + maxIndexUsedMemoryPercentage = ServerConfiguration.PROPERTY_MAX_DATA_MEMORY_PERCENTAGE_DEFAULT; + } + this.maxPKUsedMemoryPercentage = configuration.getDouble( ServerConfiguration.PROPERTY_MAX_PK_MEMORY_PERCENTAGE, ServerConfiguration.PROPERTY_MAX_PK_MEMORY_PERCENTAGE_DEFAULT); - if (maxPKUsedMemoryPercentage <= 0) { + if (maxPKUsedMemoryPercentage <= 0.0D) { maxPKUsedMemoryPercentage = ServerConfiguration.PROPERTY_MAX_PK_MEMORY_PERCENTAGE_DEFAULT; } @@ -373,6 +387,14 @@ public void setMaxDataUsedMemory(long maxDataUsedMemory) { this.maxDataUsedMemory = maxDataUsedMemory; } + public long getMaxIndexUsedMemory() { + return maxIndexUsedMemory; + } + + public void setMaxIndexUsedMemory(long maxIndexUsedMemory) { + this.maxIndexUsedMemory = maxIndexUsedMemory; + } + public long getMaxPKUsedMemory() { return maxPKUsedMemory; } @@ -426,24 +448,34 @@ public void start() throws DataStorageManagerException, LogNotAvailableException maxDataUsedMemory = (long) (maxDataUsedMemoryPercentage * maxMemoryReference); } + /* If max index memory for pages isn't configured or is too high default it to a maxMemoryReference percentage */ + if (maxIndexUsedMemory == 0 || maxIndexUsedMemory > maxMemoryReference) { + maxIndexUsedMemory = (long) (maxIndexUsedMemoryPercentage * maxMemoryReference); + } + /* If max index memory for pages isn't configured or is too high default it to a maxMemoryReference percentage */ if (maxPKUsedMemory == 0 || maxPKUsedMemory > maxMemoryReference) { maxPKUsedMemory = (long) (maxPKUsedMemoryPercentage * maxMemoryReference); } /* If max used memory is too high lower index and data accordingly */ - if (maxDataUsedMemory + maxPKUsedMemory > maxMemoryReference) { + if (maxDataUsedMemory + maxIndexUsedMemory + maxPKUsedMemory > maxMemoryReference) { long data = (int) ((double) maxDataUsedMemory - / ((double) (maxDataUsedMemory + maxPKUsedMemory)) * maxMemoryReference); + / ((double) (maxDataUsedMemory + maxIndexUsedMemory + maxPKUsedMemory)) * maxMemoryReference); + + long index = (int) ((double) maxIndexUsedMemory + / ((double) (maxDataUsedMemory + maxIndexUsedMemory + maxPKUsedMemory)) * maxMemoryReference); + long pk = (int) ((double) maxPKUsedMemory - / ((double) (maxDataUsedMemory + maxPKUsedMemory)) * maxMemoryReference); + / ((double) (maxDataUsedMemory + maxIndexUsedMemory + maxPKUsedMemory)) * maxMemoryReference); maxDataUsedMemory = data; + maxIndexUsedMemory = data; maxPKUsedMemory = pk; } - memoryManager = new MemoryManager(maxDataUsedMemory, maxPKUsedMemory, maxLogicalPageSize); + memoryManager = new MemoryManager(maxDataUsedMemory, maxIndexUsedMemory, maxPKUsedMemory, maxLogicalPageSize); metadataStorageManager.start(); diff --git a/herddb-core/src/main/java/herddb/core/MemoryManager.java b/herddb-core/src/main/java/herddb/core/MemoryManager.java index c8e59bf50..0642543a0 100644 --- a/herddb-core/src/main/java/herddb/core/MemoryManager.java +++ b/herddb-core/src/main/java/herddb/core/MemoryManager.java @@ -38,15 +38,18 @@ public class MemoryManager { MemoryManager.class.getName() + ".pageReplacementPolicy", "cp").toLowerCase(Locale.US); private final long maxDataUsedMemory; + private final long maxIndexUsedMemory; private final long maxPKUsedMemory; private final long maxLogicalPageSize; private final PageReplacementPolicy dataPageReplacementPolicy; + private final PageReplacementPolicy indexPageReplacementPolicy; private final PageReplacementPolicy pkPageReplacementPolicy; - public MemoryManager(long maxDataUsedMemory, long maxPKUsedMemory, long maxLogicalPageSize) { + public MemoryManager(long maxDataUsedMemory, long maxIndexUsedMemory, long maxPKUsedMemory, long maxLogicalPageSize) { this.maxDataUsedMemory = maxDataUsedMemory; + this.maxIndexUsedMemory = maxIndexUsedMemory; this.maxPKUsedMemory = maxPKUsedMemory; this.maxLogicalPageSize = maxLogicalPageSize; @@ -55,33 +58,51 @@ public MemoryManager(long maxDataUsedMemory, long maxPKUsedMemory, long maxLogic + ") must be greater or equal than page size (" + maxLogicalPageSize + ")"); } + // Max index memory 0 is acceptable, will use data memory instead + if (maxIndexUsedMemory > 0 && maxIndexUsedMemory < maxLogicalPageSize) { + throw new IllegalArgumentException("Max memory for index pages (" + maxIndexUsedMemory + + ") must be greater or equal than page size (" + maxLogicalPageSize + ")"); + } + if (maxPKUsedMemory < maxLogicalPageSize) { throw new IllegalArgumentException("Max memory for primary key index pages (" + maxPKUsedMemory + ") must be greater or equal than page size (" + maxLogicalPageSize + ")"); } final int dataPages = (int) (maxDataUsedMemory / maxLogicalPageSize); + final int indexPages = (int) (maxIndexUsedMemory / maxLogicalPageSize); final int pkPages = (int) (maxPKUsedMemory / maxLogicalPageSize); - LOGGER.log(Level.INFO, "Maximum amount of memory for data and indexes {0}", (maxDataUsedMemory / (1024 * 1024)) + " MB"); - LOGGER.log(Level.INFO, "Maximum amount of memory for primary key indexes {0}", (maxPKUsedMemory / (1024 * 1024)) + " MB"); + LOGGER.log(Level.INFO, "Maximum amount of memory for primary key indexes {0} ({1} pages)", + new Object[]{(maxPKUsedMemory / (1024 * 1024)) + " MB", pkPages}); + + if (indexPages > 0) { + LOGGER.log(Level.INFO, "Maximum amount of memory for data {0} ({1} pages)", + new Object[]{(maxDataUsedMemory / (1024 * 1024)) + " MB", dataPages}); + LOGGER.log(Level.INFO, "Maximum amount of memory for indexes {0} ({1} pages)", + new Object[]{(maxIndexUsedMemory / (1024 * 1024)) + " MB", indexPages}); + } else { + LOGGER.log(Level.INFO, "Maximum amount of memory for data and indexes {0} ({1} pages)", + new Object[]{(maxDataUsedMemory / (1024 * 1024)) + " MB", dataPages}); + } - LOGGER.log(Level.INFO, "Maximum number of loaded pages for data {0}" - + ", maximum number of loadedd pages for primary key indexes {1}", new Object[]{dataPages, pkPages}); switch (PAGE_REPLACEMENT_POLICY) { case "random": dataPageReplacementPolicy = new RandomPageReplacementPolicy(dataPages); + indexPageReplacementPolicy = indexPages > 0 ? new RandomPageReplacementPolicy(dataPages) : dataPageReplacementPolicy; pkPageReplacementPolicy = new RandomPageReplacementPolicy(pkPages); break; case "cp": dataPageReplacementPolicy = new ClockProPolicy(dataPages); + indexPageReplacementPolicy = indexPages > 0 ? new ClockProPolicy(dataPages) : dataPageReplacementPolicy; pkPageReplacementPolicy = new ClockProPolicy(pkPages); break; case "car": default: dataPageReplacementPolicy = new ClockAdaptiveReplacement(dataPages); + indexPageReplacementPolicy = indexPages > 0 ? new ClockAdaptiveReplacement(dataPages) : dataPageReplacementPolicy; pkPageReplacementPolicy = new ClockAdaptiveReplacement(pkPages); } @@ -92,6 +113,10 @@ public long getMaxDataUsedMemory() { } public long getMaxIndexUsedMemory() { + return maxIndexUsedMemory; + } + + public long getMaxPKUsedMemory() { return maxPKUsedMemory; } @@ -103,6 +128,10 @@ public PageReplacementPolicy getDataPageReplacementPolicy() { return dataPageReplacementPolicy; } + public PageReplacementPolicy getIndexPageReplacementPolicy() { + return indexPageReplacementPolicy; + } + public PageReplacementPolicy getPKPageReplacementPolicy() { return pkPageReplacementPolicy; } diff --git a/herddb-core/src/main/java/herddb/index/brin/BRINIndexManager.java b/herddb-core/src/main/java/herddb/index/brin/BRINIndexManager.java index c216b341d..229dff332 100644 --- a/herddb-core/src/main/java/herddb/index/brin/BRINIndexManager.java +++ b/herddb-core/src/main/java/herddb/index/brin/BRINIndexManager.java @@ -88,7 +88,7 @@ public BRINIndexManager(Index index, MemoryManager memoryManager, AbstractTableM writeLockTimeout, readLockTimeout); this.data = new BlockRangeIndex<>( memoryManager.getMaxLogicalPageSize(), - memoryManager.getDataPageReplacementPolicy(), + memoryManager.getIndexPageReplacementPolicy(), storageLayer); } diff --git a/herddb-core/src/main/java/herddb/server/ServerConfiguration.java b/herddb-core/src/main/java/herddb/server/ServerConfiguration.java index 1ccd43853..2ad8532be 100644 --- a/herddb-core/src/main/java/herddb/server/ServerConfiguration.java +++ b/herddb-core/src/main/java/herddb/server/ServerConfiguration.java @@ -280,6 +280,12 @@ public final class ServerConfiguration { public static final String PROPERTY_MAX_DATA_MEMORY = "server.memory.data.limit"; public static final long PROPERTY_MAX_DATA_MEMORY_DEFAULT = 0L; + /** + * Maximum amount of memory used for data pages + */ + public static final String PROPERTY_MAX_INDEX_MEMORY = "server.memory.index.limit"; + public static final long PROPERTY_MAX_INDEX_MEMORY_DEFAULT = 0L; + /** * Maximum amount of memory used for primary index pages */ @@ -287,13 +293,20 @@ public final class ServerConfiguration { public static final long PROPERTY_MAX_PK_MEMORY_DEFAULT = 0L; /** - * Percentage of maximum memory used for data pages, will be used if {@link PROPERTY_MAX_PK_MEMORY} is not given + * Percentage of maximum memory used for data pages, will be used if {@link PROPERTY_MAX_DATA_MEMORY} is not given */ public static final String PROPERTY_MAX_DATA_MEMORY_PERCENTAGE = "server.memory.data.percentage"; public static final double PROPERTY_MAX_DATA_MEMORY_PERCENTAGE_DEFAULT = 0.50D; /** - * Percentage of maximum memory used for primary index pages, will be used if {@link PROPERTY_MAX_DATA_MEMORY} is + * Percentage of maximum memory used for index pages, will be used if {@link PROPERTY_MAX_INDEX_MEMORY} is not given. + * A value of zero means "no dedicated index memory": data memory will be used instead. + */ + public static final String PROPERTY_MAX_INDEX_MEMORY_PERCENTAGE = "server.memory.index.percentage"; + public static final double PROPERTY_MAX_INDEX_MEMORY_PERCENTAGE_DEFAULT = 0.00D; + + /** + * Percentage of maximum memory used for primary index pages, will be used if {@link PROPERTY_MAX_PK_MEMORY} is * not given */ public static final String PROPERTY_MAX_PK_MEMORY_PERCENTAGE = "server.memory.pk.percentage"; diff --git a/herddb-core/src/test/java/herddb/cluster/follower/ChangeRoleTest.java b/herddb-core/src/test/java/herddb/cluster/follower/ChangeRoleTest.java index fd2073188..24b1d9392 100644 --- a/herddb-core/src/test/java/herddb/cluster/follower/ChangeRoleTest.java +++ b/herddb-core/src/test/java/herddb/cluster/follower/ChangeRoleTest.java @@ -103,11 +103,13 @@ public void testChangeRoleAndReleaseMemory() throws Exception { new HashSet<>(Arrays.asList("server1", "server2")), "server1", 1, 0), StatementEvaluationContext.DEFAULT_EVALUATION_CONTEXT(), TransactionContext.NO_TRANSACTION); assertEquals(0, server2MemoryManager.getDataPageReplacementPolicy().size()); + assertEquals(0, server2MemoryManager.getIndexPageReplacementPolicy().size()); assertEquals(0, server2MemoryManager.getPKPageReplacementPolicy().size()); server_2.waitForTableSpaceBoot(TableSpace.DEFAULT, false); - assertEquals(2, server2MemoryManager.getDataPageReplacementPolicy().size()); + assertEquals(1, server2MemoryManager.getDataPageReplacementPolicy().size()); + assertEquals(1, server2MemoryManager.getIndexPageReplacementPolicy().size()); assertEquals(1, server2MemoryManager.getPKPageReplacementPolicy().size()); // stop tablespace on server2 @@ -125,6 +127,7 @@ public void testChangeRoleAndReleaseMemory() throws Exception { // memory must have been totally released assertEquals(0, server2MemoryManager.getDataPageReplacementPolicy().size()); + assertEquals(0, server2MemoryManager.getIndexPageReplacementPolicy().size()); assertEquals(0, server2MemoryManager.getPKPageReplacementPolicy().size()); // start tablespace on server2, as let it become leader @@ -135,6 +138,7 @@ public void testChangeRoleAndReleaseMemory() throws Exception { assertTrue("unexpected value " + server2MemoryManager.getDataPageReplacementPolicy().size(), server2MemoryManager.getDataPageReplacementPolicy().size() >= 1); + assertEquals(1, server2MemoryManager.getIndexPageReplacementPolicy().size()); assertEquals(1, server2MemoryManager.getPKPageReplacementPolicy().size()); server_1.getManager().executeStatement(new AlterTableSpaceStatement(TableSpace.DEFAULT, @@ -149,6 +153,7 @@ public void testChangeRoleAndReleaseMemory() throws Exception { // memory must have been totally released again assertEquals(0, server2MemoryManager.getDataPageReplacementPolicy().size()); + assertEquals(0, server2MemoryManager.getIndexPageReplacementPolicy().size()); assertEquals(0, server2MemoryManager.getPKPageReplacementPolicy().size()); } diff --git a/herddb-core/src/test/java/herddb/index/BLinkKeyToPageIndexTest.java b/herddb-core/src/test/java/herddb/index/BLinkKeyToPageIndexTest.java index 821214d6c..31b42873e 100644 --- a/herddb-core/src/test/java/herddb/index/BLinkKeyToPageIndexTest.java +++ b/herddb-core/src/test/java/herddb/index/BLinkKeyToPageIndexTest.java @@ -45,7 +45,7 @@ public class BLinkKeyToPageIndexTest extends KeyToPageIndexTest { @Override KeyToPageIndex createIndex() { - MemoryManager mem = new MemoryManager(5 * (1L << 20), 10 * (128L << 10), (128L << 10)); + MemoryManager mem = new MemoryManager(5 * (1L << 20), 10 * (128L << 10), 0, (128L << 10)); MemoryDataStorageManager ds = new MemoryDataStorageManager(); BLinkKeyToPageIndex idx = new BLinkKeyToPageIndex("tblspc", "tbl", mem, ds); diff --git a/herddb-core/src/test/java/herddb/index/blink/BlinkBench.java b/herddb-core/src/test/java/herddb/index/blink/BlinkBench.java index 560818e4e..6fc8026b4 100644 --- a/herddb-core/src/test/java/herddb/index/blink/BlinkBench.java +++ b/herddb-core/src/test/java/herddb/index/blink/BlinkBench.java @@ -122,7 +122,7 @@ public static void main(String[] args) { PrintStream eps = new PrintStream(eos); System.setErr(eps); - MemoryManager mem = new MemoryManager(5 * (1L << 20), 10 * (128L << 10), (128L << 10)); + MemoryManager mem = new MemoryManager(5 * (1L << 20), 10 * (128L << 10), 0, (128L << 10)); try (MemoryDataStorageManager ds = new MemoryDataStorageManager(); BLinkKeyToPageIndex idx = new BLinkKeyToPageIndex("tblspc", "tbl", mem, ds)) { diff --git a/herddb-core/src/test/java/herddb/index/blink/BlinkRandomBench.java b/herddb-core/src/test/java/herddb/index/blink/BlinkRandomBench.java index afe0e51a0..7f23beb34 100644 --- a/herddb-core/src/test/java/herddb/index/blink/BlinkRandomBench.java +++ b/herddb-core/src/test/java/herddb/index/blink/BlinkRandomBench.java @@ -122,7 +122,7 @@ public static void main(String[] args) { PrintStream eps = new PrintStream(eos); System.setErr(eps); - MemoryManager mem = new MemoryManager(5 * (1L << 20), 10 * (128L << 10), (128L << 10)); + MemoryManager mem = new MemoryManager(5 * (1L << 20), 10 * (128L << 10), 0, (128L << 10)); // MemoryManager mem = new MemoryManager(5 * (1L << 20), (128L << 10), (128L << 10)); try (MemoryDataStorageManager ds = new MemoryDataStorageManager();