<?php<448901948@qq.com>
namespace think\queue\connector;
use think\Db;
use think\queue\Connector;
use think\queue\job\Database as DatabaseJob;
class Database extends Connector
{
protected $db;
protected $options = [
'expire' => 60,
'default' => 'default',
'table' => 'jobs',
'dsn' => []
];
public function __construct($options)
{
if (!empty($options)) {
$this->options = array_merge($this->options, $options);
}
$this->db = Db::connect($this->options['dsn']);
}
public function push($job, $data = '', $queue = null)
{
return $this->pushToDatabase(0, $queue, $this->createPayload($job, $data));
}
public function later($delay, $job, $data = '', $queue = null)
{
return $this->pushToDatabase($delay, $queue, $this->createPayload($job, $data));
}
public function pop($queue = null)
{
$queue = $this->getQueue($queue);
if (!is_null($this->options['expire'])) {
$this->releaseJobsThatHaveBeenReservedTooLong($queue);
}
if ($job = $this->getNextAvailableJob($queue)) {
$this->markJobAsReserved($job->id);
$this->db->commit();
return new DatabaseJob($this, $job, $queue);
}
$this->db->commit();
}
public function release($queue, $job, $delay)
{
return $this->pushToDatabase($delay, $queue, $job->payload, $job->attempts);
}
protected function pushToDatabase($delay, $queue, $payload, $attempts = 0)
{
return $this->db->name($this->options['table'])->insert([
'queue' => $this->getQueue($queue),
'payload' => $payload,
'attempts' => $attempts,
'reserved' => 0,
'reserved_at' => null,
'available_at' => time() + $delay,
'created_at' => time()
]);
}
protected function getNextAvailableJob($queue)
{
$this->db->startTrans();
$job = $this->db->name($this->options['table'])
->lock(true)
->where('queue', $this->getQueue($queue))
->where('reserved', 0)
->where('available_at', '<=', time())
->order('id', 'asc')
->find();
return $job ? (object) $job : null;
}
protected function markJobAsReserved($id)
{
$this->db->name($this->options['table'])->where('id', $id)->update([
'reserved' => 1,
'reserved_at' => time()
]);
}
protected function releaseJobsThatHaveBeenReservedTooLong($queue)
{
$expired = time() - $this->options['expire'];
$this->db->name($this->options['table'])
->where('queue', $this->getQueue($queue))
->where('reserved', 1)
->where('reserved_at', '<=', $expired)
->update([
'reserved' => 0,
'reserved_at' => null,
'attempts' => ['exp', 'attempts + 1']
]);
}
public function deleteReserved($id)
{
$this->db->name($this->options['table'])->delete($id);
}
protected function getQueue($queue)
{
return $queue ?: $this->options['default'];
}
}