Completed
Push — master ( 3cdfc2...4aa078 )
by Maksim
03:09
created

AliasProcessor::setRootName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

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