Best practices to rollback in case of a create, update or a delete failure - database

I am having an internal tree structure in my project and every node has some fields attached to it such as name, id etc. Now, whenever I add a new node to a tree a similar entry for that node is created in a database to persist the changes in case of a failure but there are multiple steps in creating a new node. I want to know how can we actually rollback in case of a failure and keep both the database and the internal structure in sync.
This is a problem because for example I am updating a node and I have changed it halfway but now a step causes an error, then I don't know how to rollback to the previous state easily. It becomes a very tedious task. Any help would be appreciated.

Related

Citus "... is a metadata node, but is out of sync HINT: If the node is up, wait until metadata gets synced to it and try again."

I've got a Citus (v10.1) sharded PostgreSQL (v13) cluster with 4 nodes. Master node address is 10.0.0.2 and the rest are up to .5 When trying to manage my sharded table, I've got this error:
ERROR: 10.0.0.5:5432 is a metadata node, but is out of sync
HINT: If the node is up, wait until metadata gets synced to it and try again.
I've been waiting. After 30 minutes or more, I've literally did drop schema ... cascade, drop extension Citus cascade; and after re-importing the data, creating a shard I've got the same error message once more and can't get past it.
Some additional info:
Another thing that might be an actual hint is I cannot distribute my function through create_distributed_function(), because it's says it in a DeadLock state, and transaction cannot be committed.
I've checked idle processes, nothing out of ordinary.
Created shard like that:
SELECT create_distributed_table('test_table', 'id');
SELECT alter_distributed_table('test_table', shard_count:=128, cascade_to_colocated:=true);
There is no topic in google search result regarding this subject.
EDIT 1:
I did bomb (20k-200k hits per second) my shard with a huge amount of requests for a function that does insert/update or delete if specific argument is set.
This is a rather strange error. It might be the case that you hit the issue in https://github.com/citusdata/citus/issues/4721
Do you have column defaults that are generated by sequences? If so, consider using bigserial types for these columns.
If it does not work, you can disable metadata syncing by SELECT stop_metadata_sync)to_node('10.0.0.5','5432') optionally followed by SELECT start_metadata_sync_to_node('10.0.0.5','5432') to stop waiting for metadata syncing and (optionally) retry metadata creation from scratch.

SalesForce DML set-based operations and atomic transactions

I have just begun to read about Salesforce APEX and its DML. It seems you can do bulk updates by constructing a list, adding items to be updated to the list, and then issuing an update myList call.
Does such an invocation of update create an atomic transaction, so that if for any reason an update to one of the items in the list should fail, the entire update operation is rolled back? If not, is there a way to wrap an update in an atomic transaction?
Your whole context is an atomic transaction. By the time Apex code runs SF has already started, whether it's a Visualforce button click, a trigger or any other entry point. If you hit a validation error, null pointer reference exception, attempt to divide by zero, thrown exception etc - whole thing will be rolled back.
update myList; works in "all or nothing" mode. If one of records fails on validation rule required field missing etc - you'll get an exception. You can wrap it in a try-catch block but still - whole list just failed to load.
If you need "save what you can" behavio(u)r - read up about Database.update() version of this call. It takes optional parameter that lets you do exactly that.
Last but not least if you're inserting complex scenarios (insert account, insert contacts, one of contacts fails but you had that in try-catch so the account saved OK so what now, do you have to manually delete it? Weak...) you have Database.setSavepoint() and Database.rollback() calls.
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/langCon_apex_dml_database.htm
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/langCon_apex_transaction_control.htm
https://salesforce.stackexchange.com/questions/9410/rolling-back-dml-operation-in-apex-method

Conditional associated record deletion in afterDelete()

I have the following setup:
Models:
Team
Task
Change
TasksTeam
TasksTeam is a hasManyThrough, that associates teams to tasks. Change is used to record changes in the details of tasks, including when teams are attached/detached (i.e. through records in TasksTeam).
TasksTeam also cascades deletes of Task. If a task is deleted, all related team associations should also be deleted.
When a TasksTeam is deleted, it means a team has left a task, and I'd like to record a Change for that. I'm using the TasksTeam afterDelete() to record teams leaving. In the TasksTeam beforeDelete I save the data to $this->predelete so it'll be available in the afterDelete().
Here is the non-working code in TasksTeam:
public function afterDelete(){
$team_id = $this->predelete['TasksTeam']['team_id'];
$task_role_id = $this->predelete['TasksTeam']['task_role_id'];
$task_id = $this->predelete['TasksTeam']['task_id'];
// Wanted: only record a change if the task isn't deleted
if($this->Task->exists($task_id)){
$this->Task->Change->removeTeamFromTask($task_id, $team_id, $task_role_id);
}
return true;
}
Problem:
When a task is deleted, the delete cascades to TasksTeam correctly. However, a change will be recorded even if the Task is deleted. From another answer to something similar on SO, I think the reason is that the callbacks are called before Model:del(), meaning the task hasn't yet been deleted when it hits TasksTeam afterDelete()
Question
How can I successfully save a Change only if the task isn't deleted?
Thanks in advance.
If the callbacks are getting called before the actual delete, I see maintaining an assoc. array of flags with task IDs as keys, or a set of task IDs, which are added when afterDelete is called on Task. Then you could create a method in Task, such as isDeleting or similar, which queries the array, to tell you if the task is in the process of being deleted.
Using the suggestion from #James Dunne I ended up adding a tinyint field to the Task model called is_deleted and simply set this boolean true in the Task beforeDelete(). I then check for this flag and only save a Change if the flag is boolean false. It seems wasteful to add a field for something that is only affected just before the record is deleted, but for my purposes it works fine. I think a "real solution" would involve the Cake Events System , avoiding the need for chained callbacks.

