[PEAK] "Global" Services and Service Areas (was Re: peak logs)

Phillip J. Eby pje at telecommunity.com
Fri Jan 2 18:13:15 EST 2004

At 02:51 PM 1/2/04 -0500, Phillip J. Eby wrote:
>It seems a simpler solution would be some way to say "I'd like to have 
>this component be used as a home for global services."  The hard parts 
>are: 1) which component?  and 2) what services?
>In practice, most breakage today comes from configuration files loaded 
>into non-root components, so that would be an obvious place to start on 
>part 1.  But part 2 is not nearly as obvious.  Some components *want* to 
>stay global.  For example, my hypothetical ZConfig schema service.
>I'm going to have to think about this one some more.  At least for now, 
>fixing it is easy when you know what's happening: just add a Component 
>Factories declaration for the affected services.

Ty and I just spent some time on trying to find a "global" solution to this 
issue.  Here's what we came up with.

In the real world, components have a "box", with things that are "inside" 
and things that are "outside".  One explicitly connects things on the 
"outside" of the component.  For example, if I bring a toaster home from 
the store and put it in my kitchen, it doesn't plug itself in.  I have to 
do that, explicitly.

But on the inside of the toaster, there is likely a wiring bus, where 
certain services (such as "power") are made globally available, and thus 
implicitly connected.

Right now, PEAK treats everything under a given component root as a single 
undifferentiated mass of components.  Everything is both explicit and 
implicit.  Every component is a box, and yet no component is.  Thus, it is 
easy to miswire a component when you provide a new source (offerAs, .ini 
file) for a signal on a wire, and that same wire is used for another source 
higher up on the "bus".  We then end up with a short circuit, as in 
Darryl's problem today, or my gaffe a few weeks ago in giving the 
Supervisor tool a private reactor, without also giving it a private mainloop.

So it seems we need components to be able to "express their 
boundaries".  In a sense, we draw a "box" around a collection of components 
that desires to be "self-contained".  Service lookups performed by 
components "inside the box" must be satisfied by a service that is also 
"inside".  Right now, when you use [Component Factories], you're declaring 
a single global default instantiation of that service.  What we'll be 
changing to, is that [Component Factories] are "one instance per box", 
instead of "one instance per declaration".

In practical terms, what that means is we'll "draw a box" around 
configuration files loaded by 'runIni', so that the .ini file is treated as 
a self-contained app.  It'll still inherit configuration from its parent 
components - even the *definition* of component factories.  It's just that 
the "box" will have its own *instance* of such components.  So, this would 
produce the same effect as the workaround I gave Darryl; it'll be as though 
all the [Component Factories] sections of parent .ini files were 
automatically copied into his .ini file.

Technically, the way we will "draw a box" around a component is to have it 
implement a config.IServiceArea interface.  In the common case, this will 
probably be by having it subclass a 'config.ServiceArea' base 
class.  IConfigurationRoot will extend IServiceArea, so a configuration 
root will implicitly be a "box" (service area), just as it effectively is now.

Few programs or components will need or want to do this, 
though.  Essentially, you'll do it only when you're overriding something 
defined by the next "service area" up the component hierarchy, and want to 
ensure that any other "global" components you or your children use will 
respect your override.  The process supervisor tool needs to do this 
because it absolutely needs its reactor to be local, to avoid conflict with 
a reactor used by its child processes.  Thus, it can be, and should become, 
its own service area.

Creating a service area essentially means that you can't access services 
outside that area, without explicitly connecting them -- just like you 
can't get power to the toaster unless you plug it in.  This means, for 
example, that if I needed the process supervisor tool to use an 
externally-provided transaction service, I'd need to explicitly register a 
way to get it from outside the service area.

On the whole, this change should make it a lot harder to mess up 
runIni-based apps' configurations, although you'll still have to bear in 
mind that loading configuration files into a service area's child 
components will not affect the services provided there.  It should also 
make it easier to intentionally "box in" a set of components, as I had 
intended to do with the process supervisor.

Finally, it should make it a little easier to understand where certain 
kinds of configuration take effect.  If you want a setting to affect any 
"global" services, you must apply the configuration directly to the 
appropriate service area, rather than to a child component.

I'm bumping this change to the head of the line for alpha 3, because pretty 
much everything else I'm working on for PEAK involves adding more global 
services, and therefore more opportunities for these sorts of errors.

More information about the PEAK mailing list