insertInvalidationEvent()   B
last analyzed

Complexity

Conditions 9
Paths 39

Size

Total Lines 77
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 0 Features 0
Metric Value
cc 9
eloc 34
c 6
b 0
f 0
nc 39
nop 9
dl 0
loc 77
rs 8.0555

How to fix   Long Method    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace Padosoft\SuperCacheInvalidate\Helpers;
4
5
use Illuminate\Support\Facades\DB;
6
use Illuminate\Support\Facades\Log;
7
use Illuminate\Support\Facades\Redis;
8
use Illuminate\Support\Carbon;
9
10
class SuperCacheInvalidationHelper
11
{
12
    /**
13
     * Insert a cache invalidation event into the database.
14
     *
15
     * @param string      $type            'key' or 'tag'
16
     * @param string      $identifier      The cache key or tag to invalidate
17
     * @param string|null $connection_name The Redis Connection name (optional, 'default')
18
     * @param string|null $reason          Reason for invalidation (optional)
19
     * @param int|null    $totalShards     Total number of shards (from config if null)
20
     * @param int|null    $priority        Priority of the event
21
     * @param int|null    $processed       Processed = 0 o 1
22
     * @param Carbon|null $event_time      Orario vero in cui si è richiesta l'invalidazione
23
     */
24
    public function insertInvalidationEvent(
25
        string $type,
26
        string $identifier,
27
        ?string $connection_name = null,
28
        ?string $reason = null,
29
        ?int $totalShards = 0,
30
        ?int $priority = 0,
31
        ?int $processed = 0,
32
        ?Carbon $event_time = null,
33
        /* ?array $associatedIdentifiers = [], */
34
        ?int $shard = -1,
35
    ): void {
36
        if ($shard < 0) {
37
            $shard = crc32($identifier) % ($totalShards > 0 ? $totalShards : config('super_cache_invalidate.total_shards', 10));
38
        }
39
        $redisConnectionName = $connection_name ?? config('super_cache_invalidate.default_connection_name');
40
        $data = [
41
            'type' => $type,
42
            'identifier' => $identifier,
43
            'connection_name' => $redisConnectionName,
44
            'reason' => $reason,
45
            'priority' => $priority,
46
            'event_time' => $event_time ?? now(),
47
            'processed' => $processed, // ATTENZIONE, poichè abbiamo solo 2 priorità, nel caso di priorità 1 verrà passato 1 perchè l'invalidazione la fa il progetto
48
            'shard' => $shard,
49
        ];
50
51
        $maxAttempts = 5;
52
        $attempts = 0;
53
        $insertOk = false;
54
55
        while ($attempts < $maxAttempts && !$insertOk) {
56
            // DB::beginTransaction();
57
58
            try {
59
                // Cerca di bloccare il record per l'inserimento
60
                switch ($processed) {
61
                    case 0:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $processed of type integer|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
62
                        $partitionCache_invalidation_events = $this->getCacheInvalidationEventsUnprocessedPartitionName($shard, $priority);
0 ignored issues
show
Bug introduced by
It seems like $priority can also be of type null; however, parameter $priorityId of Padosoft\SuperCacheInval...rocessedPartitionName() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

62
                        $partitionCache_invalidation_events = $this->getCacheInvalidationEventsUnprocessedPartitionName($shard, /** @scrutinizer ignore-type */ $priority);
Loading history...
63
                        break;
64
                    case 1:
65
                        $partitionCache_invalidation_events = $this->getCacheInvalidationEventsProcessedPartitionName($shard, $priority, $event_time ?? now());
0 ignored issues
show
Bug introduced by
It seems like $priority can also be of type null; however, parameter $priorityId of Padosoft\SuperCacheInval...rocessedPartitionName() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

65
                        $partitionCache_invalidation_events = $this->getCacheInvalidationEventsProcessedPartitionName($shard, /** @scrutinizer ignore-type */ $priority, $event_time ?? now());
Loading history...
66
                        break;
67
                    default:
68
                        $attempts = 5; // Mi fermo
69
                        throw new \RuntimeException('Invalid value for processed');
70
                }
71
                // $eventId = DB::table(DB::raw("`cache_invalidation_events` PARTITION ({$partitionCache_invalidation_events})"))->insertGetId($data);
72
                DB::table(DB::raw("`cache_invalidation_events` PARTITION ({$partitionCache_invalidation_events})"))->insert($data);
73
                // Insert associated identifiers
74
                // TODO JB 31/12/2024: per adesso commentato, da riattivare quando tutto funziona alla perfezione usando la partizione,
75
                // anche perchè la chiave primaria è data da id e partiton_key, per cui va capito cosa restituisce insertGetId e se è bloccante
76
                /*
77
                if (!empty($associatedIdentifiers)) {
78
                    $associations = [];
79
                    foreach ($associatedIdentifiers as $associated) {
80
                        $associations[] = [
81
                            'event_id' => $eventId,
82
                            'associated_type' => $associated['type'], // 'key' or 'tag'
83
                            'associated_identifier' => $associated['identifier'],
84
                            'connection_name' => $associated['connection_name'],
85
                            'created_at' => now(),
86
                        ];
87
                    }
88
                    DB::table('cache_invalidation_event_associations')->insert($associations);
89
                }
90
                */
91
                $insertOk = true;
92
                // DB::commit(); // Completa la transazione
93
            } catch (\Throwable $e) {
94
                // DB::rollBack(); // Annulla la transazione in caso di errore
95
                $attempts++;
96
                Log::error("SuperCacheInvalidate: impossibile eseguire insert, tentativo $attempts di $maxAttempts: " . $e->getMessage());
97
                // Logica per gestire i tentativi falliti
98
                if ($attempts >= $maxAttempts) {
99
                    // Salta il record dopo il numero massimo di tentativi
100
                    Log::error("SuperCacheInvalidate: impossibile eseguire insert dopo $maxAttempts tentativi: " . $e->getMessage());
101
                }
102
            }
103
        }
104
    }
105
106
    /**
107
     * Acquire a lock for processing a shard.
108
     *
109
     * @param  int          $shardId         The shard number
110
     * @param  int          $lockTimeout     Lock timeout in seconds
111
     * @param  string       $connection_name The Redis Connection name
112
     * @return string|false The lock value if acquired, false otherwise
113
     */
114
    public function acquireShardLock(int $shardId, int $priority, int $lockTimeout, string $connection_name): bool|string
115
    {
116
        $lockKey = 'shard_lock:' . $shardId . '_' . $priority;
117
        // Il metodo has/exists occupa troppa memoria!!!
118
        $retrieveValue = Redis::connection($connection_name)->get($lockKey);
119
        if ($retrieveValue !== null) {
120
            // Lock già attivo
121
            return false;
122
        }
123
        $lockValue = uniqid('', true);
124
        $isLocked = Redis::connection($connection_name)->set($lockKey, $lockValue);
125
126
        if ($lockTimeout > 0) {
127
            Redis::connection($connection_name)->expire($lockKey, $lockTimeout);
128
        }
129
130
        return $isLocked ? $lockValue : false;
131
    }
132
133
    /**
134
     * Release the lock for a shard.
135
     *
136
     * @param int    $shardId         The shard number
137
     * @param string $lockValue       The lock value to validate ownership
138
     * @param string $connection_name The Redis Connection name
139
     */
140
    public function releaseShardLock(int $shardId, int $priority, string $lockValue, string $connection_name): void
141
    {
142
        $lockKey = 'shard_lock:' . $shardId . '_' . $priority;
143
        $currentValue = Redis::connection($connection_name)->get($lockKey);
144
        if ($currentValue === $lockValue) {
145
            Redis::connection($connection_name)->del($lockKey);
146
        }
147
    }
148
149
    public function getCacheInvalidationEventsUnprocessedPartitionName(int $shardId, int $priorityId): string
150
    {
151
        $partitionValue = ($priorityId * 10) + $shardId;
152
153
        return "p_unprocessed_{$partitionValue}";
154
    }
155
156
    public function getCacheInvalidationEventsProcessedPartitionName(int $shardId, int $priorityId, Carbon $event_time): string
157
    {
158
        $partitionValue = ($event_time->year * 10000) + ($event_time->weekOfYear * 100) + ($priorityId * 10) + $shardId;
159
160
        return "p_processed_{$partitionValue}";
161
    }
162
}
163