Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hybrid plugin system #1360

Merged
merged 3 commits into from
Aug 6, 2024

Conversation

feltech
Copy link
Member

@feltech feltech commented Jul 16, 2024

Description

Closes #1202.

OpenAssetIO currently supports two different manager plugin backends in the core library, Python and C++. We expect more backends to be added in the future. For example, there has been active investigation into a gRPC backend.

By "backend" we specifically mean an implementation of the abstract base class ManagerImplementationFactoryInterface. The host application chooses an implementation, then injects it into a ManagerFactory, which is then responsible for producing Manager instances that the host application can use.

The idea of a hybrid plugin system is to allow multiple backend plugin providers to be composed, such that API calls can be routed to the most appropriate plugin. For example, performance-critical API requests could route to a C++ plugin, whilst less critical requests route to a Python plugin.

As an added bonus, having a built-in facility to compose multiple backends reduces boilerplate for hosts that wish to support multiple plugin system backends.

So add a HybridPluginSystemManagerImplementationFactory, which implements the ManagerImplementationFactoryInterface interface, and takes a list of child ManagerImplementationFactoryInterface implementations during construction. The host application can then provide an instance of a hybrid factory to their ManagerFactory, taking care of the boilerplate of managing multiple plugin systems.

When a host application attempts to load a plugin with a particular unique identifier, then all child plugin systems are searched. If a match is found in a single plugin system, then its manager implementation is returned directly. If a match is found in multiple plugin systems, then a proxy manager is constructed that routes calls to the appropriate manager implementation.

The "appropriate" manager implementation to route to varies. In most cases, the highest priority implementation that advertises the capability associated with the API call is chosen (i.e. it is routed based on the response of the manager(s) to hasCapability(...)).

The priority order of implementations corresponds to the order of the child factories in the list that was provided to the hybrid factory on construction.

Some API methods don't have an associated capability. For these, routing is "common sense":

For initialize, all implementations are provided with all the settings. For example, if using a .toml OpenAssetIO config file (see OPENASSETIO_DEFAULT_CONFIG), then the settings in that file will be provided to all the matching manager implementations. This could cause problems if two plugins use the same settings key but require different values. Future work may look at ways to disambiguate.

For identifier and displayName the highest priority manager implementation is chosen.

For info and settings, the results of all manager implementations are merged, with the highest priority manager taking precedence if any dictionary keys conflict.

For flushCaches all manager implementations are flushed.

  • I have updated the release notes.
  • I have updated all relevant user documentation.

Reviewer Notes

See Jupyter Notebook for illustration in OpenAssetIO/OpenAssetIO-MediaCreation#99

@feltech feltech self-assigned this Jul 16, 2024
@feltech feltech requested a review from a team as a code owner July 16, 2024 14:48
@feltech feltech changed the title Work/1202 hybrid plugin system Add hybrid plugin system Jul 16, 2024
@feltech feltech force-pushed the work/1202-hybridPluginSystem branch from d0fe8b1 to be1c50b Compare July 16, 2024 14:53
@feltech feltech marked this pull request as draft July 16, 2024 14:54
@feltech
Copy link
Member Author

feltech commented Jul 16, 2024

Set to Draft state pending #1348

@feltech feltech force-pushed the work/1202-hybridPluginSystem branch 2 times, most recently from 44c918e to fc1d533 Compare July 17, 2024 08:37
@feltech
Copy link
Member Author

feltech commented Jul 19, 2024

For reference, output of "ABI diff check" CI job, before I update the ABI snapshot...

TLDR: it's just saying there's a new class, so there's no need to flag a breaking change in the release notes.

===== Checking ABI of libopenassetio.so.1.0.0 =====

Leaf changes summary: 8 artifacts changed
Changed leaf types summary: 0 leaf type changed
Removed/Changed/Added functions summary: 0 Removed, 0 Changed, 4 Added functions
Removed/Changed/Added variables summary: 0 Removed, 0 Changed, 0 Added variable
Function symbols changes summary: 0 Removed, 0 Added function symbol not referenced by debug info
Variable symbols changes summary: 0 Removed, 4 Added variable symbols not referenced by debug info
Errors while running CTest

