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());
}
}
@rofr
Copy link

rofr commented Sep 21, 2011

What are you trying to demonstrate/accomplish? This all looks a bit scary to me, especially the recursive call from the catch block.
How does this relate to prevayler or livedb which will protect the prevalent system (LiveBank) from concurrency issues.

@felipecruz
Copy link
Author

i'm trying to show a lockless 'thread-safe' code that could be used to design prevalent systems..

@felipecruz
Copy link
Author

instead of a recursive call, a loop could be used.. but important thing is that if some thread is processing current ammount and a transaction arrives, it'll try again..

@klauswuestefeld
Copy link

Ask the Prevayler discussion list what they think. Can we do this in a generic way?

@felipecruz
Copy link
Author

I think it's possible..
I also think that, with some improvements, we can achieve a 'clean' rollback, discarding transactions

Thread 1 - deposit 100
Thread 2 - deposit 50
Thread 3 - withdraw 50
Thread 1 - rollback - remove deposit100

account.ammount_now() == 0

@klauswuestefeld
Copy link

I dont understand the example. Use examples with powers of 2 so that different combinations will not add up to the same value.

@felipecruz
Copy link
Author

i really don't see how different combinations can lead to different results.. maybe I'm tired.. :(

@klauswuestefeld
Copy link

If transactions 2 and 3 happen or if they dont happen the result is the same: zero.

@rofr
Copy link

rofr commented Sep 25, 2011

Threading and concurrency is interesting per se but I still don't see how it relates specifically to prevelance. In #liveDB the engine controls access to the model (prevalent system) allowing multiple readers and a single writer. The locking strategy is configurable and can be disabled. This is useful if the model itself is thread safe or if access is known to be single threaded. So far we haven't experienced any performance bottlenecks due to the single writer lock. One way the prevalence framework could assist is providing more granular locks, a feature easily implemented. We should go with transparent concurrency as far as possible.

@felipecruz
Copy link
Author

klaus, you mean.. it's useless to keep those 2 transactions? it could be a snapshot plus more recent transactions?

robert, I think about prevalence as a execution journaling.. and also, that is possible to design concurrent systems trying to avoid shared resources as much as possible by keeping objects history available to be used on a "re-play" (a runtime evaluation of it's state). My point is that we can put this 2 things together. (disabling locking strategy in #livebd like you said) ( and I'm not sure how this model can fit prevayler.. but it can be used on prevalent systems.. )

other than that, this approach (it's not common in Java at least..) has some points that I find very interesting and also I'm asking what you feel about this whole idea.

  • in a concurrent runtime ammount_now(); will always give you a real value, because while it's evaluating, if another thread goes and writes something, ConcurrentModificationException is thrown and it will try evaluate again and the concurrent(new) deposit or withdraw instances will be on transaction list..
  • keep (some)things unlocked(assuming that concurrent access do not happen so often in applications), or at least using the most granular locks as possible using java api.. (if you don't experiences any bottlenecks in your apps, we can think that this point is not a stronger one:) )
  • it can be used (of course not so naively) to implement ACID transactions with rollback without 'heavy' locking and deep copies

plus, it works only with obejcts in memory :)

@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