2016-07-18 5 views
0

클라이언트에서 특정 위치에 도착했을 때 POST를받는 laravel 5.2 앱이 있습니다. 경우에 따라 클라이언트는 동시에 두 개의 동일한 요청을 실행하지만 동일한 레코드 (이러한 중복 요청에 포함되지 않은 마지막 이벤트)를 사용하여 처리 방법을 결정합니다.Laravel 동시 쓰기 방지

각 요청에 대해 클라이언트가 이미 해당 사용자의 마지막 이벤트를 확인하여 동일한 위치에 로그인했는지 여부를 확인하여 중복 체크 인/체크 아웃을 방지하려고합니다.

예 :

<?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이 제대로 처리 된 복제본의 결과 인 $ lastCheckin을 볼 수있는 중복 요청 집합의 두 번째 요청이 필요합니다 (때로는 중복 일 수도 있지만 몇 분으로 분리됨). 그리고 처리되었습니다. 따라서.

+1

사용자가 어떻게 두 가지 이벤트를 발생 시키나요? 클라이언트 측에서이를 막기가 더 쉽지 않을까요? – James

+0

잘 모르겠습니다. 이것을 추적하려고합니다. 그들은 백그라운드 업데이트, 비동기 스레드에서 위치 업데이트 및 체크 인 이벤트에 응답합니다. –

+0

해결할 문제를 해결하기보다는 루트 문제를 해결할 것입니다. – James

답변

1

Laravel 5.2는 괜찮은 API for Queues을 가지고 있습니다. 체크 아웃하고 싶을 수도 있습니다. 클라이언트에서 시작된 비동기 이벤트는 중복을 방지하기 위해 대기열에 있어야합니다.

Laravel Documentation에 "Pushing Jobs Onto The Queue"및 "Delayed Jobs"에 대한 설명서를보고 싶을 수 있습니다.

+0

내가 이해할 수있는 것처럼 큐에서 실행하면 동기식으로 실행됩니다. 그래서, 내가 아는 바에 따르면, POST 체크에 대한 모든 요청은 대기 상태가 될 것이고, 요청이 수신되었음을 인정하는 클라이언트에게 임의의 응답이 제공된다. 한편, 대기열에서 실제로 유효한 체크인인지 또는 복제인지 여부를 결정하는 데 필요한 논리를 수행하고 클라이언트에 제공되지 않은 결과 (또는 푸시 알림으로 클라이언트에 제공됨) ? –

+0

이것이 제가 한 일입니다. 데이터베이스 기반 큐를 사용하도록 Laravel을 구성하고, 입력 데이터를 잡아서 조작하고, 작업자에게 전달하고, 처리를 위해 대기중인 체크 인을 나타내는 즉각적인 응답을 반환합니다. 대기열 작업에서는 모든 유효성 검사 논리를 수행하며, 내가 말할 수있는 한, 중복을 얻지는 못합니다. 몇 초 정도 분리되어있는 것처럼 보입니다. –