[ 
https://issues.apache.org/jira/browse/FELIX-4866?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14545962#comment-14545962
 ] 

Pierre De Rop commented on FELIX-4866:
--------------------------------------

David, 

Can you please take a look at the bndtools project that I have joined to this 
issue, which seems to reproduce the problem I was talking about in the mailing 
list. I have checked it 100 times, but I'm not able to find a bug and now, with 
the last patches applied in the framework, I have some failing DM integrations 
tests.

The tool is not based on dependency manager, but only on a little fluent APi 
that replaces DM, and it is only based on standard service trackers. 

It requires Java8, BndTools, and Eclipse Mars.

Using Felix 5.0.0, or using Equinox 3.10.1, the tool runs and completes 
seamlessly.
but using the latest Felix fwk from trunk that includes the FELIX-4866 patch, 
then the tool fails.
and I have now four failing DM integration tests.

What is doing this load test ?
============================

This loader creates a graph of service components that have dependencies 
between each other. For
sake of simplicity, a simple scenario domain is used (actually, this example 
domain has been
inspired from the "Java8 Lambdas" book, O'reilly) and we have the following 
kind of services: 

- Artist service: An Artist is an individual or group of musicians, who creates 
some "Albums". One
  Artist service depends on several Album services.

- Album service: is a single release of musics, comprising several music 
Tracks. One Album depends
  on several Track services. 

- Track service: A piece of music.

The scenario consists in starting/stopping 10000 times a bundle that will 
synchronously create the
graph of components (7 by default). A scenario constroller monitors the number 
of created components 
(Artists, Albums, Tracks) and when the number of expected components are 
created, then the controller
stops the bundle. Finally, when the controller detects that all components are 
unregistered, the
elapsed time is recorded in a list of time duration (in nano seconds).

The same is done for another bundle that does exactly the same, but using 
concurrent component
registration. But when registering components concurrently, this does not
work with the latest framework.

At the end of the test (that is, when the bundle that creates the components 
has been
started/stopped 100000 times), then the list of time duration is sorted: the 
first element of the
list corresponds to the shortest elapsed time used by the bundle to create and 
destroy the
components; and the last element in the list corresponds to the slowest elapsed 
time. The 
middle in the duration time list is the average. We display the first entry 
(fastest), the entry at 1/4 
of the list, the middle of the list, the entry at 3/4 of the list, and the last 
entry (slowest time). 
We don't do an average, because usually, when running
benchmark, some measurements don't reflect reality, especially, when there is a 
full GC or when the
JVM is warming up. (we actually do the same as in Java Chronicle:
https://github.com/peter-lawrey/Java-Chronicle).

Bundle descriptions:
===============

- org.apache.felix.framework.loadtest.tracker: a test bundle that creates 7 
components synchronously
  when the bundle is started, and when it is stopped, then the components are 
unregistered. This test works fine with the 
  latest Felix framework / trunk.
- org.apache.felix.framework.loadtest.tracker.parallel: same as before, but the 
components are
  created concurrently. this test fails with the FELIX-4866 patch applied in 
the trunk.
- org.apache.felix.framework.loadtest.scenario: this bundle contains the 
component classes that are
  part of the scenario: we have an Artist service that depends on some Albums 
services, each Album
  also depends on some music Track services. The components are bounded using a 
special "id" service
  property. 
- org.apache.felix.framework.loadtest.scenario.impl: the simple 
Artist/Albums/Track implementations.
- org.apache.felix.framework.loadtest.controller: provides a ScenarioController 
service that is
  injected in all Artist/Album/Track components. When an Artist, an Album, or a 
Track component is
  started, it notifies the ScenerioController. Then when the controller detects 
that all components
  are properly created, it then stops the bundle, which in turns unregisters 
all components.
- org.apache.felix.framework.loadtest.controller.impl: this is the 
ScenarioController implementation.

API used to define service components:
=============================

Internally, basic Service Trackers are used to define dependencies between 
components.
But we are using a light fluent api in order to describe component definitions, 
which is used to
replace the DependencyManage API. Notice that DM is still used, but only to 
activate the controller
bundle. And the Artist/Album/Track components are not defined using DM, and are 
only defined using the
fluent API, which is simply based on standard ServiceTracker.

To create a component, a static "component" method of the
"org.apache.felix.framework.loadtest.tracker.Component" class is used, and 
takes as parameter a java8 Supplier 
object that will be used to instantiate the component implementation.  

For example, to create an "Album" component, we are using the fluent API like 
this:

{code}
import static org.apache.felix.framework.loadtest.tracker.Component.component;

public class Activator implements BundleActivator {
        @Override
        public void start(BundleContext context) throws Exception {
                Component<AlbumImpl> album = component(context, () -> new 
AlbumImpl(context))
                album.start();
        }
}
{code}

Now, assuming that the Album component needs to be called in its start() 
callback when all required dependencies are available, and in its stop() 
callback when one of the required dependencies is lost, you can then define the 
start/stop lifecycle callbacks using "onStart" and "onStop" methods, like this:
{code}
public class Activator implements BundleActivator {
        @Override
        public void start(BundleContext context) throws Exception {
                Component<AlbumImpl> album = component(context, () -> new 
AlbumImpl(context))
                       .onStart(AlbumImpl::start)
                       .onStop(AlbumImpl::stop);
                album.start();
        }
}
{code}

Since an "Album" component depends on some musical "Track" components, you then 
configure the
dependencies with the "dependsOn" method that takes as parameters the 
dependency type, the
dependency filter, and a method reference to invoke on the AlbumImpl class in 
order to bind the Track service to the AlbumImpl  component instance:

{code}
public class Activator implements BundleActivator {
        @Override
        public void start(BundleContext context) throws Exception {
                Component<AlbumImpl> album = component(context, () -> new 
AlbumImpl(context))
                       .onStart(AlbumImpl::start)
                       .onStop(AlbumImpl::stop)
                       .dependsOn(Track.class, "(title=some title)", 
AlbumImpl::addTrack)
                album.start();
        }
}
{code}

Finally, since the AlbumImpl provides the "Album" service interface, you use 
the "provides" method which takes are parameters the provided service, and a 
varargs list of service properties (which size must be even, like "property1", 
"value1", "property2", "value2", etc ...):

{code}
public class Activator implements BundleActivator {
        @Override
        public void start(BundleContext context) throws Exception {
                Component<AlbumImpl> album = component(context, () -> new 
AlbumImpl(context))
                       .onStart(AlbumImpl::start)
                       .onStop(AlbumImpl::stop)
                       .dependsOn(Track.class, "(title=track title)", 
AlbumImpl::addTrack)
                       .provides(Album.class, "title", "album title")
                album.start();
        }
}
{code}

Running the loader with the last released Felix framework (5.0.0):
===============================================

First, use Eclipse Mars and BndTools 2.4.1. Eclise must be configured with a 
Jdk8 JRE.

Then, import the project under bndtools, and click on the bnd.bnd file; then 
click on the "Run" tab, then click on "Run OSGi".
You will then get the following output result:

{code}
g! Starting benchmarks (each tested bundle will add/remove 7 components during 
bundle activation).

        [Starting benchmarks with no processing done in components start 
methods]


Benchmarking bundle: org.apache.felix.framework.loadtest.tracker ... \
-> results in nanos: [546,822 | 613,264 | 670,311 | 846,572 | 168,057,014]


Benchmarking bundle: org.apache.felix.framework.loadtest.tracker.parallel ... \
-> results in nanos: [525,062 | 651,846 | 748,940 | 1,006,306 | 84,381,682]
{code}

let's describe the result shown above:

1) The "org.apache.felix.framework.loadtest.tracker" bundle is the one that 
synchronously creates the components. It is started/stopped 10000 times, and 
the meaningful elapsed times are displayed:
Here, the shortest time used to create and remove the 7 components takes around 
546,822 nanos. And
in the midle, you can see the average (670,311 nannoseconds), and in the last 
entry, the slowest
time (168,057,014 nanos). 

2) The "org.apache.felix.framework.loadtest.tracker.parallel" bundle is the 
same as the previous
one, but creates components concurrently:
Here, the results are not better because by default, the size of the threadpool 
is 10 (see the "-Dthreads=10" in bnd.bnd)
and I only have 4 cores my labtop.
With the latest framework 5.1.0, this test does not complete and at a point in 
time, some components
are not created timely.

Running the loader with the last released Felix framework (5.1.0-SNAPSHOT):
=========================================================

Edit the bnd.bnd file, comment the current "-runfwk" option and uncomment the 
other one, which uses
the latest version (5.1.0-SNAPSHOT) that  is included in the local repository:

{code}
#-runfw: 
org.eclipse.osgi;version='[3.10.1.v20140909-1633,3.10.1.v20140909-1633]'
#-runfw: org.apache.felix.main;version='[5.0.0, 5.0.0]'
-runfw: org.apache.felix.main;version=latest
{code}

Then click on the "Run OSGi".
You will normally see the following (under bndtools):

{code}
g! Starting benchmarks (each tested bundle will add/remove 7 components during 
bundle activation).

        [Starting benchmarks with no processing done in components start 
methods]


Benchmarking bundle: org.apache.felix.framework.loadtest.tracker ... \
-> results in nanos: [546,681 | 614,242 | 673,661 | 1,012,047 | 43,224,831]


Benchmarking bundle: org.apache.felix.framework.loadtest.tracker.parallel ... -
Could not start components timely (7): started components: 5.
{code}

Here, the scenario controller has started the 
"org.apache.felix.framework.loadtest.tracker.parallel" bundle, but only 5 
components have been created, and two are missing.

I think that one possible issue would be that one ServiceTracker is not called 
in its adding method, but it's hard to diagnose ...


> Improve service registry
> ------------------------
>
>                 Key: FELIX-4866
>                 URL: https://issues.apache.org/jira/browse/FELIX-4866
>             Project: Felix
>          Issue Type: Improvement
>          Components: Framework
>    Affects Versions: framework-5.0.0
>            Reporter: Carsten Ziegeler
>            Assignee: David Bosschaert
>             Fix For: framework-5.2.0
>
>
> The current service registry is currently not using any of the Java 5 
> concurrent data structures. Using those could improve the implementation, the 
> readibility of the code and potentially the performance of the service 
> registry.



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

Reply via email to