'2018/06'에 해당되는 글 4건

  1. 2018.06.26 [Typescript] Lock - Code snippet.
  2. 2018.06.26 [Jest] Sharing code and symbolic link 문제.
  3. 2018.06.23 [Angular6] Inter-module communication.
  4. 2018.06.07 [Essay] Leader에 대한 member들의 평가... 에 대한 해석.

[Typescript] Lock - Code snippet.

Language/Typescript 2018.06.26 06:05
Typescript code to implement simple lock... 시험 환경: NodeJS: 8.X.X Typescript: 2.7.X lock.ts
import * as logger from '../logger';
import * as cls from 'cls-hooked';

class Settler {
    constructor(
        public resolve: any,
        public reject: any,
        public owner: string,
        public lock: Lock
    ) {}
}

const CLS_NS = '../XXX/lock';
const CLS_KEY_LOCK = 'lock';
const clsns = cls.createNamespace(CLS_NS);

const TEMP_OWNER_PREFIX = '$#@#$+|_)';
let tempOwnerInc = 0;
function getTempOwner(): string {
    return TEMP_OWNER_PREFIX + tempOwnerInc++;
}

/**
 * Class instance method decorator.
 * Return type of decoratee(method) MUST be 'Promise'!
 * NOTE that, this decorator change return type of method to Promise<any>!
 *
 * @param lockProperty name of 'Lock' property name to use.
 */
export function clsLocked(lockProperty: string): any {
    return function (
        target: any, key: string | symbol, descriptor: TypedPropertyDescriptor<any>
    ): TypedPropertyDescriptor<any> | void {
        if (descriptor === undefined) {
            descriptor = Object.getOwnPropertyDescriptor(target, key)!;
        }
        let orig = descriptor.value;
        descriptor.value = async function (): Promise<any> {
            let instance = <any>this;
            let funcArgs = arguments;
            let lockobj = <Lock>instance[lockProperty];
            log.assert(!!lockobj);
            let clsLockSet = <Set<Lock>>clsns.get(CLS_KEY_LOCK);
            if (clsLockSet) {
                // in cls lock context
                if (clsLockSet.has(lockobj)) {
                    log.assert(lockobj.getOwner() === clsns);
                     return await orig!.apply(instance, funcArgs);
                } else {
                     clsLockSet.add(lockobj);
                    return lockobj.run(clsns, async () => {
                        return await orig!.apply(instance, funcArgs);
                    });
                }
            } else {
                // Start with new context lock.
                return clsns.runAndReturn(async () => {
                    clsns.set(CLS_KEY_LOCK, new Set<Lock>([lockobj]));
                    return lockobj.run(clsns, async () => {
                        return await orig!.apply(instance, funcArgs);
                    });
                });
            }
        };
        return descriptor;
    };
}


//////////////////////////////////////////////////////////////////////////////
//
//
//
//////////////////////////////////////////////////////////////////////////////

export async function runClsLocked(
    lock: Lock, f: (...args: any[]) => Promise<any>, ...args: any[]
): Promise<any> {
    return clsns.runAndReturn(async () => {
        clsns.set(CLS_KEY_LOCK, new Set<Lock>([lock]));
        return lock.run(clsns, async () => {
            return await f(...args);
        });
    });
}

// Interface of Lock
//
// - await lock.get()
// - await lock.put()
// - await lock.run(owner, func, ...)
// [TODO]
// - Implement Read/Write Lock
//
// - await lock.try()
// - await lock.get(timeout)
// - await lock.run(timeout, owner, func, ...)
export class Lock {
    private owner: any = undefined;
    private waitq: Settler[] = [];
    constructor(
        public readonly name: string // Usually, this is used for debugging
    ) {}

    getOwner(): any {
        return this.owner;
    }

    waitsz(): number {
        return this.waitq.length;
    }

    /**
     * DO NOT USE 'get/put' combination. Use 'run' if possible.
     */
    async get(owner: string): Promise<Lock> {
        if (this.owner) {
            return new Promise<Lock>((res, rej) => {
                this.waitq.push(new Settler(res, rej, owner, this));
            });
        } else {
            this.owner = owner;
            return this;
        }
    }

