Skip to content

Instantly share code, notes, and snippets.

@tacone
Created May 20, 2016 14:11
Show Gist options
  • Save tacone/1f54137da7e4e0bc717d6fa3ef2a936e to your computer and use it in GitHub Desktop.
Save tacone/1f54137da7e4e0bc717d6fa3ef2a936e to your computer and use it in GitHub Desktop.
<?php
namespace App;
use Illuminate\Database\QueryException;
class Increments
{
public static function generate($key)
{
// we need a transaction because lockForUpdate only
// locks the row when used inside a transaction
return \DB::transaction(function () use ($key) {
// if no rows exist there is no row to lock
// so we ALWAYS try to insert an empty row
// as the first thing, as it's the only way
// to ensure absolute consistency between
// concurrent requests.
//
// since "key" is the primary key of the table
// it will always fail except the first time
try {
\DB::table('increments')->insert([
'key' => $key,
'value' => 0,
]);
} catch (QueryException $e) {
// we need to use a try catch because Laravel
// does not support INSERT IGNORE and alikes
}
// we can now lock the row
$record = \DB::table('increments')
->where('key', $key)
->lockForUpdate()
->first();
// increment the counter
$value = $record->value + 1;
// write it
\DB::table('increments')
->where('key', $key)
->update(['value' => $value]);
// and hand it to the user
return $value;
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment