History for HowTriggersWork
??changed:
-
The !ZPatterns Event Model
The (Sub)Transaction Cycle
The !ZPatterns event model is based heavily upon the Zope transaction model. Every Zope web hit represents an atomic transaction which will either be completely executed, or completely cancelled. Within that overall transaction, Zope allows checkpoints or "subtransactions" to occur. In Zope, subtransactions are currently used mainly as a way to reduce memory requirements for transactions which change large numbers of objects. In !ZPatterns, however, a subtransaction commit can also be used to signal that a batch of related changes have been finished and that it's okay for rules to fire, data to be saved to external databases, and so on.
Why is this important? Well, lots of things that involve updating external databases or checking to see if rules should fire are pretty expensive in performance terms. You don't want a bunch of rules being checked every time you change an attribute, for performance reasons alone. But besides performance reasons, you may also have business rules that require complete information in order to be evaluated, and thus may require that several attributes be set before you can begin checking the rule. So !ZPatterns was designed to wait until the transaction is in the process of committing before checking for rules to fire and updating external databases.
This approach does have a few drawbacks, however. The first and most obvious is that if you need to see the effects of the fired rules before you can do something else, you can't. The second is that the time it takes to actually commit a transaction can be extended somewhat. (This latter is only likely to be an issue for transactions which include changes to both ZODB and non-ZODB data during the commit phase.)
Both drawbacks are rather uncommon, and can be easily taken care of using subtransactions. By calling Zope's 'get_transaction().commit(1)' operation, a subtransaction commit is signalled. !ZPatterns will fire all appropriate rules, and the ZODB will commit any intermediate results at that point to temporary storage. (And if the rules call any SQL methods or otherwise manipulate external databases, they will also take effect at that point.)
Throughout this document we refer to "(sub)transactions" to indicate that each subtransaction commit is handled the same as a full transaction commit. Rules fire, caches are cleared, change flags and mementoes are reset, and so on. This means that there is some performance penalty involved with committing a subtransaction compared to waiting till full transaction commit, so you probably only want to use subtransactions when you want to 1) see the effects of rule firings in the same transaction, or 2) have a complex non-ZODB operation in the same transaction with a ZODB operation and you want to avoid blocking other ZODB commits for any longer than necessary. In case 1, you would issue a subtransaction commit at the point where you would like the rules to take effect, and in case 2, you would issue a subtransaction commit just before the main transaction commit so that the complex operation would occur outside the blocking phase of the main commit operation.
Point-of-View
What constitutes an event in !ZPattterns? What makes an object (or an attribute) considered to be added, changed, or deleted? It all depends on your point of view... literally.
DataSkins work by asking their DataManagers ([Racks] and [Customizers]) to give them a list of data plug-ins which support a particular interface (e.g. set an attribute) for a particular target (e.g. an attribute name). The !DataSkin then sends events to the plug-ins or calls their methods to perform operations on the DataSkin's behalf. It is then the job of the data plug-ins to determine what the overall result is, from their own point of view.
This means that under most circumstances, you can think of events as being seen according to the point of view of the DataManagers involved. (The exception is when a plug-in is shared between DataManagers, which would mean you have to think from that plug-in's point of view.) So, for example, if a !DataSkin is moved from one folder to another using Zope cut-and-paste, and the two folders are served by different [Customizers], then the plug-ins in the customizer for the origin folder will see that transaction as a 'DELETED' event, and the destination folder's customizer's plug-ins will see that transaction as an 'ADDED' event. But if both folders were served by the same Customizer, that customizer's plug-ins would see the transaction as a single 'CHANGED' event.
RuleAgents (such as those compiled from !SkinScript declarations) see each (sub)transaction as having only one outcome per !DataSkin: either 'ADDED', 'CHANGED', or 'DELETED'. The same !DataSkin could have been added, deleted, re-added, and then changed, all in the same transaction, but the end result would be 'ADDED' from the agent's point of view. (Because the object was not present at the beginning of the transaction, and a transition from non-presence to presence is an 'add'.)
Notice, by the way that being 'ADDED' does not necessarily mean that the object was *created* in this (sub)transaction. ZPatterns' internal event model actually does have an '_objectCreating()' event, but it can only be fired for rack-mounted DataSkins at present due to limitations in Zope's overall event model. This means that the 'INITIALIZE OBJECT WITH' statement in !SkinScript is only useful in [Racks] at this time.
Event Cascading
What happens if you have a rule that makes changes to the !DataSkin it applies to? It depends upon the sequence of the rules. If the first rule to fire makes changes, then subsequent rules will simply see that later state as what they should commit. However, if a rule makes changes after another rule has already fired, the changes will be invisible to the first rule. *Rules and triggers get only one chance to fire per (sub)transaction.* This means that you must pay careful attention to the order you place your rules in, so that everything will happen as it should.
Precedence, Search Order, and Wildcards
- monitoring/notification sequence
- DataSkins search for providers to read/set/delete attributes
- note '*' lookups secondary to explicit name lookups