laravel-expo-notifier icon indicating copy to clipboard operation
laravel-expo-notifier copied to clipboard

SQLSTATE[23505]: Unique violation: 7 ERROR: duplicate key value violates unique constraint "expo_tickets_ticket_id_unique"

Open Arsiievych opened this issue 1 year ago • 2 comments

I encountered an issue when sending multiple notifications using notify().

Steps to Reproduce:

$user = User::find(1);
$user2 = User::find(2);

$user->notify(new NewMessageNotification());
$user2->notify(new UpcomingCallNotification());

Expected Result: Notifications should be sent without exceptions.

Actual Result:

SQLSTATE[23505]: Unique violation: 7 ERROR: duplicate key value violates unique constraint "expo_tickets_ticket_id_unique"
DETAIL: Key (ticket_id)=(XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXdb407) already exists.

INSERT INTO
  "expo_tickets" ("ticket_id", "token", "updated_at", "created_at")
VALUES
  (
    XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXX9fbf2,
    ExponentPushToken[XXXXXXXXXXXXXXXX],
    2024-10-30 15:22:05,
    2024-10-30 15:22:05
  ) returning "id"

It is important to note that the issue does not depend on which user the notify method is executed for, nor does it vary based on the specific notification being sent. The error occurs consistently across different users and notification types.

After investigating, I found that the problems arise here in the ExpoNotificationsService::checkAndStoreTickets(Collection $tokens), particularly in this line: $this->ticketStorage->store($ticket->ticketId, $tokens->get($index));

Variable dumps from the first call ($user->notify(new NewMessageNotification());):


$this->tickets: Illuminate\Support\Collection Object
(
    [items:protected] => Array
        (
            [0] => YieldStudio\LaravelExpoNotifier\Dto\PushTicketResponse Object
                (
                    [status] => ok
                    [ticketId] => XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXX9fbf2
                    [message] => 
                    [details] => 
                )
        )
    [escapeWhenCastingToString:protected] => 
)
  
$tokens: Illuminate\Support\Collection Object
(
    [items:protected] => Array
        (
            [0] => ExponentPushToken[XXXXXXXXXXXXXXXX]
        )
    [escapeWhenCastingToString:protected] => 
)

From the next call ($user2->notify(new UpcomingCallNotification());):

$this->tickets: Illuminate\Support\Collection Object
(
    [items:protected] => Array
        (
            [0] => YieldStudio\LaravelExpoNotifier\Dto\PushTicketResponse Object
                (
                    [status] => ok
                    [ticketId] => XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXX9fbf2
                    [message] => 
                    [details] => 
                )
            [1] => YieldStudio\LaravelExpoNotifier\Dto\PushTicketResponse Object
                (
                    [status] => ok
                    [ticketId] => XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXdbb49
                    [message] => 
                    [details] => 
                )
        )
    [escapeWhenCastingToString:protected] => 
)
  
$tokens: Illuminate\Support\Collection Object
(
    [items:protected] => Array
        (
            [0] => ExponentPushToken[XXXXXXXXXXXXXXXX]
        )
    [escapeWhenCastingToString:protected] => 
)

It tries to re-store record [0], instead of storing only [1].

I also couldn’t find any logic to clear $this->tokens, which means that beyond the storage problem, I encounter issues using the notify method in a queue container. Over time, $this->tokens accumulates many records.

I was also able to reproduce the issue by executing the following in Tinker:

php artisan tinker
$user = User::find(1);
$user->notify(new NewMessageNotification());
$user->notify(new NewMessageNotification());

This results in the following error:

Illuminate\Database\UniqueConstraintViolationException  
SQLSTATE[23505]: Unique violation: 7 ERROR: duplicate key value violates unique constraint "expo_tickets_ticket_id_unique"

Arsiievych avatar Oct 30 '24 16:10 Arsiievych