I have some CRUD operations in SQL procedure that I need to set in transaction.
BEGIN TRY
BEGIN TRANSACTION
--some statements
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
--return ERROR_MESSAGE() as output parameter or in select, otherwise it will be supressed
END CATCH
I call this procedure from service and that call is written inside try block. My question is: Is it better to write this part of procedure without try/catch block since we will catch exception in catch block of service method? Will the transaction be rolled back automatically after exception? Am I wrong?
The Catch & Rollback will undo any changes within the Database, whereas the Exception handling in the WebService will only catch the error after it happens - it won't touch the data.
You could automatically rollback any failed transactions in SQL, or start the Transaction in the WebService, but simply catching an exception in the WebService can't affect any data unless you explicitly tell it to.
Related
I am creating a mobile application in flutter where I need to do entries in the transaction table and also need to update the balance in the user table. Whenever a new transaction is to be added I first check the current balance of the user, add the entry in the transaction table and update the balance. My question is how can I make the entire operation atomic. Say if the update balance fails because for any reason how can I roll back from the transaction table also.
That is exactly what SQLite transactions are for! (Sorry for the unfortunate name of your table which is also called transaction)
More info on how to use transactions in sqflite here:
https://github.com/tekartik/sqflite/blob/master/sqflite/doc/sql.md#transaction
some copy/pasted information for convenience:
transaction
transaction handle the 'all or nothing' scenario. If one command fails (and throws an error), all other commands are reverted.
await db.transaction((txn) async {
await txn.insert('my_table', {'name': 'my_name'});
await txn.delete('my_table', where: 'name = ?', whereArgs: ['cat']);
});
Make sure to sure the inner transaction object - txn in the code above - is used in a transaction (using the db object itself will cause a deadlock),
You can throw an error during a transaction to cancel a transaction,
When an error is thrown during a transaction, the action is cancelled right away and previous commands in the transaction are reverted,
No other concurrent modification on the database (even from an outside process) can happen during a transaction,
The inner part of the transaction is called only once, it is up to the developer to handle a try-again loop - assuming it can succeed at some point.
As a potential way of storing metadata about transactions, I would like to execute a query at end end of every transaction.
I have looked at adding logic inside the transact event, but there does not seem to be a way to make another request using the current transaction. Is there a way that this could be done using pg-promise? Is this an anti-pattern?
You can only query against the transaction connection while inside a transaction callback. You cannot do it by handling transact event, it does not have the transaction connection available.
I'm trying to understand how a java (client) application that communicates, through JDBC, with a pgSQL database (server) can "catch" the result produced by a query that will be fired (using a trigger) whenever a record is inserted into a table.
So, to clarify, via JDBC I install a trigger procedure prepared to execute a query whenever a record is inserted into a given database table, and from this query's execution will result an output (wrapped in a resultSet, I suppose). And my problem is that I have no idea how the client will be aware of those results, that are asynchronously produced.
I wonder if JDBC supports any "callback" mechanism able to catch the results produced by a query that is fired through a trigger procedure under the "INSERT INTO table" condition. And if there is no such "callback" mechanism, what is the best approach to achieve this result?
Thank you in advance :)
Triggers can't return a resultset.
There's no way to send such a result to the JDBC driver.
There are a few dirty hacks you can use to get results from a trigger to the client, but they're all exactly that. Things like:
DECLARE a cursor for the resultset, then send the cursor name as a NOTIFY payload, so the app can FETCH ALL FROM <cursorname>;
Create a TEMPORARY table and report the name via NOTIFY
It is more typical to append anything the trigger needs to communicate to the app to a table that exists for that purpose and have the app SELECT from it after the operation that fired the trigger ran.
In most cases if you need to do this, you're probably using a trigger where a regular function is a better fit.
I have a scenario where 2 db connections might both run Model.find_or_initialize_by(params) and raise an error: PG::UniqueViolation: ERROR: duplicate key value violates unique constraint
I'd like to update my code so it could gracefully recover from it. Something like:
record = nil
begin
record = Model.find_or_initialize_by(params)
rescue ActiveRecord::RecordNotUnique
record = Model.where(params).first
end
return record
The trouble is that there's not a nice/easy way to reproduce this on my local machine, so I'm not confident that my fix actually works.
So I thought I'd get a bit creative and try calling create 2 times (locally) in a row which should raise then PG::UniqueViolation: ERROR, then I could rescue from it and make sure everything is handled gracefully.
But I get this error: PG::InFailedSqlTransaction: ERROR: current transaction is aborted, commands ignored until end of transaction block
I get this error even when I wrap everything in individual transaction blocks
record = nil
Model.transaction do
record = Model.create(params)
end
begin
Model.transaction do
record = Model.create(params)
end
rescue ActiveRecord::RecordNotUnique
end
Model.transaction do
record = Model.where(params).first
end
return record
My questions:
What's the right way to gracefully handle the race condition I mentioned at the very beginning of this post?
How do I test this locally?
I imagine there's probably something simple that I'm missing here, but it's late and perhaps I'm not thinking too clearly.
I'm running postgres 9.3 and rails 4.
EDIT Turns out that find_or_initialize_by should have been find_or_create_by and the errors I was getting was from the actual save call that happened later on in execution. #VeryTiredWhenIWroteThis
Has this actually happenend?
Model.find_or_initialize_by(params)
should never raise an ´ActiveRecord::RecordNotUnique´ error as it is not saving anything to db. It just creates a new ActiveRecord.
However in the second snippet you are creating records.
create (without bang) does not throw exceptions caused by validations, but
ActiveRecord::RecordNotUnique is always thrown in case of a duplicate by both create and create!
If you're creating records you don't need transactions at all. As Postgres being ACID compliant guarantees that only one of the both operations succeeds and if it responds so it's changes will be durable. (a single statement query against postgres is also a transaction). So your above code is almost fine if you replace through find_or_create_by
begin
record = Model.find_or_create_by(params)
rescue ActiveRecord::RecordNotUnique
record = Model.where(params).first
end
You can test if the code behaves correctly by simply trying to create the same record twice in row. However this will not test ActiveRecord::RecordNotUnique is actually thrown correctly on race conditions.
It's also no the responsibility of your app to test and testing it is not easy. You would have to start rails in multithread mode on your machine, or test against a multi process staging rails instance. Webrick for example handles only one request at a time. You can use puma application server, however on MRI there is no true concurrency (GIL). Threads only share the GIL only on IO blocking. Because talking to Postgres is IO, i'd expect some concurrent requests, but to be 100% sure, the best testing scenario would be to deploy on passenger with multiple workers and then use jmeter to run concurrent request agains the server.
HI,
Iam trying to insert batch of records at a time when any of the record fails to insert i need to trap that record and log that to my failed record maintanance table and then the insert should continue. Kindly help on how to do this.
If using a Spring or EJB container there is a simple trick which works very well : provide a LogService witn a logWarning(String message) method. The method must be annotated/configured with the REQUIRES_NEW transaction setting.
If not then you'll have to simulate it using API calls. Open a different connection for the logging, when you enter the method begin the transaction, before leaving commit the transaction.
When not using transactions for the insert, there is actually nothing special you need to do, as by default most database run in autocommit and commit after every statement.