You are viewing a plain text version of this content. The canonical link for it is here.
Posted to proton@qpid.apache.org by Rafael Schloming <rh...@alum.mit.edu> on 2014/01/15 19:35:12 UTC

engine API improvements

Hi Everyone,

In a recent thread I alluded to some improvements I have in mind for the
engine API and promised a separate post on the topic, so here it is.

There are two areas where I'm interested in extending the API. These
extensions are strictly speaking independent of each other, but at the same
time somewhat complimentary in their utility.

The first area would be adding a formal Container class to the engine API.
If you are familiar with the AMQP specification then you might have noticed
that while the engine API models connections, sessions, and links pretty
faithfully to how they are defined in the specification, one notable
difference is that the engine API doesn't explicitly model the concept of
an AMQP Container. I believe adding such a class to the engine API would
allow us to refactor some of the more generally useful stuff that Messenger
does into the engine proper, while leaving the less desirable stuff (such
as blocking behaviour and hard coded driver) in messenger proper.

The second area is providing an event oriented interface into the engine.
One of the original ideas behind the engine design is that you don't need
event callbacks because the engine itself is simply a state machine, and
changes in its state are only ever triggered by calling into the engine
API. This means that whenever you make such a call you can simply query to
see if anything relevant has changed. While this is strictly speaking true,
the whole "query to see what has changed part" is responsible for a fair
amount of biolerplate code in using the engine and makes for a steeper
learning curve than is desirable. So the idea behind an event oriented
interface is to provide a uniform way of discovering what has changed that
has a strong analog to the event callback systems most people are used to,
but at the same time doesn't go quite so far as to use callbacks.

The details of this event oriented interface are somewhat TBD, but my
thinking right now on this is to introduce a Collector object that can be
registered with an arbitrary number of Containers and/or Connections. The
Collector will work in concert with an Event class.

The Event class will contain optional references to at most one of each of
the following: Container, Connection, Session, Link, Delivery. The event
class will also have type, i.e. an enum field indicating what of interest
may have occurred.

When something of interest happens to a Container, Connection, Session,
Link, or Delivery, the library will check to see if there is a Collector
registered, and if so create/initialize an Event that points to the
relevant engine objects and indicates the appropriate type. Note that no
callback occurs here, the Collector just holds the event until the
application comes looking for it.

The Collector of course has an API for accessing and consuming any events.

There are a couple of important principles to note. As stated above there
are no callbacks in this interface, this makes it easy to swig into other
languages, however it should be only a few lines of code to write a
dispatch loop on top of the swigged version of this API, i.e.:

  for event in collector:
    dispatch(event)

Another important point is that Events are simple transient value objects.
They don't carry any significant state in and of themselves, rather they
are simply pointers to part of the existing engine state machine that may
be of interest. The protocol state is still entirely encapsulated in the
engine objects proper, not in the Events.

It's also worth noting that I believe both of these extensions are purely
additive, i.e. would not require changing any existing API, simply adding
new API.

Hopefully this type of event oriented interface will provide something that
people will have a bit of an easier time digesting/using, but even so, I
think it also provides some interesting avenues of future exploration. I
believe it would be possible to define a concept of
interceptors/filter/whatever that could be configured into a Collector. We
could use this to provide reusable/preconfigured behaviours, e.g. if you
wanted to use the engine API but didn't want to have to worry about credit,
you could install an interceptor that would handle all the credit events
for you. Ultimately when combining this interface with the Container
concept and introducing Container level events, I believe we could even
provide reusable chunks of logic that could handle all of the
connection/session/link establishment stuff and thereby provide a single
integrated API that would offer the full range of trade-offs between
Messenger's simplicity and engine's flexibility.

I'd like to flesh this idea out a bit more befire diving in. As a first
step I'm working on documenting the existing proton design more thoroughly.
You can see what I have so far here, although it is very much in progress
still:

  https://cwiki.apache.org/confluence/display/qpid/Proton+Architecture

Once the above document is more complete I will produce a version of the
UML that describes in more detail what I have outlined above as a delta to
the existing design.

--Rafael

Re: engine API improvements

Posted by Ted Ross <tr...@redhat.com>.
Both of these proposals sound like improvements to me.

I'll point out one consideration that I've had to deal with in 
Dispatch.  The Engine data structures are such that it can be used in a 
multi-threaded application as long as no two threads are handling the 
same connection at the same time.  By connection, I mean the connection 
and any sessions, links, and deliveries associated with that connection.

