39
39
*/
40
40
class ReplicaManager implements ReplicaManagerInterface
41
41
{
42
+ public const ALGOLIA_SETTINGS_KEY_REPLICAS = 'replicas ' ;
43
+
42
44
protected const _DEBUG = true ;
43
45
protected array $ _algoliaReplicaConfig = [];
44
46
protected array $ _magentoReplicaPossibleConfig = [];
@@ -94,7 +96,7 @@ protected function getReplicaConfigurationFromAlgolia($primaryIndexName, bool $r
94
96
try {
95
97
$ currentSettings = $ this ->algoliaHelper ->getSettings ($ primaryIndexName );
96
98
$ this ->_algoliaReplicaConfig [$ primaryIndexName ] = array_key_exists ('replicas ' , $ currentSettings )
97
- ? $ currentSettings [' replicas ' ]
99
+ ? $ currentSettings [self :: ALGOLIA_SETTINGS_KEY_REPLICAS ]
98
100
: [];
99
101
} catch (\Exception $ e ) {
100
102
$ msg = "Unable to retrieve replica settings for $ primaryIndexName: " . $ e ->getMessage ();
@@ -119,7 +121,7 @@ protected function clearAlgoliaReplicaSettingCache($primaryIndexName = null): vo
119
121
* relevant to the Magento integration
120
122
*
121
123
* @param string $primaryIndexName
122
- * @return string[]
124
+ * @return string[] Array of replica index names
123
125
* @throws LocalizedException
124
126
*/
125
127
protected function getMagentoReplicaConfigurationFromAlgolia (string $ primaryIndexName ): array
@@ -130,21 +132,37 @@ protected function getMagentoReplicaConfigurationFromAlgolia(string $primaryInde
130
132
}
131
133
132
134
/**
133
- * Replicas will be considered Magento managed if they are prefixed with the primary index name
135
+ * Filter out non Magento managed replicas
134
136
* @param string $baseIndexName
135
137
* @param string[] $algoliaReplicas
136
138
* @return string[]
139
+ * @throws NoSuchEntityException
137
140
*/
138
141
protected function getMagentoReplicaSettings (string $ baseIndexName , array $ algoliaReplicas ): array
139
142
{
140
143
return array_filter (
141
144
$ algoliaReplicas ,
142
145
function ($ algoliaReplicaSetting ) use ($ baseIndexName ) {
143
- return str_starts_with ($ this ->getBareIndexNameFromReplicaSetting ($ algoliaReplicaSetting ), $ baseIndexName );
146
+ return $ this -> isMagentoReplicaIndex ($ this ->getBareIndexNameFromReplicaSetting ($ algoliaReplicaSetting ), $ baseIndexName );
144
147
}
145
148
);
146
149
}
147
150
151
+ /**
152
+ * Perform logic to determine if this is a Magento managed replica index
153
+ * (By default replicas will be considered Magento managed if they are prefixed with the primary index name)
154
+ *
155
+ * @param string $replicaIndexName
156
+ * @param int|string $storeIdOrIndex
157
+ * @return bool
158
+ * @throws NoSuchEntityException
159
+ */
160
+ protected function isMagentoReplicaIndex (string $ replicaIndexName , int |string $ storeIdOrIndex ): bool
161
+ {
162
+ $ primaryIndexName = is_string ($ storeIdOrIndex ) ? $ storeIdOrIndex : $ this ->indexNameFetcher ->getProductIndexName ($ storeIdOrIndex );
163
+ return str_starts_with ($ replicaIndexName , $ primaryIndexName );
164
+ }
165
+
148
166
/**
149
167
* @param string $primaryIndexName
150
168
* @return array
@@ -160,12 +178,15 @@ protected function getNonMagentoReplicaConfigurationFromAlgolia(string $primaryI
160
178
/**
161
179
* In order to avoid interfering with replicas configured directly in the Algolia dashboard,
162
180
* we must know which replica indices are Magento managed and which are not.
181
+ * This method seeks to determine this based on Magento before/after state on a sorting config change
182
+ * The downside here is that it will not work if Magento and Algolia get out of sync
163
183
*
164
184
* @param int $storeId
165
185
* @param bool $refreshCache
166
186
* @return array
167
187
* @throws LocalizedException
168
188
* @throws NoSuchEntityException
189
+ * @deprecated This method has been supplanted by the much simpler getMagentoReplicaSettings() method
169
190
*/
170
191
protected function getMagentoReplicaSettingsFromConfig (int $ storeId , bool $ refreshCache = false ): array
171
192
{
@@ -220,12 +241,12 @@ protected function setReplicasOnPrimaryIndex(int $storeId): array
220
241
221
242
$ this ->algoliaHelper ->setSettings (
222
243
$ indexName ,
223
- [' replicas ' => array_merge ($ newMagentoReplicasSetting , $ nonMagentoReplicasSetting )]
244
+ [self :: ALGOLIA_SETTINGS_KEY_REPLICAS => array_merge ($ newMagentoReplicasSetting , $ nonMagentoReplicasSetting )]
224
245
);
225
246
$ setReplicasTaskId = $ this ->algoliaHelper ->getLastTaskId ();
226
247
$ this ->algoliaHelper ->waitLastTask ($ indexName , $ setReplicasTaskId );
227
248
$ this ->clearAlgoliaReplicaSettingCache ($ indexName );
228
- $ this ->deleteReplicas ($ replicasToDelete );
249
+ $ this ->deleteReplicaIndices ($ replicasToDelete );
229
250
230
251
if (self ::_DEBUG ) {
231
252
$ this ->logger ->log (
@@ -317,7 +338,7 @@ protected function getBareIndexNameFromReplicaSetting(string $replicaSetting): s
317
338
* @return void
318
339
* @throws AlgoliaException
319
340
*/
320
- protected function deleteReplicas (array $ replicasToDelete ): void
341
+ protected function deleteReplicaIndices (array $ replicasToDelete ): void
321
342
{
322
343
foreach ($ replicasToDelete as $ deletedReplica ) {
323
344
$ this ->algoliaHelper ->deleteIndex ($ deletedReplica );
@@ -381,4 +402,79 @@ public function getMaxVirtualReplicasPerIndex() : int
381
402
{
382
403
return self ::MAX_VIRTUAL_REPLICA_LIMIT ;
383
404
}
405
+
406
+ /**
407
+ * @param string $indexName
408
+ * @param array $replicas
409
+ * @param int $setReplicasTaskId
410
+ * @return void
411
+ * @throws AlgoliaException
412
+ * @throws ExceededRetriesException
413
+ */
414
+ protected function deleteUnusedReplicas (string $ indexName , array $ replicas , int $ setReplicasTaskId ): void
415
+ {
416
+ $ indicesToDelete = [];
417
+
418
+ $ allIndices = $ this ->algoliaHelper ->listIndexes ();
419
+ foreach ($ allIndices ['items ' ] as $ indexInfo ) {
420
+ //skip any indices that don't match the primary index
421
+ if (mb_strpos ($ indexInfo ['name ' ], $ indexName ) !== 0 || $ indexInfo ['name ' ] === $ indexName ) {
422
+ continue ;
423
+ }
424
+
425
+ // skip temp indices and expected replicas
426
+ if (mb_strpos ($ indexInfo ['name ' ], IndexNameFetcher::INDEX_TEMP_SUFFIX ) === false && in_array ($ indexInfo ['name ' ], $ replicas ) === false ) {
427
+ $ indicesToDelete [] = $ indexInfo ['name ' ];
428
+ }
429
+ }
430
+
431
+ if (count ($ indicesToDelete ) > 0 ) {
432
+ $ this ->algoliaHelper ->waitLastTask ($ indexName , $ setReplicasTaskId );
433
+
434
+ foreach ($ indicesToDelete as $ indexToDelete ) {
435
+ $ this ->algoliaHelper ->deleteIndex ($ indexToDelete );
436
+ }
437
+ }
438
+ }
439
+
440
+ /**
441
+ * @throws AlgoliaException
442
+ */
443
+ protected function clearReplicasSettingInAlgolia (string $ primaryIndexName ): void
444
+ {
445
+ $ this ->algoliaHelper ->setSettings ($ primaryIndexName , [ self ::ALGOLIA_SETTINGS_KEY_REPLICAS => []]);
446
+ }
447
+
448
+ /**
449
+ * @throws AlgoliaException
450
+ * @throws NoSuchEntityException
451
+ * @throws LocalizedException
452
+ */
453
+ public function deleteReplicasFromAlgolia (int $ storeId , bool $ unusedOnly = false ): void
454
+ {
455
+ $ primaryIndexName = $ this ->indexNameFetcher ->getProductIndexName ($ storeId );
456
+
457
+ // get all possible Magento managed product indices for this store
458
+ $ allIndices = $ this ->algoliaHelper ->listIndexes ();
459
+
460
+ $ currentReplicas = $ this ->getBareIndexNamesFromReplicaSetting ($ this ->getMagentoReplicaConfigurationFromAlgolia ($ primaryIndexName ));
461
+
462
+ if (!$ unusedOnly ) {
463
+ $ this ->clearReplicasSettingInAlgolia ($ primaryIndexName );
464
+ }
465
+
466
+ $ replicasToDelete = [];
467
+
468
+ foreach ($ allIndices ['items ' ] as $ indexInfo ) {
469
+ $ indexName = $ indexInfo ['name ' ];
470
+ if ($ this ->isMagentoReplicaIndex ($ indexName , $ primaryIndexName )
471
+ && !$ this ->indexNameFetcher ->isTempIndex ($ indexName )
472
+ && (!$ unusedOnly || !array_search ($ indexName , $ currentReplicas ))
473
+ ) {
474
+ $ replicasToDelete [] = $ indexName ;
475
+ }
476
+ }
477
+
478
+ $ this ->deleteReplicaIndices ($ replicasToDelete );
479
+ }
384
480
}
0 commit comments