Laravel - my Web services Api를 여러 번 호출하면 데이터베이스의 단일 항목과 데이터베이스의 복제 기록 논리를 무시합니다.
웹 서비스를 가지고 있습니다. API는 데이터베이스에 여러 번 데이터를 삽입하지만 기록이 존재할 경우 한 번만 삽입하고 두 번째만 업데이트하면 됩니다.
트랜잭션으로 인해 문제가 발생했습니다.여기서 무슨 일이 일어나고 있는지 다 설명해 드렸습니다.
Laravel 5.4 데이터베이스:Maria Db 10.1.21 호스트: 로컬 호스트
웹 서비스 포함
- 결과를 가져오기 위해 외부 apis 호출
- 데이터베이스에 가져오기 결과 저장
// 코드를 여기에
public function getWebsiteDetails(Request $request) {
Log::info("call ");
try
{
return DB::transaction(function () use($request)
{
// Get record from website_master of given business id
$businessWebsiteRecord = Website::where([
'business_id' => $businessId
])->first();
// calling external api to get result
$pageSpeedResult = $this->pageSpeedResult($url);
Log::info("crossed external api point");
/**
* Again check to confirm record is exist or not
* code write to only for testing purpose
*/
$recordChecker = Website::where
(
'business_id', $businessId
)->first();
if(!empty($recordChecker))
{
Log::info("if" );
}
else
{
Log::info("else");
}
/**
* Update record if exist
* else
* Create record
*/
Website::updateOrCreate(
['business_id' => $businessId],
[
'website' => $url
]
);
// saving data in another table after some operatins
$thirdObj->globalIssueGenerator(/* some data*/);
return $this->helpReturn('Website data saved & issues are generated in system');
});
} catch (Exception $e) {
Log::info(" getWebsiteDetails >> " . $e->getMessage());
return $this->helpError(1, 'Some Problem happened to run script.');
}
}
데이터베이스 테이블에 한 행 항목만 추가해야 합니다.그러나 문제는 api를 여러 번 호출하면 데이터베이스에 데이터를 여러 번 삽입하고 처음/단일 시간만 추가하면 된다는 것입니다.애즈 라라벨Updateorcreate
함수가 사용중입니다.
테스트 케이스
내가 처음으로 API를 호출한 후 3-5초 후에 다시 API를 두 번째로 누르면 됩니다.
해당 api는 체크를 생략하고 삽입 데이터와 같은 데이터를 이 스크린샷과 같이 여러 번 다시 삽입합니다.
그래서 아래 코드로 확인하는 겁니다.
/**
* Again check to confirm record is exist or not
* code write to only for testing purpose
*/
$recordChecker = Website::where
('business_id', $businessId)->first();
if(!empty($recordChecker))
{
Log::info("if");
}
else
{
Log::info("else");
}
로그:
로그는 데이터베이스 삽입 스크린샷에서와 동일한 시간에 생성되었습니다.
[2018-02-12 06:18:58] local.INFO: call
[2018-02-12 06:19:03] local.INFO: call
[2018-02-12 06:19:16] local.INFO: crossed external api point
데이터베이스에서 데이터를 사용할 수 없기 때문에 다른 방법으로 들어갑니다.
[2018-02-12 06:19:16] local.INFO: else
스크린샷 데이터가 데이터베이스에 삽입되었습니다.
두 번째 통화 로그
[2018-02-12 06:19:19] local.INFO: else
[2018-02-12 06:19:19] local.INFO: crossed external api point
데이터가 아직 데이터베이스에 없기 때문에 다시 다른 곳으로 들어갑니다.
데이터베이스에서 데이터를 제출했지만 두 번째 통화 응답이 진행 중입니다.
문제 탐지:
트랜잭션을 사용하고 있습니다. 레코드를 삽입/업데이트하여 두 개의 테이블을 업데이트하고 있습니다.
거래없음 거래코드를 댓글로 달아서 위의 테스트 케이스 데이터를 적용하면 한 번만 삽입됩니다.내가 요구하는 것.
여기 거래가 없는 곳의 기록이 있습니다.
[2018-02-12 06:59:56] local.INFO: call
[2018-02-12 07:00:00] local.INFO: call
[2018-02-12 07:00:14] local.INFO: crossed external api point
데이터베이스에서 데이터를 사용할 수 없기 때문에 데이터가 생성되므로 다른 방법으로 들어갑니다.
[2018-02-12 07:00:14] local.INFO: else
[2018-02-12 07:00:17] local.INFO: crossed external api point
데이터베이스에서 사용 가능한 데이터 때문에 들어가는 경우
[2018-02-12 07:00:17] local.INFO: if
하지만 첫 번째 테이블에 데이터를 삽입한 후 대부분의 작업을 수행하고 테이블에 여러 개의 데이터를 삽입하기 때문에 트랜잭션을 제거할 수 없습니다.
파일 시스템이나 Redis에서 플래그를 업데이트하기 위해 다른 작업을 사용하려고 생각하고 있습니다만, 그것은 다른 기술입니다.나는 이것을 처리할께요.
같은 기술을 사용하는 웹 서비스가 더 많기 때문에 이 문제를 해결할 수 있는 사람이 있으면 좋겠습니다.
FULL Code
public function getWebsiteDetails(Request $request)
{
Log::info("call ");
try {
return DB::transaction(function () use($request)
{
$thirdObj = new ThirdPartyEntity();
// user extract
$checkPoint = $this->setCurrentUser($request->get('token'))->userAllow();
$user = $checkPoint['records'];
$businessDetail = $this->businessEntity->userSelectedBusiness($user);
$businessDetail = $businessDetail['records'];
$userId = $user['id'];
$businessId = $businessDetail['business_id'];
$url = $businessDetail['website'];
/**
* Get record from website_master of given business id
*/
$businessWebsiteRecord = Website::where([
'business_id' => $businessId
])->first();
// if business_master (url) is exist then go to if block
if ($url != '') {
$data = [];
$pageSpeedScore = '';
$mobileReadyScore = '';
$data['website'] = $url;
$url = 'http://'.$url;
$pageSpeedResult = $this->pageSpeedResult($url);
if( $pageSpeedResult['_metadata']['outcomeCode'] == 200 )
{
$pageSpeedData = $pageSpeedResult['records'];
$speedResult = json_encode($pageSpeedData['formattedResults']['ruleResults']);
$data['title_tag'] = $pageSpeedData['title'];
$data['page_speed_score'] = $pageSpeedData['score'];
$data['page_speed_suggestion'] = $speedResult;
$pageSpeedScore = $pageSpeedData['score'];
}
else
{
$speedResult = NULL;
$data['title_tag'] = NULL;
$data['page_speed_score'] = NULL;
$data['page_speed_suggestion'] = NULL;
}
$mobileFriendlyResult = NULL;
$data['mobile_ready_score'] = 0;
$data['mobile_ready'] = 0;
$data['mobile_ready_suggestion'] = NULL;
$data['google_analytics'] = 0;
Log::info("busnes " . $businessId);
Website::updateOrCreate(
['business_id' => $businessId],
$data
);
$issueData = []
$thirdObj->globalIssueGenerator($userId, $businessId, '', $issueData, 'website', 'website');
return $this->helpReturn('Website data saved & issues are generated in system');
}
});
} catch (Exception $e) {
Log::info(" getWebsiteDetails >> " . $e->getMessage());
return $this->helpError(1, 'Some Problem happened to run script.');
}
}
거래 중 레코드 잠금을 사용하여 레코드 중복을 방지합니다.
잠금은 테이블 레벨에서 적용할 수 있습니다. - 레코드를 수정할 수 없도록 - 또는 레코드 레벨에서 - 다른 프로세스에서 해당 레코드만 수정할 수 없도록 합니다.
중복 항목이 있으므로 테이블 레벨 잠금을 사용해야 할 수 있습니다.
@btl, @니콜라
현재 코드 위치 변경 및 결과를 올바르게 반환하고 데이터베이스에 레코드를 삽입하여 시도하고 있습니다.
데이터베이스에 체크 데이터가 있는지 없는지 트랜잭션 코드를 붙여넣습니다.현재 무슨 일이 일어나고 있는지 파악하고 있지만 현재 상태를 공유하고 있습니다. 새 코드와 이전 코드를 참조하십시오.새로운 코드가 작동중이고 또한 두 코드의 로그를 추가합니다.
변경후새코드
public function getWebsiteDetails(Request $request)
{
Log::info("call ");
try {
/**
* out from transction
*/
// extract user
$checkPoint = $this->setCurrentUser($request->get('token'))->userAllow();
$user = $checkPoint['records'];
// extract business of user
$businessDetail = $this->businessEntity->userSelectedBusiness($user);
$businessDetail = $businessDetail['records'];
$userId = $user['id'];
$businessId = $businessDetail['business_id'];
$url = $businessDetail['website'];
/**
* Get record from website_master of given business id
*/
$businessWebsiteRecord = Website::where([
'business_id' => $businessId
])->first();
$result = DB::transaction(function () use($request, $url, $businessId, $userId,$businessWebsiteRecord)
{
$thirdObj = new ThirdPartyEntity();
// if business_master (url) is exist then go to if block
if ($url != '') {
$data = [];
$pageSpeedScore = '';
$mobileReadyScore = '';
$data['website'] = $url;
$url = 'http://' . $url;
$pageSpeedResult = $this->pageSpeedResult($url);
if ($pageSpeedResult['_metadata']['outcomeCode'] == 200) {
$pageSpeedData = $pageSpeedResult['records'];
$speedResult = json_encode($pageSpeedData['formattedResults']['ruleResults']);
$data['title_tag'] = $pageSpeedData['title'];
$data['page_speed_score'] = $pageSpeedData['score'];
$data['page_speed_suggestion'] = $speedResult;
$pageSpeedScore = $pageSpeedData['score'];
} else {
$speedResult = NULL;
$data['title_tag'] = NULL;
$data['page_speed_score'] = NULL;
$data['page_speed_suggestion'] = NULL;
}
$mobileFriendlyResult = NULL;
$data['mobile_ready_score'] = 0;
$data['mobile_ready'] = 0;
$data['mobile_ready_suggestion'] = NULL;
$data['google_analytics'] = 0;
Log::info("busnes " . $businessId);
$recordChecker = Website::where('business_id', $businessId)->first();
if (!empty($recordChecker)) {
Log::info("if busnes " . $businessId);
} else {
Log::info("else busnes " . $businessId);
}
Website::updateOrCreate(
['business_id' => $businessId],
[
'website' => $url
]
);
$issueData = [
[
'key' => 'title_tags',
'value' => $data['title_tag'],
'issue' => 36,
],
[
'key' => 'page_speed',
'value' => $pageSpeedScore,
'issue' => 38,
],
[
'key' => 'mobile_speed',
'value' => $mobileReadyScore,
'issue' => 37,
],
[
'key' => 'google_analytics',
'value' => $data['google_analytics'],
'issue' => 39,
]
];
$thirdObj->globalIssueGenerator($userId, $businessId, '', $issueData, 'website', 'website');
return $this->helpReturn('Website data saved & issues are generated in system');
}
});
Log::info("finish");
return $result;
} catch (Exception $e) {
Log::info(" getWebsiteDetails >> " . $e->getMessage());
return $this->helpError(1, 'Some Problem happened to run script.');
}
}
구코드
public function getWebsiteDetails(Request $request)
{
Log::info("call ");
try {
return DB::transaction(function () use($request)
{
$thirdObj = new ThirdPartyEntity();
// user extract
$checkPoint = $this->setCurrentUser($request->get('token'))->userAllow();
$user = $checkPoint['records'];
$businessDetail = $this->businessEntity->userSelectedBusiness($user);
$businessDetail = $businessDetail['records'];
$userId = $user['id'];
$businessId = $businessDetail['business_id'];
$url = $businessDetail['website'];
/**
* Get record from website_master of given business id
*/
$businessWebsiteRecord = Website::where([
'business_id' => $businessId
])->first();
// if business_master (url) is exist then go to if block
if ($url != '') {
$data = [];
$pageSpeedScore = '';
$mobileReadyScore = '';
$data['website'] = $url;
$url = 'http://'.$url;
$pageSpeedResult = $this->pageSpeedResult($url);
if( $pageSpeedResult['_metadata']['outcomeCode'] == 200 )
{
$pageSpeedData = $pageSpeedResult['records'];
$speedResult = json_encode($pageSpeedData['formattedResults']['ruleResults']);
$data['title_tag'] = $pageSpeedData['title'];
$data['page_speed_score'] = $pageSpeedData['score'];
$data['page_speed_suggestion'] = $speedResult;
$pageSpeedScore = $pageSpeedData['score'];
}
else
{
$speedResult = NULL;
$data['title_tag'] = NULL;
$data['page_speed_score'] = NULL;
$data['page_speed_suggestion'] = NULL;
}
$mobileFriendlyResult = NULL;
$data['mobile_ready_score'] = 0;
$data['mobile_ready'] = 0;
$data['mobile_ready_suggestion'] = NULL;
$data['google_analytics'] = 0;
Log::info("busnes " . $businessId);
Website::updateOrCreate(
['business_id' => $businessId],
$data
);
$issueData = []
$thirdObj->globalIssueGenerator($userId, $businessId, '', $issueData, 'website', 'website');
return $this->helpReturn('Website data saved & issues are generated in system');
}
});
} catch (Exception $e) {
Log::info(" getWebsiteDetails >> " . $e->getMessage());
return $this->helpError(1, 'Some Problem happened to run script.');
}
}
참고: 데이터베이스에서 데이터를 사용할 수 없기 때문에 다른 데이터로 들어가므로 데이터가 이미 존재하기 때문에 데이터가 생성됩니다.(그것이 제가 얻은 것입니다)
네코드 로그
[2018-02-12 14:18:49] local.INFO: call
[2018-02-12 14:18:53] local.INFO: call
[2018-02-12 14:19:08] local.INFO: crossed external api point
[2018-02-12 14:19:08] local.INFO: else
[2018-02-12 14:19:08] local.INFO: finish
[2018-02-12 14:19:09] local.INFO: crossed external api point
[2018-02-12 14:19:09] local.INFO: if
[2018-02-12 14:19:09] local.INFO: finish
이전 코드 로그
[2018-02-12 14:17:16] local.INFO: call
[2018-02-12 14:17:19] local.INFO: call
[2018-02-12 14:17:34] local.INFO: crossed external api point
[2018-02-12 14:17:34] local.INFO: else
[2018-02-12 14:17:34] local.INFO: finish
[2018-02-12 14:17:35] local.INFO: crossed external api point
[2018-02-12 14:17:36] local.INFO: else
[2018-02-12 14:17:36] local.INFO: finish
현재 첫 번째 트랜잭션 후 실행되는 이전 로그에 따르면 이전 코드는 두 번째 트랜잭션에서 데이터베이스에서 아직 레코드를 찾지 못했지만 새 코드 로그에는 두 번째 트랜잭션을 시작하기 전의 데이터가 데이터베이스에서 발견된 데이터를 표시합니다.
네, 실패할 가능성이 있지만 응답이 같고 둘 다 동시에 데이터를 데이터베이스에 저장하는 경우에는 다른 기술을 사용할 것입니다. 하지만 이전 코드에서 실패하고 있던 3-5초 전에 레코드가 이미 삽입된 경우 새 코드가 레코드를 추적할 수 있는 더 좋은 기회를 가집니다.
고마워요 @btl and @Nikola.하지만 이 행동에 대한 내용이 있으면 공유해주세요.
언급URL : https://stackoverflow.com/questions/48745279/laravel-calling-my-web-services-api-multiple-times-bypass-the-logic-of-single
'programing' 카테고리의 다른 글
여러 열을 mysql의 고유 식별자로 사용 (0) | 2023.09.08 |
---|---|
문자열의 가능한 모든 순열 목록 생성 (0) | 2023.09.08 |
함수(콜백)를 다른 함수의 인수로 사용하는 것은 파이썬에서 어떻게 작동합니까? (0) | 2023.09.08 |
엑셀 시트로 오늘 날짜를 어떻게 알 수 있습니까? (0) | 2023.09.08 |
Angular2 중첩 템플릿 기반 폼 (0) | 2023.09.03 |