From bc0ccbc64b4025550462c64e3fa5b0fab5735ce4 Mon Sep 17 00:00:00 2001 From: Jiri Date: Thu, 13 Aug 2020 21:03:29 +0200 Subject: [PATCH 1/2] Added usage of aarch64 build of native llmdb * Added more distinguished names for x86_64, aarch64, ppc64le and unified arch64 for all three linuxes * Renamed unifying x86_64 varable to make spotbugs happy --- pom.xml | 16 ++++++++++++++++ src/main/java/org/lmdbjava/Library.java | 16 ++++++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 1e8874a0..f94b76b7 100644 --- a/pom.xml +++ b/pom.xml @@ -70,6 +70,18 @@ org.hamcrest hamcrest + + org.lmdbjava + lmdbjava-native-linux-aarch64 + 0.9.24-2-SNAPSHOT + true + + + org.lmdbjava + lmdbjava-native-linux-ppc64le + 0.9.24-2-SNAPSHOT + true + org.lmdbjava lmdbjava-native-linux-x86_64 @@ -112,6 +124,8 @@ maven-dependency-plugin + org.lmdbjava:lmdbjava-native-linux-ppc64le + org.lmdbjava:lmdbjava-native-linux-aarch64 org.lmdbjava:lmdbjava-native-linux-x86_64 org.lmdbjava:lmdbjava-native-windows-x86_64 org.lmdbjava:lmdbjava-native-osx-x86_64 @@ -142,6 +156,8 @@ + org.lmdbjava:lmdbjava-native-linux-ppc64le + org.lmdbjava:lmdbjava-native-linux-aarch64 org.lmdbjava:lmdbjava-native-linux-x86_64 org.lmdbjava:lmdbjava-native-windows-x86_64 org.lmdbjava:lmdbjava-native-osx-x86_64 diff --git a/src/main/java/org/lmdbjava/Library.java b/src/main/java/org/lmdbjava/Library.java index 18d3e717..7e9aeb6c 100644 --- a/src/main/java/org/lmdbjava/Library.java +++ b/src/main/java/org/lmdbjava/Library.java @@ -102,8 +102,12 @@ final class Library { final String libToLoad; final String arch = getProperty("os.arch"); - final boolean arch64 = "x64".equals(arch) || "amd64".equals(arch) + final boolean x64 = "x64".equals(arch) || "amd64".equals(arch) || "x86_64".equals(arch); + final boolean aarch64 = "aarch64".equals(arch); + final boolean ppc64le = "ppc64le".equals(arch); + + // final boolean arch64 = x64 || aarch64 || ppc64le; final String os = getProperty("os.name"); final boolean linux = os.toLowerCase(ENGLISH).startsWith("linux"); @@ -112,11 +116,15 @@ final class Library { if (SHOULD_USE_LIB) { libToLoad = getProperty(LMDB_NATIVE_LIB_PROP); - } else if (SHOULD_EXTRACT && arch64 && linux) { + } else if (SHOULD_EXTRACT && aarch64 && linux) { + libToLoad = extract("org/lmdbjava/lmdbjava-native-linux-aarch64.so"); + } else if (SHOULD_EXTRACT && ppc64le && linux) { + libToLoad = extract("org/lmdbjava/lmdbjava-native-linux-ppc64le.so"); + } else if (SHOULD_EXTRACT && x64 && linux) { libToLoad = extract("org/lmdbjava/lmdbjava-native-linux-x86_64.so"); - } else if (SHOULD_EXTRACT && arch64 && osx) { + } else if (SHOULD_EXTRACT && x64 && osx) { libToLoad = extract("org/lmdbjava/lmdbjava-native-osx-x86_64.dylib"); - } else if (SHOULD_EXTRACT && arch64 && windows) { + } else if (SHOULD_EXTRACT && x64 && windows) { libToLoad = extract("org/lmdbjava/lmdbjava-native-windows-x86_64.dll"); } else { libToLoad = LIB_NAME; From dacfdc807f961828f351fd0557855733885b3951 Mon Sep 17 00:00:00 2001 From: Michal Karm Babacek Date: Wed, 23 Sep 2020 08:42:27 +0200 Subject: [PATCH 2/2] Porting to systems with large pages - Page size is configurable via env and sys prop PAGE_SIZE - Allocation happens page by page, not by an arbitrary chunk --- pom.xml | 138 ++++++++++++++++-- src/main/java/org/lmdbjava/Env.java | 46 +++++- src/main/java/org/lmdbjava/Library.java | 3 +- .../java/org/lmdbjava/CursorIterableTest.java | 9 +- src/test/java/org/lmdbjava/DbiTest.java | 3 +- src/test/java/org/lmdbjava/EnvTest.java | 14 +- src/test/java/org/lmdbjava/TxnTest.java | 5 +- 7 files changed, 191 insertions(+), 27 deletions(-) diff --git a/pom.xml b/pom.xml index f94b76b7..4c961c46 100644 --- a/pom.xml +++ b/pom.xml @@ -70,18 +70,6 @@ org.hamcrest hamcrest - - org.lmdbjava - lmdbjava-native-linux-aarch64 - 0.9.24-2-SNAPSHOT - true - - - org.lmdbjava - lmdbjava-native-linux-ppc64le - 0.9.24-2-SNAPSHOT - true - org.lmdbjava lmdbjava-native-linux-x86_64 @@ -124,8 +112,6 @@ maven-dependency-plugin - org.lmdbjava:lmdbjava-native-linux-ppc64le - org.lmdbjava:lmdbjava-native-linux-aarch64 org.lmdbjava:lmdbjava-native-linux-x86_64 org.lmdbjava:lmdbjava-native-windows-x86_64 org.lmdbjava:lmdbjava-native-osx-x86_64 @@ -156,8 +142,6 @@ - org.lmdbjava:lmdbjava-native-linux-ppc64le - org.lmdbjava:lmdbjava-native-linux-aarch64 org.lmdbjava:lmdbjava-native-linux-x86_64 org.lmdbjava:lmdbjava-native-windows-x86_64 org.lmdbjava:lmdbjava-native-osx-x86_64 @@ -232,4 +216,126 @@ GitHub Actions https://github.com/${github.org}/${github.repo}/actions + + + linux-aarch64 + + + Linux + unix + aarch64 + + + + + org.lmdbjava + lmdbjava-native-linux-aarch64 + 0.9.24-2-SNAPSHOT + true + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + org.lmdbjava:lmdbjava-native-linux-aarch64 + org.lmdbjava:lmdbjava-native-linux-x86_64 + org.lmdbjava:lmdbjava-native-windows-x86_64 + org.lmdbjava:lmdbjava-native-osx-x86_64 + + + com.github.jnr:jffi + + + + + org.apache.maven.plugins + maven-shade-plugin + + + lmdbjava-shade + + shade + + package + + + + org.lmdbjava:lmdbjava-native-linux-aarch64 + org.lmdbjava:lmdbjava-native-linux-x86_64 + org.lmdbjava:lmdbjava-native-windows-x86_64 + org.lmdbjava:lmdbjava-native-osx-x86_64 + + + + + + + + + + + linux-ppc64le + + + Linux + unix + ppc64le + + + + + org.lmdbjava + lmdbjava-native-linux-ppc64le + 0.9.24-2-SNAPSHOT + true + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + org.lmdbjava:lmdbjava-native-linux-ppc64le + org.lmdbjava:lmdbjava-native-linux-x86_64 + org.lmdbjava:lmdbjava-native-windows-x86_64 + org.lmdbjava:lmdbjava-native-osx-x86_64 + + + com.github.jnr:jffi + + + + + org.apache.maven.plugins + maven-shade-plugin + + + lmdbjava-shade + + shade + + package + + + + org.lmdbjava:lmdbjava-native-linux-ppc64le + org.lmdbjava:lmdbjava-native-linux-x86_64 + org.lmdbjava:lmdbjava-native-windows-x86_64 + org.lmdbjava:lmdbjava-native-osx-x86_64 + + + + + + + + + + diff --git a/src/main/java/org/lmdbjava/Env.java b/src/main/java/org/lmdbjava/Env.java index 7634a342..4ca70b8b 100644 --- a/src/main/java/org/lmdbjava/Env.java +++ b/src/main/java/org/lmdbjava/Env.java @@ -195,7 +195,8 @@ public List getDbiNames() { * @param mapSize the new size, in bytes */ public void setMapSize(final long mapSize) { - checkRc(LIB.mdb_env_set_mapsize(ptr, mapSize)); + // A number divisible by the number of pages is expected. + checkRc(LIB.mdb_env_set_mapsize(ptr, Builder.alignToWholePages(mapSize))); } /** @@ -417,6 +418,25 @@ private void validatePath(final File path) { validateDirectoryEmpty(path); } + public static int getPageSize() { + int pageSize = 0; + for (final String s: new String[]{ + System.getenv().get("PAGE_SIZE"), + System.getProperty("PAGE_SIZE")}) { + if (s != null && s.length() > 0) { + pageSize = Integer.parseInt(s); + break; + } + } + // This move is closely dependent on this logic: + // @see https://github.com/LMDB/lmdb/blob/LMDB_0.9.24/libraries/liblmdb/mdb.c#L498:L514 + if (pageSize >= 32_768) { + return 32_768; + } + // Silently defaulting to 4096 + return 4_096; + } + /** * Object has already been closed and the operation is therefore prohibited. */ @@ -454,8 +474,10 @@ public AlreadyOpenException() { */ public static final class Builder { + static final int PAGE_SIZE = getPageSize(); static final int MAX_READERS_DEFAULT = 126; - private long mapSize = 1_024 * 1_024; + // On a system with pages 4096 bytes big, this translates to default max map size of 4 MB + private long mapSize = PAGE_SIZE * 1_024; private int maxDbs = 1; private int maxReaders = MAX_READERS_DEFAULT; private boolean opened; @@ -486,6 +508,8 @@ public Env open(final File path, final int mode, checkRc(LIB.mdb_env_create(envPtr)); final Pointer ptr = envPtr.getValue(); try { + // This call actually takes a number divisible by the page size, + // not an arbitrary number of bytes. checkRc(LIB.mdb_env_set_mapsize(ptr, mapSize)); checkRc(LIB.mdb_env_set_maxdbs(ptr, maxDbs)); checkRc(LIB.mdb_env_set_maxreaders(ptr, maxReaders)); @@ -525,10 +549,26 @@ public Builder setMapSize(final long mapSize) { if (mapSize < 0) { throw new IllegalArgumentException("Negative value; overflow?"); } - this.mapSize = mapSize; + // A number divisible by the number of pages is expected. + this.mapSize = alignToWholePages(mapSize); return this; } + /** + * Aligns the allocated amount to whole pages. + * Note this is related to: + * https://github.com/LMDB/lmdb/blob/LMDB_0.9.24/libraries/liblmdb/mdb.c#L498:L514 + * https://github.com/LMDB/lmdb/blob/LMDB_0.9.24/libraries/liblmdb/lmdb.h#L813:L845 + * @param sizeb size in bytes + * @return size in bytes + */ + public static long alignToWholePages(final long sizeb) { + if ((sizeb % Builder.PAGE_SIZE) != 0) { + return (sizeb / Builder.PAGE_SIZE + 1) * Builder.PAGE_SIZE; + } + return sizeb; + } + /** * Sets the maximum number of databases (ie {@link Dbi}s permitted. * diff --git a/src/main/java/org/lmdbjava/Library.java b/src/main/java/org/lmdbjava/Library.java index 7e9aeb6c..7ee71fea 100644 --- a/src/main/java/org/lmdbjava/Library.java +++ b/src/main/java/org/lmdbjava/Library.java @@ -29,6 +29,7 @@ import static java.util.Objects.requireNonNull; import static jnr.ffi.LibraryLoader.create; import static jnr.ffi.Runtime.getRuntime; +import static org.lmdbjava.Env.Builder.PAGE_SIZE; import java.io.File; import java.io.IOException; @@ -153,7 +154,7 @@ private static String extract(final String name) { OutputStream out = Files.newOutputStream(file.toPath())) { requireNonNull(in, "Classpath resource not found"); int bytes; - final byte[] buffer = new byte[4_096]; + final byte[] buffer = new byte[PAGE_SIZE]; while (-1 != (bytes = in.read(buffer))) { out.write(buffer, 0, bytes); } diff --git a/src/test/java/org/lmdbjava/CursorIterableTest.java b/src/test/java/org/lmdbjava/CursorIterableTest.java index cc6cd0b6..8fc648b9 100644 --- a/src/test/java/org/lmdbjava/CursorIterableTest.java +++ b/src/test/java/org/lmdbjava/CursorIterableTest.java @@ -125,7 +125,14 @@ public void atMostTest() { public void before() throws IOException { final File path = tmp.newFile(); env = create() - .setMapSize(KIBIBYTES.toBytes(100)) + // 100 KB triggers Environment mapsize reached (-30792) on Txn.commit(Txn.java:110) + // on ppc64le and aarch64 systems with 65536 b big OS pages, i.e. 32768 b LMDB DB pages. + // 289 KB is just enough to hold the data without triggering the error. + // On a system with 4096 b OS pages, it translates to: + // Asked for : 295936 bytes, got 299008, which is 73 DB pages. + // On a system with 65536 n OS pages, it translates to: + // Asked for : 295936 bytes, got 327680, which is 10 DB pages. + .setMapSize(KIBIBYTES.toBytes(289)) .setMaxReaders(1) .setMaxDbs(1) .open(path, POSIX_MODE, MDB_NOSUBDIR); diff --git a/src/test/java/org/lmdbjava/DbiTest.java b/src/test/java/org/lmdbjava/DbiTest.java index b9bf453c..6c89612c 100644 --- a/src/test/java/org/lmdbjava/DbiTest.java +++ b/src/test/java/org/lmdbjava/DbiTest.java @@ -44,6 +44,7 @@ import static org.lmdbjava.DbiFlags.MDB_CREATE; import static org.lmdbjava.DbiFlags.MDB_DUPSORT; import static org.lmdbjava.DbiFlags.MDB_REVERSEKEY; +import static org.lmdbjava.Env.Builder.PAGE_SIZE; import static org.lmdbjava.Env.create; import static org.lmdbjava.EnvFlags.MDB_NOSUBDIR; import static org.lmdbjava.GetOp.MDB_SET_KEY; @@ -457,7 +458,7 @@ public void stats() { assertThat(stat.entries, is(3L)); assertThat(stat.leafPages, is(1L)); assertThat(stat.overflowPages, is(0L)); - assertThat(stat.pageSize, is(4_096)); + assertThat(stat.pageSize, is(PAGE_SIZE)); } @Test(expected = MapFullException.class) diff --git a/src/test/java/org/lmdbjava/EnvTest.java b/src/test/java/org/lmdbjava/EnvTest.java index 3e9ece95..e131425d 100644 --- a/src/test/java/org/lmdbjava/EnvTest.java +++ b/src/test/java/org/lmdbjava/EnvTest.java @@ -30,6 +30,8 @@ import static org.lmdbjava.CopyFlags.MDB_CP_COMPACT; import static org.lmdbjava.DbiFlags.MDB_CREATE; import static org.lmdbjava.Env.Builder.MAX_READERS_DEFAULT; +import static org.lmdbjava.Env.Builder.PAGE_SIZE; +import static org.lmdbjava.Env.Builder.alignToWholePages; import static org.lmdbjava.Env.create; import static org.lmdbjava.Env.open; import static org.lmdbjava.EnvFlags.MDB_NOSUBDIR; @@ -68,7 +70,8 @@ public void byteUnit() throws IOException { .setMapSize(MEBIBYTES.toBytes(1)) .open(path, MDB_NOSUBDIR)) { final EnvInfo info = env.info(); - assertThat(info.mapSize, is(MEBIBYTES.toBytes(1))); + // Allocation happens page by page, not in arbitrary chunks. + assertThat(info.mapSize, is(alignToWholePages(MEBIBYTES.toBytes(1)))); } } @@ -277,7 +280,7 @@ public void info() throws IOException { assertThat(info.lastPageNumber, is(1L)); assertThat(info.lastTransactionId, is(0L)); assertThat(info.mapAddress, is(0L)); - assertThat(info.mapSize, is(123_456L)); + assertThat(info.mapSize, is(alignToWholePages(123_456L))); assertThat(info.maxReaders, is(4)); assertThat(info.numReaders, is(0)); assertThat(info.toString(), containsString("maxReaders=")); @@ -335,7 +338,10 @@ public void setMapSize() throws IOException { final Random rnd = new Random(); try (Env env = create() .setMaxReaders(1) - .setMapSize(50_000) + // 50_000 was enough on system with OS and DB pages of 4096 b. + // On systems with OS pages 65536 b, i.e. DB pages of 32768 b, it is 196609 b. + // It translates to: Asked for: 196609 bytes, got 229376, which is 7 DB pages. + .setMapSize(PAGE_SIZE > 4096 ? 196_609 : 50_000) .setMaxDbs(1) .open(path)) { final Dbi db = env.openDbi(DB_1, MDB_CREATE); @@ -390,7 +396,7 @@ public void stats() throws IOException { assertThat(stat.entries, is(0L)); assertThat(stat.leafPages, is(0L)); assertThat(stat.overflowPages, is(0L)); - assertThat(stat.pageSize, is(4_096)); + assertThat(stat.pageSize, is(PAGE_SIZE)); assertThat(stat.toString(), containsString("pageSize=")); } } diff --git a/src/test/java/org/lmdbjava/TxnTest.java b/src/test/java/org/lmdbjava/TxnTest.java index dda4694e..bd9712ec 100644 --- a/src/test/java/org/lmdbjava/TxnTest.java +++ b/src/test/java/org/lmdbjava/TxnTest.java @@ -84,7 +84,10 @@ public void after() { public void before() throws IOException { path = tmp.newFile(); env = create() - .setMapSize(KIBIBYTES.toBytes(100)) + // 100 was enough on systems with OS and DB pages of 4096 b. + // For systems with OS pages 65536 b big, i.e. 32768 b DB pages, + // 353 KB is the minimum for this test. + .setMapSize(KIBIBYTES.toBytes(353)) .setMaxReaders(1) .setMaxDbs(2) .open(path, POSIX_MODE, MDB_NOSUBDIR);