Service dependencies #2135
Replies: 4 comments 7 replies
-
Some questions / thoughts: Do service implementations really depend on other service implementations? Is this a problem that actually needs to be solved? It seems like identify is a special case, and this can be solved by better documentation in services that require specific configurations. |
Beta Was this translation helpful? Give feedback.
-
TLDR for below thoughts:There are other patterns out there that are related to the problem we're trying to solve, but it would be a much more significant haul to implement & maintain them. Let's start with the simple approach and see how it goes. If we want to move to something more complex later, we should ensure the work here doesn't make that harder. Quote & Response1.
I think this is a fairly straightforward and simple approach that would work for the stated problem. It reminds me a lot of older AngularJS Dependency Injection (DI) and AMD (requirejs) type approach to modules. libp2p logic for this would just be something like import { serviceName, serviceDependencies } from '@libp2p/interface'
/**
* Ensure that every service's `serviceDependencies` in the array of services is present in the array of services.
*/
function checkLibp2pServiceDepAlignment(services) {
const serviceNamesSet = new Set(...services.map(service => service[serviceName]))
services.forEach(service => {
if (!service[serviceDependencies].every(dependency => serviceNames.has(dependency))) {
throw new Error(`Service ${service.serviceName} could not find all of its dependencies in the provided services array.`)
}
})
// TODO: catch circular deps
// TODO: catch duplicate services?
// TODO: print out a nice error message including ALL errors if ANY (missing/circ/duplicate)
} Can we force the 2.
I like this idea but I feel like versions in the service key will ultimately result in two "version not compatible" error messages: compiletime + runtime, and be very troublesome. If we could guarantee the additional brain-dump:
|
Beta Was this translation helpful? Give feedback.
-
I am in need for this feature, but rather just being a type help (like the identify + kad or identity + circuit relay example). I want to access another service functionality from a service. What I have been thinking about is that having services in a map fundamentally makes it hard to build this cross dependencies, for the same reason it is hard to force a field with a type in a class to be present if another field with a specific type is present. IMO (service) maps are good for storing data that have not dependencies on each other, and one can/should be able to put/remove any value by its key without trouble. My dream scenario (I think) would be something much more simple const libp2p: Libp2p = await createLibp2p({ /* transports, connnection encryption, peerId */ })
const withIdentify: Libp2p & { services: { identify: IdentifyService } } = identifyService(libp2p)
const withKad: Libp2p & {services: { identify: IdentifyService, kad: DHT } } = kadDHT(withIdentify)
// another service that depends on kad dht
const somethingThatDependsOnKad: Libp2p & { services: { identify: IdentifyService, kad: DHT, anotherService: AnotherService} } = anotherService(withKad)
await libp2p.start() or just a builder pattern (this might be even better) const libp2p: Libp2p & { services: { identify: IdentifyService, kad: DHT} }
= await createLibp2p()
.withTransport(tcp)
.withService(identifyService)
.withService(kadDHT)
.build().start() Now inside kadDHT constructor I would be able to get access to the IdentifyService because I know it has been constructed before This would allow dependencies to both be validated with types but also during runtime as a service can check whether another service is present in its constructor. |
Beta Was this translation helpful? Give feedback.
-
I created an issue to track @achingbrain 's suggestion #2263 |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
We need a way for services to express their dependencies (e.g. KAD-DHT needs Identify to function properly).
I tried doing this through types but it didn't really work, plus it doesn't help anyone writing vanilla js.
@maschad tried doing this through config validation but that has it's own problems - mandated service names, libp2p needing to know about which service depends on what others, etc.
How about each service could have a string identifier, and each service could return a list of other services it requires, then on startup libp2p could verify that everything is present and correct?
It would be tempting to export the service name, but that would mean (for example) KAD-DHT would need to depend (npm-style) on the Identify implementation which currently lives in libp2p (for now anyway) so we'd probably start off with duplicated strings.
We could even have some sort of versioning, read out of the package.json -
my-service@1.0.0
, this might help with working out which module is compatible with which version of libp2p, something that's a continual pain point for people.Beta Was this translation helpful? Give feedback.
All reactions