TFS - Merge relationship - How to exclude ?

We have a case here where a developer creates a wrong branch. The branch should be: $\projectA\branch01\pg5Dev from $\projectA\main\pg5Dev\ but he creates a $\projectA\branch01\ from $\projectA\main\pg5Dev.
We deleted the folder and creates the branch again, but the merge relationship in merge wizard remains.
We need to know the database structure of Merge Relations ships to remove $\projectA\branch01\, because everytime we will make a merge, the worng branch is appearing in combobox of merge wizard.
Please, help us identify the tables in database that have this wrong record.
If the incorrect branch isn't needed then I would recommend destroying it. Once it is destroyed, it will no longer show up in the combobox. You can destroy it by running "tf destroy ". Note that a destroy is non-recoverable and it will delete all of the history for that branch.

Is there an automatic way to generate a rollback script when inserting data with LINQ2SQL?

Let's assume we have a bunch of LINQ2SQL InsertOnSubmit statements against a given DataContext. If the SubmitChanges call is successful, is there any way to automatically generate a list of SQL commands (or even LINQ2SQL statements) that could undo everything that was submitted at a later time? It's like executing a rollback even though everything worked as expected.
Note: The destination database will either be Oracle or SQL Server, so if there is specific functionality for both databases that will achieve this, I'm happy to use that as well.
Clarification:
I do not want the "rollback" to happen automatically as soon as the inserts have succesfully completed. I want to have the ability to "undo" the INSERT statements via DELETE (or some other means) up to 24 hours (for example) after the original program finished inserting data. We can ignore any possible referential integrity issues that may come up.
Assume a Table A with two columns: Id (autogenerated unique id) and Value (string)
If the LINQ2SQL code performs two inserts
INSERT INTO Table A VALUES('a') // Creates new row with Id = 1
INSERT INTO Table A VALUES('z') // Creates new row with Id = 2
<< time passes>>
At some point later I would want to be able "undo" this by executing
DELETE FROM A Where Id = 1
DELETE FROM A Where Id = 2
or something similar. I want to be able to generate the DELETE statements to match the INSERT ones. Or use some functionality that would let me capture a transaction and perform a rollback later.
We cannot just 'reset the database' to a certain point in time either as other changes not initiated by our program could have taken place since.
It is actually quite easy to do this, because you can pass in a SqlConnection into the LINQ to SQL DataContext on construction. Just run this connection in a transaction and roll that transaction back as soon as you're done.
Here's an example:
string output;
using (var connection = new SqlConnection("your conn.string"))
{
connection.Open();
using (var transaction = connection.StartTransaction())
{
using (var context = new YourDataContext(connection))
{
// This next line is needed in .NET 3.5.
context.Transaction = transaction;
var writer = new StringWriter();
context.Log = writer;
// *** Do your stuff here ***
context.SubmitChanges();
output = writer.ToString();
}
transaction.Rollback();
}
}
I am always required to provide a RollBack script to our QA team for testing before any change script can be executed in PROD.
Example: Files are sent externally with a bunch of mappings between us, the recipient and other third parties. One of these third parties wants to change, on an agreed date, the mappings between the three of us.
Exec script would maybe update some exisiting, delete some now redundant and insert some new records - scope_identity used in subsequent relational setup etc etc.
If, for some reason, after we have all executed our changes and the file transport is fired up, just like in UAT, we see some errors not encountered in UAT, we might multilaterally make the decision to roll back the changes. Hence the roll back script.
SQL has this info when you BEGIN TRAN until you COMMIT TRAN or ROLLBACK TRAN. I guess your question is the same as mine - can you output that info as a script.
Why do you need this?
Maybe you should explore the flashback possibilities of Oracle. It makes it possible to travel back in time.
It makes it possible to reset the content of a table or a database to how it once was at a specific moment in time (or at a specific system change number).
See: http://www.oracle.com/technology/deploy/availability/htdocs/Flashback_Overview.htm

Resources