遅くなりましたが、別の対処法が見つかりました。
cronで行おうとしていましたが、
checkDbMyPendignOrderやcheckSessionPendingOrderがセッションを使っているため、
cronから触るのはよくないと考え、別の対処法を検討しました。
同タイミングだとselectで同じデータが2つ取れてしまうのが根本原因なので、ロックをかけるようにする方法です。
postgresであれば、FOR UPDATEをselect文に追記することで、行ロックが行えます。
※他DBにも同様の機能はあるかと思います。
$objQuery->setOption('FOR UPDATE');
各select
$objQuery->setOption('');
これでselect後にsleepを入れて恣意的に重複させても
後から入ったcheck*****PendignOrderは
先のトランザクションがcommitされるまで待たされ、
select結果として決済処理中のデータを取得できないようになります。
全ての影響は検証できていませんが、
みなさんのご意見伺いたいです。
以下、SC_Helper_Purchaseの最後の4メソッドです。
/**
* 決済処理中スタータスの受注データのキャンセル処理
* @param $cancel_flg 決済処理中ステータスのロールバックをするか(true:する false:しない)
*/
public function cancelPendingOrder($cancel_flg)
{
if($cancel_flg == true){
$this->checkDbAllPendingOrder();
$this->checkDbMyPendignOrder();
$this->checkSessionPendingOrder();
}
}
/**
* 決済処理中スタータスの全受注検索
*/
public function checkDbAllPendingOrder()
{
$term = PENDING_ORDER_CANCEL_TIME;
if (!SC_Utils_Ex::isBlank($term) && preg_match("/^[0-9]+$/", $term)) {
$target_time = strtotime('-' . $term . ' sec');
$objQuery =& SC_Query_Ex::getSingletonInstance();
$arrVal = array(date('Y/m/d H:i:s',$target_time), ORDER_PENDING);
$objQuery->begin();
$objQuery->setOption('FOR UPDATE');
$arrOrders = $objQuery->select('order_id', 'dtb_order', 'create_date <= ? and status = ? and del_flg = 0', $arrVal);
$objQuery->setOption('');
if (!SC_Utils_Ex::isBlank($arrOrders)) {
//GC_Utils_Ex::gfPrintLog('checkDbAllPendingOrder detect order_id=' . print_r($arrOrders,true));
//sleep(10);
foreach ($arrOrders as $arrOrder) {
$order_id = $arrOrder['order_id'];
SC_Helper_Purchase_Ex::cancelOrder($order_id, ORDER_CANCEL, true);
GC_Utils_Ex::gfPrintLog('order cancel.(time expire) order_id=' . $order_id);
}
}
//GC_Utils_Ex::gfPrintLog('checkDbAllPendingOrder done.');
$objQuery->commit();
}
}
public function checkDbMyPendignOrder()
{
$objCustomer = new SC_Customer_Ex();
if ($objCustomer->isLoginSuccess(true)) {
$customer_id = $objCustomer->getValue('customer_id');
$objQuery =& SC_Query_Ex::getSingletonInstance();
$arrVal = array($customer_id, ORDER_PENDING);
$objQuery->setOrder('create_date desc');
$objQuery->begin();
$objQuery->setOption('FOR UPDATE');
$arrOrders = $objQuery->select('order_id,create_date', 'dtb_order', 'customer_id = ? and status = ? and del_flg = 0', $arrVal);
$objQuery->setOption('');
if (!SC_Utils_Ex::isBlank($arrOrders)) {
//GC_Utils_Ex::gfPrintLog('checkDbMyPendignOrder detect order_id=' . print_r($arrOrders,true));
//sleep(10);
foreach ($arrOrders as $key => $arrOrder) {
$order_id = $arrOrder['order_id'];
if ($key == 0) {
$objCartSess = new SC_CartSession_Ex();
$cartKeys = $objCartSess->getKeys();
$term = PENDING_ORDER_CANCEL_TIME;
if (preg_match("/^[0-9]+$/", $term)) {
$target_time = strtotime('-' . $term . ' sec');
$create_time = strtotime($arrOrder['create_date']);
if (SC_Utils_Ex::isBlank($cartKeys) && $target_time < $create_time) {
SC_Helper_Purchase_Ex::rollbackOrder($order_id, ORDER_CANCEL, true);
GC_Utils_Ex::gfPrintLog('order rollback.(my pending) order_id=' . $order_id);
} else {
SC_Helper_Purchase_Ex::cancelOrder($order_id, ORDER_CANCEL, true);
if ($target_time > $create_time) {
GC_Utils_Ex::gfPrintLog('order cancel.(my pending and time expire) order_id=' . $order_id);
} else {
GC_Utils_Ex::gfPrintLog('order cancel.(my pending and set cart) order_id=' . $order_id);
}
}
}
} else {
SC_Helper_Purchase_Ex::cancelOrder($order_id, ORDER_CANCEL, true);
GC_Utils_Ex::gfPrintLog('order cancel.(my old pending) order_id=' . $order_id);
}
}
}
//GC_Utils_Ex::gfPrintLog('checkDbMyPendignOrder done.');
$objQuery->commit();
}
}
public function checkSessionPendingOrder()
{
if (!SC_Utils_Ex::isBlank($_SESSION['order_id'])) {
$order_id = $_SESSION['order_id'];
unset($_SESSION['order_id']);
$objQuery =& SC_Query_Ex::getSingletonInstance();
$objQuery->begin();
$objQuery->setOption('FOR UPDATE');
$arrOrder = SC_Helper_Purchase_Ex::getOrder($order_id);
$objQuery->setOption('');
if ($arrOrder['status'] == ORDER_PENDING) {
//GC_Utils_Ex::gfPrintLog('checkSessionPendingOrder detect order_id=' . print_r($arrOrder,true));
//sleep(10);
$objCartSess = new SC_CartSession_Ex();
$cartKeys = $objCartSess->getKeys();
if (SC_Utils_Ex::isBlank($cartKeys)) {
SC_Helper_Purchase_Ex::rollbackOrder($order_id, ORDER_CANCEL, true);
GC_Utils_Ex::gfPrintLog('order rollback.(session pending) order_id=' . $order_id);
} else {
SC_Helper_Purchase_Ex::cancelOrder($order_id, ORDER_CANCEL, true);
GC_Utils_Ex::gfPrintLog('order rollback.(session pending and set card) order_id=' . $order_id);
}
}
//GC_Utils_Ex::gfPrintLog('checkSessionPendingOrder done.');
$objQuery->commit();
}
}