Filed under: Best Practices, Featured
Comments: 1
The pre-commit function is a server scoped function that flushes the instances in the current transaction to the persistence layer and locks them to the current transaction. If another transaction tries to read this resource, it either reads stale data or is denied access.
Difference between commit and pre-commit functions
The pre-commit function flushes a unit of work to the persistence layer but does not close the transaction. The commit function ends the current transaction and persists all the changes in the transaction to the database, which can be accessed by any other user or transaction.
The following example shows how a new Person instance can be created, pushed to the persistence layer, and read and updated from within the same transaction.
1 2 3 4 5 6 7 8 9 10 11 |
(define person ()) (define person1 ()) (Person'new (: lastName "Doe") (: firstName "John")) (pre-commit) (set! person (read-instance Person '() '(and (= (@ lastName) "Doe") (= (@ firstName) "John")) #f)) (person'affix "Mr") (pre-commit) (set! person1 (read-instance Person '() '(and (= (@ lastName) "Doe") (= (@ firstName) "John")) #f)) (person’affix) |
The data committed using the pre-commit function is not available for other transactions to access until the current transaction is committed.
In the following example, the read instance within the begin-transaction code block does not return any result as the data has not been released.
1 2 3 4 5 6 |
(Person'new (: lastName "Doe") (: firstName "Jane")) (pre-commit) (begin-transaction (read-instance Person '() '(and (= (@ lastName) " Doe ") (= (@ firstName) " Jane")) #f) ) |
The CRUD events and validations that are called for the commit and pre-commit functions are the same. If any instance is already pre-committed, and there has been no change to the object, the CRUD events and the validations on commit are bypassed, and the related rows in the database are released. This means that any transaction can now access the data. The validations and the CRUD events are called only once since the object hasn’t been modified after the pre-commit.
In an example where a firstName object is updated after the pre-commit, the update and commit events are called along with any validations for firstName.
1 2 3 4 5 6 7 8 9 10 |
(define person ()) (Person'new (: lastName "Doe") (: firstName "John")) (pre-commit) (commit) (Person'new (: lastName "Doe") (: firstName "Jane")) (pre-commit) (set! person (read-instance Person '() '(and (= (@ lastName) "Doe") (= (@ firstName) "Jane")) #f)) (person'firstName "Janice") (commit) |
When to use pre-commit
You can use the pre-commit function when executing a large commit that may exceed the maximum instance count in the unit of work. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
(for-each-page Act '() '() '() '() 1000 #f (lambda (page) ; Process each Act in the page (for-each (lambda (act) (process act) ) page ) ; Pre-commit the work done on the page (pre-commit) ) ) |
In this example, there are a large number of “Acts” to commit so a pre-commit would ideally flush each page of 1000 Acts to the persistence layer and circumvent the maximum instance count limit.
Note: The number of instances in a single pre-commit cannot exceed the maxChangeCount.
You can use the pre-commit function when two transactions need to be executed automatically and the second transaction cannot fail if the first transaction succeeds. For example:
1 2 3 4 5 |
begin-transaction (A) (pre-commit) (B) ) |
You can use the pre-commit function to reduce memory usage when multiple changes are kept in memory, and when dealing with a large number of instances, which may cause performance issues. These issues can be overcome by using pre-commit because it pushes the data from memory to the persistence layer.
You can also use the pre-commit function when an OID for an instance is required but the instance should not be available to other transactions. For example:
1 2 3 4 5 |
(define person ()) (Person'new (: lastName "Doe") (: firstName "Jack")) (pre-commit) (set! person (read-instance Person '() '(and (= (@ lastName) "Doe") (= (@ firstName) "Jack")) #f)) (person':oid) |
Rolling back on pre-commit failure
To roll back a failed pre-commit, you should roll back to the last successful pre-commit rather than going back to the last commit point. This avoids rolling back all the work that has been successfully pre-committed in the transaction. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
(for-each-page Person '() '() '() '() 1000 #f (lambda (page) (try ;try the entire page (begin (for-each (lambda (item) (performAction item) ) page ) (pre-commit) ;Commits data, but only visible in this transaction ) ;catch (lambda (e) (((invocation-context)'unitOfWork)'rollback #f) ) ) |
If the rollback function is used instead of this approach, the transaction is rolled back to the last successful commit. For example, if the transaction has gone through 6000 Persons, has pre-committed successfully six times (once for each page), and fails the seventh time, then the following command rolls back to the sixth successful pre-commit:
1 |
(((invocation-context)'unitOfWork)'rollback #f) |
The following command rolls back to a state at the start of this transaction and discards all pre-commits.
1 |
(((invocation-context)'unitOfWork)'rollback #t) or (rollback) |
When not to use pre-commit
You should not call the pre-commit function during the commit event in the object life cycle as this causes an exception because of the circular commit.
When using the pre-commit function, you should be careful when you are deleting any objects as this may lead to an exception if the deleted objects are still referenced in memory.
Archana Dandwate
May 26, 2016 at 6:31 am
Thanks for sharing helpful information