Be able to pass functions as parameters in forked actors
If you give me a few hints, I could try to implement it
Of course, feel free to implement and to ask any questions!
I'm thinking of the following implementation. I would not check each value in customParameters object for being a function or a class, but instead check the whole customParameters. If a user needs something more complex, than a plain JS object, he would agree to wrap the whole parameters object into a class, I guess. And this class (or a function) can be marshalled to a forked actor using ActorSystem._serializeDefinition() function. In forked-actor-worker.js, when unmarshalling, we can simply check customParameters for being a string. If it is, we unmarshall it with compileDefinition function, otherwise - use as is. In addition, we could support module-defined customParameters, but I'd rather do it in a separate task.
@vallettea , if you are going to implement this, please assign this task to yourself and use a separate feature branch.
I believe this is similar. I'm not necessarily trying to pass a function as a customParameters but thats what I tried and failed to accomplish my goal.
I have an express server that has a request handler that basically sends a sendAndReceive to an actor that has child actors that process the request and create a series of actions that the parent can send back to the request handler to execute. I'd like this server to be usable by multiple teams which each have their own needs. I wanted the child actors that process the request to be passed in so each team can use what shared actors work for them or write their own. This works perfectly when in-memory. The problem is when I switch to forked the passed in Actors get stripped from the customParameters object.
Is there a better way to do this or should I be looking at trying the implementation you laid out?
It worked to send a string of the location of actors. Although currently the example lives in the same repo as the server. At some point when it gets npm installed I'm not sure if the location method will work.
@aintnorest , can you please provide the code or pseudocode of what you are doing just to get the idea? customParameters are only used at actor creation time. Not sure, why you are passing actors through customParameters instead of creating them with createChild method in the initialize hook of an actor.
const SA = require('../index.js');
const server = new SA.Server({
staticRegEx: /.*\/public/i,
staticUrl: `${process.cwd()}/example/dist`,
actors: [
{ actor: '/actors/requestParserActor' },
{ actor: '/actors/localizationActor', config: { project: 'snkrs' } },
{ actor: '/actors/setResponseHeadersActor', config: { headers: { 'Edge-Control': '!no-store,max-age=7200' } } },
{ actor: '/example/serverSideRenderActor' },
],
});
initialize(selfActor) {
/**
* TODO Add actor validation
*/
this.actors = selfActor.getCustomParameters().actors;
// Set log on the Actor's context
this.log = selfActor.getLog();
// Compile required request keys and initialize all actors that were passed in
this.actors = this.actors.map((actorDefinition) => {
const options = {
customParameters: actorDefinition.config || {},
...(actorDefinition.actorOptions || {}),
};
// Create the child actor and register the required keys
return selfActor.createChild(actorDefinition.actor, options)
.then((actor) => {
(actor.definition.requirements || []).forEach((value) => {
if (!this.requiredRequestedKeys[value.key]) {
this.requiredRequestedKeys[value.key] = { location: value.location };
}
});
return actor;
});
});
/**
* Once the actors have all initialized this will set them on the context of the Actor
* to be referenced for iteration.
*/
return Promise.all(this.actors)
.then((actors) => {
this.actors = actors;
});
}
I am creating them through createChild. That code leaves out a bit but I could post more if your not getting the gist.
@aintnorest , I see your point. In general it's not a good idea to tell an actor which children it should have from outside. It's better to encapsulate the child structure and settings within an actor. This way, every actor will be better isolated and movable across your project structure.
You give an actor a "big idea" of what problem it should solve (with some inputs not related to actor structure), and then an actor decides by itself which children it needs to spawn and how to break down the input task to subtasks and how to distribute the subtasks between the children. You apply the same strategy recursively to every child actor.
Not every team has the same needs. I was trying to give them a way to pass in their needs