Skip to content

Conversation

fishcakez
Copy link
Member

  • Add begin(!)/1,2 to checkout and begin
  • Add rollback(!)/1,2 to rollback and checkin
  • Add commit(!)/1,2 to commit (or rollback) and checkin

rollback/2 is overloaded and has old behaviour inside transaction/3 for
backwards compatibility. However transaction/3 is deprecated because is
makes assumptions about database status that may not hold.

New return values from handle_begin/2, handle_rollback/2 and
handle_commit/2 allow to signal mistaken database status.

* Add begin(!)/1,2 to checkout and begin
* Add rollback(!)/1,2 to rollback and checkin
* Add commit(!)/1,2 to commit (or rollback) and checkin

rollback/2 is overloaded and has old behaviour inside transaction/3 for
backwards compatibility. However transaction/3 is deprecated because is
makes assumptions about database status that may not hold.

New return values from handle_begin/2, handle_rollback/2 and
handle_commit/2 allow to signal mistaken database status.
@fishcakez fishcakez requested a review from josevalim July 9, 2017 23:52
{:error, err, _meter} ->
@spec rollback(conn, (opts :: Keyword.t) | reason :: term) ::
{:ok, result} | {:error, Exception.t}
def rollback(conn, opts \\ [])
Copy link
Member Author

Choose a reason for hiding this comment

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

Given the transaction/3 behaviour is this incorrect?

meter = event(meter, query)
msg = "can not #{query} inside legacy transaction"
err = DBConnection.ConnectionError.exception(msg)
delete_disconnect(conn, conn_state, err, opts)
Copy link
Member Author

Choose a reason for hiding this comment

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

disconnect or just error? mark as failed transaction?

Copy link
Member

Choose a reason for hiding this comment

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

I don't think we need to disconnect, do we?

Copy link
Member Author

Choose a reason for hiding this comment

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

We don't need but I worry what might happen if anyone hits this path. If we fail the transaction I guess that stop any issue - i.e. force a rollback at end of transaction/3.

@fishcakez fishcakez requested a review from michalmuskala July 10, 2017 00:01
and disconnect.
to continue, `{:begin, state}` to open transaction before continuing,
`{:rollback, state}` to rollback aborted transaction,
`{:error, exception, state}` to abort the transaction and continue or
Copy link
Member

Choose a reason for hiding this comment

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

It might be that I'm not that familiar with db_connection, but what does "abort the transaction & continue" mean here?

Copy link
Member Author

Choose a reason for hiding this comment

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

In this case it probably needs rewording. The error will cause {:error, exception} to be returned by rollback/2. For example this might occur if the driver can't do a side effect free transaction status check.

`{:disconnect, exception, state}` to abort the transaction and disconnect.

A callback implementation should only return `:begin` or `:rollback` if it
can determine the database's transaction status without side effect.
Copy link
Member

Choose a reason for hiding this comment

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

Maybe "without a roundtrip" would be clearer here?

Copy link
Member Author

Choose a reason for hiding this comment

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

With a roundtrip is fine. We will need to do this for the sandbox.

DBConnection.execute!(conn, "SELECT * FROM table", [])
DBConnection.commit(conn)
after
DBConnection.rollback(conn)
Copy link
Member

Choose a reason for hiding this comment

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

How does always calling rollback work in this case? Should it only be called in case execute! raises?

Copy link
Member

Choose a reason for hiding this comment

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

I guess this is explained in rollback with:

It is possible to issue multiple rollbacks requests without a begin (begin/2
or checkout_begin/2). The semantics are left to the callback
implementation.

But it still seems a bit wired to me

Copy link
Member Author

Choose a reason for hiding this comment

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

Should it only be called in case execute! raises?

Yes, this is simplest/safest way to handle a transaction.

Oops i missed removing checkout_begin after copy and paste of docs.

But it still seems a bit wired to me

We are depending on the database's state, not our own, so the callback implementation will use information from the last request when available.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sorry to clarify this is safe to call because the connection is checked in by commit so rollback won't do anything if it doesn't raise. This is a standard approach and imo should be the encouraged way as it ensures safety with minimal fuss.

Copy link
Member

Choose a reason for hiding this comment

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

Ok, sounds great. If it's an optimal approach maybe we should talk about it in the docs?

Copy link
Member Author

Choose a reason for hiding this comment

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

Okay, i will add it the examples and mention in the docs for begin.


### Example

{:ok, result} = DBConnection.begin(conn)
Copy link
Member

Choose a reason for hiding this comment

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

begin returns a 3 tuple, doesn't it?

Copy link
Member Author

Choose a reason for hiding this comment

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

yes, nice catch

end
end
defp checkin(pool, fun, meter, opts) do
run(pool, fun, meter, opts)
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure what will happen in this case. Are we going to check out a connection, call checkin from the fun and then check the connection back in?

I'm not sure I understand the use case of calling checkin with the pool & without an actual connection.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is for API completeness. Also someone using the ownership pool may wish to commit/rollback their transaction before returning connection to the inner pool. I am happy to remove this option.

meter = event(meter, query)
msg = "can not #{query} inside legacy transaction"
err = DBConnection.ConnectionError.exception(msg)
delete_disconnect(conn, conn_state, err, opts)
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we need to disconnect, do we?

Copy link
Member

@josevalim josevalim left a comment

Choose a reason for hiding this comment

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

Looks good to me. We just need to be careful with versioning here. If we plan this change to emit warnings, it will emit warnings for everyone using postgrex and mariaex today. So we probably want to soft-deprecate (no warnings) OR simply release 2.0 and remove the old chunks.

@fishcakez
Copy link
Member Author

This change is backwards compatible, so the plan is to soft deprecated transaction/3.

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

Successfully merging this pull request may close these issues.

3 participants