    /**
     * DO NOT USE 'get/put' combination. Use 'run' if possible.
     *
     * @returns size of remaining wait-queue.
     */
    put(owner: string): number {
        log.assertFatal(owner === this.owner);
        let settler = this.waitq.shift();
        if (undefined === settler) {
            this.owner = undefined;
            return 0;
        }
        this.owner = settler.owner;
        settler.resolve(this);
        return this.waitq.length;
    }

    async run(owner: any, func: (...args: any[]) => any, ...args: any[]): Promise<any> {
        owner = owner ? owner : getTempOwner();
        await this.get(owner);
        try {
            return await func(...args);
        } finally {
            this.put(owner);
        }
    }
}

Simple example used with decorator.
class Foo {
    private lock: Lock;
    constructor() {
        this.lock = new Lock('Foo');
    }
    private priv0(v: number, tm: number): Promise<number> {
        return new Promise((res, rej) => {
            setTimeout(() => {
                res(v);
            }, tm);
        });
    }

    @clsLocked('lock')
    async pub00(v: number, tm: number): Promise<number> {
        return await this.priv0(v, tm);
    }

    @clsLocked('lock')
    async pub01(v: number, tm: number): Promise<number> {
        return await this.pub00(v, tm);
    }
}

일단 어느정도 동작은 하는것 같은데, 엄밀한 검증까지는... 즉 아직 실제 production server에 사용된 적은 없다...
Trackback 0 : Comment 0

[Jest] Sharing code and symbolic link 문제.

Language/Typescript 2018.06.26 05:28

환경:

NodeJS: 8.X

typescript: 2.8.X

jest: 22.X.X

ts-jest: 22.X.X



NodeJS + Typescript를 이용해서 backend를 구성할때, 특정 module을 여러 module에서 공유하고 싶을때가 있다.

이때, 공통 모듈을 package.json 을 가지는 하나의 독립모듈로 구성하고, 이를 dependency에 넣는 것도 좋지만(npm module을 사용하는 일반적인 방법), 그냥 단순하게 module directory를 공용으로 사용하고 싶은 경우도 많다.

예를 들면.


moduleA/

|- package.json

|- src/

|- common (*1)

|- ...


moduleB/

|- package.json

|- src/

|- common (*2)

|- ...


에서 (*1)과 (*2)에서 동일한 코드를 사용하고자 한다고 가정해 보자. 매번 똑같은 파일을 유지하는 것은 문제의 여지가 많으므로, 아래와 같은 구조를 만들고 싶을 것이다.



moduleA/

|- package.json

|- tsconfig.json

|- src/

|- commonA --> (*3) // Symbolic link to (*3)

|- ...


moduleB/

|- package.json

|- tsconfig.json

|- src/

|- commonA --> (*3) // Symbolic link to (*3)

|- ...


shared/

|- commonA/ (*3)

|- ...



그리고, moduleA와 moduleB의 tsconfig.json에는 symbolic link를 쫓아갈 수 있도록 


"compilerOptions": {

...

"preserveSymblinks:" true

...

}


로 설정한다.


자... 이제 모든 것이 문제없이 동작하는 것처럼 보인다.

그런데.. Jest를 수행시키면, 바로 문제가 발생한다.(제대로 Type을 Import하지 못한다!)

그리고, Symbolic link를 제대로 쫓아가지 못하는 것처럼 보인다.

Jest관련 각종 정보를 찾아보면, 일단은, 이 부분은 Known Issue이고, 지원하지 않기로 결정한 것으로 보인다.

(어딘가 본 기억이 있는데, 정확한 link는 잊어 먹었다는... 뭐 이후 또 바뀔수도 있으니....)

그래서, workaround를 고민해 보면, 아래와 같이 그냥 shared module에 ts-jest를 설치해 주면 문제가 해결되는 것 같다.



shared/

|- package.json // only 'ts-jest' exists at 'devDependencies'

