/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package com.github.geophile.erdo.systemtest.lockmanagement; import com.github.geophile.erdo.Database; import com.github.geophile.erdo.OrderedMap; import com.github.geophile.erdo.TransactionCallback; import com.github.geophile.erdo.TransactionRolledBackException; import com.github.geophile.erdo.apiimpl.DatabaseImpl; import com.github.geophile.erdo.transaction.Transaction; import com.github.geophile.erdo.transaction.DeadlockException; import java.io.IOException; import java.util.Random; import java.util.logging.Level; import java.util.logging.Logger; public class ErdoTestThread extends Thread { public void run() { int deadlockAborts = 0; int conflictAborts = 0; try { for (int i = 0; i < nTransactions; i++) { boolean committed = false; StringBuilder description = new StringBuilder(); do { transaction = transaction(); int from; int to; do { from = random.nextInt(nAccounts); to = random.nextInt(nAccounts); } while (from == to); int amount = MIN_AMOUNT + random.nextInt(MAX_AMOUNT); AccountId fromAccountId = new AccountId(from); AccountId toAccountId = new AccountId(to); try { Account fromAccount = account(fromAccountId); Account toAccount = account(toAccountId); if (LOG.isLoggable(Level.INFO)) { LOG.log(Level.INFO, "{0}: Attempt transfer of {1} from {2} to {3}", new Object[]{transaction, amount, fromAccountId, toAccountId}); } description.setLength(0); description.append(String.format("%s, %s", fromAccount, toAccount)); fromAccount.balance(fromAccount.balance() - amount); toAccount.balance(toAccount.balance() + amount); description.append(String.format(" -> %s, %s", fromAccount, toAccount)); accounts.ensurePresent(fromAccount); accounts.ensurePresent(toAccount); db.commitTransactionAsynchronously(transactionCallback); committed = true; if (LOG.isLoggable(Level.INFO)) { LOG.log(Level.INFO, "{0}: Transferred {1}: {2}", new Object[]{transaction, amount, description}); } } catch (DeadlockException e) { LOG.log(Level.INFO, "{0}: transfer of {1} from {2} to {3} failed due to deadlock", new Object[]{transaction, amount, fromAccountId, toAccountId}); deadlockAborts++; } catch (TransactionRolledBackException e) { LOG.log(Level.INFO, "{0}: transfer of {1} from {2} to {3} failed due to conflict", new Object[]{transaction, amount, fromAccountId, toAccountId}); conflictAborts++; } } while (!committed); } } catch (Throwable th) { LOG.log(Level.SEVERE, "Caught exception", th); termination = th; } finally { LOG.log(Level.INFO, "{0} transactions, {1} deadlock aborts, {2} conflict aborts, termination: {3}", new Object[]{nTransactions, deadlockAborts, conflictAborts, (termination == null ? null : termination.getMessage())}); } } public Throwable termination() { return termination; } public ErdoTestThread(int threadId, Database db, OrderedMap accounts, int nTransactions, int nAccounts) { this.threadId = threadId; this.db = db; this.accounts = accounts; this.nTransactions = nTransactions; this.nAccounts = nAccounts; this.random = new Random(System.nanoTime()); } private Account account(AccountId accountId) throws IOException, InterruptedException { return (Account) accounts.find(accountId); } private Transaction transaction() { return ((DatabaseImpl)db).factory().transactionManager().currentTransaction(); } private static final int MIN_AMOUNT = 1; private static final int MAX_AMOUNT = 100; private static final Logger LOG = Logger.getLogger(ErdoTestThread.class.getName()); private final int threadId; private final int nTransactions; private final int nAccounts; private final Database db; private final OrderedMap accounts; private final Random random; private Throwable termination; private Transaction transaction; private volatile int commitCount = 0; private final TransactionCallback transactionCallback = new TransactionCallback() { public void whenDurable(Object commitInfo) { } public void whenSuspect(Object commitInfo) { LOG.log(Level.SEVERE, "Suspect transaction!"); } }; }