Closed brianoliver closed 11 years ago
Many things about the listener API seem strange: What purpose does the CacheEntryEventFilter fulfill?
The filter is used on registration of a listener. It allows the caching implementation to filter out events that should be sent to the listener. This can be more efficient than sending events to a listener.
Does a synchronous listener also have to honor ordering of events within a given key?
Yes. For example in Coherence a key is owned by a single server in the cluster which handles locking and ordering.
If the old value is requested but the operation that triggers the event doesn't normally require it - must I retrieve it? (What about an eventual model here - must the old value be correct?)
The only listeners to which the old value is require are those that where registered stating so, otherwise the old value is not required. Obviously there is extra cost which is why this is a parameter.
If the listener is asynchronous can I batch events up and fire a bunch of them together?
Yes. The CacheEventListener explains the contract:
Listeners which are "asynchronous" may be delivered and iterate through multiple events with an undefined ordering.
What do value and old-value mean for a removed event?
New value is null. Old value is the value that was removed.
CacheEntryEvent doesn't encode the event type within it - so it isn't really an event at all.
Neither does Java’s EventObject which is our superclass. It doesn’t need it as the caching implementation directs the event to the right listener.
What's going on with the listener behavior for transactional caches - is it useful to someone or anyone?
You can similarly argue of the usefulness and behaviour of data base triggers which have external effects. These cannot be rolled back unless the external effect is part of a transaction and is synchronous. Listeners can have external effects and are therefore similar.
Are transactions useful to anyone?
That is a separate debate.
Can you register the same listener multiple times with different filters? I ask this because this is one potential advantage I could see for filters, they could extract out commonality between a number of listeners.
Yes, of course.
Are transactions useful to anyone?
You're kidding right? :-)
There has been further comments from Terracotta. Summarising and answering them below:
Ordering of events for a Synchronous Listener
I have clarified the JavaDoc and added
Should Fillers be Serializeable?
Current API doesn't make this a requirement. In a distributed system it is expected that these will be serialized and shipped around to the JVM with the data, but to provide maximum flexibility we are not requiring Serializable. Coherence, GridGain and Gigaspaces have their own network classloading.
Returning oldValues
Just to clarify, when requireOldValue is specified, then on cache.remove() the old value is returned. If you don't want that, then don't require old value when registering.
No ordering guarantee on asynchronous avents.
Clarified this to "
i.e. we should guarantee that events on the same key are processed in order by async listeners.
It would be useful to store an event type in the CacheEntryEvent
Ok. Created EventType enum and added it to CacheEntryEvent. This will be useful for implementers, particularly for asynchronous listeners.
Can you register new listener instances with the same filter?
Yes.
On the subject of listeners on transactional caches, what I'm questioning is how useful the current restrictions on when events fire are. Currently listeners on transactional caches fire on the mutations happening, not on commit (i.e. they are not transactionally isolated). For some users this may be useful, but I think many people would want fire on commit - at least as an option.
Listeners are somewhat analogous to AFTER triggers in the database world so studying precedents on what AFTER means in the context of transactions is helpful. So which databases support apply on COMMIT?
For PostGres, they can be fired either at the end of the statement causing the triggering event, or at the end of the containing transaction; in the latter case they are said to be deferred. See http://www.postgresql.org/docs/9.1/static/sql-createtrigger.html
Within the Transaction. See http://dba.stackexchange.com/questions/35025/trigger-in-combination-with-transaction
While the feature is not unheard of it is very rare in the database world. Firing inside the transaction is the norm.
However there is the problem where a listener causes a change to a nontransactional system which cannot be rolled back such as appending to a file or sending an email. It does make sense that you might want this to happen only if the transaction succeeds. For cases where the listener makes changes to a transactional system within the transaction such as updating another cache or sending via JMS, then rollback should return all systems to the pre state.
The end user can always write code to perform these non rollbackable actions after the transaction. So do the transaction and if that works send an email.
So, in conclusion I think this is a rare edge case that the user can easily deal with themselves and what we are doing by firing listeners in the transaction is correct.
I'm not sure I buy this analogy to be honest. My understanding of this kind of database trigger is that it's primarily used for two things:
Since my understanding is that this kind of self-mutilation is not supported from within our listeners (it would cause locking nightmares for implementors) I fail to see how the analogy holds up.
I think what Greg is saying and what the specification tries to define is that Listeners, whether transactional or not, are an implementation of the Observer Pattern. Specifically, when a developer implements and registers a Listener, the contract is that the said Listener(s) will be notified AFTER some Event has occurred.
In the context of transactions (which I don't necessarily agree with on principle), Listeners are fired AFTER each atomic cache (non-commit) operation. Personally I think they would be better to be fired AFTER commit but I can see the reason why AFTER a cache operation is ok.
Perhaps the Transactional Cache implementations need this as a configuration parameter? Listeners options may be: a). not supported at all, b). fire after each statement, c). fire after commit.
Chris the full description in CacheEntryListener is:
I think the three of us should get on a call to work through this.
After discussion resolved to clarify the JavaDoc around self-mutation and deadlock.
It is generally thought acceptable that the end user can deal with the after commit actions with their program statement. Implementations can also offer additional modes for listeners beyond the spec.
Removing unformatted version.