Skip to content

Configurable server instances factories (singleton, etc) #102

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
ShayOinif opened this issue Jun 15, 2024 · 6 comments
Open

Configurable server instances factories (singleton, etc) #102

ShayOinif opened this issue Jun 15, 2024 · 6 comments

Comments

@ShayOinif
Copy link

Hello, i know this lib is very new and things are still moving.
But I like the concept very much, and I would love to use it in some of my projects.
I have 2 main issues:

  1. My server side is going to be on Linux and right now there isn't ktor server transport for this target.
  2. As I see the impl and underlying protocol, I have to create a KRPC server for each client.
    I would love to simply supply the same instance for all clients.
    I understand that the main reason is ID'S not being unique between different client calls.
    Maybe uuid v7 which is time based can be used?
    Anyhow, I know that some work around could be to make my impl for the service an inner class of main service or singleton and make them very thin and simply redirect the calls to main/singleton service.
    But still, I don't like all this instantiation.

Thanks in advance and I am really looking forward to use this lib in my code!

@Mr3zee
Copy link
Collaborator

Mr3zee commented Jul 15, 2024

Hi, @ShayOinif ! These are two different requests as I see
First one can be represented by #95 and #83 , while the second one more complicated.
It will be a part of a bigger protocol rework we plan for later, as we have a lot of things to consider at once, so we want to take out time with it

@Mr3zee
Copy link
Collaborator

Mr3zee commented Jul 15, 2024

I will rename the issue to address only the second point

@Mr3zee Mr3zee changed the title Ktor transport (server/client) for more tragets Configurable server instances factories (singleton, etc) Jul 15, 2024
@tomhula
Copy link

tomhula commented Mar 14, 2025

Hi, I came across my usage problem and this seems to be the most relevant issue.
Since the project is in early phases, I understand there is weak documentation, so my understanding on how to correctly use the library and how it works is limited. But do I understand correctly, that a new rpc service instance is created for each client connection? For me that does not make much sense. I would expect a service to be one instance, which gets called by multiple clients, just how it usually is. (REST, gRPC..) This way I also cannot really use the service instance at any other place in the server code.

@Mr3zee
Copy link
Collaborator

Mr3zee commented Mar 14, 2025

Hi @tomhula ! Yes your understanding is correct. One client - one server instance. That's the only option now.
While I understand, when singleton may be of use, just reusing service in different parts on the server code base is weak reason. Usually you have layered structure, and use shared services (like db) anyway and don't stare the network interface (rpc in this case)

@tomhula
Copy link

tomhula commented Mar 14, 2025

I understand the point, the library is about communication and thus the RpcService is an interface purely between the server and the client. However I will give my example of what I was describing in my project:
Take a small web app as an example.
The webapp is primarily built around frontend and backend is there to serve (the literal meaning of serve, not just return the page) the fronted. The API, public or not, is kind of an interface between the frontend and the database. Thus most of the api requests are directly correlated to the database requests.

So now using Kotlinx-rpc, we have example typical user crud interface:

@Rpc
interface UserService : RemoteService
{
    suspend fun createUser(newUser: CreateUserDto): Int
    suspend fun getUser(id: Int): UserDto?
    suspend fun getAllUsers(): List<UserDto>
    suspend fun updateUser(user: UserDto): Boolean
    suspend fun deleteUser(id: Int): Boolean
}

On the backend, I would make a DatabaseUserService (database implmentation of the service)

class DatabaseUserService(
    override val coroutineContext: CoroutineContext,
    database: Database
) : UserService
{
    override suspend fun createUser(newUser: UserDto, authToken: String?): Int
    {
        // sql request...
    }
    override suspend fun getUser(id: Int): UserDto?
    {
        // sql request...
    }
    override suspend fun getAllUsers(): List<UserDto>
    {
        // sql request...
    }
    override suspend fun updateUser(user: UserDto): Boolean
    {
        // sql request...
    }
    override suspend fun deleteUser(id: Int): Boolean
    {
        // sql request...
    }
}

This is what I currently do, however from my understanding from this issue, this is not correct. (especially because the class gets instantiated a lot of times)
So the actual solution would be for the implmelentation of UserService to only handle the communication stuff (authentication, input validation, exceptions..) and delegate the data retrieval to a data layer.
This makes sense, however it introduces code duplication, especially in small projects, where the api requests are basically 1:1 to the data layer requests. To fix the above problem, I would technically rename the service to UserServiceImpl for example and then make a seperate UserDatabaseRepository, (or Service or something) which would have 1:1 methods, which do the actual data retrieval.
My point is, that in my app, I would have 6 RpcServices and then 6 more database services, which would both have same methods and just call each other, which in smaller projects is unnecessary.

I hope this explains my point, sorry for lengthy explanation.

@Mr3zee
Copy link
Collaborator

Mr3zee commented Mar 17, 2025

That is a good point indeed for the smaller applications, just minimizing unnecessary complexity.
I we will keep this issue open, so that we can get back on this, but for now, only factories are supported, unfortunately.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants