LockForUpdate causes Deadlock if table is empty - laravel

Currently I am trying to upload some images parallel with dropzone.js.
This is part of the code, that handles the upload.
\DB::beginTransaction();
$maxPos = GalleryImage::lockForUpdate()->max('pos');
try {
// some other code
// ....
$galleryImage->pos = $maxPos + 1;
$galleryImage->save();
} catch (\Exception $e) {
\DB::rollBack();
\Log::error($e->getMessage());
return response('An error occured', 500);
}
\DB::commit();
If I do not use the lockForUpdate() I end up with duplicate positions in database.
The problem with above solution ist, that if the table is empty I get the error:
Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction
But only for the second image I am uploading.
Additionally the autoincrement id skips the value 2 and goes like 1,3,4,5,6,7, ...
The Positions in the table are shown correctly 1,2,3,4....
I think the problem has to do with the table beeing empty initially, as I did not notice this problem, when there are already some entries in the table.
Any suggestions what I am doing wrong? Or Maybe using lockForUpdate() in combination with an aggregate function is wrong at all...

I recommend a different approach to transactions.
try {
app('db')->transaction(function ()) {
$maxPos = GalleryImage::lockForUpdate()->max('pos');
// other code (...)
$galleryImage->pos = $maxPos + 1;
$galleryImage->save();
}
} catch (\Exception $e) {
// display an error to user
}
Should any exception occurs inside the callback, the transaction will rollback and free any locks.

Related

Laravel 8 - Conditionally remember a value in cache [duplicate]

I'm developing one of my first applications with the Laravel 4 framework (which, by the way, is a joy to design with). For one component, there is an AJAX request to query an external server. The issue is, I want to cache these responses for a certain period of time only if they are successful.
Laravel has the Cache::remember() function, but the issue is there seems to be no "failed" mode (at least, none described in their documentation) where a cache would not be stored.
For example, take this simplified function:
try {
$server->query();
} catch (Exception $e) {
return Response::json('error', 400);
}
I would like to use Cache::remember on the output of this, but only if no Exception was thrown. I can think of some less-than-elegant ways to do this, but I would think that Laravel, being such an... eloquent... framework, would have a better way. Any help? Thanks!
This is what worked for me:
if (Cache::has($key)) {
$data = Cache::get($key);
} else {
try {
$data = longQueryOrProcess($key);
Cache::forever($key, $data); // only stored when no error
} catch (Exception $e) {
// deal with error, nothing cached
}
}
And of course you could use Cache::put($key, $data, $minutes); instead of forever
I found this question, because I was looking for an answer about this topic.
In the meanwhile I found a solution and like to share it here:
(also check out example 2 further on in the code)
<?php
/**
* Caching the query - Example 1
*/
function cacheQuery_v1($server)
{
// Set the time in minutes for the cache
$minutes = 10;
// Check if the query is cached
if (Cache::has('db_query'))
{
return Cache::get('db_query');
}
// Else run the query and cache the data or return the
// error response if an exception was catched
try
{
$query = $server->query(...);
}
catch (Exception $e)
{
return Response::json('error', 400);
}
// Cache the query output
Cache::put('db_query', $query, $minutes);
return $query;
}
/**
* Caching the query - Example 2
*/
function cacheQuery_v2($server)
{
// Set the time in minutes for the cache
$minutes = 10;
// Try to get the cached data. Else run the query and cache the output.
$query = Cache::remember('db_query', $minutes, function() use ($server) {
return $server->query(...);
});
// Check if the $query is NULL or returned output
if (empty($query))
{
return Response::json('error', 400);
}
return $query;
}
I recommend you to use Laravel's Eloquent ORM and/or the Query Builder to operate with the Database.
Happy coding!
We're working around this by storing the last good value in Cache::forever(). If there's an error in the cache update callback, we just pull the last value out of the forever key. If it's successful, we update the forever key.

Laravel: Do one query update and then, if that succeeds, do the next, otherwise, do neither