4 Added functions:

  [A] 'method openassetio::v1::pluginSystem::HybridPluginSystemManagerImplementationFactory::HybridPluginSystemManagerImplementationFactory(openassetio::v1::pluginSystem::HybridPluginSystemManagerImplementationFactory::ManagerImplementationFactoryInterfaces, openassetio::v1::log::LoggerInterfacePtr)'    {_ZN11openassetio2v112pluginSystem46HybridPluginSystemManagerImplementationFactoryC2ESt6vectorISt10shared_ptrINS0_7hostApi37ManagerImplementationFactoryInterfaceEESaIS7_EES4_INS0_3log15LoggerInterfaceEE, aliases _ZN11openassetio2v112pluginSystem46HybridPluginSystemManagerImplementationFactoryC1ESt6vectorISt10shared_ptrINS0_7hostApi37ManagerImplementationFactoryInterfaceEESaIS7_EES4_INS0_3log15LoggerInterfaceEE}
  [A] 'method virtual openassetio::v1::Identifiers openassetio::v1::pluginSystem::HybridPluginSystemManagerImplementationFactory::identifiers()'    {_ZN11openassetio2v112pluginSystem46HybridPluginSystemManagerImplementationFactory11identifiersEv}
    note that this adds a new entry to the vtable of class openassetio::v1::pluginSystem::HybridPluginSystemManagerImplementationFactory
  [A] 'method virtual openassetio::v1::managerApi::ManagerInterfacePtr openassetio::v1::pluginSystem::HybridPluginSystemManagerImplementationFactory::instantiate(const openassetio::v1::Identifier&)'    {_ZN11openassetio2v112pluginSystem46HybridPluginSystemManagerImplementationFactory11instantiateERKSs}
    note that this adds a new entry to the vtable of class openassetio::v1::pluginSystem::HybridPluginSystemManagerImplementationFactory
  [A] 'method openassetio::v1::pluginSystem::HybridPluginSystemManagerImplementationFactoryPtr openassetio::v1::pluginSystem::HybridPluginSystemManagerImplementationFactory::make(openassetio::v1::log::LoggerInterfacePtr)'    {_ZN11openassetio2v112pluginSystem46HybridPluginSystemManagerImplementationFactory4makeESt6vectorISt10shared_ptrINS0_7hostApi37ManagerImplementationFactoryInterfaceEESaIS7_EES4_INS0_3log15LoggerInterfaceEE}

4 Added variable symbols not referenced by debug info:

  [A] _ZTIN11openassetio2v112pluginSystem46HybridPluginSystemManagerImplementationFactoryE
  [A] _ZTSN11openassetio2v112pluginSystem46HybridPluginSystemManagerImplementationFactoryE
  [A] _ZTSSt23_Sp_counted_ptr_inplaceIN11openassetio2v112pluginSystem46HybridPluginSystemManagerImplementationFactoryESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE
  [A] _ZTVN11openassetio2v112pluginSystem46HybridPluginSystemManagerImplementationFactoryE

@feltech feltech force-pushed the work/1202-hybridPluginSystem branch 2 times, most recently from f9950cf to 2fd7ad0 Compare July 22, 2024 09:55
@feltech feltech marked this pull request as ready for review July 22, 2024 12:06
@feltech feltech force-pushed the work/1202-hybridPluginSystem branch from 57f7fdf to 9558e3f Compare July 22, 2024 15:02
@feltech feltech force-pushed the work/1202-hybridPluginSystem branch 2 times, most recently from c69d98a to 95fa9f5 Compare July 26, 2024 12:31
@foundry-markf
Copy link

foundry-markf commented Jul 29, 2024

The "appropriate" manager implementation to route to varies. In most cases, the highest priority implementation that advertises the capability associated with the API call is chosen (i.e. it is routed based on the response of the manager(s) to hasCapability(...)).

The priority order of implementations corresponds to the order of the child factories in the list that was provided to the hybrid factory on construction.

Taken from the PR description above.
I get that 'priority order' is the order of the factories passed to the ctor as in your walk-through earlier.

But 'In most cases', what do you mean? Is there another mechanism?

EDIT: reading further, is it

For info and settings, the results of all manager implementations are merged, with the highest priority manager taking precedence if any dictionary keys conflict.

that as a merging process different from choosing from the order in the ctor, that you meant?

@feltech
Copy link
Member Author

feltech commented Jul 29, 2024

The "appropriate" manager implementation to route to varies. In most cases, the highest priority implementation that advertises the capability associated with the API call is chosen (i.e. it is routed based on the response of the manager(s) to hasCapability(...)).

The priority order of implementations corresponds to the order of the child factories in the list that was provided to the hybrid factory on construction.

Taken from the PR description above. I get that 'priority order' is the order of the factories passed to the ctor as in your walk-through earlier.

But 'In most cases', what do you mean? Is there another mechanism?

EDIT: reading further, is it

For info and settings, the results of all manager implementations are merged, with the highest priority manager taking precedence if any dictionary keys conflict.