You might want to consider making Collectors specific to connections, so 
a thread servicing the events in the collector will not touch something 
that's being handled concurrently by another thread.

-Ted


On 01/15/2014 01:35 PM, Rafael Schloming wrote:
> Hi Everyone,
>
> In a recent thread I alluded to some improvements I have in mind for the
> engine API and promised a separate post on the topic, so here it is.
>
> There are two areas where I'm interested in extending the API. These
> extensions are strictly speaking independent of each other, but at the same
> time somewhat complimentary in their utility.
>
> The first area would be adding a formal Container class to the engine API.
> If you are familiar with the AMQP specification then you might have noticed
> that while the engine API models connections, sessions, and links pretty
> faithfully to how they are defined in the specification, one notable
> difference is that the engine API doesn't explicitly model the concept of
> an AMQP Container. I believe adding such a class to the engine API would
> allow us to refactor some of the more generally useful stuff that Messenger
> does into the engine proper, while leaving the less desirable stuff (such
> as blocking behaviour and hard coded driver) in messenger proper.
>
> The second area is providing an event oriented interface into the engine.
> One of the original ideas behind the engine design is that you don't need
> event callbacks because the engine itself is simply a state machine, and
> changes in its state are only ever triggered by calling into the engine
> API. This means that whenever you make such a call you can simply query to
> see if anything relevant has changed. While this is strictly speaking true,
> the whole "query to see what has changed part" is responsible for a fair
> amount of biolerplate code in using the engine and makes for a steeper
> learning curve than is desirable. So the idea behind an event oriented
> interface is to provide a uniform way of discovering what has changed that
> has a strong analog to the event callback systems most people are used to,
> but at the same time doesn't go quite so far as to use callbacks.
>
> The details of this event oriented interface are somewhat TBD, but my
> thinking right now on this is to introduce a Collector object that can be
> registered with an arbitrary number of Containers and/or Connections. The
> Collector will work in concert with an Event class.
>
> The Event class will contain optional references to at most one of each of
> the following: Container, Connection, Session, Link, Delivery. The event
> class will also have type, i.e. an enum field indicating what of interest
> may have occurred.
>
> When something of interest happens to a Container, Connection, Session,
> Link, or Delivery, the library will check to see if there is a Collector
> registered, and if so create/initialize an Event that points to the
> relevant engine objects and indicates the appropriate type. Note that no
> callback occurs here, the Collector just holds the event until the
> application comes looking for it.
>
> The Collector of course has an API for accessing and consuming any events.
>
> There are a couple of important principles to note. As stated above there
> are no callbacks in this interface, this makes it easy to swig into other
> languages, however it should be only a few lines of code to write a
> dispatch loop on top of the swigged version of this API, i.e.:
>
>    for event in collector:
>      dispatch(event)
>
> Another important point is that Events are simple transient value objects.
> They don't carry any significant state in and of themselves, rather they
> are simply pointers to part of the existing engine state machine that may
> be of interest. The protocol state is still entirely encapsulated in the
> engine objects proper, not in the Events.
>
> It's also worth noting that I believe both of these extensions are purely
> additive, i.e. would not require changing any existing API, simply adding
> new API.
>
> Hopefully this type of event oriented interface will provide something that
> people will have a bit of an easier time digesting/using, but even so, I
> think it also provides some interesting avenues of future exploration. I
> believe it would be possible to define a concept of
> interceptors/filter/whatever that could be configured into a Collector. We
> could use this to provide reusable/preconfigured behaviours, e.g. if you
> wanted to use the engine API but didn't want to have to worry about credit,
> you could install an interceptor that would handle all the credit events
> for you. Ultimately when combining this interface with the Container
> concept and introducing Container level events, I believe we could even
> provide reusable chunks of logic that could handle all of the
> connection/session/link establishment stuff and thereby provide a single
> integrated API that would offer the full range of trade-offs between
> Messenger's simplicity and engine's flexibility.
>
> I'd like to flesh this idea out a bit more befire diving in. As a first
> step I'm working on documenting the existing proton design more thoroughly.
> You can see what I have so far here, although it is very much in progress
> still:
>
>    https://cwiki.apache.org/confluence/display/qpid/Proton+Architecture
>
> Once the above document is more complete I will produce a version of the
> UML that describes in more detail what I have outlined above as a delta to
> the existing design.
>
> --Rafael
>


Re: engine API improvements

Posted by Alan Conway <ac...@redhat.com>.

