/* * Copyright 2009-2016 Tilmann Zaeschke. All rights reserved. * * This file is part of ZooDB. * * ZooDB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ZooDB is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ZooDB. If not, see <http://www.gnu.org/licenses/>. * * See the README and COPYING files for further information. */ package org.zoodb.test.jdo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import javax.jdo.Constants; import javax.jdo.Extent; import javax.jdo.JDOHelper; import javax.jdo.PersistenceManager; import javax.jdo.PersistenceManagerFactory; import javax.jdo.Query; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.zoodb.jdo.ZooJdoProperties; import org.zoodb.test.api.TestSuper; import org.zoodb.test.testutil.TestTools; import org.zoodb.tools.ZooConfig; /** * This test performs create, update, query, extent and delete operations in parallel with * multiple threads on a single PersistenceManager. * * @author ztilmann * */ public class Test_025_SingleSessionConcurrency { private final int N = 10000; private final int COMMIT_INTERVAL = 250; private final int T = 8; private final static ArrayList<Throwable> errors = new ArrayList<>(); @BeforeClass public static void beforeClass() { ZooConfig.setFileManager(ZooConfig.FILE_MGR_IN_MEMORY); } @AfterClass public static void afterClass() { ZooConfig.setDefaults(); } @Before public void setUp() { TestTools.removeDb(); TestTools.createDb(); TestTools.defineSchema(TestSuper.class); TestTools.defineIndex(TestSuper.class, "_id", false); } @After public void tearDown() { TestTools.closePM(); try { TestTools.removeDb(); } catch (IllegalStateException e) { errors.add(e); } if (!errors.isEmpty()) { RuntimeException e = new RuntimeException("errors: " + errors.size(), errors.get(0)); for (Throwable t: errors) { e.addSuppressed(t); } errors.clear(); throw e; } } private void checkErrors() { if (!errors.isEmpty()) { RuntimeException e = new RuntimeException("errors: " + errors.size(), errors.get(0)); for (Throwable t: errors) { e.addSuppressed(t); } errors.clear(); throw e; } } private abstract static class Worker extends Thread { final PersistenceManager pm; final int N; final int COMMIT_INTERVAL; int n = 0; final int ID; private Worker(int id, int n, int commitInterval, PersistenceManager pm) { this.ID = id; this.pm = pm; this.N = n; this.COMMIT_INTERVAL = commitInterval; } @Override public void run() { try { runWorker(); } catch (Throwable t) { Test_025_SingleSessionConcurrency.errors.add(t); t.printStackTrace(); } } abstract void runWorker(); } private static class Reader extends Worker { private Reader(int id, int n, PersistenceManager pm) { super(id, n, -1, pm); } @SuppressWarnings("unchecked") @Override public void runWorker() { Extent<TestSuper> ext = pm.getExtent(TestSuper.class); for (TestSuper t: ext) { assertTrue(t.getData()[0] >= 0 && t.getData()[0] < N); TestSuper t2 = (TestSuper) pm.getObjectById( JDOHelper.getObjectId(t) ); assertEquals(t.getId(), t2.getId()); if (t.getId() == ID && t.getTime() < N/2) { n++; } } Collection<TestSuper> col = (Collection<TestSuper>) pm.newQuery( TestSuper.class, "_id == " + ID + " && _time >= " + (N/2)).execute(); for (TestSuper t: col) { assertTrue(t.getId() == ID); assertTrue(t.getData()[0] >= 0 && t.getData()[0] < N); TestSuper t2 = (TestSuper) pm.getObjectById( JDOHelper.getObjectId(t) ); assertEquals(t.getId(), t2.getId()); n++; } } } private static class Writer extends Worker { private final ArrayList<Object> oids = new ArrayList<>(); private Writer(int id, int n, int commitInterval, PersistenceManager pm) { super(id, n, commitInterval, pm); } @Override public void runWorker() { for (int i = 0; i < N; i++) { TestSuper o = new TestSuper(i, ID, new long[]{i}); pm.makePersistent(o); oids.add(pm.getObjectId(o)); n++; } } } private static class Deleter extends Worker { private Deleter(int id, int n, int commitInterval, PersistenceManager pm) { super(id, n, commitInterval, pm); } @SuppressWarnings("unchecked") @Override public void runWorker() { Query q = pm.newQuery(TestSuper.class, "_id==" + ID); Collection<TestSuper> col = (Collection<TestSuper>)q.execute(); Iterator<TestSuper> iter = col.iterator(); while (iter.hasNext()) { pm.deletePersistent(iter.next()); n++; if (n % COMMIT_INTERVAL == 0) { //start a new query, just for fun... col = (Collection<TestSuper>) q.execute(); iter = col.iterator(); } } } } private static class Updater extends Worker { private static final int DELTA = 100; private final ArrayList<Object> oids = new ArrayList<Object>(); private Updater(int id, int n, int commitInterval, ArrayList<Object> oids, PersistenceManager pm) { super(id, n, commitInterval, pm); this.oids.addAll(oids); } @Override public void runWorker() { while (n < oids.size()) { TestSuper t = (TestSuper) pm.getObjectById(oids.get(n)); n++; t.setId(t.getId()+DELTA); } } } /** * Test concurrent read. * @throws InterruptedException */ @Test public void testParallelRead() throws InterruptedException { PersistenceManager pm = TestTools.openPM(); pm.setMultithreaded(true); pm.currentTransaction().begin(); //write Writer w = new Writer(0, N, COMMIT_INTERVAL, pm); w.start(); w.join(); pm.currentTransaction().commit(); pm.currentTransaction().begin(); //read ArrayList<Reader> readers = new ArrayList<>(); for (int i = 0; i < T; i++) { readers.add(new Reader(0, N, pm)); } for (Reader reader: readers) { reader.start(); } for (Reader reader: readers) { reader.join(); } checkErrors(); for (Reader reader: readers) { assertEquals("id=" + reader.ID, N, reader.n); } pm.currentTransaction().commit(); TestTools.closePM(); } /** * Test concurrent write. * @throws InterruptedException */ @Test public void testParallelWrite() throws InterruptedException { PersistenceManager pm = TestTools.openPM(); pm.setMultithreaded(true); pm.currentTransaction().begin(); //write ArrayList<Writer> writers = new ArrayList<>(); for (int i = 0; i < T; i++) { writers.add(new Writer(0, N, COMMIT_INTERVAL, pm)); } for (Writer w: writers) { w.start(); } for (Writer w: writers) { w.join(); assertEquals(N, w.n); } //read Reader r = new Reader(0, N, pm); r.start(); r.join(); assertEquals(N * T, r.n); pm.currentTransaction().commit(); TestTools.closePM(); } /** * Test concurrent write. * @throws InterruptedException */ @Test public void testParallelReadWrite() throws InterruptedException { PersistenceManager pm = TestTools.openPM(); pm.setMultithreaded(true); pm.currentTransaction().begin(); //read and write ArrayList<Thread> workers = new ArrayList<>(); for (int i = 0; i < T; i++) { workers.add(new Reader(i, N, pm)); workers.add(new Writer(i, N, COMMIT_INTERVAL, pm)); } for (Thread w: workers) { w.start(); } for (Thread w: workers) { w.join(); if (w instanceof Writer) { assertEquals(N, ((Writer)w).n); } } //read only ArrayList<Reader> readers = new ArrayList<>(); for (int i = 0; i < T; i++) { readers.add(new Reader(i, N, pm)); } for (Reader reader: readers) { reader.start(); } for (Reader reader: readers) { reader.join(); assertEquals(N, reader.n); } pm.currentTransaction().commit(); TestTools.closePM(); } /** * Updates object in parallel (each object by one thread only). * @throws InterruptedException */ @Test public void testParallelUpdater() throws InterruptedException { PersistenceManager pm = TestTools.openPM(); pm.setMultithreaded(true); pm.currentTransaction().begin(); //read and write ArrayList<Writer> writers = new ArrayList<>(); for (int i = 0; i < T; i++) { writers.add(new Writer(i, N, COMMIT_INTERVAL, pm)); } for (Writer w: writers) { w.start(); } for (Writer w: writers) { w.join(); assertEquals(N, w.n); } pm.currentTransaction().commit(); pm.currentTransaction().begin(); //update objects in parallel (no object touched twice) ArrayList<Updater> updaters = new ArrayList<>(); for (int i = 0; i < T; i++) { updaters.add(new Updater(i, N, COMMIT_INTERVAL, writers.get(i).oids, pm)); } for (Updater w: updaters) { w.start(); } for (Updater w: updaters) { w.join(); assertEquals(N, w.n); } pm.currentTransaction().commit(); pm.currentTransaction().begin(); //read only ArrayList<Reader> readers = new ArrayList<>(); for (int i = 0; i < T; i++) { readers.add(new Reader(i+Updater.DELTA, N, pm)); } for (Reader reader: readers) { reader.start(); } for (Reader reader: readers) { reader.join(); assertEquals("ID=" + reader.ID, N, reader.n); } pm.currentTransaction().commit(); TestTools.closePM(); } /** * Update the same objects concurrently from several threads. */ @Test public void testConcurrentUpdater() throws InterruptedException { PersistenceManager pm = TestTools.openPM(); pm.setMultithreaded(true); pm.currentTransaction().begin(); //read and write Writer w = new Writer(0, N, COMMIT_INTERVAL, pm); w.start(); w.join(); pm.currentTransaction().commit(); pm.currentTransaction().begin(); //update objects concurrently (objects updated concurrently) ArrayList<Updater> updaters = new ArrayList<>(); for (int i = 0; i < T; i++) { updaters.add(new Updater(0, N, COMMIT_INTERVAL, w.oids, pm)); } for (Updater u: updaters) { u.start(); } for (Updater u: updaters) { u.join(); assertEquals(N, w.n); } pm.currentTransaction().commit(); pm.currentTransaction().begin(); //read only Reader r = new Reader(T*Updater.DELTA, N, pm); r.start(); r.join(); assertTrue("N="+N + " r.n="+ r.n, N >= r.n); assertTrue(r.n > 0); for (Object oid: w.oids) { TestSuper t = (TestSuper) pm.getObjectById(oid); //All objects should show at least one increment assertTrue(t.getId() >= Updater.DELTA); } pm.currentTransaction().commit(); TestTools.closePM(); } @Test public void testParallelDeleter() throws InterruptedException { PersistenceManager pm = TestTools.openPM(); pm.setMultithreaded(true); pm.currentTransaction().begin(); //write ArrayList<Writer> writers = new ArrayList<>(); for (int i = 0; i < T; i++) { writers.add(new Writer(i, N, COMMIT_INTERVAL, pm)); } for (Writer w: writers) { w.start(); } for (Writer w: writers) { w.join(); assertEquals(N, w.n); } pm.currentTransaction().commit(); pm.currentTransaction().begin(); //delete ArrayList<Deleter> workers = new ArrayList<>(); for (int i = 0; i < T; i++) { workers.add(new Deleter(i, N, COMMIT_INTERVAL, pm)); } for (Deleter w: workers) { w.start(); } for (Deleter w: workers) { w.join(); assertEquals(N, w.n); } pm.currentTransaction().commit(); TestTools.closePM(); } @Test public void testPmAPI() { PersistenceManager pm = TestTools.openPM(); pm.currentTransaction().begin(); assertFalse(pm.getMultithreaded()); pm.setMultithreaded(true); assertTrue(pm.getMultithreaded()); pm.setMultithreaded(false); pm.currentTransaction().rollback(); TestTools.closePM(); } @Test public void testPmfAPI() { PersistenceManager pm = TestTools.openPM(); PersistenceManagerFactory pmf = pm.getPersistenceManagerFactory(); pm.close(); assertFalse(pmf.getMultithreaded()); pm = pmf.getPersistenceManager(); assertFalse(pm.getMultithreaded()); pm.close(); pmf.setMultithreaded(true); assertTrue(pmf.getMultithreaded()); pm = pmf.getPersistenceManager(); assertTrue(pm.getMultithreaded()); pm.close(); pmf.setMultithreaded(false); assertFalse(pmf.getMultithreaded()); pm = pmf.getPersistenceManager(); assertFalse(pm.getMultithreaded()); pm.close(); pmf.close(); } @Test public void testConfig() { ZooJdoProperties props = TestTools.getProps(); props.setMultiThreaded(true); PersistenceManager pm = TestTools.openPM(props); pm.currentTransaction().begin(); pm.setMultithreaded(true); assertTrue(pm.getMultithreaded()); pm.setMultithreaded(false); pm.currentTransaction().rollback(); TestTools.closePM(); props = TestTools.getProps(); props.setMultiThreaded(false); assertFalse(Boolean.parseBoolean(props.getProperty(Constants.PROPERTY_MULTITHREADED))); pm = TestTools.openPM(props); pm.currentTransaction().begin(); assertFalse(pm.getMultithreaded()); pm.setMultithreaded(false); pm.currentTransaction().rollback(); TestTools.closePM(); } }