deepkit / deepkit-framework

A new full-featured and high-performance TypeScript framework
https://deepkit.io/
MIT License
3.2k stars 123 forks source link

@deepkit/rpc - persistent rxjs Subject #124

Closed apasare closed 3 years ago

apasare commented 3 years ago

Hey, I'm trying to implement a Subject which could be used by multiple DeepkitClients(used in a React App), but the problem that I'm facing is that when a client refreshes the browser(or is disconnected from backend due to a network failure) the Subject is closed. My implementation is mostly inspired by the sensorData example from here https://deepkit.io/library/rpc.

# frontend, inside a component

useEffect(() => {
  const sub = sensorData.subscribe((next) => {
      // update state
  });

  // cleanup on component unmount
  return () => sub.unsubscribe();
});
# backend

class MyController implements MyControllerInterface {
    protected sensorData = new Subject<number>();

    @rpc.action()
    sensorData(): Subject<number> {
        // return already existing subjects
        return sensorData;
    }
}

Am I misusing @deepkit/rpc? Is it not intended for such use-cases? Is there a way to make the Subject persistent, and not be closed when client's connection is lost?

marcj commented 3 years ago

All returned subjects are saved in a list and as soon as the client disconnects we call complete on them, so that the subject-creator can handle it correctly and to make sure no memory-leaks happen (as the creator can then unsubscribe from dependencies necessary to feed the subject). The client can also call unsubscribe on the subject created on the client side, which in turn calls complete on the returned subject on the server as well. So, implication of that is as soon as you return a subject that was not created in the RPC action itself and rather stored on the class or somewhere else, it will be complete sooner or later. We have to do that to not get memory leaks.

To now get a subject correctly working, you should create a new subject and subscribe to your "global" subject basically only redirecting values, without closing the origin/global subject when the client disconnects.


class MyController implements MyControllerInterface {
    protected sensorData = new Subject<number>();

    @rpc.action()
    sensorData(): Subject<number> {
        const subject = new Subject<number>();
        //redirect all values
        const sub = this.sensorData.subscribe(subject);
        //unsubscribe from sensorData when client disconnects or closes the subject without closing sensorData
        subject.subscribe().add(() => sub.unsubscribe());
        return subject;
    }
}
apasare commented 3 years ago

Hey, thank you for your reply. Quite a neat solution.