that as a merging process different from choosing from the order in the ctor, that you meant?

Yes exactly. Most of the methods have a similar pattern - choose the "first" plugin that satisfies the capability, where "first" corresponds to the order the child factories were passed in the ctor.

But for some methods that doesn't make sense or isn't possible, such as displayName() or settings().

Copy link

@foundry-markf foundry-markf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pybind11 question is open for me, but otherwise, I don't see anything outstanding.

@feltech feltech force-pushed the work/1202-hybridPluginSystem branch from a8fde7b to e1b3f94 Compare August 1, 2024 15:27
@feltech
Copy link
Member Author

feltech commented Aug 1, 2024

The pybind11 question is open for me, but otherwise, I don't see anything outstanding.

Thanks! Squashed it all down and pushed. Let's see if there's any takers for pybind stuff. At least its only a tiny part of the PR.

@feltech feltech force-pushed the work/1202-hybridPluginSystem branch from e1b3f94 to e62da7a Compare August 2, 2024 06:50
@feltech feltech force-pushed the work/1202-hybridPluginSystem branch from e62da7a to c50d789 Compare August 2, 2024 07:26
Closes OpenAssetIO#1202.

OpenAssetIO currently supports two different manager plugin backends in
the core library, Python and C++. We expect more backends to be added in
the future. For example, there has been active investigation into a gRPC
backend.

By "backend" we specifically mean an implementation of the abstract base
class `ManagerImplementationFactoryInterface`. The host application
chooses an implementation, then injects it into a `ManagerFactory`,
which is then responsible for producing `Manager` instances that the
host application can use.

The idea of a hybrid plugin system is to allow multiple backend plugin
providers to be composed, such that API calls can be routed to the most
appropriate plugin. For example, performance-critical API requests could
route to a C++ plugin, whilst less critical requests route to a Python
plugin.

As an added bonus, having a built-in facility to compose multiple
backends reduces boilerplate for hosts that wish to support multiple
plugin system backends.

So add a `HybridPluginSystemManagerImplementationFactory`, which
implements the `ManagerImplementationFactoryInterface` interface, and
takes a list of child `ManagerImplementationFactoryInterface`
implementations during construction. The host application can then
provide an instance of a hybrid factory to their `ManagerFactory`,
taking care of the boilerplate of managing multiple plugin systems.

When a host application attempts to load a plugin with a particular
unique identifier, then all child plugin systems are searched. If a
match is found in a single plugin system, then its manager
implementation is returned directly. If a match is found in multiple
plugin systems, then a proxy manager is constructed that routes calls to
the appropriate manager implementation.

The "appropriate" manager implementation to route to varies. In most
cases, the highest priority implementation that advertises the
capability associated with the API call is chosen (i.e. it is routed
based on the response of the manager(s) to `hasCapability(...)`).

The priority order of implementations corresponds to the order of the
child factories in the list that was provided to the hybrid factory on
construction.

Some API methods don't have an associated capability. For these, routing
is "common sense":

For `initialize`, all implementations are provided with all the
settings. For example, if using a `.toml` OpenAssetIO config file (see
`OPENASSETIO_DEFAULT_CONFIG`), then the settings in that file will be
provided to _all_ the matching manager implementations. This could cause
problems if two plugins use the same settings key but require different
values. Future work may look at ways to disambiguate.

For `identifier` and `displayName` the highest priority manager
implementation is chosen.

For `info` and `settings`, the results of all manager implementations
are merged, with the highest priority manager taking precedence if any
dictionary keys conflict.

For `flushCaches` all manager implementations are flushed.

Signed-off-by: David Feltell <[email protected]>
Part of OpenAssetIO#1202. The hybrid plugin system should be the default choice for
host applications, given it reduces boilerplate and allows composing
plugins. So update the simppleResolver example terminal host to make use
of this new best practice.

Signed-off-by: David Feltell <[email protected]>
Part of OpenAssetIO#1202. Now that the hybrid plugin system is available, the
`openassetio.test.manager` API Compliance test suite can use it to
validate C++, Python, and composite plugins.

Integration testing is provided by the SimpleCppManager unit tests,
as well as the BAL integration tests in GitHub CI (`integrations.yml`).
There are no explicit tests using the API Compliance suite on a
hybridized plugin, yet.

Signed-off-by: David Feltell <[email protected]>
@feltech feltech force-pushed the work/1202-hybridPluginSystem branch from c50d789 to cbf086c Compare August 6, 2024 11:26
@feltech feltech merged commit 0401265 into OpenAssetIO:main Aug 6, 2024
42 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Hybrid Plugin System
2 participants