Skip to content
This repository has been archived by the owner on Jul 29, 2020. It is now read-only.

Implements transaction #51

Merged
merged 9 commits into from
Sep 14, 2018
Merged

Implements transaction #51

merged 9 commits into from
Sep 14, 2018

Conversation

joelmdesouza
Copy link
Contributor

Implements transaction support, at the packet level.
Implemented only for Delete yet so we can discuss the way forward.
@felipeweb @crgimenes @avelino

Copy link
Member

@avelino avelino left a comment

Choose a reason for hiding this comment

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

team, what's your opinion?

adapter.go Outdated
"net/http"
"net/url"
)

//Adapter interface
type Adapter interface {
GetTx() (tx *sql.Tx, err error)
Copy link
Member

Choose a reason for hiding this comment

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

I prefer explicit name, example GetTransaction

Copy link
Contributor

Choose a reason for hiding this comment

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

+1 to GetTransaction

@fabriziomello
Copy link
Contributor

Personally I don't see any advantage to run single SQL commands inside a explicit transaction block... in PostgreSQL when you execute, for example an INSERT, it will be executed inside a implicit transaction. Actually all statements runs inside transactions in PostgreSQL. Implement it in this way just add unnecessary complexity and more round trips on code.

@joelmdesouza
Copy link
Contributor Author

@fabriziomello

The implementation is to perform multiple actions on a transaction.
Example:

tx, _ := adapter.GetTx()
sc := dbAdapter.DeleteTx(tx, `DELETE 1 ...`, ...)
...
sc = dbAdapter.DeleteTx(tx, `DELETE 2 ...`, ...)
...
tx.Commit()

@joelmdesouza
Copy link
Contributor Author

Rename from GetTX to GetTransaction.
Implemented transaction in Insert, Update, Delete methods.
Is this a good way?
Anyone have any suggestions?

Copy link
Member

@avelino avelino left a comment

Choose a reason for hiding this comment

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

followed pattern names (get like):

  • InsertTx > InsertTransaction
  • UpdateTx > UpdateTransaction
  • DeleteTx > DeleteTransaction

@joelmdesouza
Copy link
Contributor Author

@avelino
GetTransaction returns a transaction.
Other methods use a transaction.
We keep the InsertTransaction pattern or we use InsertWithTransaction.

@avelino
Copy link
Member

avelino commented Sep 4, 2018

My opinion InsertWithTransaction

Team help pls

@crgimenes
Copy link
Contributor

I also prefer InsertWithTransaction.
More explicit is always better.

@fabriziomello
Copy link
Contributor

fabriziomello commented Sep 4, 2018

IMHO I didn't like the idea of create other DML methods for transaction... is more simple create new methods just for BEGIN, COMMIT and ROLLBACK and then internally perform the properly checks to make sure we're inside a transaction... or maybe I missed something.

@joelmdesouza
Copy link
Contributor Author

@fabriziomello
The reason for creating new methods is not to break applications that are already using the pREST as a package. Because you would have to change the signature of the method to receive a transaction as a parameter.
What I did was to encapsulate the logic of delete in a private method.
So for example, Insert calls it by providing a connection to the database, and InsertWithTransaction calls it by providing the transaction it received as a parameter.

@joelmdesouza
Copy link
Contributor Author

joelmdesouza commented Sep 4, 2018

@fabriziomello
Example:

func (adapter *Postgres) Delete(SQL string, params ...interface{}) (sc adapters.Scanner) {
	db, err := connection.Get()
	if err != nil {
		log.Println(err)
		sc = &scanner.PrestScanner{Error: err}
		return
	}
	sc = adapter.delete(db, nil, SQL, params...)
	return
}

func (adapter *Postgres) DeleteWithTransaction(tx *sql.Tx, SQL string, params ...interface{}) (sc adapters.Scanner) {
	sc = adapter.delete(nil, tx, SQL, params...)
	return
}

func (adapter *Postgres) delete(db *sqlx.DB, tx *sql.Tx, SQL string, params ...interface{}) (sc adapters.Scanner) {
       ... implementation ...
}

@fabriziomello
Copy link
Contributor

fabriziomello commented Sep 4, 2018 via email

@joelmdesouza
Copy link
Contributor Author

Passing the transaction explicitly is more versatile and gives you more control because you have control over which transaction you are currently executing.
I do not like the idea of using a global transaction.
Using a local transaction makes the scope of actions better delimited.

What is the opinion of others on this issue?

@fabriziomello
Copy link
Contributor

fabriziomello commented Sep 4, 2018 via email

@joelmdesouza
Copy link
Contributor Author

I renamed the methods in the Postgres adapter and added the methods in the Mock adapter.

Copy link
Member

@felipeweb felipeweb left a comment

Choose a reason for hiding this comment

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

the code is fine, but I will document why I do not like this approach because we would have to create new endpoints for transaction actions.

What I would do is create 3 new endpoints open transaction that replies with UUID only for the transaction and another to commit and another to abort, if we pass? _TxID = "123" it would use the existing transaction otherwise it would continue as it is today

@joelmdesouza
Copy link
Contributor Author

joelmdesouza commented Sep 12, 2018

@felipeweb
Yes, I did not intend to expose a different endpoint to each transaction method.
This approach is only for the package level.
These methods I created would only be used in the commit endpoint.
The commit endpoint will execute all entries stored for that transaction, using InsertWithTransaction, DeleteWithTransaction.

tx, _ := adapter.GetTransaction()

    for (entries stored){
        adapter.InsertWithTransaction(tx, `Insert x ...`, ...)
        or
        adapter.DeleteWithTransaction(tx, `Delete x ...`, ...)
    }
tx.Commit()

@@ -54,7 +54,7 @@ type Stmt struct {
}

// Prepare statement
func (s *Stmt) Prepare(db *sqlx.DB, SQL string) (statement *sql.Stmt, err error) {
func (s *Stmt) Prepare(db *sqlx.DB, tx *sql.Tx, SQL string) (statement *sql.Stmt, err error) {
Copy link
Member

@avelino avelino Sep 13, 2018

Choose a reason for hiding this comment

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

Changed function signature, you need open PR (another repo) upgrading where you use the adapter

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This function is only used inside the adapter package.
The other packages in the pRest project do not access it.

@felipeweb felipeweb merged commit b7f47eb into prest:master Sep 14, 2018
@joelmdesouza joelmdesouza deleted the transaction branch September 14, 2018 13:59
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
5 participants