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

feat(cmd-api-server): multi-protocol support #503

Closed
3 tasks done
petermetz opened this issue Jan 15, 2021 · 3 comments
Closed
3 tasks done

feat(cmd-api-server): multi-protocol support #503

petermetz opened this issue Jan 15, 2021 · 3 comments
Assignees
Labels
API_Server Core_API Changes related to the Core API Package dependencies Pull requests that update a dependency file Developer_Experience enhancement New feature or request Significant_Change Applying this label triggers the more stringent review of the maintainers and the 2+1 PR rule. SPIKE Exploratory work to better scope additional effort

Comments

@petermetz
Copy link
Contributor

petermetz commented Jan 15, 2021

Is your feature request related to a problem? Please describe.

There is no one size fits all solution when it comes to providing our APIs, so the best solution may be to remain flexible and allow plugin developers to choose what they prefer/fits their specific use case.

Describe the solution you'd like

  1. Plugin implementations can pick and choose which protocol(s) to implement (as long as they pick at least one)
  2. Stream aggregation for protocols that cannot handle pub-sub (looking at you OpenAPI)
  3. Maintain (enforce?) as much convergence as possible among protocol implementations, a few ideas for example
    3.1. All clients should work on the browser platform as well as on the back-end
    3.2. API surface on the client side should be as close to identical as possible to the point that to a client side caller it can hopefully be interchangeable when using AsyncAPI or gRPC-web clients because they both would return Observable<T> for streams for example (OpenAPI would be the outlier here, not being able to handle streams it would just return Array<T> and the stream aggregation would happen on the backend.
  4. More interchangeability examples:
    4.1. Corda connector plugin written in Kotlin or Java, exposes a gRPC-web API on the back-end. It also exports a Typescript API client that can talk to said gRPC-web API from both the browser and from NodeJS. This ensures that user-facing applications in the browser and other plugins within the Cactus node can both use the Corda plugin in question.
  5. Runtime discover-ability of capabilities of plugins baked into the core API, e.g. the API server can ask a plugin instance about which protocol it exposes.
    5.1. 5) might not be the best idea because ideally we just simply wouldn't need that information, but I doubt we'll be able to make it that smooth even with the recently introduced customization possibilities for plugin factories that we added for language independence.
  6. Using the different clients of the various protocols are not mutually exclusive, e.g. I can have an application that uses both OpenAPI and gRPC-web for talking to a plugin if said plugin happens to implement both of those (the nice thing would be if all plugins would implement all protocols, but mandating that would put a lot of burden on people writing plugins, potentially unnecessarily).
  7. Horizontal scalability is not hampered by the protocol implementations. It is vital that regardless of which protocols are being used, an API server should be able to co-exist with N number of other API servers doing the exact same thing. Without this we will likely end up being the bottleneck in-between ledgers, especially in deployment scenarios where there's a large number of different ledgers that Cactus is connecting to while being in the middle of the circle. (Hub and spoke fashion one could say?)
  8. All protocol implementations should also somehow be enforced to work with whatever we come up with later for authentication (OpenID Connect has been a strong candidate for a long time). The important thing here is that we want plugin back-ends to focus on the functionality provided by the plugin, not on delivering a standalone, full blown web service with it's own identity/authz/n etc. That would rapidly increase complexity of deploying/developing against Cactus and therefore limit it's usefulness as a framework/SDK of SDKs. We want/need to provide flexibility, but at the same time also need to provide clear boundaries on what plugins should and should not implement on their own.

Describe alternatives you've considered

Considered just trying to pick a one to rule them all protocol, but they all have different downsides, trade-offs and large communities of people backing them as well so trying to pick a winner instead of embracing our core design principle of flexibility seems like a not so wise choice at the moment.

Additional context

This is a massive task, will probably end up breaking it into smaller, implementation specific pieces later, but also wanted a single issue where we can discuss the larger topic.

cc: @takeutak @sfuji822 @hartm @jonathan-m-hamilton

