|
20 | 20 |
|
21 | 21 | package org.lmdbjava;
|
22 | 22 |
|
| 23 | +import static java.nio.ByteBuffer.allocateDirect; |
| 24 | +import static java.nio.charset.StandardCharsets.UTF_8; |
| 25 | +import static org.junit.Assert.fail; |
| 26 | +import static org.lmdbjava.DbiFlags.MDB_CREATE; |
| 27 | +import static org.lmdbjava.Env.create; |
| 28 | + |
| 29 | +import java.io.File; |
| 30 | +import java.io.IOException; |
| 31 | +import java.nio.ByteBuffer; |
| 32 | + |
23 | 33 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
24 | 34 | import org.junit.Rule;
|
25 | 35 | import org.junit.Test;
|
26 | 36 | import org.junit.rules.TemporaryFolder;
|
27 | 37 | import org.mockito.MockedStatic;
|
28 | 38 | import org.mockito.Mockito;
|
29 | 39 |
|
30 |
| -import java.io.File; |
31 |
| -import java.io.IOException; |
32 |
| -import java.nio.ByteBuffer; |
33 |
| - |
34 |
| -import static java.nio.ByteBuffer.allocateDirect; |
35 |
| -import static java.nio.charset.StandardCharsets.UTF_8; |
36 |
| -import static org.junit.Assert.fail; |
37 |
| -import static org.lmdbjava.DbiFlags.MDB_CREATE; |
38 |
| -import static org.lmdbjava.Env.create; |
39 |
| - |
40 | 40 | @SuppressFBWarnings({"DM_GC", "RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT"})
|
41 | 41 | @SuppressWarnings("PMD.DoNotCallGarbageCollectionExplicitly")
|
42 | 42 | public class GarbageCollectionTest {
|
43 | 43 |
|
44 |
| - private static final String DB_NAME = "my DB"; |
45 |
| - private static final String KEY_PREFIX = "Uncorruptedkey"; |
46 |
| - private static final String VAL_PREFIX = "Uncorruptedval"; |
| 44 | + private static final String DB_NAME = "my DB"; |
| 45 | + private static final String KEY_PREFIX = "Uncorruptedkey"; |
| 46 | + private static final String VAL_PREFIX = "Uncorruptedval"; |
47 | 47 |
|
48 |
| - @Rule |
49 |
| - public final TemporaryFolder tmp = new TemporaryFolder(); |
50 |
| - private void putBuffer(Dbi<ByteBuffer> db, Txn<ByteBuffer> txn, int i) { |
51 |
| - ByteBuffer key = allocateDirect(24); |
52 |
| - ByteBuffer val = allocateDirect(24); |
53 |
| - key.put((KEY_PREFIX+i).getBytes(UTF_8)).flip(); |
54 |
| - val.put((VAL_PREFIX+i).getBytes(UTF_8)).flip(); |
55 |
| - db.put(txn, key, val); |
56 |
| - } |
57 |
| - @Test |
58 |
| - public void buffersNotGarbageCollectedTest() throws IOException { |
59 |
| - final File path = tmp.newFolder(); |
| 48 | + @Rule |
| 49 | + public final TemporaryFolder tmp = new TemporaryFolder(); |
60 | 50 |
|
61 |
| - final Env<ByteBuffer> env = create() |
62 |
| - .setMapSize(2_085_760_999) |
63 |
| - .setMaxDbs(1) |
64 |
| - .open(path); |
65 |
| - final Dbi<ByteBuffer> db = env.openDbi(DB_NAME, MDB_CREATE); |
| 51 | + @Test |
| 52 | + public void buffersNotGarbageCollectedTest() throws IOException { |
| 53 | + final File path = tmp.newFolder(); |
| 54 | + try (Env<ByteBuffer> env = create() |
| 55 | + .setMapSize(2_085_760_999) |
| 56 | + .setMaxDbs(1) |
| 57 | + .open(path)) { |
| 58 | + final Dbi<ByteBuffer> db = env.openDbi(DB_NAME, MDB_CREATE); |
66 | 59 |
|
67 |
| - // Trigger compilation and whatnot |
68 |
| - try (Txn<ByteBuffer> txn = env.txnWrite()) { |
69 |
| - for (int i = 0; i < 5000; i++) { |
70 |
| - putBuffer(db, txn, i); |
71 |
| - } |
72 |
| - txn.commit(); |
| 60 | + try (Txn<ByteBuffer> txn = env.txnWrite()) { |
| 61 | + for (int i = 0; i < 5_000; i++) { |
| 62 | + putBuffer(db, txn, i); |
73 | 63 | }
|
74 |
| - // Call gc before writing to lmdb and after last reference to buffer by changing the behavior of mask |
75 |
| - try (MockedStatic<MaskedFlag> mockedStatic = Mockito.mockStatic(MaskedFlag.class)) { |
76 |
| - mockedStatic.when(MaskedFlag::mask).thenAnswer(invocationOnMock -> { |
77 |
| - System.gc(); |
78 |
| - return 0; |
79 |
| - }); |
80 |
| - try (Txn<ByteBuffer> txn = env.txnWrite()) { |
81 |
| - for (int i = 0; i < 1000; i++) { |
82 |
| - putBuffer(db, txn, i); |
83 |
| - } |
84 |
| - txn.commit(); |
85 |
| - } |
| 64 | + txn.commit(); |
| 65 | + } |
| 66 | + |
| 67 | + // Call GC before writing to LMDB and after last reference to buffer by |
| 68 | + // changing the behavior of mask |
| 69 | + try (MockedStatic<MaskedFlag> mockedStatic = Mockito.mockStatic( |
| 70 | + MaskedFlag.class)) { |
| 71 | + mockedStatic.when(MaskedFlag::mask).thenAnswer(invocationOnMock -> { |
| 72 | + System.gc(); |
| 73 | + return 0; |
| 74 | + }); |
| 75 | + final int gcRecordWrites = Integer.getInteger("gcRecordWrites", 50); |
| 76 | + try (Txn<ByteBuffer> txn = env.txnWrite()) { |
| 77 | + for (int i = 0; i < gcRecordWrites; i++) { |
| 78 | + putBuffer(db, txn, i); |
| 79 | + } |
| 80 | + txn.commit(); |
86 | 81 | }
|
| 82 | + } |
87 | 83 |
|
88 |
| - // Find corrupt keys |
89 |
| - try (Txn<ByteBuffer> txn = env.txnRead()) { |
90 |
| - try (Cursor<ByteBuffer> c = db.openCursor(txn)) { |
91 |
| - if (c.first()) { |
92 |
| - do { |
93 |
| - byte[] rkey = new byte[c.key().remaining()]; |
94 |
| - c.key().get(rkey); |
95 |
| - byte[] rval = new byte[c.val().remaining()]; |
96 |
| - c.val().get(rval); |
97 |
| - String skey = new String(rkey, UTF_8); |
98 |
| - String sval = new String(rval, UTF_8); |
99 |
| - if (!skey.startsWith("Uncorruptedkey")) { |
100 |
| - fail("Found corrupt key " + skey); |
101 |
| - } |
102 |
| - if (!sval.startsWith("Uncorruptedval")) { |
103 |
| - fail("Found corrupt val " + sval); |
104 |
| - } |
105 |
| - } while (c.next()); |
106 |
| - } |
107 |
| - } |
| 84 | + // Find corrupt keys |
| 85 | + try (Txn<ByteBuffer> txn = env.txnRead()) { |
| 86 | + try (Cursor<ByteBuffer> c = db.openCursor(txn)) { |
| 87 | + if (c.first()) { |
| 88 | + do { |
| 89 | + final byte[] rkey = new byte[c.key().remaining()]; |
| 90 | + c.key().get(rkey); |
| 91 | + final byte[] rval = new byte[c.val().remaining()]; |
| 92 | + c.val().get(rval); |
| 93 | + final String skey = new String(rkey, UTF_8); |
| 94 | + final String sval = new String(rval, UTF_8); |
| 95 | + if (!skey.startsWith("Uncorruptedkey")) { |
| 96 | + fail("Found corrupt key " + skey); |
| 97 | + } |
| 98 | + if (!sval.startsWith("Uncorruptedval")) { |
| 99 | + fail("Found corrupt val " + sval); |
| 100 | + } |
| 101 | + } while (c.next()); |
| 102 | + } |
108 | 103 | }
|
109 |
| - env.close(); |
| 104 | + } |
110 | 105 | }
|
| 106 | + } |
| 107 | + |
| 108 | + private void putBuffer(final Dbi<ByteBuffer> db, final Txn<ByteBuffer> txn, |
| 109 | + final int i) { |
| 110 | + final ByteBuffer key = allocateDirect(24); |
| 111 | + final ByteBuffer val = allocateDirect(24); |
| 112 | + key.put((KEY_PREFIX + i).getBytes(UTF_8)).flip(); |
| 113 | + val.put((VAL_PREFIX + i).getBytes(UTF_8)).flip(); |
| 114 | + db.put(txn, key, val); |
| 115 | + } |
| 116 | + |
111 | 117 | }
|
0 commit comments