----- Original Message -----
> Hi Everyone,
> 
> In a recent thread I alluded to some improvements I have in mind for the
> engine API and promised a separate post on the topic, so here it is.
> 
> There are two areas where I'm interested in extending the API. These
> extensions are strictly speaking independent of each other, but at the same
> time somewhat complimentary in their utility.
> 
> The first area would be adding a formal Container class to the engine API.
> If you are familiar with the AMQP specification then you might have noticed
> that while the engine API models connections, sessions, and links pretty
> faithfully to how they are defined in the specification, one notable
> difference is that the engine API doesn't explicitly model the concept of
> an AMQP Container. I believe adding such a class to the engine API would
> allow us to refactor some of the more generally useful stuff that Messenger
> does into the engine proper, while leaving the less desirable stuff (such
> as blocking behaviour and hard coded driver) in messenger proper.
> 
> The second area is providing an event oriented interface into the engine.
> One of the original ideas behind the engine design is that you don't need
> event callbacks because the engine itself is simply a state machine, and
> changes in its state are only ever triggered by calling into the engine
> API. This means that whenever you make such a call you can simply query to
> see if anything relevant has changed. While this is strictly speaking true,
> the whole "query to see what has changed part" is responsible for a fair
> amount of biolerplate code in using the engine and makes for a steeper
> learning curve than is desirable. So the idea behind an event oriented
> interface is to provide a uniform way of discovering what has changed that
> has a strong analog to the event callback systems most people are used to,
> but at the same time doesn't go quite so far as to use callbacks.
> 
> The details of this event oriented interface are somewhat TBD, but my
> thinking right now on this is to introduce a Collector object that can be
> registered with an arbitrary number of Containers and/or Connections. The
> Collector will work in concert with an Event class.
> 
> The Event class will contain optional references to at most one of each of
> the following: Container, Connection, Session, Link, Delivery. The event
> class will also have type, i.e. an enum field indicating what of interest
> may have occurred.
> 
> When something of interest happens to a Container, Connection, Session,
> Link, or Delivery, the library will check to see if there is a Collector
> registered, and if so create/initialize an Event that points to the
> relevant engine objects and indicates the appropriate type. Note that no
> callback occurs here, the Collector just holds the event until the
> application comes looking for it.
> 
> The Collector of course has an API for accessing and consuming any events.
> 
> There are a couple of important principles to note. As stated above there
> are no callbacks in this interface, this makes it easy to swig into other
> languages, however it should be only a few lines of code to write a
> dispatch loop on top of the swigged version of this API, i.e.:
> 
>   for event in collector:
>     dispatch(event)
> 
> Another important point is that Events are simple transient value objects.
> They don't carry any significant state in and of themselves, rather they
> are simply pointers to part of the existing engine state machine that may
> be of interest. The protocol state is still entirely encapsulated in the
> engine objects proper, not in the Events.

Is there a potential problem here if state pointed at by event A is changed by event B in the same block of input data (i.e. without the user being able to see event A until after B has been put in the queue)? That would make A's pointer misleading at best since it doesn't point at the data resulting from event A, and makes processing B a challenge if the data has already been processed while looking at it via A.

Re: engine API improvements

Posted by Ken Giusti <kg...@redhat.com>.
Hey Rafi -

This model is exactly what I've been looking for.  When it comes to using the engine, I've found myself needing the same boilerplate code across different implementation languages.  It would really be nice to have this stuff in the engine, where it can be utilized by all.

Just FYI, I've worked a bit on a simplified, callback-based python 'wrapper' around the engine.  My need was for something a bit more connection-oriented than messenger, but also something not so general-purpose as the engine+binding itself.

For your amusement:  https://github.com/kgiusti/fusion

I managed to include most of the 'boilerplate' code I'm always re-implementing: endpoint processing and socket I/O primarily.

I've also defined a simple Container class, which simply contains (heh) the active connections, and provides some hierarchy to the namespaces used for links.

I've skipped over exposing sessions - I just provide Connection and Link objects.  Just to simplify my model a bit.  For now all links on a connection share the same session, but that is an implementation detail.

What you may find interesting is the 'event handler' objects I've created - these define the callback interfaces.  There is a event handler interface for each class (Container - currently unused, Connection, SenderLink, and ReceiverLink).  Each class defines its own particular events - like 'message received' on a ReceiverLink, etc.

Extending the engine API in the way you describe would eliminate a lot of the code I've needed to write for this.

