Passed
Pull Request — main (#3)
by
unknown
03:02
created

insertInvalidationEvent()   B

Complexity

Conditions 6
Paths 16

Size

Total Lines 63
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 6
eloc 27
c 3
b 0
f 0
nc 16
nop 7
dl 0
loc 63
rs 8.8657

How to fix   Long Method   

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:

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
9
class SuperCacheInvalidationHelper
10
{
11
    /**
12
     * Insert a cache invalidation event into the database.
13
     *
14
     * @param string      $type                  'key' or 'tag'
15
     * @param string      $identifier            The cache key or tag to invalidate
16
     * @param string|null $connection_name       The Redis Connection name (optional, 'default')
17
     * @param string|null $reason                Reason for invalidation (optional)
18
     * @param int|null    $totalShards           Total number of shards (from config if null)
19
     * @param int|null    $priority              Priority of the event
20
     * @param array|null  $associatedIdentifiers Optional array of associated tags or keys
21
     */
22
    public function insertInvalidationEvent(
23
        string $type,
24
        string $identifier,
25
        ?string $connection_name = null,
26
        ?string $reason = null,
27
        ?int $totalShards = 0,
28
        ?int $priority = 0,
29
        ?array $associatedIdentifiers = [],
0 ignored issues
show
Unused Code introduced by
The parameter $associatedIdentifiers is not used and could be removed. ( Ignorable by Annotation )

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

29
        /** @scrutinizer ignore-unused */ ?array $associatedIdentifiers = [],

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
30
    ): void {
31
        $shard = crc32($identifier) % ($totalShards > 0 ? $totalShards : config('super_cache_invalidate.total_shards', 10));
32
33
        $redisConnectionName = $connection_name ?? config('super_cache_invalidate.default_connection_name');
34
        $data = [
35
            'type' => $type,
36
            'identifier' => $identifier,
37
            'connection_name' => $redisConnectionName,
38
            'reason' => $reason,
39
            'priority' => $priority,
40
            'event_time' => now(),
41
            'processed' => 0,
42
            'shard' => $shard,
43
        ];
44
45
        $maxAttempts = 5;
46
        $attempts = 0;
47
        $insertOk = false;
48
49
        while ($attempts < $maxAttempts && !$insertOk) {
50
            DB::beginTransaction();
51
52
            try {
53
                // Cerca di bloccare il record per l'inserimento
54
                $partitionCache_invalidation_events = $this->getCacheInvalidationEventsPartitionName($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...onEventsPartitionName() 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

54
                $partitionCache_invalidation_events = $this->getCacheInvalidationEventsPartitionName($shard, /** @scrutinizer ignore-type */ $priority);
Loading history...
55
56
                $eventId = DB::table(DB::raw("`cache_invalidation_events` PARTITION ({$partitionCache_invalidation_events})"))->insertGetId($data);
0 ignored issues
show
Unused Code introduced by
The assignment to $eventId is dead and can be removed.
Loading history...
57
58
                // Insert associated identifiers
59
                // TODO JB 31/12/2024: per adesso commentato, da riattivare quando tutto funziona alla perfezione usando la partizione
60
                /*
61
                if (!empty($associatedIdentifiers)) {
62
                    $associations = [];
63
                    foreach ($associatedIdentifiers as $associated) {
64
                        $associations[] = [
65
                            'event_id' => $eventId,
66
                            'associated_type' => $associated['type'], // 'key' or 'tag'
67
                            'associated_identifier' => $associated['identifier'],
68
                            'connection_name' => $associated['connection_name'],
69
                            'created_at' => now(),
70
                        ];
71
                    }
72
                    DB::table('cache_invalidation_event_associations')->insert($associations);
73
                }
74
                */
75
                $insertOk = true;
76
                DB::commit(); // Completa la transazione
77
            } catch (\Throwable $e) {
78
                DB::rollBack(); // Annulla la transazione in caso di errore
79
                $attempts++;
80
                Log::error("SuperCacheInvalidate: impossibile eseguire insert, tentativo $attempts di $maxAttempts: " . $e->getMessage());
81
                // Logica per gestire i tentativi falliti
82
                if ($attempts >= $maxAttempts) {
83
                    // Salta il record dopo il numero massimo di tentativi
84
                    Log::error("SuperCacheInvalidate: impossibile eseguire insert dopo $maxAttempts tentativi: " . $e->getMessage());
85
                }
86
            }
87
        }
88
    }
89
90
    /**
91
     * Acquire a lock for processing a shard.
92
     *
93
     * @param  int          $shardId         The shard number
94
     * @param  int          $lockTimeout     Lock timeout in seconds
95
     * @param  string       $connection_name The Redis Connection name
96
     * @return string|false The lock value if acquired, false otherwise
97
     */
98
    public function acquireShardLock(int $shardId, int $priority, int $lockTimeout, string $connection_name): bool|string
99
    {
100
        $lockKey = 'shard_lock:' . $shardId . '_' . $priority;
101
        // Il metodo has/exists occupa troppa memoria!!!
102
        $retrieveValue = Redis::connection($connection_name)->get($lockKey);
103
        if ($retrieveValue !== null) {
104
            // Lock già attivo
105
            return false;
106
        }
107
        $lockValue = uniqid('', true);
108
        $isLocked = Redis::connection($connection_name)->set($lockKey, $lockValue);
109
110
        if ($lockTimeout > 0) {
111
            Redis::connection($connection_name)->expire($lockKey, $lockTimeout);
112
        }
113
114
        return $isLocked ? $lockValue : false;
115
    }
116
117
    /**
118
     * Release the lock for a shard.
119
     *
120
     * @param int    $shardId         The shard number
121
     * @param string $lockValue       The lock value to validate ownership
122
     * @param string $connection_name The Redis Connection name
123
     */
124
    public function releaseShardLock(int $shardId, int $priority, string $lockValue, string $connection_name): void
125
    {
126
        $lockKey = 'shard_lock:' . $shardId . '_' . $priority;
127
        $currentValue = Redis::connection($connection_name)->get($lockKey);
128
        if ($currentValue === $lockValue) {
129
            Redis::connection($connection_name)->del($lockKey);
130
        }
131
    }
132
133
    public function getCacheInvalidationEventsPartitionName(int $shardId, int $priorityId): string
134
    {
135
        // Calcola il valore della partizione
136
        $shards = config('super_cache_invalidate.total_shards', 10);
137
        $priorities = [0, 1];
138
139
        $partitionStatements = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $partitionStatements is dead and can be removed.
Loading history...
140
141
        $partitionValueId = ($priorityId * $shards) + $shardId + 1;
142
143
        // Partitions for unprocessed events
144
        foreach ($priorities as $priority) {
145
            for ($shard = 0; $shard < $shards; $shard++) {
146
                $partitionName = "p_unprocessed_s{$shard}_p{$priority}";
147
                $partitionValue = ($priority * $shards) + $shard + 1;
148
                if ($partitionValueId < $partitionValue) {
149
                    return $partitionName;
150
                }
151
            }
152
        }
153
154
        return '';
155
    }
156
}
157