TestNativeStoreMemoryOverflow.java

/*******************************************************************************
 * Copyright (c) 2015 Eclipse RDF4J contributors, Aduna, 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
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *******************************************************************************/
package org.eclipse.rdf4j.sail.nativerdf;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.io.File;

import org.eclipse.rdf4j.common.iteration.CloseableIteration;
import org.eclipse.rdf4j.common.iteration.Iterations;
import org.eclipse.rdf4j.common.transaction.IsolationLevel;
import org.eclipse.rdf4j.common.transaction.IsolationLevels;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.ValueFactory;
import org.eclipse.rdf4j.repository.Repository;
import org.eclipse.rdf4j.repository.RepositoryConnection;
import org.eclipse.rdf4j.repository.sail.SailRepository;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;

/**
 * @author James Leigh
 */
public class TestNativeStoreMemoryOverflow {
	private Repository testRepository;

	private RepositoryConnection testCon;

	private RepositoryConnection testCon2;

	@BeforeEach
	public void setUp(@TempDir File dataDir) {
		testRepository = createRepository(dataDir);
	}

	private void setupConnections(IsolationLevel level) {
		testCon = testRepository.getConnection();
		testCon.setIsolationLevel(level);
		testCon.clear();
		testCon.clearNamespaces();

		testCon2 = testRepository.getConnection();
		testCon2.setIsolationLevel(level);
	}

	private Repository createRepository(File dataDir) {
		return new SailRepository(new NativeStore(dataDir, "spoc"));
	}

	@AfterEach
	public void tearDown() {
		testCon2.close();
		testCon.close();
		testRepository.shutDown();
	}

	@ParameterizedTest
	@EnumSource(IsolationLevels.class)
	public void test(IsolationLevel level) {
		setupConnections(level);

		int size = 10000; // this should really be bigger
		// load a lot of triples in two different contexts
		testCon.begin();
		ValueFactory vf = testCon.getValueFactory();
		IRI context1 = vf.createIRI("http://my.context.1");
		IRI context2 = vf.createIRI("http://my.context.2");
		IRI predicate = vf.createIRI("http://my.predicate");
		IRI object = vf.createIRI("http://my.object");

		testCon.add(new DynamicIteration(size, predicate, object, vf), context1);
		testCon.add(new DynamicIteration(size, predicate, object, vf), context2);

		assertEquals(size, Iterations.asList(testCon.getStatements(null, null, null, false, context1)).size());
		assertEquals(size, Iterations.asList(testCon.getStatements(null, null, null, false, context2)).size());
		testCon.commit();

		assertEquals(size, Iterations.asList(testCon.getStatements(null, null, null, false, context1)).size());
		assertEquals(size, Iterations.asList(testCon.getStatements(null, null, null, false, context2)).size());

		testCon.close();
	}

	private static final class DynamicIteration implements CloseableIteration<Statement> {

		private final int size;

		private final IRI predicate;

		private final IRI object;

		private final ValueFactory vf;

		private int i;

		private DynamicIteration(int size, IRI predicate, IRI object, ValueFactory vf) {
			this.size = size;
			this.predicate = predicate;
			this.object = object;
			this.vf = vf;
		}

		@Override
		public boolean hasNext() throws RuntimeException {
			return i < size;
		}

		@Override
		public Statement next() throws RuntimeException {
			return vf.createStatement(vf.createIRI("http://my.subject" + i++), predicate, object);
		}

		@Override
		public void remove() throws RuntimeException {
			throw new UnsupportedOperationException();
		}

		@Override
		public void close() throws RuntimeException {
			// no-op
		}
	}
}