Transaction/Closure/Object - what's the difference?
Is rollback an intrinsic property of objects?
What's the difference between rolling back a transaction and destroying an object?
Transaction scope and object lifetime are orthogonal issues, not directly related to each other: While performing a UnitOfWork, I may create and discard many individual objects. (Particularly true if the objects are fine-grained, like integer, string, and point.) On the other hand, a transaction may involve calling only one method on an object, changing only a small part of its state.
The ideal relationship between objects and transactions would be that rolling back a transaction would cause all objects involved in that transaction to revert back to their previous state. This is probably only achieved with some OO databases and particularly sophisticated OO to relational mapping products. (I suspect that this issue is one of the main reasons for Microsoft's strange object lifetime rules in MicrosoftTransactionServer.)
You can create objects, like AcidCommand, that directly represent transaction semantics (or database resources). But such objects are the exception rather than the rule. -- JeffGrigg
raise(Employee_number, Raise) -> Closure = fun() -> [Employee] = mnesia:read({employee, Employee_number}), Salary = Employee#employee.salary + Raise, Updated_employee = Employee#employee{salary = Salary}, mnesia:write(Updated_employee) end, mnesia:transaction(Closure).The "raise" function will give an employee, identified by the Employee_number, a raise in the amount of Raise. To do this, it defines a function, bound to the variable "Closure", and asks the mnesia database to execute that function as a transaction (i.e. all the database changes succeed or all fail, etc.)
The function will read the current employee data from the database, update its salary, and write the updated employee data to the database.
-- PatrickLogan
Offhand, it looks to me that closures in this instance are an implementation detail of the MnesiaDatabase interface: I would guess that mnesia:transaction() puts some kind of marker in the closure it's given. mnesia:read() and mnesia:write() pick up this marker to join the transaction. -- JeffGrigg
It's not that magical. The closure is just a regular Erlang closure. The mnesia:transaction function creates a new transaction in a concurrent mnesia process (an Erlang process is a lightweight thread). Then the closure is executed. Each mnesia call will execute in that mnesia process that has an active transaction. Then the mnesia:transaction function commits the transaction in that mnesia process. A concurrent transaction can be executed from another Erlang process. It is not necessary to pass a connection argument to the mnesia:transaction function. A concurrent transaction works with its own mnesia process. The process is the connection. Erlang is efficient enough to execute tens of thousands of concurrent processes. They are very lightweight. --PatrickLogan
A similar concept in the OO world could look like this:
That's it, Jeff. It shows the correspondence between a closure and an object relative to a transaction. --PatrickLogan