Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save felipecruz/1233217 to your computer and use it in GitHub Desktop.
Save felipecruz/1233217 to your computer and use it in GitHub Desktop.
can_you_lead_account_current_ammount_to_inconsistency.java
public class LiveBank implements Serializable{
private Map<Integer, Account> accounts;
private AtomicInteger next_account_number = new AtomicInteger(0);
public LiveBank() {
this.accounts = new HashMap<Integer, Account>(); // concurrent hashmap maybe
}
public Account createAccount() {
int id = this.next_account_number.incrementAndGet();
Account account = new Account(id);
this.accounts.put(new Integer(id), account);
return account;
}
public Account getAccount(Integer id) {
return this.accounts.get(id);
}
public void deposit(int id, int ammount) {
// can collection .add throw ConcurrentModificationException?
// no.. it will not throw
getAccount(id).append_transaction(new Deposit(ammount));
}
public void withdraw(int id, int ammount) {
// can collection .add throw ConcurrentModificationException?
// no it will not throw
getAccount(id).append_transaction(new Withdraw(ammount));
}
}
public interface BankTransaction {
public int apply(int ammount);
}
public class Withdraw implements BankTransaction {
public int ammount;
public Withdraw(int ammount) {
this.ammount = ammount;
}
@Override
public int apply(int ammount) {
ammount -= this.ammount;
return ammount;
}
}
public class Deposit implements BankTransaction {
public int ammount;
public Deposit(int ammount) {
this.ammount = ammount;
}
@Override
public int apply(int ammount) {
ammount += this.ammount;
return ammount;
}
}
public class Account implements Serializable {
public int id;
public Collection<BankTransaction> transactions;
public int ammount = 0;
public Account(int id) {
this.id = id;
// alternatives:
// Vector or Collections.synchronizedList
// or (and i think(we need to test) this is the best option)
// CopyOnWriteArrayList
// http://download.oracle.com/javase/6/docs/api/java/util/concurrent/CopyOnWriteArrayList.html
this.transactions = new ArrayList<BankTransaction>();
}
public append_transaction(Transaction transaction) {
// check atomic_write_counter
// increment_atomic_write_counter
// collection.append
// decrement write counter
}
//instead of locking, why not retry? optimistic design
public int ammount_now() {
int local_ammount = 0;
//try {
for(BankTransaction transaction : this.transactions) {
local_ammount = transaction.apply(local_ammount);
Cool.sleep(3); //do not use this on production :)
if (atomic_write_count > 0) { /* try again since someone is writing */ }
}
//}
/* maybe a retry loop
catch (ConcurrentModificationException ce) {
System.out.println("write fucked read");
// recursive call cause I'm lazy.. is this naive?
local_ammount = this.ammount_now();
} */
return local_ammount;
}
}
public class LiveBankTest {
public static void main(String[] args) {
final LiveBank bank = new LiveBank();
final Account account = bank.createAccount();
Thread t = new Thread(new MyRunnable(bank, account));
t.start();
System.out.println(account.id);
bank.deposit(account.id, 100);
System.out.println(account.ammount_now());
bank.withdraw(account.id, 50);
for (int i = 0; i < 1000; i++) {
System.out.println(i + " - " + account.ammount_now());
Cool.sleep(20);
}
}
}
class MyRunnable implements Runnable {
private LiveBank bank2;
private Account account2;
public MyRunnable(LiveBank bank, Account account) {
this.account2 = account;
this.bank2 = bank;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
bank2.deposit(account2.id, 100);
System.out.println("more 100");
org.prevayler.foundation.Cool.sleep(1000);
}
System.out.println(account2.ammount_now());
}
}
@jsampson
Copy link

I'm not quite understanding what you're trying to do. One quick comment: ConcurrentModificationException has pretty much nothing to do with concurrency. It's primarily used to kill an iterator when the underlying collection is modified during iteration. Methods like add() don't throw it.

@felipecruz
Copy link
Author

if it's used to kill a iterator when underlying collection is modified during iteration, no locking is needed.. since if a writes arrives while a state evaluation is happening, it will try to evaluate again..

I wrote that comment about CMException because i really dont know what can happen if 2 threads executes .add at the same time on a ArrayList (not a Collections.synchronizedList() for instance)

I'm trying to think if it's possible to design java code with a journaling approach(wich is similar to STM) rather than traditional locking/shared variables approach.. and if this approach can be used to implement rollback mechanisms.

how a rollback can be implemented on prevayler today?

correct me if I'm wrong.. In prevayler, when a command arrives, it's executed and serialized on disk (or serialized and executed). To get a rollback function, is possible to discard this operation on disk deleting from transactions log but how can we rollback in memory?

prevayler already keep track of all executed commands.. so it's possible do replay those trancastions with rollbacked operations discarded.. in a real case, replay all Prevalent system transactions is a expensive process but what about always "replay" in smaller objects like account?

if my point ins't clear.. i'll try to explain as much as I can.. but I'm not a great english writer :(

@jsampson
Copy link

No, I should have said more strongly: ConcurrentModificationException is not intended for detecting modification by different threads. If two different threads attempt to add to an unsynchronized ArrayList, anything can happen. One or the other thing might be added, one or the other might fail with some runtime exception, one or the other could hang in an infinite loop. You can't count on any particular behavior with unsynchronized data structures accessed by multiple threads.

@felipecruz
Copy link
Author

ok.. you're right about concurrent modification exception.. http://download.oracle.com/javase/6/docs/api/java/util/ConcurrentModificationException.html

but same thing would be possible with a immutable list (http://functionaljava.org/) and AtomicReference.. or with an atomic integer counting writes and evaluation loop (ammount_now()) checking this counter,, right? if it's dirty, try again..

do you think that those approaches are broken?

@felipecruz
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment