hiero-ledger / hiero-ledger/hiero-consensus-node

Platform Context, Services & Lifecycles

Open
#5,267 8 comments 0 reactions 0 assignees View on GitHub
Epic Platform
Dominant language
Java
Stars
406
Forks
226
Avg merge
3d 4h
Merged PRs (30d)
210

Description

This epic defines the platform context and services that are provided by the platform context.

```[tasklist]
### Tasks
- [ ] https://github.com/hashgraph/hedera-services/issues/5292
- [ ] https://github.com/hashgraph/hedera-services/issues/13266
- [ ] https://github.com/hashgraph/hedera-services/issues/13267
```

# Old and outdated definition of this epic

We need to define a basic API / pattern / architecture how we define
- a context for the platform
- services (or how we will call the facades for a specific API / functionality / feature)
- lifecycles for services that can have a state

# Draft of a first definition

The platform context (PC) is a context that will provide access to the core/base services of the platform and handle the lifecycle of such services. The PC is not a singleton since several applications can be executed in a single JVM. Based on that we need a factory (method) to create a PC instance. The PC itself has a basic lifecycle and it can be disposed. This is needed to handled stopped applications at runtime and to write tests without changing global states in a global context. Especially for tests it would be great if the PC implements AutoCloseable. Based on this lifecycle definition basic PC interface can look like this:

```
public interface PlatformContext extends AutoCloseable {

static PlatformContext createInstance() {
// creates a new PC and initialize it
}
}
```

The context should be used to access the basic services of the platform like config, metrics or thread handling. Such access could simply be provided by adding getter methods to the PC that provides services & facades for the mentioned functionalities:

```
public interface PlatformContext extends AutoCloseable {

ConfigManager getConfigManager();
MetricsManager getMetricsManager();
Cryptography getCryptography();
NotificationEngine getNotificationEngine();
ResourceManager getResourceManager();
}
```

By doing so the PC needs to depend on the given APIs. To do so it would be perfect if all needed APIs are splitted in an API and an IMPL module (see [#5004](https://github.com/swirlds/swirlds-platform/issues/5004)). In such case the PC only needs to depend on the API modules:

dependencies-1

While a PC can already be created without having the final module split we should define the given module structure as a goal to have a well implemented PC with public API and hidden implementations.

Another possible approach would be a generic PC that does not directly provide Services but provides generic access:

```
public interface PlatformContext extends AutoCloseable {

T getService(Class serviceClass);

boolean isServiceAvailable(Class serviceClass);

}
```

By doing so the PC API would bot depend on any service API:
dependencies-2

By doing so we do not need a module separation between API & IMPL of services. At the start we can easily work with a split within a module (and hide access by `module-info.java`):
dependencies-3
The big benefit would be that we can work on the PC and the services independent from the module reorganization ([#5004](https://github.com/swirlds/swirlds-platform/issues/5004)). We could extract services step by step from the `swirlds-common` module (as we currently do with the config API -> [#5777](https://github.com/swirlds/swirlds-platform/issues/5777)):

dependencies-4-1

In such case the modules can register services for the PC by providers that will automatically be loaded by [the Java SPI](https://docs.oracle.com/javase/9/docs/api/java/util/ServiceLoader.html). A Provider could look like this:

```
public interface ServiceProvider {

/**
* Creates a new service instance
*
* @param platformContext
* the platform context
* @return a new service instance
*/
T createService(PlatformContext platformContext);

/**
* Returns the type of the service that can be provided by this provider
*
* @return the type of the service
*/
Class getServiceType();

/**
* Returns a list of services that must be registered at the {@link PlatformContext} before the
* {@link #createService(PlatformContext)} method can be called.
*
* @return a list of services that must be registered.
*/
default Set getDependencies() {
return Collections.emptySet();
}
}
```

A minimalistic PoC of such generic PC can be found here: https://github.com/swirlds/swirlds-platform/pull/5853

**Note:** Since I (@hendrikebbers) have created the PoC and really like the idea of a generic I'm biased. In general both ideas will end in a useable PC.

### Lifecycle of services

Every service / facade that is accessible by the PC needs a well defined lifecycle. The lifecycle of most services will be bound to the lifecycle of the PC. Next to this some services might be singletons that are shared between several PC instances. I do not assume that we will have any other lifecycle (@cody-littley @lpetrovic05 do I miss anything?). We should provide a basic interface that defines the lifecycle of services:

```
public interface ServiceLifecycle {

void init();

boolean isInitialized();

void dispose();

}
```
The PC implementation will take care of the lifecycle of all services that are registered/accessible by the PC. Such interface could be added to the codebase as fast as possible and we can already start to integrate it in the base services and facades independent of a concrete PC implementation since we want to remove the static state of all services anyway. Best would be that services will be reuseable and once a services is disposed it can be reactivated by calling the `init()` method. Having such lifecycle will help us to write unit tests without side-effects:

```
private final ConfigService configService = ...

@BeforeEach
public void init() {
configService.init();
}

@AfterEach
public void dispose() {
configService.dispose();
}

@Test
public void test() {
//use the service
}
```

Contributor guide

Open the contributing guide

Research direction

Start by reviewing the tasklist issues 5292, 13266, and 13267, then read the linked PoC pull request 5853 and the discussion around API versus generic service access. The issue proposes multiple PlatformContext and ServiceLifecycle designs rather than naming a specific implementation location or test. Done would require an agreed architecture and coordinated work across the related tasks.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
backend, developer-experience
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
15/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.