Open localpath opened 2 years ago
@localpathcomp Hi! Thank you for the report :) I've tried following steps, but it seems I can't reproduce the problem.
created
event.namespace App\Models;
use Illuminate\Support\Facades\Log;
use Kitar\Dynamodb\Model\Model as DynamodbModel;
use function Illuminate\Events\queueable;
class Model extends DynamodbModel
{
protected $table = 'my-table';
protected $primaryKey = 'PK';
protected $sortKey = 'SK';
protected $fillable = ['PK', 'SK'];
protected static function booted()
{
static::created(queueable(function ($item) {
Log::info("Model created (PK:{$item->PK}, SK:{$item->SK})");
}));
}
}
QUEUE_CONNECTION=redis
use App\Models\Model;
Model::create(['PK' => 'PK1', 'SK' => 'SK1']);
php artisan queue:work
And then, job was executed with no error and log was successfully created.
[2022-06-25 05:28:02] local.INFO: Model created (PK:PK1, SK:SK1)
Could you share a bit more details for reproducing the problem?
Hi Sure thing. Here's the two models I use. I can try again as well with more debugging in place if it would help. I was thinking maybe its calling get default connection and I have a multi tenant setup?
Queue connection = database
<?php
namespace App\Models;
use Illuminate\Support\Str;
use Kitar\Dynamodb\Model\Model;
class SystemEvent extends Model
{
/**
* The database connection that should be used by the model.
*
* @var string
*/
protected $connection = 'dynamodb';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'system_events';
/**
* The Partition Key.
*
* @var string
*/
protected $primaryKey = 'PK';
/**
* The Sort Key.
*
* @var string|null
*/
protected $sortKey = 'SK';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'id',
'tenant_uuid',
'event_data',
'event_origin',
'event_type',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'PK',
'SK',
'GSI1PK',
'GSI1SK',
'GSI2PK',
'GSI2SK',
'TYPE',
];
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'event_data' => 'array',
];
protected static function booted()
{
static::creating(function ($systemEvent) {
if (empty($systemEvent->event_type)) {
throw new \InvalidArgumentException();
}
$uuid = (string) Str::uuid();
// index attributes
$systemEvent->PK = "EVENT#{$uuid}";
$systemEvent->SK = "EVENT#{$uuid}";
$systemEvent->GSI1PK = "EVENT_TYPE#{$systemEvent->event_type}";
$systemEvent->GSI1SK = "EVENT#{$uuid}";
if (! empty($systemEvent->tenant_uuid)) {
$systemEvent->GSI2PK = "TENANT#{$systemEvent->tenant_uuid}";
$systemEvent->GSI2SK = "EVENT#{$uuid}";
}
$systemEvent->TYPE = self::class;
// item attributes
$systemEvent->id = $uuid;
});
}
public static function find($id)
{
return parent::find(['PK' => "EVENT#{$id}", 'SK' => "EVENT#{$id}"]);
}
public static function extractLastEvaluatedKey($item)
{
if (empty($item)) {
return null;
}
return $item->meta()['LastEvaluatedKey'] ?? null;
}
}
<?php
namespace App\Models;
use Kitar\Dynamodb\Model\Model;
class WebhookLog extends Model
{
/**
* The database connection that should be used by the model.
*
* @var string
*/
protected $connection = 'dynamodb';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'webhook_logs';
/**
* The Partition Key.
*
* @var string
*/
protected $primaryKey = 'PK';
/**
* The Sort Key.
*
* @var string|null
*/
protected $sortKey = 'SK';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'id',
'event_id',
'application_id',
'attempt',
'headers',
'http_verb',
'webhook_url',
'payload',
'response',
'error_type',
'error_message',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'PK',
'SK',
'GSI1PK',
'GSI1SK',
'GSI2PK',
'GSI2SK',
'TYPE',
];
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'payload' => 'array',
'response' => 'array',
'headers' => 'array',
];
protected static function booted()
{
static::creating(function ($webhookLog) {
if (empty($webhookLog->id)) {
throw new \InvalidArgumentException();
}
if (empty($webhookLog->application_id)) {
throw new \InvalidArgumentException();
}
if (empty($webhookLog->event_id)) {
throw new \InvalidArgumentException();
}
// index attributes
$webhookLog->PK = "WEBHOOK#{$webhookLog->id}";
$webhookLog->SK = "WEBHOOK#{$webhookLog->id}";
$webhookLog->GSI1PK = "APPLICATION_ID#{$webhookLog->application_id}";
$webhookLog->GSI1SK = "WEBHOOK#{$webhookLog->id}";
$webhookLog->GSI2PK = "EVENT#{$webhookLog->event_id}";
$webhookLog->GSI2SK = "WEBHOOK#{$webhookLog->id}";
$webhookLog->TYPE = self::class;
});
}
public static function find($id)
{
return parent::find(['PK' => "WEBHOOK#{$id}", 'SK' => "WEBHOOK#{$id}"]);
}
public static function extractLastEvaluatedKey($item)
{
if (empty($item)) {
return null;
}
return $item->meta()['LastEvaluatedKey'] ?? null;
}
}
Thanks for sharing! The code looks interesting. However, I can't reproduce the problem yet. Let me clarify a few things.
DB_CONNECTION
is not dynamodb
, but the models given above are using dynamodb connection)SystemEventCreated
job? (There may be some operations causing the laravel-dynamodb bug in that job somewhere like restoreCollection()
)If you dump the $connection
variable inside the queue (eg. $model->getConnectionName()
), and if it is dynamodb
, there is likely to be a different problem than the connection.
Hi @kitar. yes we are using multiple DB connections.
Are you using multiple DB connections? (eg. your default DB_CONNECTION is not dynamodb, but the models given above are using dynamodb connection)
I think thats what it is. On my dynamo DB model do I need to explicitly define the getConnectionName
? I bet thats what it is bc the connection is returning null.
Do you have a problem interacting with DynamoDB even outside the queue?
The model works perfectly outside of the queue.
What are you trying to do in SystemEventCreated job? (There may be some operations causing the laravel-dynamodb bug in that job somewhere like restoreCollection())
The event class. I wanted to inject the model instance and not the plain array.
<?php
namespace App\Events\SystemEvents;
use App\Models\SystemEvent;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class SystemEventCreated
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*
* @param array $systemEvent
* @return void
*/
public function __construct(
public array $systemEvent,
) {
}
}
The listener for example
<?php
namespace App\Listeners\Webhooks;
use App\Events\SystemEvents\SystemEventCreated;
use App\Models\WebhookSubscriber;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Cache;
use Spatie\WebhookServer\WebhookCall;
class SendSystemEventNotifications implements ShouldQueue
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param \App\Events\SystemEvents\SystemEventCreated $event
* @return void
*/
public function handle(SystemEventCreated $event)
{
// use the model off the event class for example
}
}
@localpath Sorry for the late reply.
Thanks for the information and you were indeed right. However, there are several issues involved that make it difficult to resolve immediately. Please pass keys or array instead of a model instance to the listener at this time.
Notes on this issue:
newQueryForRestoration()
, first()
, firstOrFail()
SerializesAndRestoresModelIdentifiers::getRestoredPropertyValue()
expects identifier is always single value, but with DynamoDB, it could be multiple values (like PK and SK). @kitar Hi. Thanks for looking at it. Yes that makes perfect sense. Easy enough to get around and just rehydrate models later. Thanks!!
Hello. Ty for the great package. Using it I've come across a serialization issue when using queued event listeners. Seems like it's trying to connect on a null connection? Like it's reverting to the default database driver? Using the model created event and listening to that event with a queued event listener fails. I can toArray() or pass the DynamoDB partition keys to do a lookup but seems like laravel should be able to hydrate no matter the driver right?
Laravel 9.x laravel-dynamodb 1.x