|
23 | 23 | import static org.junit.Assert.assertNull; |
24 | 24 | import static org.junit.Assert.assertThrows; |
25 | 25 | import static org.junit.Assert.assertTrue; |
| 26 | +import static org.dizitart.no2.common.util.IndexUtils.deriveIndexMapName; |
| 27 | +import static org.dizitart.no2.filters.FluentFilter.where; |
26 | 28 | import static org.mockito.ArgumentMatchers.anyInt; |
27 | 29 | import static org.mockito.Mockito.doAnswer; |
| 30 | +import static org.mockito.Mockito.doReturn; |
28 | 31 | import static org.mockito.Mockito.mock; |
29 | 32 |
|
30 | 33 | import java.lang.reflect.Field; |
31 | 34 | import java.nio.file.Files; |
32 | 35 | import java.nio.file.Path; |
| 36 | +import java.util.ArrayList; |
| 37 | +import java.util.Collections; |
33 | 38 | import java.util.Iterator; |
| 39 | +import java.util.List; |
| 40 | +import java.util.Set; |
34 | 41 | import java.util.concurrent.CountDownLatch; |
35 | 42 | import java.util.concurrent.ExecutorService; |
36 | 43 | import java.util.concurrent.Executors; |
37 | 44 | import java.util.concurrent.Future; |
38 | 45 | import java.util.concurrent.TimeUnit; |
39 | 46 |
|
| 47 | +import org.dizitart.no2.NitriteConfig; |
| 48 | +import org.dizitart.no2.collection.FindPlan; |
| 49 | +import org.dizitart.no2.collection.NitriteId; |
| 50 | +import org.dizitart.no2.common.DBValue; |
| 51 | +import org.dizitart.no2.common.Fields; |
40 | 52 | import org.dizitart.no2.exceptions.NitriteIOException; |
| 53 | +import org.dizitart.no2.filters.ComparableFilter; |
| 54 | +import org.dizitart.no2.filters.IndexScanFilter; |
| 55 | +import org.dizitart.no2.index.IndexDescriptor; |
| 56 | +import org.dizitart.no2.index.IndexType; |
| 57 | +import org.dizitart.no2.index.NonUniqueIndexer; |
41 | 58 | import org.dizitart.no2.store.NitriteMap; |
42 | 59 | import org.h2.mvstore.MVStore; |
43 | 60 | import org.junit.Test; |
@@ -167,6 +184,78 @@ public void testCompactingClosesAreSerialized() throws Exception { |
167 | 184 | } |
168 | 185 | } |
169 | 186 |
|
| 187 | + @Test |
| 188 | + public void testDropThroughAWrapperWhoseMapIsAlreadyGone() { |
| 189 | + // MVMap.getName() answers null once the map is removed, so a second holder's drop() |
| 190 | + // used to hand removeMap a null name and fail |
| 191 | + final NitriteMVStore store = openInMemoryStore(); |
| 192 | + try { |
| 193 | + NitriteMap<String, String> stale = store.openMap("m", String.class, String.class); |
| 194 | + stale.close(); |
| 195 | + NitriteMap<String, String> current = store.openMap("m", String.class, String.class); |
| 196 | + current.drop(); |
| 197 | + |
| 198 | + stale.drop(); |
| 199 | + assertEquals("m", stale.getName()); |
| 200 | + assertFalse(store.hasMap("m")); |
| 201 | + } finally { |
| 202 | + store.close(); |
| 203 | + } |
| 204 | + } |
| 205 | + |
| 206 | + @Test |
| 207 | + public void testConcurrentFirstReadOfALegacyIndexMigratesOnce() throws Exception { |
| 208 | + // the first multi-threaded read after a reopen used to build one index instance per |
| 209 | + // thread, each migrating and dropping the legacy map; the second drop failed |
| 210 | + final int threads = 16; |
| 211 | + final ExecutorService executorService = Executors.newFixedThreadPool(threads); |
| 212 | + try { |
| 213 | + for (int round = 0; round < 50; round++) { |
| 214 | + final NitriteMVStore store = openInMemoryStore(); |
| 215 | + try { |
| 216 | + final IndexDescriptor desc = new IndexDescriptor(IndexType.NON_UNIQUE, |
| 217 | + Fields.withNames("a"), "c"); |
| 218 | + final NitriteId id = NitriteId.createId(1L); |
| 219 | + store.openMap(deriveIndexMapName(desc), DBValue.class, ArrayList.class) |
| 220 | + .put(new DBValue("k"), new ArrayList<>(Collections.singletonList(id))); |
| 221 | + |
| 222 | + final NitriteConfig config = mock(NitriteConfig.class); |
| 223 | + doReturn(store).when(config).getNitriteStore(); |
| 224 | + final FindPlan plan = new FindPlan(); |
| 225 | + plan.setIndexDescriptor(desc); |
| 226 | + plan.setIndexScanFilter(new IndexScanFilter(Collections.singletonList( |
| 227 | + (ComparableFilter) where("a").eq("k")))); |
| 228 | + |
| 229 | + final NonUniqueIndexer indexer = new NonUniqueIndexer(); |
| 230 | + final CountDownLatch start = new CountDownLatch(1); |
| 231 | + final List<Future<Set<NitriteId>>> reads = new ArrayList<>(); |
| 232 | + for (int t = 0; t < threads; t++) { |
| 233 | + reads.add(executorService.submit(() -> { |
| 234 | + start.await(); |
| 235 | + return indexer.findByFilter(plan, config); |
| 236 | + })); |
| 237 | + } |
| 238 | + start.countDown(); |
| 239 | + for (Future<Set<NitriteId>> read : reads) { |
| 240 | + assertEquals(Collections.singleton(id), read.get(5, TimeUnit.SECONDS)); |
| 241 | + } |
| 242 | + assertFalse(store.hasMap(deriveIndexMapName(desc))); |
| 243 | + } finally { |
| 244 | + store.close(); |
| 245 | + } |
| 246 | + } |
| 247 | + } finally { |
| 248 | + executorService.shutdownNow(); |
| 249 | + } |
| 250 | + } |
| 251 | + |
| 252 | + private NitriteMVStore openInMemoryStore() { |
| 253 | + final NitriteMVStore store = new NitriteMVStore(); |
| 254 | + store.setStoreConfig(new MVStoreConfig()); |
| 255 | + store.openOrCreate(); |
| 256 | + return store; |
| 257 | + } |
| 258 | + |
170 | 259 | private NitriteMVStore createCompactingStore(final MVStore mvStore) throws Exception { |
171 | 260 | final NitriteMVStore nitriteMVStore = new NitriteMVStore(); |
172 | 261 | final MVStoreConfig config = new MVStoreConfig(); |
|
0 commit comments