Services and Contexts are one of the more confusing concepts in REEF. The glossary gives you a brief overview of the concept, this page shall provide a more in depth look at how they are implemented. At the core, Contexts are formed by a stack of injectors. The one at the very bottom is called the Root Context, the one on the very top of that stack is called the Active context (hence the event name). The two can be one and the same, that is: There is only one context required. You can instantiate a Task on top of it:

Task
Context B
Context A
RootContext

The semantics are that the Task is instantiated on a fork of the injector held by Context B. That means that whatever is already instantiated as part of Context B will be a singleton, and those instances get passed into the Task. This is immensely useful when e.g. passing state between subsequent Tasks. That state can be kept within the Context B.

Also, Context B's injector is a fork of the one held by Context A. Hence, all the objects instantiated for it are available in Context B and therefore the Task. This is useful when stringing together pipelines of different operators: Each operator has its own Context, and the one below these is used to keep state between operators.

But what about Services?

So far, we haven't touched on Services at all. In fact, services aren't really needed to understand the concepts behind Contexts. However, their implementation can't be understood without them. Services solve a problem that arises in the above description: Tang's Configuration and Injector are monotonic. That is, information can only be added, never removed or overwritten. Now consider the case of the Context ID, which is kept in a Context's configuration and therefore its Injector. Forking that Injector with another context Configuration would result in a violation of Tang's monotonicity axiom, as that second context Configuration contains a binding to the same named parameter. This is where ServiceConfiguration comes into play: The ServiceConfiguration carries all the configuration data that shall be shared with contexts started on the current one. The ContextConfiguration contains all the information local to the context. We arrive at the following picture:

 

Task TaskInjector
Context BServiceInjector BContextInjector B
Context AServiceInjector AContextInjector A
RootContextRootServiceInjectorRootContextInjector

 

The various injector are formed like this:

Injector rootServiceInjector = new Injector(rootServiceConfiguration);
Injector rootContextInjector = rootServiceInjector.fork(rootContextConfiguration);

Injector serviceInjectorA    = rootServiceInjector.fork(serviceConfigurationA);
Injector contextInjectorA    = serviceInjectorA.fork(contextConfigurationA);

Injector serviceInjectorB    = serviceInjectorA.fork(serviceConfigurationB);
Injector contextInjectorB    = serviceInjectorB.fork(contextConfigurationB);

Injector taskInjector        = contextInjectorB.fork(taskConfiguration)

This ensures that only shareable information is passed to subsequent contexts and their injectors.

  • No labels