Completed
Push — master ( 35bf32...5024d1 )
by Karel
12s
created

AliasProcessor::deleteIndex()   A

Complexity

Conditions 2
Paths 3

Size

Total Lines 13

Duplication

Lines 13
Ratio 100 %

Code Coverage

Tests 4
CRAP Score 2.686

Importance

Changes 0
Metric Value
dl 13
loc 13
ccs 4
cts 9
cp 0.4444
rs 9.8333
c 0
b 0
f 0
cc 2
nc 3
nop 2
crap 2.686
1
<?php
2
3
/*
4
 * This file is part of the FOSElasticaBundle package.
5
 *
6
 * (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace FOS\ElasticaBundle\Index;
13
14
use Elastica\Client;
15
use Elastica\Exception\ExceptionInterface;
16
use Elastica\Request;
17
use FOS\ElasticaBundle\Configuration\IndexConfig;
18
use FOS\ElasticaBundle\Elastica\Index;
19
use FOS\ElasticaBundle\Exception\AliasIsIndexException;
20
21
class AliasProcessor
22
{
23
    /**
24
     * Sets the randomised root name for an index.
25
     *
26
     * @param IndexConfig $indexConfig
27
     * @param Index       $index
28
     */
29 2
    public function setRootName(IndexConfig $indexConfig, Index $index)
30
    {
31 2
        $index->overrideName(
32 2
            sprintf('%s_%s',
33 2
                $indexConfig->getElasticSearchName(),
34 2
                date('Y-m-d-His')
35
            )
36
        );
37 2
    }
38
39
    /**
40
     * Switches an index to become the new target for an alias. Only applies for
41
     * indexes that are set to use aliases.
42
     *
43
     * $force will delete an index encountered where an alias is expected.
44
     *
45
     * @param IndexConfig $indexConfig
46
     * @param Index       $index
47
     * @param bool        $force
48
     * @param bool        $delete
49
     *
50
     * @throws AliasIsIndexException
51
     */
52 7
    public function switchIndexAlias(IndexConfig $indexConfig, Index $index, $force = false, $delete = true)
53
    {
54 7
        $client = $index->getClient();
55
56 7
        $aliasName = $indexConfig->getElasticSearchName();
57 7
        $oldIndexName = null;
58 7
        $newIndexName = $index->getName();
59
60
        try {
61 7
            $oldIndexName = $this->getAliasedIndex($client, $aliasName);
62 3
        } catch (AliasIsIndexException $e) {
63 2
            if (!$force) {
64 1
                throw $e;
65
            }
66
67 1
            if ($delete) {
68 1
                $this->deleteIndex($client, $aliasName);
69
            } else {
70
                $this->closeIndex($client, $aliasName);
71
            }
72
        }
73
74
        try {
75 5
            $aliasUpdateRequest = $this->buildAliasUpdateRequest($oldIndexName, $aliasName, $newIndexName);
76 5
            $client->request('_aliases', 'POST', $aliasUpdateRequest);
77 1
        } catch (ExceptionInterface $e) {
78 1
            $this->cleanupRenameFailure($client, $newIndexName, $e);
79
        }
80
81
        // Delete the old index after the alias has been switched
82 4
        if (null !== $oldIndexName) {
83 2
            if ($delete) {
84 2
                $this->deleteIndex($client, $oldIndexName);
85
            } else {
86
                $this->closeIndex($client, $oldIndexName);
87
            }
88
        }
89 4
    }
90
91
    /**
92
     * Builds an ElasticSearch request to rename or create an alias.
93
     *
94
     * @param string|null $aliasedIndex
95
     * @param string      $aliasName
96
     * @param string      $newIndexName
97
     *
98
     * @return array
99
     */
100 5
    private function buildAliasUpdateRequest($aliasedIndex, $aliasName, $newIndexName)
101
    {
102 5
        $aliasUpdateRequest = ['actions' => []];
103 5
        if (null !== $aliasedIndex) {
104
            // if the alias is set - add an action to remove it
105 3
            $aliasUpdateRequest['actions'][] = [
106 3
                'remove' => ['index' => $aliasedIndex, 'alias' => $aliasName],
107
            ];
108
        }
109
110
        // add an action to point the alias to the new index
111 5
        $aliasUpdateRequest['actions'][] = [
112 5
            'add' => ['index' => $newIndexName, 'alias' => $aliasName],
113
        ];
114
115 5
        return $aliasUpdateRequest;
116
    }
