Is it possible to use different transaction isolation levels within the same application? For example, I want to use READ_COMMITTED for simple client requests and READ_REPEATABLE for more complicated requests.
I have wasted weeks trying to figure this out so I am posting a record of what I learned. Additional answers are welcome.
While it is technically possible to change the transaction isolation level using Connection.setTransactionIsolation(int) I highly recommend against doing this.
The transaction isolation must be set before the beginning a transaction. Doing otherwise results in "implementation-defined" behavior.
In order to detect the appropriate transaction isolation you will have to figure out the transaction isolation required by any methods you invoke (and any methods they invoke).
This quickly evolves into a maintenance nightmare where you're struggling to keep track of the right isolation level for each method.
If you attempt to programmatically query the right isolation level at runtime (by asking your dependencies what isolation level they need) you will end up with very ugly code and again it becomes a maintenance nightmare.
Instead, I recommend biting the bullet and picking a single transaction isolation level across your entire application. Start out with something safe and slowly move to the higher-performing isolation levels.
Related
I am trying to understand microservices. While going through Saga pattern, I came across this sentence
Whilst somewhat close to having ACID guarantees, the saga pattern is
still missing isolation. This means that it is possible to read and
write data from an incomplete transaction, thus introducing various
isolation anomalies
I am still not clear on why saga pattern lacks isolation. Can someone please explain with an example.
There's no isolation since the intermediate states are manifested in the services that are part of the saga
when you call another service in a saga it performs the action (e.g. you are buying a ticket, if you reserve a seat for a show and you're still on the process to pay for the ticket). It isn't isolated as any other interaction with the same service will see the effect of your action (e.g. the specific seat will seem taken for others who are trying to purchase a ticket).
There is total of 6 solutions including:
Semantic lock
Commutative update
Pessimistic view
Reread value
Versioning
By value
you can read more about each of them in this book
Ok so one of our team members has suggested that at the beginning of every http request we begin a DB transaction (we are using Entity Framework Core), do the work of the request, and then complete the transaction if the response is 200 Ok, or roll back if it is anything else.
This means we would only commit on successful requests.
That is well and good, when we perform reads and writes to the DB.
However I am wondering does this come at a cost, if we don't actually make any reads or writes to the db?
If you use TransactionScope for this then the transaction is only physically opened on the first database access. The cost for an unused scope is extremely low.
If you use normal EF transactions then an empty transaction will hit the database three times:
BEGIN TRAN
COMMIT
Reset connection for connection pooling
Each of these is extremely low cost. You can test the cost of this by simply running this 100000 times in a loop. It might very well be the case that you don't care about this small cost.
I still would advise against this. In my experience web applications require more flexibility than a 1:1 correspondence of web request and transaction. Also, the rule to use the HTTP status code to decide the transaction will turn out to be inflexible.
Also, you must pick an isolation level (and possibly timeout) for each transaction. At the beginning of an HTTP request it is not known what the right values are. Only the action knows.
I had good experiences with using one EF context per HTTP request and then manually using transactions inside of each action. The overhead in terms of LOC is very small. There is no pressing need to centralize this.
Don't blindly put BEGIN...COMMIT around everything. There are cases where this is just wrong.
What if the web page records the presence of the user, or the loading of the particular page? Having a ROLLBACK destroys that information.
What if there are two actions on the page, and they are independent of each other? That is a ROLLBACK for one is OK, but you want to COMMIT the other?
What if there are no writes on the page? Then there is no need for BEGIN...COMMIT.
I am beginner to spring , now in my project we have method to make booking ,now if the available booking is 1 , when two user are trying booking at same time ideally only one booking is allowed , but my application is allowing two bookings, now i made the method as synchronized now it is working fine, but synchronization concept belongs to JVM now if I am configuring my application in cluster mode there are different servers in different machines(so different JVMS), now synchronization wont work.
Can any one please tell me the possible solution for this to restrict the booking,tell me solution from JAVA side and and also from DB side
If the application may be deployed in cluster, the synchronized method will indeed not be enough.
You should rely on a node shared by all server instances : the DB.
You could use DB locking features, especially optimistic locking and pessimistic locking.
With them, you could avoid collisions resulting from concurrent updates to the same line by concurrent clients.
Choose which one that matches better to your use case.
Concurrency control in databases extract:
Optimistic - Delay the checking of whether a transaction meets the
isolation and other integrity rules (e.g., serializability and
recoverability) until its end, without blocking any of its (read,
write) operations ("...and be optimistic about the rules being
met..."), and then abort a transaction to prevent the violation, if
the desired rules are to be violated upon its commit. An aborted
transaction is immediately restarted and re-executed, which incurs an
obvious overhead (versus executing it to the end only once). If not
too many transactions are aborted, then being optimistic is usually a
good strategy.
Pessimistic - Block an operation of a transaction, if
it may cause violation of the rules, until the possibility of
violation disappears. Blocking operations is typically involved with
performance reduction.
JDBC and JPA support both.
You should try with ETags - optimistic locking mechanism.
It can be easily implemented with Spring. Please check official documentation.
Hope this will help.
There are various transaction propagations like
REQUIRED - This is the case for DML operation.
SUPPORTS - This is the case for querying database.
MANDATORY - ?
REQUIRES_NEW - ?
NOT_SUPPORTED - ?
NEVER - ?
NESTED - ?
What are some real life scenarios for these transaction propagations? Why these are perfect fit into that situation?
There are various usages and there is no simple answer but I'll try to be the most explainatory
MANDATORY (expecting transaction): Typically used when you expect that any "higher-context" layer started the transaction and you only want to continue in it. For example if you want one transaction for whole operation from HTTP request to response. Then you start transaction on JAX-RS resource level and lower layers (services) require transaction to run within (otherwise exception is thrown).
REQUIRES_NEW (always create new transaction): This creates a new transaction and suspends the current one if any exists. From above example, this is the level you set on your JAX-RS resource for example. Or generally if your flow somehow changes and you want to split your logic into multiple transactions (so your code have mutliple logic operations that should be separated).
REQUIRED (continue in transaction or create if needed): Some kind of mix between MANDATORY and REQUIRES_NEW. In MANDATORY you expect that the transaction exists, for this level you hope that it exists and if not, you create it. Typically used in DAO-like services (from my experience), but it really depends on logic of your app.
SUPPORTS (caller decides whether to not/run in transaction): Used if want to use the same context as caller (higher context), if your caller was running in transaction, then you run in too. If it didn't, you are also non-transactional. May also be used in DAO-like services if you want higher context to decide.
NESTED (sub-transaction): I must admit I didn't use this one in real code but generally you create a real sub-transaction that works as some kind of checkpoint. So it runs in the context of "parent" transaction but if it fails it returns to that checkpoint (start of nested transaction). This may be useful when you require this kind of logic in your application, for example if you want to insert large number of items to the database, commiting valid ones and keeping track of invalid ones (so you can catch exception when nested transaction fails but can still commit the whole transaction for valid ones)
NEVER (expecting there is no transaction): This is the case when you want to be sure that no transaction exists at all. If some code that runs in transaction reaches this code, exception is thrown. So typically this is for cases completely opposite to MANDARTORY. E.g. when you know that no transaction should be affected by this code - very likely because the transaction should not exist.
NOT_SUPPORTED (continue non-transactionally): This is weaker than NEVER, you want the code to be run non-transactionally. If somehow you enter this code from context where transaction is, you suspend this transaction and continue non-transactionally.
From my experience, you very often want one business action to be atomic. Thus you want only one transaction per request/... For example simple REST call via HTTP that does some DB operations all in one HTTP-like transaction. So my typical usage is REQUIRES_NEW on the top level (JAX-RS Resource) and MANDATORY on all lower level services that are injected to this resource (or even lower).
This may be useful for you. It describes how code behave with given propagation (caller->method)
REQUIRED: NONE->T1, T1->T1
REQUIRES_NEW: NONE->T1, T1->T2
MANDATORY: NONE->Exception, T1->T1
NOT_SUPPORTED: NONE->NONE, T1->NONE
SUPPORTS: NONE->NONE, T1->T1
NEVER: NONE->NONE, T1->Exception
I refer to the example stated in the URL which is to apply spring aop transaction across all methods in service layers, but this will also apply transaction management for read-only methods like SELECT.
Is there any adverse effect, if I apply transaction management for readOnly transaction?
<aop:config>
<aop:advisor pointcut="execution(* com.xyz.someapp.service.*.*(..)))"
advice-ref="transactionAdvice" />
</aop:config>
http://www.studytrails.com/frameworks/spring/spring-declarative-transactions.jsp
I have another question. If I have orderService.create() which invokes billingService.create(), how will the transaction isolation be applied?
You generally want to use transactions even for read-only operations, since a transaction will give you a consistent view of your data. If you skip transactions, you risk seeing partial updates from some other incomplete transaction.
I dont't think that there will be any adverse effects because a transaction simply defines an atomic set of operations. If a transaction fails then a rollback will be performed, but read-only operations don't affect the database in any way.
The only big disadvantage I can think of is related to performance. The locking-strategy of the underlying database (whether table or row is locked) determines your isolation level. Acoording to your locking-strategy some kind of isolation is applied during the transaction. For example, when you're writing to a database your isolation level decides if the changes in progress could be seen by parallel reads (causing e.g. dirty reads, non-repeatable reads and so on) or if these changes have to be commited before they become visible to others.
As a consequence you would lock resources you only want to read, which could be performance overkill on highly available applications. Summing up: It doesn't have bad effects aside from causing performance issues.
Addressing your second question: This depends on how you're managing transactions. I guess in this case both service methods require transactions, so I think the default behavior is that the transaction of the first service call is inherited by the second service method call (this is called 'transaction propagation'). This is required because nested transactions aren't allowed.