D
D
Dmitry Petrov2019-06-07 11:33:34
.NET
Dmitry Petrov, 2019-06-07 11:33:34

What to do in case of nested transactions?

I don't have that much system design experience, and when I ran into a situation, I had doubts about how to properly resolve it.
There is a Role table - user roles.
The software is conditionally divided into two levels - user code and api (also code, but separated separately as the core of the system).
The api has the AddRoleToUser method - we pass the user identifier, the role identifier to the method -> the method checks if the role and the user exist -> if successful, assigns the role by writing to a couple of secondary tables, then sends letters by e-mail. Requests inside AddRoleToUser are wrapped in transactionScope (scope1).
The user code does the following:
1. Creates its own transactionScope (scope2) in Requires, Serializable mode;
2. Writes data to secondary tables table1 and table2;
3. Writes directly to the Role table, creating a role there.
4. Calls AddRoleToUser, passing it the identifier of the created role and the identifier of some user (obviously existing);
5. Exits scope2 without commit (that is, rollback occurs).
I had a misunderstanding of how to properly act inside AddRoleToUser with reading data from the database. The following scenarios may occur:
1) scope1 in requires mode, any isolation level - the role is read without problems, the data is written, the letter is sent, but after rollback in scope2 all records created in AddRoleToUser are rolled back. Bottom line: there is no role, there is no role assignment, there is a letter.
2) scope1 in suppress mode, isolation level read uncommitted - the role is read, the data is written, the role assignment is created, the letter is sent, rollback fails nothing. Bottom line: there is no role, there is a role assignment, there is a letter.
3) scope1 in suppress mode, any isolation level except read uncommitted - the role is not read, the request falls due to the waiting timeout. Bottom line: no role, no appointment, no letter.
Scenario 3 looks the cleanest in terms of implications, but is it the right one?
Perhaps it's better to commit scope2 first, then call AddRoleToUser? But what if the logic inside scope2 depends on the result of AddRoleToUser ?
One of the obvious conclusions is that Role should also be created inside api methods. Yes, ideally it should, but now the situation is not the same.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
I
Ivan Filatov, 2019-06-07
@NYMEZIDE

google the Unit of Work pattern
or here for starters - the Unit of Work
UoW pattern is in the EntityFramework, you can reuse it.
if you have a microservice architecture, then google the Saga pattern,
or read it - Pattern: Saga
In general, a letter about a role change should be sent by a separate microservice, and not by the code that writes to the database.
based on the event, so ideally you need to raise the bus (RabbitMQ) and bind them asynchronously.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question