Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
37.50% covered (danger)
37.50%
6 / 16
33.33% covered (danger)
33.33%
1 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
HubspotSyncJob
37.50% covered (danger)
37.50%
6 / 16
33.33% covered (danger)
33.33%
1 / 3
14.79
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 handle
35.71% covered (danger)
35.71%
5 / 14
0.00% covered (danger)
0.00%
0 / 1
8.25
 backoff
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace App\Jobs;
4
5use App\Http\Models\Auth\User;
6use App\Http\Services\HubspotServiceV2;
7use Illuminate\Bus\Queueable;
8use Illuminate\Contracts\Queue\ShouldQueue;
9use Illuminate\Foundation\Bus\Dispatchable;
10use Illuminate\Queue\InteractsWithQueue;
11use Illuminate\Queue\SerializesModels;
12use Illuminate\Support\Facades\Cache;
13use Illuminate\Support\Facades\Log;
14
15class HubspotSyncJob implements ShouldQueue
16{
17    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
18
19    public $tries = 5;
20
21    public function __construct(
22        public readonly string $email,
23        public readonly string $userId,
24    ) {
25        $this->onQueue('hubspot');
26    }
27
28    public function handle(HubspotServiceV2 $hubspotService): void
29    {
30        $lockKey = "hubspot_sync_lock_{$this->email}";
31
32        // 180s covers the realistic worst-case runtime of syncUserState() under
33        // slow HubSpot or MongoDB conditions. A shorter TTL risks the lock
34        // expiring mid-job; a re-queued sibling could then acquire it and run
35        // concurrently — idempotent but doubles API/queue cost.
36        $lock = Cache::lock($lockKey, 180);
37
38        if (! $lock->get()) {
39            // Another job for this user is already running; re-queue in 15s
40            // so the latest DB state is picked up after the current job finishes.
41            $this->release(15);
42
43            return;
44        }
45
46        try {
47            $user = User::find($this->userId);
48
49            if (! $user) {
50                Log::warning("HubspotSyncJob: user {$this->userId} not found ({$this->email})");
51
52                return;
53            }
54
55            $hubspotService->syncUserState($user);
56        } catch (\Throwable $e) {
57            Log::error("HubspotSyncJob failed for {$this->email}{$e->getMessage()}");
58            throw $e;
59        } finally {
60            $lock->release();
61        }
62    }
63
64    public function backoff(): array
65    {
66        return [10, 30, 60, 120, 300];
67    }
68}