/* * 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.assertTrue; import java.io.File; import java.util.Collection; import javax.jdo.JDOHelper; import javax.jdo.PersistenceManager; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; import org.zoodb.api.DBArrayList; import org.zoodb.jdo.ZooJdoHelper; import org.zoodb.test.api.PersistentDummyImpl; import org.zoodb.test.testutil.TestTools; import org.zoodb.tools.ZooCheckDb; import org.zoodb.tools.ZooConfig; public class Test_100_FreeSpaceManager { @Before public void before() { TestTools.removeDb(); //Config.setFileManager(Config.FILE_MGR_IN_MEMORY); //Config.setFileProcessor(Config.FILE_PAF_BB_MAPPED_PAGE); //Config.setFilePageSize(Config.FILE_PAGE_SIZE_DEFAULT * 4); TestTools.createDb(); TestTools.defineSchema(TestClass.class); //TestTools.defineSchema(TestClassTiny.class); } @After public void after() { TestTools.closePM(); ZooCheckDb.enableStringOutput(); ZooCheckDb.main(new String[]{}); } @Test public void testObjectsRollback() { final int MAX = 1000; File f = new File(TestTools.getDbFileName()); long len1 = f.length(); //create some object PersistenceManager pm = TestTools.openPM(); pm.currentTransaction().begin(); for (int i = 0; i < MAX; i++) { TestClass tc = new TestClass(); pm.makePersistent(tc); //Object oidP = pm.getObjectId(tc); } pm.currentTransaction().rollback(); TestTools.closePM(); assertEquals(len1, f.length()); } @SuppressWarnings("unchecked") @Test public void testObjectsReusePagesDeleted() { final int MAX = 1000; File f = new File(TestTools.getDbFileName()); //First, create objects PersistenceManager pm = TestTools.openPM(); pm.currentTransaction().begin(); for (int i = 0; i < MAX; i++) { TestClass tc = new TestClass(); pm.makePersistent(tc); } pm.currentTransaction().commit(); pm.currentTransaction().begin(); //now delete them Collection<TestClass> col = (Collection<TestClass>) pm.newQuery(TestClass.class).execute(); for (TestClass tc: col) { pm.deletePersistent(tc); } pm.currentTransaction().commit(); TestTools.closePM(); //check length long len1 = f.length(); //create objects pm = TestTools.openPM(); pm.currentTransaction().begin(); for (int i = 0; i < MAX; i++) { TestClass tc = new TestClass(); pm.makePersistent(tc); //Object oidP = pm.getObjectId(tc); } pm.currentTransaction().commit(); TestTools.closePM(); //check that the new Objects reused previous pages //w/o FSM, the values were 274713 vs 401689 //w/o #2 380920 / 524280 int ps = ZooConfig.getFilePageSize(); // System.out.println("l1=" + len1/ps + " l2=" + f.length()/ps); assertTrue("l1=" + len1/ps + " l2=" + f.length()/ps, len1*1.1 > f.length()); } @SuppressWarnings("unchecked") @Test public void testObjectsReusePagesDeletedMulti() { final int MAX = 2000; final int MAX_ITER = 50; File f = new File(TestTools.getDbFileName()); long len1 = -1; for (int j = 0; j < MAX_ITER; j++) { //First, create objects PersistenceManager pm = TestTools.openPM(); pm.currentTransaction().begin(); for (int i = 0; i < MAX; i++) { TestClass tc = new TestClass(); pm.makePersistent(tc); } pm.currentTransaction().commit(); //also close tx every now and then if (j % 3 == 0) { TestTools.closePM(); pm = TestTools.openPM(); } pm.currentTransaction().begin(); //now delete them Collection<TestClass> col = (Collection<TestClass>) pm.newQuery(TestClass.class).execute(); for (TestClass tc: col) { pm.deletePersistent(tc); } pm.currentTransaction().commit(); TestTools.closePM(); //check length only from 3rd iteration on... if (j == 3) { len1 = f.length(); } } //check that the new Objects reused previous pages int ps = ZooConfig.getFilePageSize(); // System.out.println("l1=" + len1/ps + " l2=" + f.length()/ps); assertTrue("l1=" + len1/ps + " l2=" + f.length()/ps, (len1*1.1 > f.length()) || (f.length()/ps - len1/ps < 20)); } @Test public void testObjectsReusePagesDroppedMulti() { final int MAX = 2000; final int MAX_ITER = 50; File f = new File(TestTools.getDbFileName()); long len1 = -1; for (int j = 0; j < MAX_ITER; j++) { //First, create objects PersistenceManager pm = TestTools.openPM(); pm.currentTransaction().begin(); for (int i = 0; i < MAX; i++) { TestClass tc = new TestClass(); pm.makePersistent(tc); } pm.currentTransaction().commit(); //also close tx every now and then if (j % 3 == 0) { TestTools.closePM(); pm = TestTools.openPM(); } pm.currentTransaction().begin(); //now delete them ZooJdoHelper.schema(pm).getClass(TestClass.class).dropInstances(); pm.currentTransaction().commit(); TestTools.closePM(); //check length only from 3rd iteration on... if (j == 3) { len1 = f.length(); } } //check that the new Objects reused previous pages int ps = ZooConfig.getFilePageSize(); // System.out.println("l1=" + len1/ps + " l2=" + f.length()/ps); assertTrue("l1=" + len1/ps + " l2=" + f.length()/ps, (len1*1.1 > f.length()) || (f.length()/ps - len1/ps < 20)); } @Test public void testObjectsReusePagesDeletedSchema() { TestTools.removeSchema(TestClass.class); final int MAX = 2000; final int MAX_ITER = 50; long l = 0; File f = new File(TestTools.getDbFileName()); long len1 = -1; for (int j = 0; j < MAX_ITER; j++) { //First, create objects PersistenceManager pm = TestTools.openPM(); pm.currentTransaction().begin(); ZooJdoHelper.schema(pm).addClass(TestClass.class); ZooJdoHelper.schema(pm).getClass(TestClass.class).createIndex("_int" , false); ZooJdoHelper.schema(pm).getClass(TestClass.class).createIndex("_long" , true); for (int i = 0; i < MAX; i++) { TestClass tc = new TestClass(); tc.setLong(l++); pm.makePersistent(tc); } pm.currentTransaction().commit(); //also close tx every now and then if (j % 3 == 0) { TestTools.closePM(); pm = TestTools.openPM(); } pm.currentTransaction().begin(); //now delete them ZooJdoHelper.schema(pm).getClass(TestClass.class).dropInstances(); ZooJdoHelper.schema(pm).getClass(TestClass.class).removeIndex("_int"); //we try to drop _long implicitly. ZooJdoHelper.schema(pm).getClass(TestClass.class).remove(); pm.currentTransaction().commit(); TestTools.closePM(); //check length only from 3rd iteration on... if (j == 3) { len1 = f.length(); } } //check that the new Objects reused previous pages int ps = ZooConfig.getFilePageSize(); // System.out.println("l1=" + len1/ps + " l2=" + f.length()/ps); assertTrue("l1=" + len1/ps + " l2=" + f.length()/ps, (len1*1.1 > f.length()) || (f.length()/ps - len1/ps < 20)); } @SuppressWarnings("unchecked") @Test public void testObjectsReusePagesDirtyObjects() { final int MAX = 1000; File f = new File(TestTools.getDbFileName()); //First, create objects PersistenceManager pm = TestTools.openPM(); pm.currentTransaction().begin(); for (int i = 0; i < MAX; i++) { TestClass tc = new TestClass(); pm.makePersistent(tc); } pm.currentTransaction().commit(); TestTools.closePM(); //now make them dirty pm = TestTools.openPM(); pm.currentTransaction().begin(); Collection<TestClass> col1 = (Collection<TestClass>) pm.newQuery(TestClass.class).execute(); for (TestClass tc: col1) { JDOHelper.makeDirty(tc, null); } pm.currentTransaction().commit(); TestTools.closePM(); //check length long len1 = f.length(); //now make them dirty again, this should reuse pages of the original objects pm = TestTools.openPM(); pm.currentTransaction().begin(); Collection<TestClass> col = (Collection<TestClass>) pm.newQuery(TestClass.class).execute(); for (TestClass tc: col) { JDOHelper.makeDirty(tc, null); } pm.currentTransaction().commit(); TestTools.closePM(); //check that the new Objects reused previous pages //w/o FSM, the values were 274713 vs 401689 //w/o #2 380920 / 524280 int ps = ZooConfig.getFilePageSize(); // assertEquals(len1/1024, f.length()/1024); assertTrue("l1=" + len1/ps + " l2=" + f.length()/ps, len1*1.1 > f.length()); } @SuppressWarnings("unchecked") @Test public void testObjectsReusePagesAfterCommitOnly() { final int MAX = 1000; File f = new File(TestTools.getDbFileName()); //First, create objects PersistenceManager pm = TestTools.openPM(); pm.currentTransaction().begin(); for (int i = 0; i < MAX; i++) { TestClass tc = new TestClass(); tc.setInt(14); pm.makePersistent(tc); //Object oidP = pm.getObjectId(tc); } pm.currentTransaction().commit(); //check length long len1 = f.length(); //now delete them and create new ones. pm.currentTransaction().begin(); Collection<TestClass> col = (Collection<TestClass>) pm.newQuery(TestClass.class).execute(); for (TestClass tc: col) { pm.deletePersistent(tc); } //create objects for (int i = 0; i < MAX; i++) { TestClass tc = new TestClass(); tc.setInt(18); pm.makePersistent(tc); //Object oidP = pm.getObjectId(tc); } pm.currentTransaction().commit(); //ensure that the new object got written and the previous one disappeared from the indices pm.currentTransaction().begin(); col = (Collection<TestClass>) pm.newQuery(TestClass.class).execute(); for (TestClass tc: col) { assertEquals(18, tc.getInt()); pm.deletePersistent(tc); } pm.currentTransaction().rollback(); TestTools.closePM(); //check that the new Objects did NOT reuse previous pages //w/o FSM, the values were 258329 vs 381209 int ps = ZooConfig.getFilePageSize(); assertTrue("l1=" + len1/ps + " l2=" + f.length()/ps, len1*1.4 < f.length()); } /** * Test with multi-page objects */ @SuppressWarnings("unchecked") @Test public void testObjectsReusePagesWithLargeObjects() { final int MAX = 10; final int SIZE = 100000; byte[] ba = new byte[SIZE]; File f = new File(TestTools.getDbFileName()); //First, create objects PersistenceManager pm = TestTools.openPM(); pm.currentTransaction().begin(); for (int i = 0; i < MAX; i++) { TestClass tc = new TestClass(); tc.setByteArray(ba); pm.makePersistent(tc); //Object oidP = pm.getObjectId(tc); } pm.currentTransaction().commit(); pm.currentTransaction().begin(); //now delete them Collection<TestClass> col = (Collection<TestClass>) pm.newQuery(TestClass.class).execute(); for (TestClass tc: col) { pm.deletePersistent(tc); } pm.currentTransaction().commit(); TestTools.closePM(); //check length long len1 = f.length(); //create objects pm = TestTools.openPM(); pm.currentTransaction().begin(); for (int i = 0; i < MAX; i++) { TestClass tc = new TestClass(); tc.setByteArray(ba); pm.makePersistent(tc); //Object oidP = pm.getObjectId(tc); } pm.currentTransaction().commit(); TestTools.closePM(); //check that the new Objects reused previous pages //w/o FSM, the values were 1179929 vs 2212121 //assertEquals(len1/1024, f.length()/1024); int ps = ZooConfig.getFilePageSize(); assertTrue("l1=" + len1/ps + " l2=" + f.length()/ps, len1*1.1 > f.length()); } /** * Test with multi-page objects */ @SuppressWarnings("unchecked") @Test public void testObjectsDoNotReusePagesWithOverlappingObjects() { final int MAX = 100; final int SIZE = 10000; //multi-page object must be likely int nTotal = 0; byte[] ba1 = new byte[SIZE]; byte[] ba2 = new byte[SIZE]; for (int i = 0; i < SIZE; i++) { ba1[i] = 11; ba2[i] = 13; } //First, create objects PersistenceManager pm = TestTools.openPM(); pm.currentTransaction().begin(); for (int i = 0; i < MAX; i++) { TestClass tc = new TestClass(); tc.setByteArray(ba1); pm.makePersistent(tc); nTotal++; //Object oidP = pm.getObjectId(tc); } pm.currentTransaction().commit(); pm.currentTransaction().begin(); //now delete them Collection<TestClass> col = (Collection<TestClass>) pm.newQuery(TestClass.class).execute(); int n = 0; for (TestClass tc: col) { if ((n++ % 2) == 0) { pm.deletePersistent(tc); nTotal--; } } pm.currentTransaction().commit(); TestTools.closePM(); //create objects pm = TestTools.openPM(); pm.currentTransaction().begin(); for (int i = 0; i < MAX; i++) { TestClass tc = new TestClass(); tc.setByteArray(ba2); pm.makePersistent(tc); nTotal++; } pm.currentTransaction().commit(); TestTools.closePM(); // now check objects pm = TestTools.openPM(); pm.currentTransaction().begin(); col = (Collection<TestClass>) pm.newQuery(TestClass.class).execute(); n = 0; for (TestClass tc: col) { n++; byte[] ba = tc.getBytaArray(); int b0 = ba[0]; for (byte b2: ba) { assertEquals(b0, b2); } } assertEquals(nTotal, n); pm.currentTransaction().commit(); TestTools.closePM(); } /** * test batch loading. */ @SuppressWarnings("unchecked") @Test public void testFsmBug1_BatchLoading() { //System.out.println("Batch-test"); TestTools.defineSchema(PersistentDummyImpl.class); PersistenceManager pm = null; Object oid = null; pm = TestTools.openPM(); pm.currentTransaction().begin(); DBArrayList<Object> dbv = new DBArrayList<Object>(); // dbv.add("TestString"); for (int i = 0 ; i < 100; i++) { dbv.add(new PersistentDummyImpl()); } // dbv.add("TestString2"); pm.makePersistent(dbv); oid = pm.getObjectId(dbv); pm.currentTransaction().commit(); pm.close(); pm = null; // pm = TestTools.openPM(); // pm.currentTransaction().begin(); // dbv = (DBArrayList<Object>) pm.getObjectById(oid); long t1 = System.currentTimeMillis(); // for (Object o: dbv) { // o.hashCode(); // } long t2 = System.currentTimeMillis(); // System.out.println("NORMAL: " + (t2 - t1)); // pm.currentTransaction().commit(); // pm.close(); // pm = null; // // pm = TestTools.openPM(); // pm.currentTransaction().begin(); // dbv = (DBArrayList<Object>) pm.getObjectById(oid); // t1 = System.currentTimeMillis(); // dbv.setBatchSize(1000); // for (Object o: dbv) { // o.hashCode(); // } // t2 = System.currentTimeMillis(); // System.out.println("BATCHED: " + (t2 - t1)); // pm.currentTransaction().commit(); // pm.close(); // pm = null; // // //Close the store and load the stuff // pm = TestTools.openPM(); // pm.currentTransaction().begin(); // dbv = (DBArrayList<Object>) pm.getObjectById(oid); // t1 = System.currentTimeMillis(); // dbv.setBatchSize(1); // for (Object o: dbv) { // o.hashCode(); // } // t2 = System.currentTimeMillis(); // System.out.println("NORMAL: " + (t2 - t1)); // pm.currentTransaction().commit(); // pm.close(); // pm = null; // // //Close the store and load the stuff // pm = TestTools.openPM(); // pm.currentTransaction().begin(); // dbv = (DBArrayList<Object>) pm.getObjectById(oid); // t1 = System.currentTimeMillis(); // dbv.setBatchSize(0); // for (Object o: dbv) { // o.hashCode(); // } // t2 = System.currentTimeMillis(); // System.out.println("BATCHED: " + (t2 - t1)); // pm.currentTransaction().commit(); // pm.close(); // pm = null; //Close the store, load the stuff and test with transient object pm = TestTools.openPM(); pm.currentTransaction().begin(); dbv = (DBArrayList<Object>) pm.getObjectById(oid); PersistentDummyImpl dummyTrans = new PersistentDummyImpl(); dbv.add(13, dummyTrans); t1 = System.currentTimeMillis(); dbv.setBatchSize(0); for (Object o: dbv) { o.hashCode(); } t2 = System.currentTimeMillis(); assertEquals(dummyTrans, dbv.get(13)); System.out.println("BATCHED: " + (t2 - t1)); pm.currentTransaction().commit(); pm.close(); pm = null; //Close the store, load the stuff and test with modified object pm = TestTools.openPM(); pm.currentTransaction().begin(); dbv = (DBArrayList<Object>) pm.getObjectById(oid); ((PersistentDummyImpl)dbv.get(18)).setData(new byte[]{15}); t1 = System.currentTimeMillis(); dbv.setBatchSize(0); for (Object o: dbv) { o.hashCode(); } t2 = System.currentTimeMillis(); assertEquals(15, ((PersistentDummyImpl)dbv.get(18)).getData()[0]); System.out.println("BATCHED but dirty: " + (t2 - t1)); pm.currentTransaction().rollback(); TestTools.closePM(); //TODO use setBatch() also for all other tests to verify batch loading! //Or call these tests here again, outside the store.! } /** * test batch loading. */ @SuppressWarnings("unchecked") @Test public void testFsmBug1_BatchLoading2() { //System.out.println("Batch-test 2"); TestTools.defineSchema(PersistentDummyImpl.class); PersistenceManager pm = null; Object oid = null; try { pm = TestTools.openPM(); pm.currentTransaction().begin(); DBArrayList<Object> dbv = new DBArrayList<Object>(); for (int i = 0 ; i < 120; i++) { dbv.add(new PersistentDummyImpl()); } pm.makePersistent(dbv); oid = pm.getObjectId(dbv); pm.currentTransaction().commit(); pm.close(); pm = null; pm = TestTools.openPM(); pm.currentTransaction().begin(); dbv = (DBArrayList<Object>) pm.getObjectById(oid); dbv.setBatchSize(110); for (Object o: dbv) { o.getClass(); } pm.currentTransaction().commit(); } finally { if (pm != null) { if (pm.currentTransaction().isActive()) { pm.currentTransaction().rollback(); } pm.close(); } } } @AfterClass public static void tearDown() { TestTools.removeDb(); ZooConfig.setFilePageSize(ZooConfig.FILE_PAGE_SIZE_DEFAULT); } }