So, I want to update two different user accounts with the same value.
Scenario: User 1 transfers money to User 2:
$transfer_from = $account->decrement('balance', $amount);
$transfer_to = Account::where('user_wallet_address', $receiver_wallet_address)->increment('balance', $amount);
But, what I want, is something like this:
$account->decrement('balance', $amount)->$transfer_to;
As in if the decrement succeeds, then update the other user's balance, otherwise, both should fail
Thoughts?
Please use database transaction feature
See below code example:
public function readRecord() {
DB::beginTransaction();
try {
// put your code here in try block
$transfer_from = $account->decrement('balance', $amount);
$transfer_to = Account::where('user_wallet_address', $receiver_wallet_address)->increment('balance', $amount);
DB::commit();
} catch (Exception $ex) {
// If any failure case generate, it will rollback the DB operations automatically.
DB::rollBack();
echo $ex->getMessage() . $ex->getLine();
}
I hope this will help you. In this case if any failure case generate it will automatic revert the DB translocation and you don't need to do anything.
Reference URL: https://laravel.com/docs/5.7/database

Performance issue while inserting 2000 records using mybatis(3.2.8 version)

I am trying to insert 2000 records in Employee table in batch (using mybatis). My requirements are:
1. To log the error if any of the record fails to insert.
2. To continue with the insertion even if any one of the record fails.
3. Rollback should not happen for other if any one of the record fails.
4. Good performance.
Sample code of Dao implementation: Here I have come up with 2 scenarios.
Calling sqlSession.commit() outside the loop.
SqlSession sqlSession = MyBatisUtil.getSqlSessionFactory().openSession(ExecutorType.BATCH);
BatchMapper batchMapper = sqlSession.getMapper(BatchMapper.class);
try
{
for(Employee e: empList){
batchMapper.addEmployee(e);
}
}
catch (Exception ex)
{
}
finally{
sqlSession.commit();
sqlSession.close();
}
In this case sqlSession.commit() is outside the for loop. Here insertion is happening for all the records at once after calling sqlSession.commit(). Here the performance is good and it takes 4 seconds to insert 2000 records.
But I am not able to log the errors and also it stops the insertion when exception occures.
Calling sqlSession.commit() inside the loop.
SqlSession sqlSession = MyBatisUtil.getSqlSessionFactory().openSession(ExecutorType.BATCH);
BatchMapper batchMapper = sqlSession.getMapper(BatchMapper.class);
try
{
for(Employee e: empList){
batchMapper.addEmployee(e);
sqlSession.commit();
}
}
catch (Exception ex)
{
}
finally{
sqlSession.close();
}
In this case sqlSession.commit() is inside the for loop. Here insertion happens one by one when we call sqlSession.commit(). Here the performance is not good and it takes 10 minutes to insert 2000 records.
But I am able to log the errors and continue with the insertion even if the exception occures for say 100th record.
Please help me with this. Thanks in advance.
If you are using java 8, try parallel stream using lambda expressions.
empList.stream().parallel().forEach(s -> {
try{
batchMapper.addEmployee(e);
sqlSession.commit();
} catch(Exception ex){
}
});

Data Fetching Crashes in Xamarin Forms

I am trying to fetch Customer data to parse them into customer object to display on TableView. The following code sometimes works, sometimes not. Whenever it does crash, it shows Customer data is empty in the foreach loop even though I run the same code every time. I do not have clue what could be wrong in this circumstances. I am quite new on this platform. If I am missing anything/ extra information, please let me know.
namespace TableViewExample
{
public partial class MyDataServices : ContentPage
{
private ODataClient mODataClient;
private IEnumerable <IDictionary<string,object>> Customers;
public MyDataServices ()
{
InitializeComponent ();
InitializeDataService ();
GetDataFromOdataService ();
TableView tableView = new TableView{ };
var section = new TableSection ("Customer");
foreach (var customers in Customers) {
//System.Diagnostics.Debug.WriteLine ((string)customers ["ContactName"]);
var name = (string)customers ["ContactName"];
var cell = new TextCell{ Text = name };
section.Add (cell);
}
tableView.Root.Add (section);
Padding = new Thickness (10, 20, 10, 10);
Content = new StackLayout () {
Children = { tableView }
};
}
private void InitializeDataService(){
try {
mODataClient = new ODataClient ("myURL is here");
}
catch {
System.Diagnostics.Debug.WriteLine("ERROR!");
}
}
private void GetDataFromOdataService (){
try {
Customers = mODataClient.For ("Customers").FindEntries ();
}
catch {
System.Diagnostics.Debug.WriteLine("ERROR!");
}
}
}
}
Its hard helping out here, however here are some things to consider:-
It sounds like the dataservice could either be not contactable / offline; too busy or it could even be throwing an exception itself and returning a data response that you are not expecting to receive, that then triggers an exception and crash in your application as your always expecting an exact response without catering for any abnormal responses / events.
If you are contacting an external service over the internet it may just be your internet connection is slow / faulty and not returning the information fast enough as other possibilities.
In your code you are assuming that you always get a response from the server - and that this response will always be of an anticipated structure that your expecting to decode - without factoring in any possibility of abnormal responses returned by the dataservice. I have not used ODataClient personally, so not sure how it behaves in the event of maybe no data received / timeout or in your case the dataservice and how it behaves internally in the response to a bad-request etc.
I am assuming an exception would get thrown, and you do get your debug line executed indicating a failure.
You may want to also adjust this statement so that you write out the exception as well, i.e.:-
private void GetDataFromOdataService ()
{
try
{
Customers = mODataClient.For ("Customers").FindEntries ();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("ERROR!" + ex.ToString());
}
}
If there was a bad response, then the line at Customers = ..... would throw the exception as there may be no Customers returned or some other information packaged in the response from the dataservice.
The Customers variable would also be null at this point I am assuming due to this failing.
So when you get back to your code at foreach (var customers in Customers) { it will then throw a null reference exception as Customers is infact null.
As all your current code executes in the constructor without any try and catch block around this, it will also crash your application at this point as well.
Also you are doing all of this work in the constructor. Try seperating this out. I haven't investigated exactly where the constructor gets called in an iOS page life-cycle, however, if it is in the viewDidLoad, then you have something like 10 seconds for everything to complete, otherwise it will exit automatically. I imagine in your case, this isn't applicable however.
Going forward also try putting your layout controls in the constructor, and move your data task to maybe the OnAppearing override instead.
Using async would definitely be advisable as well, but remember you need to inspect the response from your dataservice, as the error could be embedded within the response also and you will need to detect when it is OK to process the data.

About Spring Transaction Manager

Currently i am using spring declarative transaction manager in my application. During DB operations if any constraint violated i want to check the error code against the database. i mean i want to run one select query after the exception happened. So i am catching the DataIntegrityViolationException inside my Catch block and then i am trying to execute one more error code query. But that query is not get executed . I am assuming since i am using the transaction manager if any exception happened the next query is not getting executed. Is that right?. i want to execute that error code query before i am returning the results to the client. Any way to do this?
#Override
#Transactional
public LineOfBusinessResponse create(
CreateLineOfBusiness createLineOfBusiness)
throws GenericUpcException {
logger.info("Start of createLineOfBusinessEntity()");
LineOfBusinessEntity lineOfBusinessEntity =
setLineOfBusinessEntityProperties(createLineOfBusiness);
try {
lineOfBusinessDao.create(lineOfBusinessEntity);
return setUpcLineOfBusinessResponseProperties(lineOfBusinessEntity);
}
// Some db constraints is failed
catch (DataIntegrityViolationException dav) {
String errorMessage =
errorCodesBd.findErrorCodeByErrorMessage(dav.getMessage());
throw new GenericUpcException(errorMessage);
}
// General Exceptions handling
catch (Exception exc) {
logger.debug("<<<<Coming inside General >>>>");
System.out.print("<<<<Coming inside General >>>>");
throw new GenericUpcException(exc.getMessage());
}
}
public String findErrorCodeByErrorMessage(String errorMessage)throws GenericUpcException {
try{
int first=errorMessage.indexOf("[",errorMessage.indexOf("constraint"));
int last=errorMessage.indexOf("]",first);
String errorCode=errorMessage.substring(first+1, last);
//return errorCodesDao.find(errorCode);
return errorCode;
}
catch(Exception e)
{
throw new GenericUpcException(e.getMessage());
}
}
Please help me.
I don't think problem you're describing has anything to do with Transaction management. If DataIntegrityViolationException happens within your try() block you code within catch() should execute. Perhaps exception different from DataIntegrityViolationException happens or your findErrorCodeByErrorMessage() throwing another exception. In general, Transaction logic would be applied only once you return from your method call, until then you could do whatever you like using normal Java language constructs. I suggest you put breakpoint in your error error handler or some debug statements to see what's actually happening.

Resources