|- commonA

|- ...


이렇게 해 두고, shared에서 npm install 을 통해서 ts-jestnode_modules 안쪽에 설치하고 나면, moduleA와 moduleB의 jest가 문제없이 동작하는 것을 볼 수 있다.

더 좋은 방법이 있는지는, 그리고 왜 이렇게 하면 동작하는지는 좀 더 살펴봐야할 부분이긴하나... 굳이 살펴봐야하나... 싶기도 하다... ^_^




Trackback 0 : Comment 0

[Angular6] Inter-module communication.

Domain/Web 2018.06.23 04:01

Sub module은 내가 어디에 붙어 있는지 몰라야 한다.  즉 module "내부" => "외부" 로 dependency가 없어야 한다. 따라서, Inter-Module Communication은 상위 module을 통하는게 좋아 보인다.


                      +--------------------+
              +------->  Parent  Module    +---+
              |       +---------+----------+   |
              |                 |              |
Parent Module is observing    Notify         Notify
              |                 |              |
    +---------+-+     +---------v-+     +------v---------+
    | Module A  |     | Module B  |     | ...            |
    +-----------+     +-----------+     +----------------+


이 경우, 문제는 Lazy-loading module이다.

Lazy-loading module의 Concept상, Parent Module (이 경우, Root Module)에서 Notify를 할 수 없고 해서도 안된다 - Notify를 하려면, loading을 해야하는데... 그럼 더 이상 lazy loading이 아니다!

(만약 해야할 필요가 있다면, Lazy-loading으로 만들면 안된다. )


따라서, Lazy-loading module의 경우, 일반적인 sub-module의 제약사항에 더불어 추가적으로, 다음의 조건이 필요하지 않나... 싶다.(아직 좀더 많은 공부가 필요하다.)

- App. singleton provider 를 사용하면 안된다.

- module이 loading되었음을 가정한, "외부 => module" 로 communication이 없어야 한다.

<= 이거 상당히 강력한 제약사항이다!!!

그리고, 이건: "Inter-lazy-loading module간 communication이 없어야 한다." 를 의미하기도 한다.


Trackback 0 : Comment 0

[Essay] Leader에 대한 member들의 평가... 에 대한 해석.

Essay/General 2018.06.07 22:29

기본적으로, 팀원 들의 팀장 에 대한 평가는 긍정적이기가 어렵다.

자신을 평가하고, 업무를 지시하는 사람에게는 당연히 불만이 생기게 되니... 어찌보면 인지상정이고 당연하다할 수 있다.

그렇다고, 팀원들이 팀장을 전부 안좋게 평가하는데, 이걸 자연스러운 현상이라고 무시할 것인가? 그것도 아닌 것 같다...

그래서 이 부분에 대해서, 개인적인 경험에 비추어 해석의 지표(?) 같은 것을 적어보고자 한다.


일단, 팀원들의 팀장에 대한 평가를 4가지로 분류한다.


적극적인 긍정(적긍)

소극적인 긍정(소긍)

소극적인 부정(소부)

적극적인 부정(적부)


'적극적'과 '소극적' 의 기준이 명확할수는 없으나, 의미는 통할 것이라고 생각한다.


개인적인 경험으로는...


"적부(< 40%) + 소부" <= 60%


까지는 정상적인 범위가 아닐까 한다. 즉 적부 가 40%정도까지, 적부 + 소부 가 60% 정도까지이다.

만약 이 수치가 60%~70% 정도라면, 좀 애매하다...


그리고,


"적부(> 50%) + 소부"  > 80%


라면, leader에게 뭔가 문제가 있다고 볼 수 있을 것 같다.


위의 수치들은 어떠한 근거도 없다. 그냥 내 경험 + 일반화 를 통해 만들어낸 수치일 뿐이다.


수치 자체보다는


"팀장에 대한 팀원의 평가는 좋기가 어렵다"는 것을 전제로, 그 정도가 지나치게 심할 경우는 확인이 필요하다.


정도로 요약하고 싶다.




Trackback 0 : Comment 0