-
Notifications
You must be signed in to change notification settings - Fork 16
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
Initial attempt to implement transactions #130
Conversation
# Conflicts: # src/tink/sql/Database.hx # src/tink/sql/drivers/node/MySql.hx # src/tink/sql/drivers/php/MySQLi.hx # src/tink/sql/format/SqlFormatter.hx # tests/Run.hx
# Conflicts: # src/tink/sql/macros/DatabaseBuilder.hx
Currently the API is used like so: db.transaction(trx -> {
// do business with trx, where $type(trx) == $type(db)
return Promise.NOISE;
}) One problem is that interface Db {
@:table var user:User;
@:table var post:Post;
}
@:genericBuild(currentDatabaseBuilder())
class Transaction<Db> {} // becomes "class Transaction0 implements Db {...}"
class Database<Db> extends Transaction<Db> {
public function transaction<T>(run:Transaction<Db>->Promise<TransactionEnd<T>>):Promise<TransactionEnd<T>>;
}
var db = new Database<Db>(name, driver);
|
The said change is now in place. There are a few (small) breaking changes.
|
Don't think you would want to pursue in this effort, but one other way of "going deeper" is using savepoints. They allow you to do "transactions in transactions". |
Currently this is kind of solved by doing so:
So, when inside a transaction, user will not be able to call transaction again. |
Hmm. I can see why you would want to try hiding the transaction method, but what's to stop the user from something like this? var db = new Database<Db>();
db.transaction(_ -> db.transaction(...)); |
The inner transaction will be performed in another isolated connection if there is a pool, or wait until the outer one finishes and releases the connection if there is no pool. |
Hmm, ok, I think I understand. I would suggest a slightly different approach then:
This would avoid the breaking change and still address your concerns, right? If not, disregard that and feel free to merge ;) |
That will work. But there is a subtle change in the implementation detail I didn't mention. It is about the // defintion:
interface Def extends DatabaseDefinition {}
// instance: Database<Def> generates the following
class Transaction0 implements Def {
function new(cnx:Connection);
}
class Database0 extends Transaction0 {
final pool:ConnectionPool; // note that ConnectionPool extends Connection and provides an additional `isolate()` method
function new(driver)
super(pool = driver.open());
public function transaction(run:Db->Promise<...>)
// create a new Transaction object and do stuff with it
new Transaction0(pool.isolate()); // the actual code regarding isolate() is slightly more complex
} If we want to stick with the old approach, then the constructor of the built |
Hmm, I'd be inclined to use an abstract over |
# Conflicts: # src/tink/sql/drivers/node/MySql.hx # src/tink/sql/drivers/node/PostgreSql.hx # src/tink/sql/drivers/php/PDO.hx # src/tink/sql/drivers/sys/StdConnection.hx # tests/Db.hx # tests/Run.hx
My rough idea is to introduce
Connection#isolate
which will:Normal queries will just execute as before, i.e. using pooled connections + auto-release when available.
On the other hand, transactions will call
isolate
under the hood and release the connection back to the pool (if any) after the transaction is finished.Regarding point 2 in the beginning of this text, sinceisolate
does actually nothing right now, so it is only safe on sync targets. We need some more thoughts on that. Perhaps we need some sort of local lock so that when that single connection is isolated, other requests that lands on the same connection has to wait until the lock is released.Only Node MySQL for now... my head aches...