/* * Copyright (c) [2016] [ <ether.camp> ] * This file is part of the ethereumJ library. * * The ethereumJ library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The ethereumJ library 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the ethereumJ library. If not, see <http://www.gnu.org/licenses/>. */ package org.ethereum.db; import org.ethereum.config.SystemProperties; import org.ethereum.datasource.DbSource; import org.ethereum.datasource.MemSizeEstimator; import org.ethereum.datasource.WriteCache; import org.ethereum.datasource.inmem.HashMapDB; import org.junit.Assert; import org.junit.Test; import java.util.Collections; import java.util.concurrent.CountDownLatch; import static org.ethereum.datasource.MemSizeEstimator.ByteArrayEstimator; import static org.ethereum.util.ByteUtil.intToBytes; /** * Created by Anton Nashatyrev on 23.12.2016. */ public class FlushDbManagerTest { @Test public void testConcurrentCommit() throws Throwable { // check that FlushManager commit(Runnable) is performed atomically final HashMapDB<byte[]> db1 = new HashMapDB<>(); final HashMapDB<byte[]> db2 = new HashMapDB<>(); final WriteCache<byte[], byte[]> cache1 = new WriteCache.BytesKey<>(db1, WriteCache.CacheType.SIMPLE); cache1.withSizeEstimators(ByteArrayEstimator, ByteArrayEstimator); final WriteCache<byte[], byte[]> cache2 = new WriteCache.BytesKey<>(db2, WriteCache.CacheType.SIMPLE); cache2.withSizeEstimators(ByteArrayEstimator, ByteArrayEstimator); final DbFlushManager dbFlushManager = new DbFlushManager(SystemProperties.getDefault(), Collections.<DbSource>emptySet(), null); dbFlushManager.addCache(cache1); dbFlushManager.addCache(cache2); dbFlushManager.setSizeThreshold(1); final CountDownLatch latch = new CountDownLatch(1); final Throwable[] exception = new Throwable[1]; new Thread() { @Override public void run() { try { for (int i = 0; i < 300; i++) { final int i_ = i; dbFlushManager.commit(new Runnable() { @Override public void run() { cache1.put(intToBytes(i_), intToBytes(i_)); try { Thread.sleep(5); } catch (InterruptedException e) {} cache2.put(intToBytes(i_), intToBytes(i_)); } }); } } catch (Throwable e) { exception[0] = e; e.printStackTrace(); } finally { latch.countDown(); } } }.start(); new Thread() { @Override public void run() { try { while (true) { dbFlushManager.commit(); Thread.sleep(5); } } catch (Exception e) { exception[0] = e; e.printStackTrace(); } finally { latch.countDown(); } } }.start(); new Thread() { @Override public void run() { try { int cnt = 0; while (true) { synchronized (dbFlushManager) { for (; cnt < 300; cnt++) { byte[] val1 = db1.get(intToBytes(cnt)); byte[] val2 = db2.get(intToBytes(cnt)); if (val1 == null) { Assert.assertNull(val2); break; } else { Assert.assertNotNull(val2); } } } } } catch (Exception e) { exception[0] = e; e.printStackTrace(); } finally { latch.countDown(); } } }.start(); latch.await(); if (exception[0] != null) throw exception[0]; } }