|
6 | 6 | */ |
7 | 7 | package org.elasticsearch.xpack.enrich; |
8 | 8 |
|
| 9 | +import org.elasticsearch.action.ActionListener; |
9 | 10 | import org.elasticsearch.action.search.SearchRequest; |
| 11 | +import org.elasticsearch.action.search.SearchResponse; |
10 | 12 | import org.elasticsearch.cluster.metadata.AliasMetadata; |
11 | 13 | import org.elasticsearch.cluster.metadata.IndexMetadata; |
12 | 14 | import org.elasticsearch.cluster.metadata.Metadata; |
| 15 | +import org.elasticsearch.common.bytes.BytesReference; |
13 | 16 | import org.elasticsearch.index.IndexNotFoundException; |
14 | 17 | import org.elasticsearch.index.IndexVersion; |
15 | 18 | import org.elasticsearch.index.query.MatchQueryBuilder; |
| 19 | +import org.elasticsearch.search.SearchHit; |
| 20 | +import org.elasticsearch.search.SearchHits; |
16 | 21 | import org.elasticsearch.search.builder.SearchSourceBuilder; |
17 | 22 | import org.elasticsearch.test.ESTestCase; |
| 23 | +import org.elasticsearch.xcontent.XContentBuilder; |
| 24 | +import org.elasticsearch.xcontent.json.JsonXContent; |
18 | 25 | import org.elasticsearch.xpack.core.enrich.EnrichPolicy; |
19 | 26 |
|
| 27 | +import java.io.IOException; |
20 | 28 | import java.util.ArrayList; |
21 | 29 | import java.util.HashMap; |
22 | 30 | import java.util.List; |
23 | 31 | import java.util.Map; |
| 32 | +import java.util.concurrent.CountDownLatch; |
| 33 | +import java.util.concurrent.TimeUnit; |
24 | 34 |
|
25 | 35 | import static org.hamcrest.Matchers.containsString; |
26 | 36 | import static org.hamcrest.Matchers.equalTo; |
@@ -138,6 +148,114 @@ public void testCaching() { |
138 | 148 | assertThat(cacheStats.getEvictions(), equalTo(4L)); |
139 | 149 | } |
140 | 150 |
|
| 151 | + public void testPutIfAbsent() throws InterruptedException { |
| 152 | + // Emulate cluster metadata: |
| 153 | + // (two enrich indices with corresponding alias entries) |
| 154 | + var metadata = Metadata.builder() |
| 155 | + .put( |
| 156 | + IndexMetadata.builder(EnrichPolicy.getBaseName("policy1") + "-1") |
| 157 | + .settings(settings(IndexVersion.current())) |
| 158 | + .numberOfShards(1) |
| 159 | + .numberOfReplicas(0) |
| 160 | + .putAlias(AliasMetadata.builder(EnrichPolicy.getBaseName("policy1")).build()) |
| 161 | + ) |
| 162 | + .put( |
| 163 | + IndexMetadata.builder(EnrichPolicy.getBaseName("policy2") + "-1") |
| 164 | + .settings(settings(IndexVersion.current())) |
| 165 | + .numberOfShards(1) |
| 166 | + .numberOfReplicas(0) |
| 167 | + .putAlias(AliasMetadata.builder(EnrichPolicy.getBaseName("policy2")).build()) |
| 168 | + ) |
| 169 | + .build(); |
| 170 | + |
| 171 | + // Emulated search requests that an enrich processor could generate: |
| 172 | + // (two unique searches for two enrich policies) |
| 173 | + var searchRequest1 = new SearchRequest(EnrichPolicy.getBaseName("policy1")).source( |
| 174 | + new SearchSourceBuilder().query(new MatchQueryBuilder("match_field", "1")) |
| 175 | + ); |
| 176 | + final List<Map<String, ?>> searchResponseMap = List.of( |
| 177 | + Map.of("key1", "value1", "key2", "value2"), |
| 178 | + Map.of("key3", "value3", "key4", "value4") |
| 179 | + ); |
| 180 | + EnrichCache enrichCache = new EnrichCache(3); |
| 181 | + enrichCache.setMetadata(metadata); |
| 182 | + |
| 183 | + { |
| 184 | + CountDownLatch queriedDatabaseLatch = new CountDownLatch(1); |
| 185 | + CountDownLatch notifiedOfResultLatch = new CountDownLatch(1); |
| 186 | + enrichCache.computeIfAbsent(searchRequest1, (searchRequest, searchResponseActionListener) -> { |
| 187 | + SearchResponse searchResponse = convertToSearchResponse(searchResponseMap); |
| 188 | + searchResponseActionListener.onResponse(searchResponse); |
| 189 | + searchResponse.decRef(); |
| 190 | + queriedDatabaseLatch.countDown(); |
| 191 | + }, new ActionListener<>() { |
| 192 | + @Override |
| 193 | + public void onResponse(List<Map<?, ?>> response) { |
| 194 | + assertThat(response, equalTo(searchResponseMap)); |
| 195 | + notifiedOfResultLatch.countDown(); |
| 196 | + } |
| 197 | + |
| 198 | + @Override |
| 199 | + public void onFailure(Exception e) { |
| 200 | + fail(e); |
| 201 | + } |
| 202 | + }); |
| 203 | + assertThat(queriedDatabaseLatch.await(5, TimeUnit.SECONDS), equalTo(true)); |
| 204 | + assertThat(notifiedOfResultLatch.await(5, TimeUnit.SECONDS), equalTo(true)); |
| 205 | + } |
| 206 | + |
| 207 | + { |
| 208 | + CountDownLatch notifiedOfResultLatch = new CountDownLatch(1); |
| 209 | + enrichCache.computeIfAbsent(searchRequest1, (searchRequest, searchResponseActionListener) -> { |
| 210 | + fail("Expected no call to the database because item should have been in the cache"); |
| 211 | + }, new ActionListener<>() { |
| 212 | + @Override |
| 213 | + public void onResponse(List<Map<?, ?>> maps) { |
| 214 | + notifiedOfResultLatch.countDown(); |
| 215 | + } |
| 216 | + |
| 217 | + @Override |
| 218 | + public void onFailure(Exception e) { |
| 219 | + fail(e); |
| 220 | + } |
| 221 | + }); |
| 222 | + assertThat(notifiedOfResultLatch.await(5, TimeUnit.SECONDS), equalTo(true)); |
| 223 | + } |
| 224 | + } |
| 225 | + |
| 226 | + private SearchResponse convertToSearchResponse(List<Map<String, ?>> searchResponseList) { |
| 227 | + SearchHit[] hitArray = searchResponseList.stream().map(map -> { |
| 228 | + try { |
| 229 | + return SearchHit.unpooled(0, "id").sourceRef(convertMapToJson(map)); |
| 230 | + } catch (IOException e) { |
| 231 | + throw new RuntimeException(e); |
| 232 | + } |
| 233 | + }).toArray(SearchHit[]::new); |
| 234 | + SearchHits hits = SearchHits.unpooled(hitArray, null, 0); |
| 235 | + return new SearchResponse( |
| 236 | + hits, |
| 237 | + null, |
| 238 | + null, |
| 239 | + false, |
| 240 | + false, |
| 241 | + null, |
| 242 | + 1, |
| 243 | + null, |
| 244 | + 5, |
| 245 | + 4, |
| 246 | + 0, |
| 247 | + randomLong(), |
| 248 | + null, |
| 249 | + SearchResponse.Clusters.EMPTY |
| 250 | + ); |
| 251 | + } |
| 252 | + |
| 253 | + private BytesReference convertMapToJson(Map<String, ?> simpleMap) throws IOException { |
| 254 | + try (XContentBuilder builder = JsonXContent.contentBuilder().map(simpleMap)) { |
| 255 | + return BytesReference.bytes(builder); |
| 256 | + } |
| 257 | + } |
| 258 | + |
141 | 259 | public void testDeepCopy() { |
142 | 260 | Map<String, Object> original = new HashMap<>(); |
143 | 261 | { |
|
0 commit comments