117
118
    /**
119
     * Cleans up an index when we encounter a failure to rename the alias.
120
     *
121
     * @param Client     $client
122
     * @param string     $indexName
123
     * @param \Exception $renameAliasException
124
     */
125 1
    private function cleanupRenameFailure(Client $client, $indexName, \Exception $renameAliasException)
126
    {
127 1
        $additionalError = '';
128
        try {
129 1
            $this->deleteIndex($client, $indexName);
130
        } catch (ExceptionInterface $deleteNewIndexException) {
131
            $additionalError = sprintf(
132
                'Tried to delete newly built index %s, but also failed: %s',
133
                $indexName,
134
                $deleteNewIndexException->getMessage()
135
            );
136
        }
137
138 1
        throw new \RuntimeException(sprintf(
139 1
            'Failed to updated index alias: %s. %s',
140 1
            $renameAliasException->getMessage(),
141 1
            $additionalError ?: sprintf('Newly built index %s was deleted', $indexName)
142 1
        ), 0, $renameAliasException);
143
    }
144
145
    /**
146
     * Delete an index.
147
     *
148
     * @param Client $client
149
     * @param string $indexName Index name to delete
150
     */
151 4 View Code Duplication
    private function deleteIndex(Client $client, $indexName)
152
    {
153
        try {
154 4
            $path = sprintf('%s', $indexName);
155 4
            $client->request($path, Request::DELETE);
156
        } catch (ExceptionInterface $deleteOldIndexException) {
157
            throw new \RuntimeException(sprintf(
158
                'Failed to delete index %s with message: %s',
159
                $indexName,
160
                $deleteOldIndexException->getMessage()
161
            ), 0, $deleteOldIndexException);
162
        }
163 4
    }
164
165
    /**
166
     * Close an index.
167
     *
168
     * @param Client $client
169
     * @param string $indexName
170
     */
171 View Code Duplication
    private function closeIndex(Client $client, $indexName)
172
    {
173
        try {
174
            $path = sprintf('%s/_close', $indexName);
175
            $client->request($path, Request::POST);
176
        } catch (ExceptionInterface $e) {
177
            throw new \RuntimeException(
178
                sprintf(
179
                    'Failed to close index %s with message: %s',
180
                    $indexName,
181
                    $e->getMessage()
182
                ),
183
                0,
184
                $e
185
            );
186
        }
187
    }
188
189
    /**
190
     * Returns the name of a single index that an alias points to or throws
191
     * an exception if there is more than one.
192
     *
193
     * @param Client $client
194
     * @param string $aliasName Alias name
195
     *
196
     * @return string|null
197
     *
198
     * @throws AliasIsIndexException
199
     */
200 7
    private function getAliasedIndex(Client $client, $aliasName)
201
    {
202 7
        $aliasesInfo = $client->request('_aliases', 'GET')->getData();
203 7
        $aliasedIndexes = [];
204
205 7
        foreach ($aliasesInfo as $indexName => $indexInfo) {
206 6
            if ($indexName === $aliasName) {
207 2
                throw new AliasIsIndexException($indexName);
208
            }
209 4
            if (!isset($indexInfo['aliases'])) {
210
                continue;
211
            }
212
213 4
            $aliases = array_keys($indexInfo['aliases']);
214 4
            if (in_array($aliasName, $aliases)) {
215 4
                $aliasedIndexes[] = $indexName;
216
            }
217
        }
218
219 5
        if (count($aliasedIndexes) > 1) {
220 1
            throw new \RuntimeException(sprintf(
221 1
                'Alias %s is used for multiple indexes: [%s]. Make sure it\'s'.
222 1
                'either not used or is assigned to one index only',
223
                $aliasName,
224 1
                implode(', ', $aliasedIndexes)
225
            ));
226
        }
227
228 4
        return array_shift($aliasedIndexes);
229
    }
230
}
231