Skip to content
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

Multiple connection support #226

Open
lafriks opened this issue Oct 13, 2021 · 12 comments
Open

Multiple connection support #226

lafriks opened this issue Oct 13, 2021 · 12 comments

Comments

@lafriks
Copy link
Contributor

lafriks commented Oct 13, 2021

It would be nice to have support to provide multiple connections with role write or read-only so that it would possible to use for example with PostgreSQL cluster that has single master and multiple read only nodes. Rel would then send write operations to master and select operations to one of slaves

@Fs02
Copy link
Member

Fs02 commented Oct 14, 2021

Rel would then send write operations to master and select operations to one of slaves

In addition to this, it'll be good if REL supports query option to allow reading from master

@Fs02
Copy link
Member

Fs02 commented Oct 16, 2021

my current idea for this is to create a new adapter that can wraps any of existing adapters. the advantage of using adapter is it should be easy to implement, and this idea can be expanded further in case we need to create some kind of db router (different table have specific db).

example api:

  • primaryreplica.New(primary Adapter, replicas ...Adapter)

example usage:

adapter := primaryreplica.New(
    mysql.Open("root@(primary:3306)/rel_test"),
    mysql.Open("root@(replica1:3306)/rel_test"),
    mysql.Open("root@(replica2:3306)/rel_test"),
)
defer adapter.Close()

// initialize REL's repo.
repo := rel.New(adapter)
repo.Ping(context.TODO())

example future improvement with dbrouter:

// dbrouter.New(map[string]Adapter sources, map[string]string routes, defaultRoute string)
adapter := dbrouter.New(
  map[string]Adapter{
    "main": primaryreplica.New(....), // primary replica setup
    "products": mysq.Open(...), // regular db setup
  },
  map[string]string{"products": "products"},
  "main",
)

I'm still not sure with the package name, other option maybe masterreplica, sourcereplica, masterstandby (https://en.wikipedia.org/wiki/Master/slave_(technology)) 🤔

@lafriks
Copy link
Contributor Author

lafriks commented Oct 16, 2021

But how would you specify that selects (except for update) go to one set of connection and inserts, updates, deletes and selects with for update to other?

@Fs02
Copy link
Member

Fs02 commented Oct 16, 2021

because the adapter wrapper also implements Adapter (https://github.com/go-rel/rel/blob/master/adapter.go), we can just add logics to select which original adapter to use?

  • Ping - ping all adapters
  • Aggregate - use replica by default
  • Query - use replica by default
  • Insert - use primary
  • InsertAll - use primary
  • Update - use primary
  • Delete - use primary
  • Exec - parse query to decide if not specified
  • Begin - use primary (return primary adapter)
  • Commit - use primary
  • Rollback - use primary
  • Apply - use primary

@Fs02
Copy link
Member

Fs02 commented Oct 17, 2021

do you have another idea?

@lafriks
Copy link
Contributor Author

lafriks commented Oct 18, 2021

if Query/Aggregate is run in transaction or Query has rel.ForUpdate it must also use primary

@Fs02
Copy link
Member

Fs02 commented Oct 18, 2021

Right, should be easy to add that logics
And from what I know that query have no effect outside transaction anyway 🤔

@lafriks
Copy link
Contributor Author

lafriks commented Oct 18, 2021

I mean if I start transaction and do insert/update etc any SELECTS that are done within that transaction should be done within same transaction using same connection

@Fs02
Copy link
Member

Fs02 commented Oct 19, 2021

yes, correct, that exactly what will happen since Begin method in Adapter is actually returns another adapter with transaction connection, so it should be covered 👍

@Fs02
Copy link
Member

Fs02 commented Oct 19, 2021

still wip but I've created poc for idea described above https://github.com/go-rel/primaryreplica

@lafriks
Copy link
Contributor Author

lafriks commented Oct 26, 2021

Shouldn't it also check context in here to see if in transaction and return primary if it is

@Fs02
Copy link
Member

Fs02 commented Oct 26, 2021

nice observation 👍

we should not need to check context in adapter, because it's taken care by repository. the flow is roughly like this:

  • when transaction is started, the adapter will return writer adapter from Begin function.
  • returned transaction will be wrapped to a context that will be passed to transaction block.
  • before running any operation, every method in repository struct will first call fetchContext method, which will return appropriate adapter. (example on Find)
  • from there, everything that happens inside transaction block will use writer adapter only, and there's no communication to primary adapter.

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

2 participants