|
13 | 13 | import com.linkedin.common.urn.Urn;
|
14 | 14 | import com.linkedin.data.ByteString;
|
15 | 15 | import com.linkedin.metadata.aspect.EnvelopedAspect;
|
| 16 | +import com.linkedin.metadata.config.TimeseriesAspectServiceConfig; |
16 | 17 | import com.linkedin.metadata.models.AspectSpec;
|
17 | 18 | import com.linkedin.metadata.models.EntitySpec;
|
18 | 19 | import com.linkedin.metadata.models.annotation.SearchableAnnotation;
|
|
53 | 54 | import java.util.Collection;
|
54 | 55 | import java.util.List;
|
55 | 56 | import java.util.Map;
|
| 57 | +import java.util.Objects; |
56 | 58 | import java.util.Optional;
|
57 | 59 | import java.util.Set;
|
| 60 | +import java.util.concurrent.ArrayBlockingQueue; |
| 61 | +import java.util.concurrent.ExecutionException; |
| 62 | +import java.util.concurrent.ExecutorService; |
| 63 | +import java.util.concurrent.Future; |
| 64 | +import java.util.concurrent.ThreadPoolExecutor; |
| 65 | +import java.util.concurrent.TimeUnit; |
58 | 66 | import java.util.stream.Collectors;
|
59 | 67 | import javax.annotation.Nonnull;
|
60 | 68 | import javax.annotation.Nullable;
|
@@ -103,18 +111,29 @@ public class ElasticSearchTimeseriesAspectService
|
103 | 111 | private final RestHighLevelClient searchClient;
|
104 | 112 | private final ESAggregatedStatsDAO esAggregatedStatsDAO;
|
105 | 113 | private final QueryFilterRewriteChain queryFilterRewriteChain;
|
| 114 | + private final ExecutorService queryPool; |
106 | 115 |
|
107 | 116 | public ElasticSearchTimeseriesAspectService(
|
108 | 117 | @Nonnull RestHighLevelClient searchClient,
|
109 | 118 | @Nonnull TimeseriesAspectIndexBuilders indexBuilders,
|
110 | 119 | @Nonnull ESBulkProcessor bulkProcessor,
|
111 | 120 | int numRetries,
|
112 |
| - @Nonnull QueryFilterRewriteChain queryFilterRewriteChain) { |
| 121 | + @Nonnull QueryFilterRewriteChain queryFilterRewriteChain, |
| 122 | + @Nonnull TimeseriesAspectServiceConfig timeseriesAspectServiceConfig) { |
113 | 123 | this.indexBuilders = indexBuilders;
|
114 | 124 | this.searchClient = searchClient;
|
115 | 125 | this.bulkProcessor = bulkProcessor;
|
116 | 126 | this.numRetries = numRetries;
|
117 | 127 | this.queryFilterRewriteChain = queryFilterRewriteChain;
|
| 128 | + this.queryPool = |
| 129 | + new ThreadPoolExecutor( |
| 130 | + timeseriesAspectServiceConfig.getQuery().getConcurrency(), // core threads |
| 131 | + timeseriesAspectServiceConfig.getQuery().getConcurrency(), // max threads |
| 132 | + timeseriesAspectServiceConfig.getQuery().getKeepAlive(), |
| 133 | + TimeUnit.SECONDS, // thread keep-alive time |
| 134 | + new ArrayBlockingQueue<>( |
| 135 | + timeseriesAspectServiceConfig.getQuery().getQueueSize()), // fixed size queue |
| 136 | + new ThreadPoolExecutor.CallerRunsPolicy()); |
118 | 137 |
|
119 | 138 | esAggregatedStatsDAO = new ESAggregatedStatsDAO(searchClient, queryFilterRewriteChain);
|
120 | 139 | }
|
@@ -400,6 +419,69 @@ public List<EnvelopedAspect> getAspectValues(
|
400 | 419 | .collect(Collectors.toList());
|
401 | 420 | }
|
402 | 421 |
|
| 422 | + @Nonnull |
| 423 | + @Override |
| 424 | + public Map<Urn, Map<String, EnvelopedAspect>> getLatestTimeseriesAspectValues( |
| 425 | + @Nonnull OperationContext opContext, |
| 426 | + @Nonnull Set<Urn> urns, |
| 427 | + @Nonnull Set<String> aspectNames, |
| 428 | + @Nullable Map<String, Long> endTimeMillis) { |
| 429 | + Map<Urn, List<Future<Pair<String, EnvelopedAspect>>>> futures = |
| 430 | + urns.stream() |
| 431 | + .map( |
| 432 | + urn -> { |
| 433 | + List<Future<Pair<String, EnvelopedAspect>>> aspectFutures = |
| 434 | + aspectNames.stream() |
| 435 | + .map( |
| 436 | + aspectName -> |
| 437 | + queryPool.submit( |
| 438 | + () -> { |
| 439 | + List<EnvelopedAspect> oneResultList = |
| 440 | + getAspectValues( |
| 441 | + opContext, |
| 442 | + urn, |
| 443 | + urn.getEntityType(), |
| 444 | + aspectName, |
| 445 | + null, |
| 446 | + endTimeMillis == null |
| 447 | + ? null |
| 448 | + : endTimeMillis.get(aspectName), |
| 449 | + 1, |
| 450 | + null, |
| 451 | + null); |
| 452 | + return !oneResultList.isEmpty() |
| 453 | + ? Pair.of(aspectName, oneResultList.get(0)) |
| 454 | + : null; |
| 455 | + })) |
| 456 | + .collect(Collectors.toList()); |
| 457 | + |
| 458 | + return Map.entry(urn, aspectFutures); |
| 459 | + }) |
| 460 | + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); |
| 461 | + |
| 462 | + return futures.entrySet().stream() |
| 463 | + .map( |
| 464 | + e -> |
| 465 | + Map.entry( |
| 466 | + e.getKey(), |
| 467 | + e.getValue().stream() |
| 468 | + .map( |
| 469 | + f -> { |
| 470 | + try { |
| 471 | + return f.get(); |
| 472 | + } catch (InterruptedException | ExecutionException ex) { |
| 473 | + throw new RuntimeException(ex); |
| 474 | + } |
| 475 | + }) |
| 476 | + .filter(Objects::nonNull) |
| 477 | + .collect(Collectors.toList()))) |
| 478 | + .collect( |
| 479 | + Collectors.toMap( |
| 480 | + Map.Entry::getKey, |
| 481 | + e -> |
| 482 | + e.getValue().stream().collect(Collectors.toMap(Pair::getKey, Pair::getValue)))); |
| 483 | + } |
| 484 | + |
403 | 485 | @Override
|
404 | 486 | @Nonnull
|
405 | 487 | public GenericTable getAggregatedStats(
|
|
0 commit comments