untu / comedy

Node.js actor framework.
Eclipse Public License 1.0
653 stars 36 forks source link

Dependencies inside Resources in forked modules #45

Open Windsdon opened 4 years ago

Windsdon commented 4 years ago

Hello!

I'm starting a new project with TypeScript and currently experimenting with Comedy as a solution for scaling the application.

I ran into an issue with the way Resources are serialized and sent to the forked process which is causing a lot of headaches and seems like a big limitation (if I'm understanding this correctly).

The original Resource definition is as follows:

import { ActorSystem, ResourceDefinition } from 'comedy';
import { getResource } from '../helpers/test';
import { ComedyResource } from '../decorators/ComedyResource';

@ComedyResource('TestResource')
export default class TestResource implements ResourceDefinition<string> {
    destroy(): Promise<void> | void {
        // nothing to do here
        return undefined;
    }

    getResource(): string {
        // return from an imported function as a test
        return getResource();
    }

    initialize(system: ActorSystem): Promise<void> | void {
        // nothing to do here
        return undefined;
    }
}

But what is sent to the child process (in the create-actor message) is:

class TestResource {
    destroy() {
        // nothing to do here
        return undefined;
    }
    getResource() {
        // return from an imported function as a test
        return test_1.getResource();
    }
    initialize(system) {
        // nothing to do here
        return undefined;
    }
}; TestResource;

As you can see, the imports are all missing and there is no way this can work.

@Zephyrrus noticed that you can use require() inside the definition and to import things, but they are imported from the wrong working directory and therefore don't resolve properly.

As a workaround I considered creating dummy "shells" that would dynamically load the correct file (from disk) with require(), but that sounds very cumbersome to maintain.

Is there any solution to this? Am I missing something?

pct-cclausen commented 4 years ago

I think you need to define the resource via a path instead of passing the object itself. For my resources and actors I create them in a file like this:

import { ResourceDefinition, ActorSystem } from "comedy";
import Knex from "knex";
import fs from "fs";

export class KnexResource implements ResourceDefinition<Knex> {
// this resource is in a package called @pct-digital/knex-db-resource
    static RESOURCE_PATH =  "@pct-digital/knex-db-resource/dist/KnexDbResourceModule";

    log: any;
    knex: Knex = null as any;

    async initialize(system: ActorSystem) {
        this.log = system.getLog();
        this.log.info("init KnexResource");

        let knexConfig = (system as any).options.config.knex;

        if (knexConfig == null) {
            this.log.error("Cannot initialize KnexResource, the global configuration must contain a knex property!");
        }

        this.log.info("Initializing knex.js pool");
        this.knex = Knex(knexConfig);
        let result = await this.knex.raw("SELECT 1");
        if (result.rowCount !== 1 || result.rows.length !== 1) {
            this.log.error("knex pool test failed!");
        } else {
            this.log.info("knex pool setup completed!");
        }
    }

    destroy() {
        this.log.info("Destroying knex.js pool");
        return this.knex.destroy();
    }

    getName() {
        return "KnexResource";
    }

    getResource() {
        return this.knex;
    }

}

Then I add an extra file from this this actually will be loaded by comedy:

import { KnexResource } from "./KnexDbResource";
module.exports = KnexResource;

I've not found a way to use the file that contains the normal "export class KnexResource" directly, the path I use to define the resource has to point to the file that does module.exports = ...

My system is initialized like this:

    const system = actors.createSystem({
        config: {
            knex: serverCfg.knex,
        },
        resources: [KnexResource.RESOURCE_PATH]
    });