Up until this point I was of the belief that roll-up summary operations didn't interfere with custom Apex and were calculated quietly in the backend. However, I am seeing otherwise.
Consider the following configuration:
- Master object: Invoice
- Detail object: Item
- Trigger on before-update of Invoice -> inserts a child Item
- Trigger on after-insert of Item -> clones Items, then explicitly updates them
Since ItemTrigger is running on after-insert it clones the records in order to be able to update them (this arrangement is part of a generalized complex business logic and is not changeable -i.e. cannot use before-insert).
When an Invoice record is edited I receive this log:
CODE_UNIT_STARTED InvoiceTrigger on Invoice trigger event BeforeUpdate for [a0J36000003g4we] ... DML_BEGIN |Op:Insert|Type:Item|Rows:1 CODE_UNIT_STARTED ItemTrigger on Item trigger event AfterInsert for [a0L3600000B2WkR] ... DML_BEGIN |Op:Update|Type:Item|Rows:1 DML_END | CODE_UNIT_FINISHED ItemTrigger on Item trigger event AfterInsert for [a0L3600000B2WkR] DML_END | EXCEPTION_THROWN |System.DmlException: Insert failed. First exception on row 0; first error: SELF_REFERENCE_FROM_TRIGGER, Object (id = a0J36000003g4we) is currently in trigger InvoiceTrigger, therefore it cannot recursively update itself:  CODE_UNIT_FINISHED InvoiceTrigger on Invoice trigger event BeforeUpdate for [a0J36000003g4we]
Line 24 of InvoiceTrigger is this:
Line 18 of ItemTrigger is this:
Assuming I didn't mix anything up during my debugging session I am surprised by the explicit database call. Is this at all expected?!