[Zope3-dev] EventService, references, and subscription semantics

Steve Alexander steve@cat-box.net
Mon, 25 Feb 2002 15:24:04 +0000


This is an email to discuss various design decisions around the 
EventService.

At present there is checked into Zope3 a file-system level EventService, 
  which is configured by zcml files.
An object can subscribe to this FS EventService, if it wants to receive 
events.
The FS EventService keeps a list of subscribers, stored by python object 
reference.
There is a zcml directive for subscribing a thing to the FS EventService.


Of course, if you subscribe something, and then restart z3.py, you have 
to resubscribe it. So, subscriptions are not persisted.

This makes it difficult for a persistent object (in the ZODB) to be a 
subscriber of the FS EventService, as it won't know when z3.py is 
restarted, so it won't know when it needs to resubscribe to the FS 
EventService.

So, only non-persistent things can subscribe to the FS EventService.

So, considering an FS EventService and a persistent ZODB EventService, 
we now have two distinct semantics for the subscribe() method:

   * a subscription that persists over server restarts

   * a subscription that is lost at server restart

However, there is only one interface. This needs fixing.


Even if the FS EventService could store subscriptions between restarts 
of z3.py (for example, in a DBM file), how would it store the references 
to objects?
It could perhaps store them by location, and traverse the ZODB to get 
the object. But then, we have different ways of subscribing an object 
depending on whether it is a singleton object attached to a module (for 
example, an FS Service), or it is a persistent object.

We also have three ways of refering to a subscribed object:

   * a direct python object (or persistent object) reference

   * a traversal path

   * a fully qualified resolvable python foo.bar.baz thing

(We could also have a third: the ruid. However, the ObjectHub (which 
manages ruids) depends on the EventService. So, the EventService can't 
use ruids to do its job, otherwise we get an infinite regress of 
ObjectHubs.)


Let's say we have a persistent ZODB EventService. Persistent objects uss 
its subscribe() method to subscribe themselves to it.
Well, FS services can't subscribe to it using that same method, because, 
even though the subscription is persisted between restarts, a singleton 
FS Service is an object attached to a module, and will be a brand new 
object each time z3.py restarts.

So, we'd need to have two distinct subscribe methods:

   * subscribe()  for persistent objects

   * transientSubscribe()  for non-persistent objects

And of course, some extra complexity in managing two collections of 
subscriptions.


One solution would be to say that the FS EventService may only be 
subscribed to by non-persistent objects, and the root ZODB EventService 
may only be subscribed to by persistent objects.

However, we'd need a separate interface for the FS EventService, as its 
subscribe() method would have different semantics than the persistent 
EventService.

We could have something like this:

      ---------------------        ------------------
      | IBaseEventService |        | ISubscribable  |
      |===================|        |================|
      | publishEvent      |        | subscribe      |
      ---------------------        | unsubscribe    |
               ^                   ------------------
               |                          ^
               |                          |
               +-------------------+      |
               |                   |      |
      ------------------------    -------------------
      | IFSEventService      |    | IEventService   |
      |======================|    |=================|
      | transientSubscribe   |    |                 |
      | transientUnsubscribe |    -------------------
      ------------------------

So, transient (ie non persisted) subscription is kept distinct from 
subscription that persists between z3.py invocations.
FS things are only allowed to use the FSEventService.
Most code that wants to use the event service will get it by asking for 
an IEventService. The root ZODB event service will propagate events to 
the FSEventService.

Of course, it might turn out that we don't really need an 
FSEventService. Although, I can see it being useful for using the 
ComponentArchitecture outside of Zope3.


Then, we still have the issue of what to use for references.
We can use object references provided we're happy to only allow 
persistent objects to be passed to subscribe().

We might want to have a range of subscribe methods:

   * subscribePersistentObject

   * subscribeTraversalLocation

   * subscribeTransientObject    (held onto by a weak reference perhaps)

   * subscribeRuidObject  (for subscribing an object via an object hub)



Anyway, there's a bunch of thoughts and conclusions.

I'd really like to refactor the EventService along the lines of the UML 
diagram above, as I don't like having messy and conflicting semantics 
for interfaces.

As for the rest; tell me what you think.

--
Steve Alexander