@petermetz petermetz added enhancement New feature or request SPIKE Exploratory work to better scope additional effort API_Server dependencies Pull requests that update a dependency file Developer_Experience Significant_Change Applying this label triggers the more stringent review of the maintainers and the 2+1 PR rule. Core_API Changes related to the Core API Package labels Jan 15, 2021
@petermetz petermetz added this to the v0.9.0 milestone Jan 15, 2021
@petermetz petermetz self-assigned this Jan 15, 2021
@petermetz petermetz pinned this issue Jan 15, 2021
@RafaelAPB
Copy link
Contributor

This is probably very important for a stable version of Cactus. I would be interested to know what are your ideas to implement this.

@petermetz
Copy link
Contributor Author

This is probably very important for a stable version of Cactus. I would be interested to know what are your ideas to implement this.

@RafaelAPB It's still very much a work in progress thing where I'm just throwing code against the wall to see what sticks/works. With that said, here's the branch 1 that I've been making good progress on with the initial steps which is to be able to include grpc backends for plugins and have Cactus API clients talk to those via a reverse proxy.
The reverse proxy is key because I do not want to let go of the universal nature of our API clients in the sense that they need to run the exact same way both in the browser and on the back-end. So, I want people to be able to write their plugins with a grpc backend instead of raw HTTP (OpenAPI) and also allow them to supply the client for it as long as they comply with the two things: 1) universal client, e.g. works in browser as well 2) API level compatibility with other potential implementations that are not grpc.

An actual example for demonstrating what I mean by point 1 and 2 is that the following code should work and it should do so in both a browser and a NodeJS environment with a fictitious new ledger called SomeDLT:

import { DefaultApi as SomeDltApi } from "@hyperledger/cactus-plugin-ledger-connector-some-dlt";
import { Observable } from "rxjs";

const apiClient  = new SomeDltApi({ basePath: "URL_OF_PLUGIN_BACKEND" });

const { data, status } = await apiClient.runTransactionV1({ ... });

// let's say the `SomeDLT` has the feature to stream all confirmed transactions over GRPC streamed responses
const observable: Observable<SomeDltTx> = await apiClient.getTransactionEventsV1({ ... });
observable.subscribe(tx => {
  // do something cool with the tx definition here that your application needs to work
});

The only difference here compared to our current usage of the OpenAPI based clients is that there are streamed responses which can be used to process certain data in real-time as it comes in. The same is not possible with OpenAPI based clients because they would have to do polling for this (so the same thing is conceptually achievable, but polling is heavily discouraged as a practice because it is very wasteful with resources in 99% of the cases where it's being used)

Taking it a step further, the exact same code example above should also work if the plugin back-end is working with WebSockets. The common compatibility layer should be (if you ask me) based on the RxJS Observable APIs: I'm a fan of reactive programming and many on the internet seem to agree that it is a great idea/pattern to follow: https://github.com/ReactiveX/rxjs/stargazers

More and better said info about grpc-web can be found here: https://grpc.io/blog/state-of-grpc-web/

petermetz added a commit to petermetz/cacti that referenced this issue Apr 27, 2021
WORK IN PROGRESS

Signed-off-by: Peter Somogyvari <peter.somogyvari@accenture.com>
@petermetz petermetz modified the milestones: v0.9.0, v0.10.0 Sep 2, 2021
@petermetz petermetz removed this from the v0.10.0 milestone Jul 24, 2023
@petermetz
Copy link
Contributor Author

We've added gRPC support through @grpc/grpc-js and there are tests covering it being functional as well!

@petermetz petermetz unpinned this issue Jul 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
API_Server Core_API Changes related to the Core API Package dependencies Pull requests that update a dependency file Developer_Experience enhancement New feature or request Significant_Change Applying this label triggers the more stringent review of the maintainers and the 2+1 PR rule. SPIKE Exploratory work to better scope additional effort
Projects
None yet
Development

No branches or pull requests

2 participants