Passed
Push — main ( e10b1f...c5069f )
by
unknown
05:06 queued 01:27
created

insertInvalidationEvent()   B

Complexity

Conditions 8
Paths 26

Size

Total Lines 74
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
cc 8
eloc 33
c 5
b 0
f 0
nc 26
nop 8
dl 0
loc 74
rs 8.1475

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
    ): void {
35
        $shard = crc32($identifier) % ($totalShards > 0 ? $totalShards : config('super_cache_invalidate.total_shards', 10));
36
        $redisConnectionName = $connection_name ?? config('super_cache_invalidate.default_connection_name');
37
        $data = [
38
            'type' => $type,
39
            'identifier' => $identifier,
40
            'connection_name' => $redisConnectionName,
41
            'reason' => $reason,
42
            'priority' => $priority,
43
            'event_time' => $event_time ?? now(),
44
            'processed' => $processed, // ATTENZIONE, poichè abbiamo solo 2 priorità, nel caso di priorità 1 verrà passato 1 perchè l'invalidazione la fa il progetto
45
            'shard' => $shard,
46
        ];
47
48
        $maxAttempts = 5;
49
        $attempts = 0;
50
        $insertOk = false;
51
52
        while ($attempts < $maxAttempts && !$insertOk) {
53
            //DB::beginTransaction();
54
55
            try {
56
                // Cerca di bloccare il record per l'inserimento
57
                switch ($processed) {
58
                    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...
59
                        $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

59
                        $partitionCache_invalidation_events = $this->getCacheInvalidationEventsUnprocessedPartitionName($shard, /** @scrutinizer ignore-type */ $priority);
Loading history...
60
                        break;
61
                    case 1:
62
                        $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

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