Monday, May 3, 2010

Update Triggers Fired Unnecessarily

Recently, I was working on some performance profiling on some QAQC scripts in Electric Office. It turns out that the big bottleneck was in the trigger and business rule code that was being called during the QAQC routine run. In my review of the business rules, I saw that a lot of the business rules were being called unnecessarily. That is, the update triggers were being called on objects where no material change had occurred to their attributes. If you call record_transaction.new_update(proposed_values).run() and the proposed_values are all the same as the existing values for those attributes, the triggers and business rules will still be fired. Because some of the business rules called code that was expensive to run, this added unnecessary time to the whole process.

Please be aware of this whenever you write code with record_transaction.new_update() and make sure that you only update attributes where the value has actually changed. The record_transaction API will not optimize your proposed_values list for you.

If you compile the following code into your image and then run any “slow” process, you will be notified of all the times a record gets updated with nothing new, causing the business rules to be fired unnecessarily…


_pragma(classify_level=basic)
_method record_transaction.new_update( record, values, _optional description )
##
## Creates an instance which specifies the update of a record.
##
## RECORD - the record to be updated
##
## VALUES - a property_list keyed on field name.
## The elements of this property_list provide proposed values
## for physical, geometry and simple valued join fields, and
## information for updating multi valued join fields.
##
## DESCRIPTION - String description on transactions to be used
## for undo messages.
##

# BEGIN CHANGE
# DO NOT PERMANENTLY PUT THIS INTO PRODUCTION CODE!!!! THIS IS
# FOR DEBUG PURPOSES ONLY.
_local anything_changed? << _false
_for k,v _over values.fast_keys_and_elements()
_loop
_if record.perform(k) <> v
_then
anything_changed? << _true
_endif
_endloop

_if _not anything_changed?
_then
write("Nothing changed for: ",record)
print(values)
# put a traceback in here to figure out which code is calling
# unnecessary new_update()
#!traceback!()
_endif
# END CHANGE

>> _clone.init( :update, _unset, record, values, description )

_endmethod
$

No comments: