/* Copyright (c) 2014 Boundless and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Distribution License v1.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/org/documents/edl-v10.html * * Contributors: * Gabriel Roldan (Boundless) - initial implementation */ package org.locationtech.geogig.repository; import java.io.File; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.management.MemoryUsage; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.locationtech.geogig.api.Node; import org.locationtech.geogig.api.ObjectId; import org.locationtech.geogig.api.Platform; import org.locationtech.geogig.api.RevObject.TYPE; import org.locationtech.geogig.api.TestPlatform; import org.locationtech.geogig.storage.NodeStorageOrder; import com.google.common.base.Stopwatch; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; import com.vividsolutions.jts.geom.Envelope; public abstract class AbstractNodeIndexTest extends Assert { private static final MemoryMXBean MEMORY_MX_BEAN = ManagementFactory.getMemoryMXBean(); @Rule public TemporaryFolder tempFolder = new TemporaryFolder(); private ExecutorService executorService; private NodeIndex index; @Before public void before() { tempFolder.newFolder(".geogig"); File workingDirectory = tempFolder.getRoot(); Platform platform = new TestPlatform(workingDirectory); executorService = Executors.newFixedThreadPool(4); index = createIndex(platform, executorService); } protected abstract NodeIndex createIndex(Platform platform, ExecutorService executorService); @After public void after() { index.close(); executorService.shutdownNow(); } @Test public void testEmpty() { Iterator<Node> nodes = index.nodes(); assertNotNull(nodes); assertFalse(nodes.hasNext()); } @Test public void test1k() throws Exception { testNodes(1000); } @Test public void testOrder1k() throws Exception { testOrder(1000); } @Test public void test10k() throws Exception { testNodes(1000 * 10); } @Test public void testOrder10k() throws Exception { testOrder(1000 * 10); } @Test public void test100k() throws Exception { int count = 1000 * 100; testNodes(count); } @Test public void testOrder100k() throws Exception { int count = 1000 * 100; testOrder(count); } @Test public void test1M() throws Exception { int count = 1000 * 1000; testNodes(count); } @Test public void testOrder1M() throws Exception { int count = 1000 * 1000; testOrder(count); } @Ignore @Test public void test5M() throws Exception { testNodes(1000 * 1000 * 5); } @Ignore @Test public void test10M() throws Exception { testNodes(1000 * 1000 * 10); } @Ignore @Test public void test25M() throws Exception { testNodes(1000 * 1000 * 25); } @Ignore @Test public void test50M() throws Exception { testNodes(1000 * 1000 * 50); } private void testNodes(final int count) throws Exception { MemoryUsage initialMem = MEMORY_MX_BEAN.getHeapMemoryUsage(); Stopwatch sw = Stopwatch.createStarted(); for (int i = 0; i < count; i++) { index.add(node(i)); } Iterator<Node> nodes = index.nodes(); assertNotNull(nodes); System.err.printf("Added %,d nodes to %s index in %s. Traversing...\n", count, index .getClass().getSimpleName(), sw.stop()); MemoryUsage indexCreateMem = MEMORY_MX_BEAN.getHeapMemoryUsage(); sw.reset().start(); int size = Iterators.size(nodes); System.err.printf("Traversed %,d nodes in %s\n", size, sw.stop()); MemoryUsage indexTraversedMem = MEMORY_MX_BEAN.getHeapMemoryUsage(); final double mbFactor = 1024 * 1024; System.err .printf("Initial memory usage: %.2fMB, after creating index: %.2fMB, after traversing: %.2fMB\n", (initialMem.getUsed() / mbFactor), (indexCreateMem.getUsed() / mbFactor), (indexTraversedMem.getUsed() / mbFactor)); assertEquals(count, size); if (count >= 1000 * 1000) { System.gc(); Thread.sleep(1000); System.gc(); Thread.sleep(1000); MemoryUsage afterGCMem = MEMORY_MX_BEAN.getHeapMemoryUsage(); System.err.printf("Mem usage after GC: %.2fMB\n", (afterGCMem.getUsed() / mbFactor)); } System.err.println(); } private void testOrder(final int count) throws Exception { List<Node> expected = new ArrayList<Node>(count); for (int i = 0; i < count; i++) { Node node = node(i); index.add(node); expected.add(node); } Collections.sort(expected, new NodeStorageOrder()); Iterator<Node> nodeIterator = index.nodes(); List<Node> actual = Lists.newArrayList(nodeIterator); assertEquals(expected.size(), actual.size()); for (int i = 0; i < expected.size(); i++) { Node expectedNode = expected.get(i); Node actualNode = actual.get(i); assertEquals("At index " + i, expectedNode, actualNode); } } private Node node(int i) { String name = String.valueOf(i); return Node.create(name, ObjectId.forString(name), ObjectId.NULL, TYPE.FEATURE, new Envelope(i, i + 1, i, i + 1)); } }