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

refactor: DB transactions context #2513

Merged
merged 16 commits into from
Apr 12, 2024

Conversation

nasdf
Copy link
Member

@nasdf nasdf commented Apr 10, 2024

Relevant issue(s)

Resolves #2512
Resolves #2516

Description

This PR moves the db transactions to the context.

Notable Changes:

  • moved txn_db.go to store.go
  • replaced explicitTxnDB and implicitTxnDb with store
  • added db/session.go to simplify setting context
  • added db/context.go to manage context values

Tasks

  • I made sure the code is well commented, particularly hard-to-understand areas.
  • I made sure the repository-held documentation is changed accordingly.
  • I made sure the pull request title adheres to the conventional commit style (the subset used in the project can be found in tools/configs/chglog/config.yml).
  • I made sure to discuss its limitations such as threats to validity, vulnerability to mistake and misuse, robustness to invalidation of assumptions, resource requirements, ...

How has this been tested?

make test

Specify the platform(s) on which this was tested:

  • MacOS

@nasdf nasdf added the area/db-system Related to the core system related components of the DB label Apr 10, 2024
@nasdf nasdf added this to the DefraDB v0.11 milestone Apr 10, 2024
@nasdf nasdf self-assigned this Apr 10, 2024
@nasdf nasdf changed the title refactor: DB session refactor: DB transactions context Apr 10, 2024
type contextKey string

const (
TxnContextKey = contextKey("txn")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: To avoid allocation on the key, you can simply use

type TxnContextKey struct{}

https://pkg.go.dev/context#WithValue

@nasdf nasdf marked this pull request as ready for review April 10, 2024 18:39
@nasdf nasdf requested a review from a team April 10, 2024 20:32
db/collection.go Outdated
if err != nil {
return nil, err
}

txn := mustGetContextTxn(ctx)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: It's a little weird that whe need to extract the txn from the context and the next function call passes the context and the transaction.

question: Why not leave it to the function that needs the txn to get it from the context?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Someone had a preference for this in the standup discussion. If a function requires a transaction it should remain as a parameter and not come from context.

db/context.go Outdated
//
// If a transactions exists on the context it will be made explicit,
// otherwise a new implicit transaction will be created.
func ensureContextTxn(ctx context.Context, db transactionDB, readOnly bool) (context.Context, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: There is a lot of ensureContextTxn followed by mustGetContextTxn. To avoid having to type cast everytime, I would suggest returning datastore.Txn here.

func ensureContextTxn(ctx context.Context, db transactionDB, readOnly bool) (context.Context, datastore.Txn, error)

db/store.go Outdated

var _ client.Store = (*store)(nil)

type store struct {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Can you explain why you prefer wrapping the db instead of using it directly for the methods bellow?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. There's no need for a separate struct anymore. Would you prefer these functions remain in store.go or would it be better to move them into db.go?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

store.go is fine with me. We could even add documentation at the top saying that the fonction within implement the client.Store interface for db.

@@ -35,7 +35,7 @@ type CCIPResponse struct {

// ExecCCIP handles GraphQL over Cross Chain Interoperability Protocol requests.
func (c *ccipHandler) ExecCCIP(rw http.ResponseWriter, req *http.Request) {
store := req.Context().Value(storeContextKey).(client.Store)
db := req.Context().Value(dbContextKey).(client.DB)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: There is nothing wrong with keeping the more restrictive type casting of client.Store since that is really what is needed here.

net/server.go Outdated
@@ -349,15 +353,14 @@ func (s *server) syncIndexedDocs(
ctx context.Context,
col client.Collection,
docID client.DocID,
txn datastore.Txn,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todo: Remove unused param.

Copy link
Collaborator

@fredcarle fredcarle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the changes. I'll just wait for the comments to be resolved before approving.

I'm wondering if we should remove the txn from function params when the context already holds it. Probably out of scope for this PR but could be done shortly after.

Edit: Will bring it up in the standup.

@jsimnz
Copy link
Member

jsimnz commented Apr 11, 2024

I'm not a huge fan of this PR, not sure if its the specific implementation or the general concept (txn via context). This was my concern when discussing it in the ACP discord thread regarding the use of txn via the context making the depedency more opaque. It can make following the call stack of a given transaction harder to reason about and easier to mess up.

Heres an example in the basicImport func

It has the following function signature
image

It uses the txn as an argument to a follow-up DB call (note the private method being called which specifically has the txn argument)

image

Then makes a call to the a public function which doesn't use the txn

image.

It is not 100% clear the txn scope the last call is using, since the calling function (basicImport) is explicitly given a txn.

Before, the last line was col.WithTxn(txn).Create(...) which makes it very obvious the txn being used.

--

Theres a possible solution here:
All actual DB/Collection functionality is implemented in the private funcs (which we basicallly do now), all private funcs only call private funcs, and private funcs take txn as a input args.

in fact after reviewing this, and the existing (develop) implementation, there is actually a bug where some call paths don't respect transaction scopes correctly because of the implicit txn creation on the Collection public calls.

Oddly enough, this PR actually fixes that without intending to because of the shared context.

Perhaps my issue isn't necessarily with the transaction context propagation, but the inconsistency we have between implicit and explicit transactions and how they are handled, both on the db object and the Collection object.

Happy to chat more during the dev call today (cc @nasdf @fredcarle )


// SetContextTxn returns a new context with the txn value set.
func SetContextTxn(ctx context.Context, txn datastore.Txn) context.Context {
return context.WithValue(ctx, txnContextKey{}, txn)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todo: I think we need to check here if there is already an existing transaction in the context. I there is one, current code will override it allowing potentially to leak the old transaction that was not discarded yet.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ensureContextTxn makes sure we don't overwrite an existing transaction. There is a case within net/server.go where we need the functionality to overwrite the transaction.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if having ensureContextTxn is enough in this case. SetContextTxn is a public method and can be called from anywhere. If ensureContextTxn is required to call before calling this one, why not to make it part of it? Otherwise I see no link between the two.

Copy link
Member Author

@nasdf nasdf Apr 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not seeing the issue here. The lifetime of the transaction is managed from the caller. If the caller overwrites a transaction and forgets to discard it, then the bug is in the caller's code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the intention is to put this responsibility on the caller's side, it's fine, but then it needs to be documented.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the function docs to be more clear.

@nasdf nasdf requested a review from fredcarle April 12, 2024 16:39
Copy link
Collaborator

@fredcarle fredcarle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@nasdf nasdf merged commit ef228b8 into sourcenetwork:develop Apr 12, 2024
26 of 28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/db-system Related to the core system related components of the DB
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implicit Collection transactions aren't respected. Move db transactions to context
4 participants