【发布时间】:2016-07-18 03:46:02
【问题描述】:
我有一个 laravel 5.2 应用程序,当客户到达特定位置时,它会收到来自客户的 POST。有时,客户端会同时触发两个相同的请求,但使用相同的记录(最后一个事件,不属于这些重复请求的一部分)来决定如何处理。
对于每个请求,我试图通过检查该用户的最后一个事件来确定客户端是否已经登录或退出同一位置,从而防止重复签入/签出。
例如:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Checkin;
use App\Models\Population;
use App\Http\Requests;
class CheckinApiController extends Controller {
public function post(Request $request) {
$uuid = $request->uuid;
$isCheckin = $request->isCheckin;
$locationId = $request->locationId;
// get the last checkin for user matching this UUID
$lastCheckin = Checkin::where("uuid", $uuid)
->orderBy('updated_at', 'desc')
->first();
if ($isCheckin == $lastCheckin->isCheckin
&& $locationId == $lastCheckin->locationId) {
/* Scenario 1:
* if this post event is the same as the last record's checkin type
* (checkin/checkout), and the locationIds are the same, this is a
* duplicate. Return an error code and message
* In this case, both the duplicate requests see the same record,
* and both are handled as duplicates
*/
abort(403, 'duplicate');
} else if ($isCheckin == true
&& $isCheckin == $lastCheckin->isCheckin
&& $locationId != $lastCheckin->locationId) {
/* Scenario 2:
* this is a checkin event, but the last event for this user was
* a checkin at a different location
* we create a mock 'checkout' for this request, and
* set the updated_at field as current time minus a few seconds
* BUT... in the case of duplicates, both duplicates see the
* last event, so both duplicates handle this the same way, and i
* get TWO $missedCheckin records.
*/
$missedCheckin = Checkin::create([
'locationId' => $lastCheckin->locationId,
'isCheckin' => false,
'uuid' => $uuid,
'updated_at' => time() - 10,
]);
}
// write this checkin event
$checkin = Checkin::create([
'locationId' => $locationId,
'isCheckin' => $isCheckin,
'uuid' => $uuid
]);
// adjust population on another model
$population = Population::firstOrCreate([
'location_id' => $locationId
]);
// increment or decrement population based on
// if this is a checkin or checkout,
// omitted here but mentioned as it is another
// database transaction on a different model
// $responseDataSet is a dictionary with info to tell the client to present to the user
return response()->json($responseDataSet);
}
}
有没有办法可以暂停可能的重复记录,允许第一条记录通过此过程,然后才允许第二条记录通过(这将被视为重复)?
我尝试在处理之前放入以下等待随机数毫秒,但似乎随机数生成器在相同的请求期间返回相同的随机数:
$msToSleep = 1 * random_int(500, 100000);
usleep($msToSleep);
客户端不一定需要知道结果,所以我不需要返回除了 200 状态代码之外的任何内容,尽管最终我可能想要返回创建的有效签入对象。
但是我需要一组重复请求的第二个请求才能看到 $lastCheckin 是第一个重复的结果,正确处理(有时它可能是重复的,但相隔几分钟),并进行相应处理.
【问题讨论】:
-
用户如何触发两个事件?在客户端阻止这种情况不是更容易吗?
-
我不确定,正在尝试追查;他们在异步线程上响应位置更新并在后台发布签入事件。
-
我会弄清楚并修复根本问题,而不是尝试创建修复程序来处理它。
标签: php database rest laravel concurrency