-K


----- Original Message -----
> From: "Rafael Schloming" <rh...@alum.mit.edu>
> To: proton@qpid.apache.org
> Sent: Wednesday, January 15, 2014 1:35:12 PM
> Subject: engine API improvements
> 
> Hi Everyone,
> 
> In a recent thread I alluded to some improvements I have in mind for the
> engine API and promised a separate post on the topic, so here it is.
> 
> There are two areas where I'm interested in extending the API. These
> extensions are strictly speaking independent of each other, but at the same
> time somewhat complimentary in their utility.
> 
> The first area would be adding a formal Container class to the engine API.
> If you are familiar with the AMQP specification then you might have noticed
> that while the engine API models connections, sessions, and links pretty
> faithfully to how they are defined in the specification, one notable
> difference is that the engine API doesn't explicitly model the concept of
> an AMQP Container. I believe adding such a class to the engine API would
> allow us to refactor some of the more generally useful stuff that Messenger
> does into the engine proper, while leaving the less desirable stuff (such
> as blocking behaviour and hard coded driver) in messenger proper.
> 
> The second area is providing an event oriented interface into the engine.
> One of the original ideas behind the engine design is that you don't need
> event callbacks because the engine itself is simply a state machine, and
> changes in its state are only ever triggered by calling into the engine
> API. This means that whenever you make such a call you can simply query to
> see if anything relevant has changed. While this is strictly speaking true,
> the whole "query to see what has changed part" is responsible for a fair
> amount of biolerplate code in using the engine and makes for a steeper
> learning curve than is desirable. So the idea behind an event oriented
> interface is to provide a uniform way of discovering what has changed that
> has a strong analog to the event callback systems most people are used to,
> but at the same time doesn't go quite so far as to use callbacks.
> 
> The details of this event oriented interface are somewhat TBD, but my
> thinking right now on this is to introduce a Collector object that can be
> registered with an arbitrary number of Containers and/or Connections. The
> Collector will work in concert with an Event class.
> 
> The Event class will contain optional references to at most one of each of
> the following: Container, Connection, Session, Link, Delivery. The event
> class will also have type, i.e. an enum field indicating what of interest
> may have occurred.
> 
> When something of interest happens to a Container, Connection, Session,
> Link, or Delivery, the library will check to see if there is a Collector
> registered, and if so create/initialize an Event that points to the
> relevant engine objects and indicates the appropriate type. Note that no
> callback occurs here, the Collector just holds the event until the
> application comes looking for it.
> 
> The Collector of course has an API for accessing and consuming any events.
> 
> There are a couple of important principles to note. As stated above there
> are no callbacks in this interface, this makes it easy to swig into other
> languages, however it should be only a few lines of code to write a
> dispatch loop on top of the swigged version of this API, i.e.:
> 
>   for event in collector:
>     dispatch(event)
> 
> Another important point is that Events are simple transient value objects.
> They don't carry any significant state in and of themselves, rather they
> are simply pointers to part of the existing engine state machine that may
> be of interest. The protocol state is still entirely encapsulated in the
> engine objects proper, not in the Events.
> 
> It's also worth noting that I believe both of these extensions are purely
> additive, i.e. would not require changing any existing API, simply adding
> new API.
> 
> Hopefully this type of event oriented interface will provide something that
> people will have a bit of an easier time digesting/using, but even so, I
> think it also provides some interesting avenues of future exploration. I
> believe it would be possible to define a concept of
> interceptors/filter/whatever that could be configured into a Collector. We
> could use this to provide reusable/preconfigured behaviours, e.g. if you
> wanted to use the engine API but didn't want to have to worry about credit,
> you could install an interceptor that would handle all the credit events
> for you. Ultimately when combining this interface with the Container
> concept and introducing Container level events, I believe we could even
> provide reusable chunks of logic that could handle all of the
> connection/session/link establishment stuff and thereby provide a single
> integrated API that would offer the full range of trade-offs between
> Messenger's simplicity and engine's flexibility.
> 
> I'd like to flesh this idea out a bit more befire diving in. As a first
> step I'm working on documenting the existing proton design more thoroughly.
> You can see what I have so far here, although it is very much in progress
> still:
> 
>   https://cwiki.apache.org/confluence/display/qpid/Proton+Architecture
> 
> Once the above document is more complete I will produce a version of the
> UML that describes in more detail what I have outlined above as a delta to
> the existing design.
> 
> --Rafael
> 

-- 
-K