From 0774ab60862b9abc1f0b965625c4c89868418b2f Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Wed, 8 Nov 2023 22:21:55 +0530 Subject: [PATCH 01/29] remove rx in entity span enricher --- .../clients/AttributeServiceCachedClient.java | 22 ++++ .../entities/DefaultTraceEntityAccessor.java | 7 +- .../trace/provider/AttributeProvider.java | 14 ++ .../DefaultTraceAttributeReader.java | 22 ++-- .../attributes/DefaultValueResolver.java | 124 +++++++++--------- .../reader/attributes/SpanValueSource.java | 5 + .../attributes/TraceAttributeReader.java | 6 +- .../TraceAttributeReaderFactory.java | 5 +- .../reader/attributes/TraceValueSource.java | 5 + .../reader/attributes/ValueResolver.java | 9 +- .../trace/reader/attributes/ValueSource.java | 2 + .../DefaultTraceAttributeReaderTest.java | 17 ++- .../attributes/DefaultValueResolverTest.java | 41 +++--- 13 files changed, 167 insertions(+), 112 deletions(-) create mode 100644 hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java create mode 100644 hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/provider/AttributeProvider.java diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java new file mode 100644 index 000000000..6ba64aa9f --- /dev/null +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java @@ -0,0 +1,22 @@ +package org.hypertrace.traceenricher.enrichment.clients; + +import org.hypertrace.core.attribute.service.v1.AttributeMetadata; +import org.hypertrace.trace.provider.AttributeProvider; + +import java.util.List; +import java.util.Optional; + +public class AttributeServiceCachedClient implements AttributeProvider { + + Optional get(String attributeScope, String attributeKey) { + + } + + Optional getById(String attributeId) { + + } + + Optional> getAllInScope(String scope) { + + } +} diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java index 8b59259eb..7ef97201f 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java @@ -87,7 +87,7 @@ private void writeEntityIfExists(EntityType entityType, StructuredTrace trace, E }); } - private Maybe buildUpsertCondition( + private Optional buildUpsertCondition( EntityType entityType, StructuredTrace trace, Event span) { if (entityType.getTimestampAttributeKey().isEmpty()) { return Maybe.empty(); @@ -105,13 +105,12 @@ private Maybe buildUpsertCondition( attribute, PredicateOperator.PREDICATE_OPERATOR_LESS_THAN, trace, span)); } - private Maybe buildUpsertCondition( + private Optional buildUpsertCondition( AttributeMetadata attribute, PredicateOperator operator, StructuredTrace trace, Event span) { return this.traceAttributeReader .getSpanValue(trace, span, attribute.getScopeString(), attribute.getKey()) - .onErrorComplete() - .flatMap(value -> this.buildUpsertCondition(attribute, operator, value)); + .map(value -> this.buildUpsertCondition(attribute, operator, value)); } private Maybe buildUpsertCondition( diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/provider/AttributeProvider.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/provider/AttributeProvider.java new file mode 100644 index 000000000..233deae31 --- /dev/null +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/provider/AttributeProvider.java @@ -0,0 +1,14 @@ +package org.hypertrace.trace.provider; + +import org.hypertrace.core.attribute.service.v1.AttributeMetadata; + +import java.util.List; +import java.util.Optional; + +public interface AttributeProvider { + Optional get(String tenantId, String attributeScope, String attributeKey); + + Optional getById(String tenantId, String attributeId); + + Optional> getAllInScope(String tenantId, String scope); +} diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReader.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReader.java index 53907ef2a..5f026ebd6 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReader.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReader.java @@ -3,24 +3,26 @@ import static org.hypertrace.trace.reader.attributes.ValueSource.TRACE_SCOPE; import io.reactivex.rxjava3.core.Single; -import org.hypertrace.core.attribute.service.cachingclient.CachingAttributeClient; import org.hypertrace.core.attribute.service.v1.AttributeMetadata; import org.hypertrace.core.attribute.service.v1.LiteralValue; import org.hypertrace.core.datamodel.Event; import org.hypertrace.core.datamodel.StructuredTrace; +import org.hypertrace.trace.provider.AttributeProvider; + +import java.util.Optional; class DefaultTraceAttributeReader implements TraceAttributeReader { - private final CachingAttributeClient attributeClient; + private final AttributeProvider attributeProvider; private final ValueResolver valueResolver; - DefaultTraceAttributeReader(CachingAttributeClient attributeClient) { - this.attributeClient = attributeClient; - this.valueResolver = ValueResolver.build(this.attributeClient); + DefaultTraceAttributeReader(AttributeProvider attributeProvider) { + this.attributeProvider = attributeProvider; + this.valueResolver = ValueResolver.build(this.attributeProvider); } @Override - public Single getSpanValue( + public Optional getSpanValue( StructuredTrace trace, Event span, String attributeScope, String attributeKey) { ValueSource valueSource = ValueSourceFactory.forSpan(trace, span); return this.getAttribute(valueSource, attributeScope, attributeKey) @@ -28,7 +30,7 @@ public Single getSpanValue( } @Override - public Single getTraceValue(StructuredTrace trace, String attributeKey) { + public Optional getTraceValue(StructuredTrace trace, String attributeKey) { ValueSource valueSource = ValueSourceFactory.forTrace(trace); return this.getAttribute(valueSource, TRACE_SCOPE, attributeKey) .flatMap(definition -> this.valueResolver.resolve(valueSource, definition)); @@ -39,10 +41,8 @@ public String getTenantId(Event span) { return span.getCustomerId(); } - private Single getAttribute( + private Optional getAttribute( ValueSource valueSource, String attributeScope, String attributeKey) { - return valueSource - .executionContext() - .wrapSingle(() -> this.attributeClient.get(attributeScope, attributeKey)); + return this.attributeProvider.get(valueSource.tenantId(), attributeScope, attributeKey); } } diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java index 78217fd76..59e7435c9 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java @@ -7,7 +7,12 @@ import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Single; import java.util.List; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; + import lombok.extern.slf4j.Slf4j; import org.hypertrace.core.attribute.service.cachingclient.CachingAttributeClient; import org.hypertrace.core.attribute.service.projection.AttributeProjection; @@ -22,34 +27,37 @@ import org.hypertrace.core.attribute.service.v1.LiteralValue.ValueCase; import org.hypertrace.core.attribute.service.v1.Projection; import org.hypertrace.core.attribute.service.v1.ProjectionExpression; +import org.hypertrace.trace.provider.AttributeProvider; @Slf4j class DefaultValueResolver implements ValueResolver { // One log a minute private static final RateLimiter LOGGING_LIMITER = RateLimiter.create(1 / 60d); - private final CachingAttributeClient attributeClient; + private final AttributeProvider attributeClient; private final AttributeProjectionRegistry attributeProjectionRegistry; DefaultValueResolver( - CachingAttributeClient attributeClient, + AttributeProvider attributeClient, AttributeProjectionRegistry attributeProjectionRegistry) { this.attributeClient = attributeClient; this.attributeProjectionRegistry = attributeProjectionRegistry; } @Override - public Single resolve( + public Optional resolve( ValueSource valueSource, AttributeMetadata attributeMetadata) { if (!attributeMetadata.hasDefinition()) { - return this.buildAndLogErrorLazily( - "Attribute definition not set for attribute: " + attributeMetadata.getId()); + if(LOGGING_LIMITER.tryAcquire()) { + log.error("Attribute definition not set for attribute: " + attributeMetadata.getId()); + } + return Optional.empty(); } return this.resolveDefinition( valueSource, attributeMetadata, attributeMetadata.getDefinition()); } - private Single resolveDefinition( + private Optional resolveDefinition( ValueSource valueSource, AttributeMetadata attributeMetadata, AttributeDefinition definition) { @@ -72,108 +80,96 @@ private Single resolveDefinition( valueSource, attributeMetadata, definition.getFirstValuePresent()); case VALUE_NOT_SET: default: - return this.buildAndLogErrorLazily("Unrecognized attribute definition"); + return Optional.empty(); } } - private Maybe maybeResolveDefinition( - ValueSource valueSource, - AttributeMetadata attributeMetadata, - AttributeDefinition definition) { - return this.resolveDefinition(valueSource, attributeMetadata, definition) - .filter(literalValue -> !literalValue.getValueCase().equals(ValueCase.VALUE_NOT_SET)) - .onErrorComplete(); - } - - private Single resolveValue( + private Optional resolveValue( ValueSource contextValueSource, String attributeScope, AttributeType attributeType, AttributeKind attributeKind, String path) { - Single matchingValueSource = - Maybe.fromOptional(contextValueSource.sourceForScope(attributeScope)) - .switchIfEmpty( - this.buildAndLogErrorLazily( - "No value source available supporting scope %s", attributeScope)); + Optional matchingValueSource = contextValueSource.sourceForScope(attributeScope); + if(matchingValueSource.isEmpty()) { + if(LOGGING_LIMITER.tryAcquire()) { + log.error("No value source available supporting scope %s", attributeScope); + } + return Optional.empty(); + } switch (attributeType) { case ATTRIBUTE: return matchingValueSource - .mapOptional(valueSource -> valueSource.getAttribute(path, attributeKind)) - .defaultIfEmpty(LiteralValue.getDefaultInstance()); + .flatMap(valueSource -> valueSource.getAttribute(path, attributeKind)); case METRIC: return matchingValueSource - .mapOptional(valueSource -> valueSource.getMetric(path, attributeKind)) - .defaultIfEmpty(LiteralValue.getDefaultInstance()); + .flatMap(valueSource -> valueSource.getMetric(path, attributeKind)); case UNRECOGNIZED: case TYPE_UNDEFINED: default: - return this.buildAndLogErrorLazily("Unrecognized projection type"); + return Optional.empty(); } } - private Single resolveProjection(ValueSource valueSource, Projection projection) { + private Optional resolveProjection(ValueSource valueSource, Projection projection) { switch (projection.getValueCase()) { case ATTRIBUTE_ID: - return valueSource - .executionContext() - .wrapSingle(() -> this.attributeClient.get(projection.getAttributeId())) + return this.attributeClient.getById(valueSource.tenantId(), projection.getAttributeId()) .flatMap(attributeMetadata -> this.resolve(valueSource, attributeMetadata)); case LITERAL: - return Single.just(projection.getLiteral()); + return Optional.of(projection.getLiteral()); case EXPRESSION: return this.resolveExpression(valueSource, projection.getExpression()); case VALUE_NOT_SET: default: - return this.buildAndLogErrorLazily("Unrecognized projection type"); + if(LOGGING_LIMITER.tryAcquire()) { + log.error("Unrecognized projection type"); + } + return Optional.empty(); } } - private Single resolveField( + private Optional resolveField( ValueSource valueSource, SourceField sourceField, AttributeKind attributeKind) { - return Maybe.fromOptional(valueSource.getSourceField(sourceField, attributeKind)) - .defaultIfEmpty(LiteralValue.getDefaultInstance()); + return valueSource.getSourceField(sourceField, attributeKind); } - private Single resolveFirstValuePresent( + private Optional resolveFirstValuePresent( ValueSource valueSource, AttributeMetadata attributeMetadata, AttributeDefinitions definitions) { - return Observable.fromIterable(definitions.getDefinitionsList()) - .concatMapMaybe( - definition -> this.maybeResolveDefinition(valueSource, attributeMetadata, definition)) - .first(LiteralValue.getDefaultInstance()); + return definitions.getDefinitionsList().stream() + .map( + definition -> this.resolveDefinition(valueSource, attributeMetadata, definition)) + .filter(Optional::isPresent) + .findFirst() + .flatMap(Function.identity()); } - private Single resolveExpression( + private Optional resolveExpression( ValueSource valueSource, ProjectionExpression expression) { - - Single projectionSingle = - Maybe.fromOptional(this.attributeProjectionRegistry.getProjection(expression.getOperator())) - .switchIfEmpty( - buildAndLogErrorLazily( - "Unregistered projection operator: %s", expression.getOperator())); - Single> argumentsSingle = + Optional maybeProjection = this.attributeProjectionRegistry.getProjection(expression.getOperator()); + if(maybeProjection.isEmpty()) { + if(LOGGING_LIMITER.tryAcquire()) { + log.error("Unregistered projection operator: {}", expression.getOperator()); + } + return Optional.empty(); + } + Optional> maybeArguments = this.resolveArgumentList(valueSource, expression.getArgumentsList()); - return zip(projectionSingle, argumentsSingle, AttributeProjection::project); + return maybeArguments.map(arguments -> maybeProjection.get().project(arguments)); } - private Single> resolveArgumentList( + private Optional> resolveArgumentList( ValueSource valueSource, List arguments) { - return Observable.fromIterable(arguments) - .flatMapSingle(argument -> this.resolveProjection(valueSource, argument)) - .collect(Collectors.toList()); - } - - private Single buildAndLogErrorLazily(String message, Object... args) { - return Single.error( - () -> { - if (LOGGING_LIMITER.tryAcquire()) { - log.error(String.format(message, args)); - } - return new UnsupportedOperationException(String.format(message, args)); - }); + Stream> resolvedArguments = arguments.stream().map(argument -> this.resolveProjection(valueSource, argument)); + try { + return Optional.of(resolvedArguments.map(Optional::orElseThrow).collect(Collectors.toUnmodifiableList())); + } catch (NoSuchElementException elementException) { + // if any of the arguments don't resolve, fail argument resolution + return Optional.empty(); + } } } diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/SpanValueSource.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/SpanValueSource.java index fc3878f2e..95227e552 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/SpanValueSource.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/SpanValueSource.java @@ -61,6 +61,11 @@ public GrpcRxExecutionContext executionContext() { return GrpcRxExecutionContext.forTenantContext(this.span.getCustomerId()); } + @Override + public String tenantId() { + return this.span.getCustomerId(); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceAttributeReader.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceAttributeReader.java index d8678bb2f..4760707cb 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceAttributeReader.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceAttributeReader.java @@ -4,10 +4,12 @@ import org.apache.avro.generic.GenericRecord; import org.hypertrace.core.attribute.service.v1.LiteralValue; +import java.util.Optional; + public interface TraceAttributeReader { - Single getSpanValue(T trace, S span, String attributeScope, String attributeKey); + Optional getSpanValue(T trace, S span, String attributeScope, String attributeKey); - Single getTraceValue(T trace, String attributeKey); + Optional getTraceValue(T trace, String attributeKey); String getTenantId(S span); } diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceAttributeReaderFactory.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceAttributeReaderFactory.java index 827fa895c..0c1928c96 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceAttributeReaderFactory.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceAttributeReaderFactory.java @@ -3,10 +3,11 @@ import org.hypertrace.core.attribute.service.cachingclient.CachingAttributeClient; import org.hypertrace.core.datamodel.Event; import org.hypertrace.core.datamodel.StructuredTrace; +import org.hypertrace.trace.provider.AttributeProvider; public interface TraceAttributeReaderFactory { static TraceAttributeReader build( - CachingAttributeClient attributeClient) { - return new DefaultTraceAttributeReader(attributeClient); + AttributeProvider attributeProvider) { + return new DefaultTraceAttributeReader(attributeProvider); } } diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceValueSource.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceValueSource.java index 9bdbe5627..ad670ce68 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceValueSource.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceValueSource.java @@ -53,6 +53,11 @@ public GrpcRxExecutionContext executionContext() { return GrpcRxExecutionContext.forTenantContext(this.trace.getCustomerId()); } + @Override + public String tenantId() { + return this.trace.getCustomerId(); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/ValueResolver.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/ValueResolver.java index 166b01628..9a9d3b723 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/ValueResolver.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/ValueResolver.java @@ -5,12 +5,15 @@ import org.hypertrace.core.attribute.service.projection.AttributeProjectionRegistry; import org.hypertrace.core.attribute.service.v1.AttributeMetadata; import org.hypertrace.core.attribute.service.v1.LiteralValue; +import org.hypertrace.trace.provider.AttributeProvider; + +import java.util.Optional; public interface ValueResolver { - Single resolve(ValueSource valueSource, AttributeMetadata attributeMetadata); + Optional resolve(ValueSource valueSource, AttributeMetadata attributeMetadata); - static ValueResolver build(CachingAttributeClient attributeClient) { - return new DefaultValueResolver(attributeClient, new AttributeProjectionRegistry()); + static ValueResolver build(AttributeProvider attributeProvider) { + return new DefaultValueResolver(attributeProvider, new AttributeProjectionRegistry()); } } diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/ValueSource.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/ValueSource.java index 5db9d5450..ee3677d55 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/ValueSource.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/ValueSource.java @@ -17,5 +17,7 @@ public interface ValueSource { GrpcRxExecutionContext executionContext(); + String tenantId(); + String TRACE_SCOPE = "TRACE"; } diff --git a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReaderTest.java b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReaderTest.java index 6c3f983bc..d14b1ea44 100644 --- a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReaderTest.java +++ b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReaderTest.java @@ -9,28 +9,31 @@ import static org.mockito.Mockito.when; import io.reactivex.rxjava3.core.Single; -import org.hypertrace.core.attribute.service.cachingclient.CachingAttributeClient; import org.hypertrace.core.attribute.service.v1.AttributeDefinition; import org.hypertrace.core.attribute.service.v1.AttributeKind; import org.hypertrace.core.attribute.service.v1.AttributeMetadata; import org.hypertrace.core.attribute.service.v1.AttributeType; import org.hypertrace.core.datamodel.Event; import org.hypertrace.core.datamodel.StructuredTrace; +import org.hypertrace.trace.provider.AttributeProvider; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import java.util.Optional; + @ExtendWith(MockitoExtension.class) class DefaultTraceAttributeReaderTest { - @Mock CachingAttributeClient mockAttributeClient; + @Mock + AttributeProvider mockAttributeProvider; private TraceAttributeReader traceAttributeReader; @BeforeEach void beforeEach() { - this.traceAttributeReader = TraceAttributeReaderFactory.build(this.mockAttributeClient); + this.traceAttributeReader = TraceAttributeReaderFactory.build(this.mockAttributeProvider); } @Test @@ -42,7 +45,7 @@ void canReadSpanValues() { .setValueKind(AttributeKind.TYPE_STRING) .setDefinition(AttributeDefinition.newBuilder().setSourcePath("attrPath").build()) .build(); - when(this.mockAttributeClient.get("TEST_SCOPE", "key")).thenReturn(Single.just(metadata)); + when(this.mockAttributeProvider.get("defaultCustomerId","TEST_SCOPE", "key")).thenReturn(Optional.of(metadata)); Event span = defaultedEventBuilder() @@ -53,7 +56,7 @@ void canReadSpanValues() { stringLiteral("attrValue"), this.traceAttributeReader .getSpanValue(mock(StructuredTrace.class), span, "TEST_SCOPE", "key") - .blockingGet()); + .get()); } @Test @@ -65,7 +68,7 @@ void canReadTraceValues() { .setValueKind(AttributeKind.TYPE_STRING) .setDefinition(AttributeDefinition.newBuilder().setSourcePath("attrPath").build()) .build(); - when(this.mockAttributeClient.get("TRACE", "key")).thenReturn(Single.just(metadata)); + when(this.mockAttributeProvider.get("defaultCustomerId","TRACE", "key")).thenReturn(Optional.of(metadata)); StructuredTrace trace = defaultedStructuredTraceBuilder() @@ -74,6 +77,6 @@ void canReadTraceValues() { assertEquals( stringLiteral("attrValue"), - this.traceAttributeReader.getTraceValue(trace, "key").blockingGet()); + this.traceAttributeReader.getTraceValue(trace, "key").get()); } } diff --git a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultValueResolverTest.java b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultValueResolverTest.java index e093fd515..da233bf47 100644 --- a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultValueResolverTest.java +++ b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultValueResolverTest.java @@ -13,7 +13,8 @@ import io.reactivex.rxjava3.core.Single; import java.util.Map; -import org.hypertrace.core.attribute.service.cachingclient.CachingAttributeClient; +import java.util.Optional; + import org.hypertrace.core.attribute.service.projection.AttributeProjectionRegistry; import org.hypertrace.core.attribute.service.v1.AttributeDefinition; import org.hypertrace.core.attribute.service.v1.AttributeDefinition.AttributeDefinitions; @@ -27,6 +28,7 @@ import org.hypertrace.core.attribute.service.v1.ProjectionOperator; import org.hypertrace.core.datamodel.Event; import org.hypertrace.core.datamodel.StructuredTrace; +import org.hypertrace.trace.provider.AttributeProvider; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -36,7 +38,8 @@ @ExtendWith(MockitoExtension.class) class DefaultValueResolverTest { - @Mock CachingAttributeClient mockAttributeClient; + @Mock + AttributeProvider mockAttributeProvider; @Mock StructuredTrace mockStructuredTrace; private DefaultValueResolver resolver; @@ -44,7 +47,7 @@ class DefaultValueResolverTest { @BeforeEach void beforeEach() { this.resolver = - new DefaultValueResolver(this.mockAttributeClient, new AttributeProjectionRegistry()); + new DefaultValueResolver(this.mockAttributeProvider, new AttributeProjectionRegistry()); } @Test @@ -66,7 +69,7 @@ void resolvesAttributes() { stringLiteral("attrValue"), this.resolver .resolve(ValueSourceFactory.forSpan(this.mockStructuredTrace, span), metadata) - .blockingGet()); + .get()); } @Test @@ -86,7 +89,7 @@ void resolvesMetrics() { longLiteral(42), this.resolver .resolve(ValueSourceFactory.forSpan(this.mockStructuredTrace, span), metadata) - .blockingGet()); + .get()); } @Test @@ -103,7 +106,7 @@ void resolvesLiteralProjections() { this.resolver .resolve( ValueSourceFactory.forSpan(this.mockStructuredTrace, mock(Event.class)), metadata) - .blockingGet()); + .get()); } @Test @@ -122,7 +125,7 @@ void resolvesAttributeProjections() { .setValueKind(AttributeKind.TYPE_INT64) .setDefinition(AttributeDefinition.newBuilder().setSourcePath("metricPath").build()) .build(); - when(this.mockAttributeClient.get("TEST_SCOPE.other")).thenReturn(Single.just(otherMetadata)); + when(this.mockAttributeProvider.getById("defaultCustomerId", "TEST_SCOPE.other")).thenReturn(Optional.of((otherMetadata))); Event span = defaultedEventBuilder().setMetrics(buildMetricsWithKeyValue("metricPath", 42)).build(); @@ -131,7 +134,7 @@ void resolvesAttributeProjections() { longLiteral(42), this.resolver .resolve(ValueSourceFactory.forSpan(this.mockStructuredTrace, span), projectionMetadata) - .blockingGet()); + .get()); } @Test @@ -166,8 +169,8 @@ void resolvesExpressionProjections() { .setValueKind(AttributeKind.TYPE_STRING) .setDefinition(AttributeDefinition.newBuilder().setSourcePath("attrPath").build()) .build(); - when(this.mockAttributeClient.get("TEST_SCOPE.first")).thenReturn(Single.just(firstMetadata)); - when(this.mockAttributeClient.get("TEST_SCOPE.second")).thenReturn(Single.just(secondMetadata)); + when(this.mockAttributeProvider.getById("defaultCustomerId", "TEST_SCOPE.first")).thenReturn(Optional.of(firstMetadata)); + when(this.mockAttributeProvider.getById("defaultCustomerId", "TEST_SCOPE.second")).thenReturn(Optional.of(secondMetadata)); Event span = defaultedEventBuilder() @@ -179,7 +182,7 @@ void resolvesExpressionProjections() { stringLiteral("42coolString"), this.resolver .resolve(ValueSourceFactory.forSpan(this.mockStructuredTrace, span), projectionMetadata) - .blockingGet()); + .get()); } @Test @@ -198,7 +201,7 @@ void resolvesProjectionsAcrossScopes() { .setValueKind(AttributeKind.TYPE_INT64) .setDefinition(AttributeDefinition.newBuilder().setSourcePath("metricPath").build()) .build(); - when(this.mockAttributeClient.get("TRACE.other")).thenReturn(Single.just(otherMetadata)); + when(this.mockAttributeProvider.getById("defaultCustomerId", "TRACE.other")).thenReturn(Optional.of(otherMetadata)); StructuredTrace trace = defaultedStructuredTraceBuilder() @@ -209,7 +212,7 @@ void resolvesProjectionsAcrossScopes() { longLiteral(42), this.resolver .resolve(ValueSourceFactory.forSpan(trace, mock(Event.class)), projectionMetadata) - .blockingGet()); + .get()); } @Test @@ -239,12 +242,12 @@ void resolveFields() { longLiteral(123), this.resolver .resolve(ValueSourceFactory.forSpan(this.mockStructuredTrace, span), metadataStartTime) - .blockingGet()); + .get()); assertEquals( longLiteral(234), this.resolver .resolve(ValueSourceFactory.forSpan(this.mockStructuredTrace, span), metadataEndTime) - .blockingGet()); + .get()); } @Test @@ -279,7 +282,7 @@ void resolvesFirstAvailableDefinition() { longLiteral(14), this.resolver .resolve(ValueSourceFactory.forSpan(this.mockStructuredTrace, span), metadata) - .blockingGet()); + .get()); } @Test @@ -309,7 +312,7 @@ void resolvesEmptyIfNoDefinitionAvailable() { LiteralValue.getDefaultInstance(), this.resolver .resolve(ValueSourceFactory.forSpan(this.mockStructuredTrace, span), metadata) - .blockingGet()); + .get()); } @Test @@ -340,7 +343,7 @@ void resolvesFirstAttributeProjection() { stringLiteral("expected-value"), this.resolver .resolve(ValueSourceFactory.forSpan(this.mockStructuredTrace, span), metadata) - .blockingGet()); + .get()); } @Test @@ -375,6 +378,6 @@ void resolvesNestedFirstAttribute() { longLiteral(13), this.resolver .resolve(ValueSourceFactory.forSpan(this.mockStructuredTrace, span), metadata) - .blockingGet()); + .get()); } } From a9117c00dbc2e61cc8f5183a3d3925ddcddf5487 Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Tue, 14 Nov 2023 15:57:47 +0530 Subject: [PATCH 02/29] wip --- .../entities/AttributeValueConverter.java | 20 +++-- .../entities/DefaultTraceEntityAccessor.java | 82 +++++++++---------- 2 files changed, 48 insertions(+), 54 deletions(-) diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/AttributeValueConverter.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/AttributeValueConverter.java index 4d7d93edb..440ac3e40 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/AttributeValueConverter.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/AttributeValueConverter.java @@ -7,28 +7,30 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Optional; + interface AttributeValueConverter { Logger LOG = LoggerFactory.getLogger(AttributeValueConverter.class); - static Maybe convertToAttributeValue(LiteralValue literalValue) { + static Optional convertToAttributeValue(LiteralValue literalValue) { switch (literalValue.getValueCase()) { case STRING_VALUE: - return attributeValueMaybe(Value.newBuilder().setString(literalValue.getStringValue())); + return attributeValueOptional(Value.newBuilder().setString(literalValue.getStringValue())); case BOOLEAN_VALUE: - return attributeValueMaybe(Value.newBuilder().setBoolean(literalValue.getBooleanValue())); + return attributeValueOptional(Value.newBuilder().setBoolean(literalValue.getBooleanValue())); case FLOAT_VALUE: - return attributeValueMaybe(Value.newBuilder().setDouble(literalValue.getFloatValue())); + return attributeValueOptional(Value.newBuilder().setDouble(literalValue.getFloatValue())); case INT_VALUE: - return attributeValueMaybe(Value.newBuilder().setLong(literalValue.getIntValue())); + return attributeValueOptional(Value.newBuilder().setLong(literalValue.getIntValue())); case VALUE_NOT_SET: - return Maybe.empty(); + return Optional.empty(); default: LOG.error("Unexpected literal value case: " + literalValue.getValueCase()); - return Maybe.empty(); + return Optional.empty(); } } - private static Maybe attributeValueMaybe(Value.Builder value) { - return Maybe.just(AttributeValue.newBuilder().setValue(value).build()); + private static Optional attributeValueOptional(Value.Builder value) { + return Optional.of(AttributeValue.newBuilder().setValue(value).build()); } } diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java index 7ef97201f..5ac72433e 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java @@ -5,6 +5,8 @@ import io.reactivex.rxjava3.core.Maybe; import java.time.Duration; +import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; @@ -30,13 +32,14 @@ import org.hypertrace.entity.type.service.rxclient.EntityTypeClient; import org.hypertrace.entity.type.service.v2.EntityType; import org.hypertrace.entity.type.service.v2.EntityType.EntityFormationCondition; +import org.hypertrace.trace.provider.AttributeProvider; import org.hypertrace.trace.reader.attributes.TraceAttributeReader; @Slf4j class DefaultTraceEntityAccessor implements TraceEntityAccessor { private final EntityTypeClient entityTypeClient; private final EntityDataClient entityDataClient; - private final CachingAttributeClient attributeClient; + private final AttributeProvider attributeProvider; private final TraceAttributeReader traceAttributeReader; private final Duration writeThrottleDuration; private final Set excludedEntityTypes; @@ -44,13 +47,13 @@ class DefaultTraceEntityAccessor implements TraceEntityAccessor { DefaultTraceEntityAccessor( EntityTypeClient entityTypeClient, EntityDataClient entityDataClient, - CachingAttributeClient attributeClient, + AttributeProvider attributeProvider, TraceAttributeReader traceAttributeReader, Duration writeThrottleDuration, Set excludedEntityTypes) { this.entityTypeClient = entityTypeClient; this.entityDataClient = entityDataClient; - this.attributeClient = attributeClient; + this.attributeProvider = attributeProvider; this.traceAttributeReader = traceAttributeReader; this.writeThrottleDuration = writeThrottleDuration; this.excludedEntityTypes = excludedEntityTypes; @@ -90,14 +93,9 @@ private void writeEntityIfExists(EntityType entityType, StructuredTrace trace, E private Optional buildUpsertCondition( EntityType entityType, StructuredTrace trace, Event span) { if (entityType.getTimestampAttributeKey().isEmpty()) { - return Maybe.empty(); + return Optional.empty(); } - - return spanTenantContext(span) - .wrapSingle( - () -> - this.attributeClient.get( - entityType.getAttributeScope(), entityType.getTimestampAttributeKey())) + return this.attributeProvider.get(this.traceAttributeReader.getTenantId(span), entityType.getAttributeScope(), entityType.getTimestampAttributeKey()) .filter(this::isEntitySourced) .flatMap( attribute -> @@ -107,13 +105,12 @@ private Optional buildUpsertCondition( private Optional buildUpsertCondition( AttributeMetadata attribute, PredicateOperator operator, StructuredTrace trace, Event span) { - return this.traceAttributeReader .getSpanValue(trace, span, attribute.getScopeString(), attribute.getKey()) - .map(value -> this.buildUpsertCondition(attribute, operator, value)); + .flatMap(value -> this.buildUpsertCondition(attribute, operator, value)); } - private Maybe buildUpsertCondition( + private Optional buildUpsertCondition( AttributeMetadata attribute, PredicateOperator operator, LiteralValue currentValue) { return AttributeValueConverter.convertToAttributeValue(currentValue) .map( @@ -127,30 +124,24 @@ private Maybe buildUpsertCondition( .build()); } - private Maybe buildEntity(EntityType entityType, StructuredTrace trace, Event span) { - Maybe> attributes = - this.resolveAllAttributes(entityType.getAttributeScope(), trace, span).cache(); - - Maybe id = - attributes.mapOptional( + private Optional buildEntity(EntityType entityType, StructuredTrace trace, Event span) { + Optional> attributes = + this.resolveAllAttributes(entityType.getAttributeScope(), trace, span); + Optional id = + attributes.flatMap( map -> this.extractNonEmptyString(map, entityType.getIdAttributeKey())); - - Maybe name = - attributes.mapOptional( + Optional name = + attributes.flatMap( map -> this.extractNonEmptyString(map, entityType.getNameAttributeKey())); - - return zip( - id, - name, - attributes, - (resolvedId, resolvedName, resolvedAttributeMap) -> - Entity.newBuilder() - .setEntityId(resolvedId) - .setEntityType(entityType.getName()) - .setEntityName(resolvedName) - .putAllAttributes(resolvedAttributeMap) - .build()) - .filter(entity -> this.canCreateEntity(entityType, entity)); + if (id.isEmpty() || name.isEmpty()) { + return Optional.empty(); + } + return Optional.of(Entity.newBuilder() + .setEntityId(id.get()) + .setEntityType(entityType.getName()) + .setEntityName(name.get()) + .putAllAttributes(attributes.get()) + .build()).filter(entity -> this.canCreateEntity(entityType, entity)); } private boolean canCreateEntity(EntityType entityType, Entity entity) { @@ -170,22 +161,23 @@ private boolean passesFormationCondition(Entity entity, EntityFormationCondition } } - private Maybe> resolveAllAttributes( + private Optional> resolveAllAttributes( String scope, StructuredTrace trace, Event span) { - return spanTenantContext(span) - .wrapSingle(() -> this.attributeClient.getAllInScope(scope)) - .flattenAsObservable(list -> list) - .filter(this::isEntitySourced) - .flatMapMaybe(attributeMetadata -> this.resolveAttribute(attributeMetadata, trace, span)) - .collect(Collectors.toMap(Entry::getKey, Entry::getValue)) - .toMaybe(); + Optional> mayBeAttributeMetadataList = this.attributeProvider.getAllInScope(this.traceAttributeReader.getTenantId(span), scope); + if (mayBeAttributeMetadataList.isEmpty()) { + return Optional.empty(); + } + Map resolvedAttributes = mayBeAttributeMetadataList.get().stream().filter(this::isEntitySourced).map(attributeMetadata -> this.resolveAttribute(attributeMetadata, trace, span)).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toMap(Entry::getKey, Entry::getValue)); + if (resolvedAttributes.isEmpty()) { + return Optional.empty(); + } + return Optional.of(resolvedAttributes); } - private Maybe> resolveAttribute( + private Optional> resolveAttribute( AttributeMetadata attributeMetadata, StructuredTrace trace, Event span) { return this.traceAttributeReader .getSpanValue(trace, span, attributeMetadata.getScopeString(), attributeMetadata.getKey()) - .onErrorComplete() .flatMap(AttributeValueConverter::convertToAttributeValue) .map(value -> Map.entry(attributeMetadata.getKey(), value)); } From 07117e8bc5bffbd043b0fb00162f6732a819346d Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Thu, 16 Nov 2023 12:49:36 +0530 Subject: [PATCH 03/29] wip --- .../clients/AttributeServiceCachedClient.java | 86 ++++++++++++++++++- .../enrichers/space/SpaceRuleEvaluator.java | 7 +- .../space/SpaceRuleEvaluatorTest.java | 8 +- .../entities/DefaultTraceEntityAccessor.java | 25 +++--- .../entities/TraceEntityAccessorBuilder.java | 13 +-- .../entities/AttributeValueConverterTest.java | 18 ++-- .../DefaultTraceEntityAccessorTest.java | 23 ++--- 7 files changed, 130 insertions(+), 50 deletions(-) diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java index 6ba64aa9f..57a86a7d7 100644 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java @@ -1,22 +1,102 @@ package org.hypertrace.traceenricher.enrichment.clients; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableTable; +import com.google.common.collect.Table; +import com.google.common.util.concurrent.RateLimiter; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import io.grpc.CallCredentials; +import io.grpc.Channel; import org.hypertrace.core.attribute.service.v1.AttributeMetadata; +import org.hypertrace.core.attribute.service.v1.AttributeServiceGrpc.AttributeServiceBlockingStub; +import org.hypertrace.core.attribute.service.v1.GetAttributesRequest; +import org.hypertrace.core.grpcutils.context.RequestContext; import org.hypertrace.trace.provider.AttributeProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.time.Duration; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; public class AttributeServiceCachedClient implements AttributeProvider { + private static final RateLimiter LOG_LIMITER = RateLimiter.create(1/60d); + private static final Logger LOGGER = LoggerFactory.getLogger(AttributeServiceCachedClient.class); + private final LoadingCache> cache; + private final Cache scopeAndKeyLookup; + private final AttributeServiceBlockingStub attributeServiceBlockingStub; + private final long deadlineMs; + AttributeServiceCachedClient(AttributeServiceBlockingStub attributeServiceBlockingStub, int cacheMaxSize, Duration expireAfterWriteDuration, Duration refreshAfterWriteDuration, Duration deadline, int executorThreads) { + deadlineMs = deadline.toMillis(); + this.attributeServiceBlockingStub = attributeServiceBlockingStub; + cache = CacheBuilder.newBuilder().maximumSize(cacheMaxSize).expireAfterWrite(expireAfterWriteDuration).refreshAfterWrite(refreshAfterWriteDuration).build(CacheLoader.asyncReloading( + CacheLoader.from(this::loadTable), + Executors.newFixedThreadPool(executorThreads, this.buildThreadFactory()))); + scopeAndKeyLookup = CacheBuilder.newBuilder().expireAfterWrite(expireAfterWriteDuration).build(); + } - Optional get(String attributeScope, String attributeKey) { + public Optional get(String tenantId, String attributeScope, String attributeKey) { + try { + return Optional.ofNullable(cache.getUnchecked(tenantId)).map(table -> table.get(attributeScope, attributeKey)); + } catch (Exception e) { + if (LOG_LIMITER.tryAcquire()) { + LOGGER.error("No attribute available for scope {} and key {}", attributeScope, attributeKey); + } + return Optional.empty(); + } + } + public Optional getById(String tenantId, String attributeId) { + try { + return Optional.ofNullable(cache.getUnchecked(tenantId)).flatMap(table -> Optional.ofNullable(scopeAndKeyLookup.getIfPresent(attributeId)).map(scopeAndKey -> table.get(scopeAndKey.scope, scopeAndKey.key))); + } catch (Exception e) { + if (LOG_LIMITER.tryAcquire()) { + LOGGER.error("No attribute available for id {}", attributeId); + } + return Optional.empty(); + } } - Optional getById(String attributeId) { + public Optional> getAllInScope(String tenantId, String attributeScope) { + try { + return Optional.ofNullable(cache.getUnchecked(tenantId)).map(table -> List.copyOf(table.row(attributeScope).values())); + } catch (Exception e) { + if (LOG_LIMITER.tryAcquire()) { + LOGGER.error("No attributes available for scope {}", attributeScope); + } + return Optional.empty(); + } + } + + private Table loadTable(String tenantId) { + List attributeMetadataList = RequestContext.forTenantId(tenantId).call(() -> attributeServiceBlockingStub.withDeadlineAfter(deadlineMs, TimeUnit.MILLISECONDS).getAttributes(GetAttributesRequest.getDefaultInstance())).getAttributesList(); + attributeMetadataList.forEach(attributeMetadata -> scopeAndKeyLookup.put(attributeMetadata.getId(), new AttributeScopeAndKey(attributeMetadata.getScopeString(), attributeMetadata.getKey()))); + return attributeMetadataList.stream().collect(ImmutableTable.toImmutableTable(AttributeMetadata::getScopeString, AttributeMetadata::getKey, Function.identity())); + } + private ThreadFactory buildThreadFactory() { + return new ThreadFactoryBuilder() + .setDaemon(true) + .setNameFormat("attribute-service-cache-%d") + .build(); } - Optional> getAllInScope(String scope) { + private static final class AttributeScopeAndKey { + private final String scope; + private final String key; + private AttributeScopeAndKey(String scope, String key) { + this.scope = scope; + this.key = key; + } } } diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/enrichers/space/SpaceRuleEvaluator.java b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/enrichers/space/SpaceRuleEvaluator.java index b066abf21..0d3e11f87 100644 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/enrichers/space/SpaceRuleEvaluator.java +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/enrichers/space/SpaceRuleEvaluator.java @@ -36,11 +36,8 @@ private List calculateSpacesForAttribute( span, attributeValueRuleData.getAttributeScope(), attributeValueRuleData.getAttributeKey()) - .mapOptional(ValueCoercer::convertToString) + .flatMap(ValueCoercer::convertToString) .filter(string -> !string.isEmpty()) - .map(List::of) - .onErrorComplete() - .defaultIfEmpty(Collections.emptyList()) - .blockingGet(); + .map(List::of).orElse(Collections.emptyList()); } } diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/test/java/org/hypertrace/traceenricher/enrichment/enrichers/space/SpaceRuleEvaluatorTest.java b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/test/java/org/hypertrace/traceenricher/enrichment/enrichers/space/SpaceRuleEvaluatorTest.java index 9ffecdc55..2f650b9fe 100644 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/test/java/org/hypertrace/traceenricher/enrichment/enrichers/space/SpaceRuleEvaluatorTest.java +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/test/java/org/hypertrace/traceenricher/enrichment/enrichers/space/SpaceRuleEvaluatorTest.java @@ -6,6 +6,8 @@ import io.reactivex.rxjava3.core.Single; import java.util.List; import java.util.NoSuchElementException; +import java.util.Optional; + import org.hypertrace.core.attribute.service.v1.LiteralValue; import org.hypertrace.core.datamodel.Event; import org.hypertrace.core.datamodel.StructuredTrace; @@ -48,7 +50,7 @@ void beforeEach() { @Test void testConvertsStringValue() { when(this.mockAttributeReader.getSpanValue(this.mockTrace, this.mockSpan, MOCK_SCOPE, MOCK_KEY)) - .thenReturn(Single.just(LiteralValue.newBuilder().setStringValue("attr-value").build())); + .thenReturn(Optional.of(LiteralValue.newBuilder().setStringValue("attr-value").build())); assertEquals( List.of("attr-value"), this.ruleEvaluator.calculateSpacesForRule(this.mockTrace, this.mockSpan, this.rule)); @@ -57,7 +59,7 @@ void testConvertsStringValue() { @Test void testConvertsIntValue() { when(this.mockAttributeReader.getSpanValue(this.mockTrace, this.mockSpan, MOCK_SCOPE, MOCK_KEY)) - .thenReturn(Single.just(LiteralValue.newBuilder().setIntValue(12).build())); + .thenReturn(Optional.of(LiteralValue.newBuilder().setIntValue(12).build())); assertEquals( List.of("12"), this.ruleEvaluator.calculateSpacesForRule(this.mockTrace, this.mockSpan, this.rule)); @@ -66,7 +68,7 @@ void testConvertsIntValue() { @Test void testConvertsNoValue() { when(this.mockAttributeReader.getSpanValue(this.mockTrace, this.mockSpan, MOCK_SCOPE, MOCK_KEY)) - .thenReturn(Single.error(new NoSuchElementException("no value"))); + .thenReturn(Optional.empty()); assertEquals( List.of(), this.ruleEvaluator.calculateSpacesForRule(this.mockTrace, this.mockSpan, this.rule)); diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java index 5ac72433e..27fb420ff 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java @@ -74,20 +74,17 @@ private boolean isExcludedEntityType(final EntityType entityType) { } private void writeEntityIfExists(EntityType entityType, StructuredTrace trace, Event span) { - this.buildEntity(entityType, trace, span) - .subscribe( - entity -> { - UpsertCondition upsertCondition = - this.buildUpsertCondition(entityType, trace, span) - .defaultIfEmpty(UpsertCondition.getDefaultInstance()) - .blockingGet(); - - this.entityDataClient.createOrUpdateEntityEventually( - RequestContext.forTenantId(this.traceAttributeReader.getTenantId(span)), - entity, - upsertCondition, - this.writeThrottleDuration); - }); + Optional entityOptional = this.buildEntity(entityType, trace, span); + if (entityOptional.isEmpty()) { + return; + } + Optional upsertConditionOptional = this.buildUpsertCondition(entityType, trace, span); + // TODO: in follow up PR batch eventual entity writes for single tenant + upsertConditionOptional.ifPresent(upsertCondition -> this.entityDataClient.createOrUpdateEntityEventually( + RequestContext.forTenantId(this.traceAttributeReader.getTenantId(span)), + entityOptional.get(), + upsertCondition, + this.writeThrottleDuration)); } private Optional buildUpsertCondition( diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/TraceEntityAccessorBuilder.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/TraceEntityAccessorBuilder.java index baecbd2df..0f4b6f781 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/TraceEntityAccessorBuilder.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/TraceEntityAccessorBuilder.java @@ -4,25 +4,26 @@ import java.time.Duration; import java.util.Set; -import org.hypertrace.core.attribute.service.cachingclient.CachingAttributeClient; + import org.hypertrace.entity.data.service.rxclient.EntityDataClient; import org.hypertrace.entity.type.service.rxclient.EntityTypeClient; +import org.hypertrace.trace.provider.AttributeProvider; import org.hypertrace.trace.reader.attributes.TraceAttributeReaderFactory; public class TraceEntityAccessorBuilder { private final EntityTypeClient entityTypeClient; private final EntityDataClient entityDataClient; - private final CachingAttributeClient attributeClient; + private final AttributeProvider attributeProvider; private Duration entityWriteThrottleDuration = Duration.ofSeconds(15); private Set excludedEntityTypes = emptySet(); public TraceEntityAccessorBuilder( EntityTypeClient entityTypeClient, EntityDataClient entityDataClient, - CachingAttributeClient attributeClient) { + AttributeProvider attributeProvider) { this.entityTypeClient = entityTypeClient; this.entityDataClient = entityDataClient; - this.attributeClient = attributeClient; + this.attributeProvider = attributeProvider; } public TraceEntityAccessorBuilder withEntityWriteThrottleDuration(Duration duration) { @@ -39,8 +40,8 @@ public TraceEntityAccessor build() { return new DefaultTraceEntityAccessor( this.entityTypeClient, this.entityDataClient, - this.attributeClient, - TraceAttributeReaderFactory.build(this.attributeClient), + this.attributeProvider, + TraceAttributeReaderFactory.build(this.attributeProvider), entityWriteThrottleDuration, excludedEntityTypes); } diff --git a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/AttributeValueConverterTest.java b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/AttributeValueConverterTest.java index efdede23c..4b5a678d9 100644 --- a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/AttributeValueConverterTest.java +++ b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/AttributeValueConverterTest.java @@ -20,35 +20,35 @@ class AttributeValueConverterTest { @Test void convertsStringValue() { assertEquals( - stringAttributeValue("foo"), convertToAttributeValue(stringLiteral("foo")).blockingGet()); + stringAttributeValue("foo"), convertToAttributeValue(stringLiteral("foo")).get()); assertEquals( - stringAttributeValue(""), convertToAttributeValue(stringLiteral("")).blockingGet()); + stringAttributeValue(""), convertToAttributeValue(stringLiteral("")).get()); } @Test void convertsBooleanValue() { assertEquals( - booleanAttributeValue(true), convertToAttributeValue(booleanLiteral(true)).blockingGet()); + booleanAttributeValue(true), convertToAttributeValue(booleanLiteral(true)).get()); assertEquals( - booleanAttributeValue(false), convertToAttributeValue(booleanLiteral(false)).blockingGet()); + booleanAttributeValue(false), convertToAttributeValue(booleanLiteral(false)).get()); } @Test void convertsIntValue() { - assertEquals(longAttributeValue(0), convertToAttributeValue(longLiteral(0)).blockingGet()); - assertEquals(longAttributeValue(100), convertToAttributeValue(longLiteral(100)).blockingGet()); + assertEquals(longAttributeValue(0), convertToAttributeValue(longLiteral(0)).get()); + assertEquals(longAttributeValue(100), convertToAttributeValue(longLiteral(100)).get()); } @Test void convertsFloatValue() { assertEquals( - doubleAttributeValue(10.4), convertToAttributeValue(doubleLiteral(10.4)).blockingGet()); + doubleAttributeValue(10.4), convertToAttributeValue(doubleLiteral(10.4)).get()); assertEquals( - doubleAttributeValue(-3.5), convertToAttributeValue(doubleLiteral(-3.5)).blockingGet()); + doubleAttributeValue(-3.5), convertToAttributeValue(doubleLiteral(-3.5)).get()); } @Test void emptyOnUnknownValue() { - assertTrue(convertToAttributeValue(LiteralValue.getDefaultInstance()).isEmpty().blockingGet()); + assertTrue(convertToAttributeValue(LiteralValue.getDefaultInstance()).isEmpty()); } } diff --git a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessorTest.java b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessorTest.java index 848b54fe9..c135f50ff 100644 --- a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessorTest.java +++ b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessorTest.java @@ -22,8 +22,9 @@ import java.util.Arrays; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Optional; import java.util.Set; -import org.hypertrace.core.attribute.service.cachingclient.CachingAttributeClient; + import org.hypertrace.core.attribute.service.v1.AttributeMetadata; import org.hypertrace.core.attribute.service.v1.AttributeSource; import org.hypertrace.core.attribute.service.v1.AttributeType; @@ -39,6 +40,7 @@ import org.hypertrace.entity.type.service.rxclient.EntityTypeClient; import org.hypertrace.entity.type.service.v2.EntityType; import org.hypertrace.entity.type.service.v2.EntityType.EntityFormationCondition; +import org.hypertrace.trace.provider.AttributeProvider; import org.hypertrace.trace.reader.attributes.TraceAttributeReader; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -119,7 +121,8 @@ class DefaultTraceEntityAccessorTest { @Mock EntityTypeClient mockTypeClient; @Mock EntityDataClient mockDataClient; - @Mock CachingAttributeClient mockAttributeClient; + @Mock + AttributeProvider mockAttributeProvider; @Mock TraceAttributeReader mockAttributeReader; MockedStatic mockSchedulers; @@ -132,7 +135,7 @@ void beforeEach() { new DefaultTraceEntityAccessor( this.mockTypeClient, this.mockDataClient, - this.mockAttributeClient, + this.mockAttributeProvider, this.mockAttributeReader, DEFAULT_DURATION, EXCLUDE_ENTITY_TYPES); @@ -288,24 +291,24 @@ private void mockTenantId() { private void mockAttributeRead(AttributeMetadata attributeMetadata, LiteralValue value) { when(this.mockAttributeReader.getSpanValue( TEST_TRACE, TEST_SPAN, attributeMetadata.getScopeString(), attributeMetadata.getKey())) - .thenReturn(Single.just(value)); + .thenReturn(Optional.of(value)); } private void mockAttributeReadError(AttributeMetadata attributeMetadata) { when(this.mockAttributeReader.getSpanValue( TEST_TRACE, TEST_SPAN, attributeMetadata.getScopeString(), attributeMetadata.getKey())) - .thenReturn(Single.error(new NoSuchElementException())); + .thenReturn(Optional.empty()); } private void mockGetAllAttributes(AttributeMetadata... attributeMetadata) { - when(this.mockAttributeClient.getAllInScope(TEST_ENTITY_TYPE_NAME)) - .thenReturn(Single.just(Arrays.asList(attributeMetadata))); + when(this.mockAttributeProvider.getAllInScope(TENANT_ID, TEST_ENTITY_TYPE_NAME)) + .thenReturn(Optional.of(Arrays.asList(attributeMetadata))); } private void mockGetSingleAttribute(AttributeMetadata attributeMetadata) { - when(this.mockAttributeClient.get( - attributeMetadata.getScopeString(), attributeMetadata.getKey())) - .thenReturn(Single.just(attributeMetadata)); + when(this.mockAttributeProvider.get( + TENANT_ID, attributeMetadata.getScopeString(), attributeMetadata.getKey())) + .thenReturn(Optional.of(attributeMetadata)); } private void mockAllEntityTypes(EntityType entityType) { From 49535432a520e27da570c26748a5c2b676147340 Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Thu, 16 Nov 2023 12:56:33 +0530 Subject: [PATCH 04/29] spotless --- .../clients/AttributeServiceCachedClient.java | 92 +++++++++++++------ .../enrichers/space/SpaceRuleEvaluator.java | 3 +- .../space/SpaceRuleEvaluatorTest.java | 3 - .../entities/AttributeValueConverter.java | 7 +- .../entities/DefaultTraceEntityAccessor.java | 53 ++++++----- .../entities/TraceEntityAccessorBuilder.java | 1 - .../trace/provider/AttributeProvider.java | 3 +- .../DefaultTraceAttributeReader.java | 4 +- .../attributes/DefaultValueResolver.java | 64 ++++++------- .../attributes/TraceAttributeReader.java | 4 +- .../TraceAttributeReaderFactory.java | 4 +- .../reader/attributes/ValueResolver.java | 5 +- .../entities/AttributeValueConverterTest.java | 15 +-- .../DefaultTraceEntityAccessorTest.java | 6 +- .../DefaultTraceAttributeReaderTest.java | 16 ++-- .../attributes/DefaultValueResolverTest.java | 17 ++-- 16 files changed, 155 insertions(+), 142 deletions(-) diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java index 57a86a7d7..619d42a53 100644 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java @@ -8,48 +8,60 @@ import com.google.common.collect.Table; import com.google.common.util.concurrent.RateLimiter; import com.google.common.util.concurrent.ThreadFactoryBuilder; -import io.grpc.CallCredentials; -import io.grpc.Channel; -import org.hypertrace.core.attribute.service.v1.AttributeMetadata; -import org.hypertrace.core.attribute.service.v1.AttributeServiceGrpc.AttributeServiceBlockingStub; -import org.hypertrace.core.attribute.service.v1.GetAttributesRequest; -import org.hypertrace.core.grpcutils.context.RequestContext; -import org.hypertrace.trace.provider.AttributeProvider; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.time.Duration; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.function.Function; +import org.hypertrace.core.attribute.service.v1.AttributeMetadata; +import org.hypertrace.core.attribute.service.v1.AttributeServiceGrpc.AttributeServiceBlockingStub; +import org.hypertrace.core.attribute.service.v1.GetAttributesRequest; +import org.hypertrace.core.grpcutils.context.RequestContext; +import org.hypertrace.trace.provider.AttributeProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class AttributeServiceCachedClient implements AttributeProvider { - private static final RateLimiter LOG_LIMITER = RateLimiter.create(1/60d); + private static final RateLimiter LOG_LIMITER = RateLimiter.create(1 / 60d); private static final Logger LOGGER = LoggerFactory.getLogger(AttributeServiceCachedClient.class); private final LoadingCache> cache; private final Cache scopeAndKeyLookup; private final AttributeServiceBlockingStub attributeServiceBlockingStub; private final long deadlineMs; - AttributeServiceCachedClient(AttributeServiceBlockingStub attributeServiceBlockingStub, int cacheMaxSize, Duration expireAfterWriteDuration, Duration refreshAfterWriteDuration, Duration deadline, int executorThreads) { + + AttributeServiceCachedClient( + AttributeServiceBlockingStub attributeServiceBlockingStub, + int cacheMaxSize, + Duration expireAfterWriteDuration, + Duration refreshAfterWriteDuration, + Duration deadline, + int executorThreads) { deadlineMs = deadline.toMillis(); this.attributeServiceBlockingStub = attributeServiceBlockingStub; - cache = CacheBuilder.newBuilder().maximumSize(cacheMaxSize).expireAfterWrite(expireAfterWriteDuration).refreshAfterWrite(refreshAfterWriteDuration).build(CacheLoader.asyncReloading( - CacheLoader.from(this::loadTable), - Executors.newFixedThreadPool(executorThreads, this.buildThreadFactory()))); - scopeAndKeyLookup = CacheBuilder.newBuilder().expireAfterWrite(expireAfterWriteDuration).build(); + cache = + CacheBuilder.newBuilder() + .maximumSize(cacheMaxSize) + .expireAfterWrite(expireAfterWriteDuration) + .refreshAfterWrite(refreshAfterWriteDuration) + .build( + CacheLoader.asyncReloading( + CacheLoader.from(this::loadTable), + Executors.newFixedThreadPool(executorThreads, this.buildThreadFactory()))); + scopeAndKeyLookup = + CacheBuilder.newBuilder().expireAfterWrite(expireAfterWriteDuration).build(); } - public Optional get(String tenantId, String attributeScope, String attributeKey) { + public Optional get( + String tenantId, String attributeScope, String attributeKey) { try { - return Optional.ofNullable(cache.getUnchecked(tenantId)).map(table -> table.get(attributeScope, attributeKey)); + return Optional.ofNullable(cache.getUnchecked(tenantId)) + .map(table -> table.get(attributeScope, attributeKey)); } catch (Exception e) { if (LOG_LIMITER.tryAcquire()) { - LOGGER.error("No attribute available for scope {} and key {}", attributeScope, attributeKey); + LOGGER.error( + "No attribute available for scope {} and key {}", attributeScope, attributeKey); } return Optional.empty(); } @@ -57,7 +69,11 @@ public Optional get(String tenantId, String attributeScope, S public Optional getById(String tenantId, String attributeId) { try { - return Optional.ofNullable(cache.getUnchecked(tenantId)).flatMap(table -> Optional.ofNullable(scopeAndKeyLookup.getIfPresent(attributeId)).map(scopeAndKey -> table.get(scopeAndKey.scope, scopeAndKey.key))); + return Optional.ofNullable(cache.getUnchecked(tenantId)) + .flatMap( + table -> + Optional.ofNullable(scopeAndKeyLookup.getIfPresent(attributeId)) + .map(scopeAndKey -> table.get(scopeAndKey.scope, scopeAndKey.key))); } catch (Exception e) { if (LOG_LIMITER.tryAcquire()) { LOGGER.error("No attribute available for id {}", attributeId); @@ -68,7 +84,8 @@ public Optional getById(String tenantId, String attributeId) public Optional> getAllInScope(String tenantId, String attributeScope) { try { - return Optional.ofNullable(cache.getUnchecked(tenantId)).map(table -> List.copyOf(table.row(attributeScope).values())); + return Optional.ofNullable(cache.getUnchecked(tenantId)) + .map(table -> List.copyOf(table.row(attributeScope).values())); } catch (Exception e) { if (LOG_LIMITER.tryAcquire()) { LOGGER.error("No attributes available for scope {}", attributeScope); @@ -77,17 +94,32 @@ public Optional> getAllInScope(String tenantId, String a } } - private Table loadTable(String tenantId) { - List attributeMetadataList = RequestContext.forTenantId(tenantId).call(() -> attributeServiceBlockingStub.withDeadlineAfter(deadlineMs, TimeUnit.MILLISECONDS).getAttributes(GetAttributesRequest.getDefaultInstance())).getAttributesList(); - attributeMetadataList.forEach(attributeMetadata -> scopeAndKeyLookup.put(attributeMetadata.getId(), new AttributeScopeAndKey(attributeMetadata.getScopeString(), attributeMetadata.getKey()))); - return attributeMetadataList.stream().collect(ImmutableTable.toImmutableTable(AttributeMetadata::getScopeString, AttributeMetadata::getKey, Function.identity())); + private Table loadTable(String tenantId) { + List attributeMetadataList = + RequestContext.forTenantId(tenantId) + .call( + () -> + attributeServiceBlockingStub + .withDeadlineAfter(deadlineMs, TimeUnit.MILLISECONDS) + .getAttributes(GetAttributesRequest.getDefaultInstance())) + .getAttributesList(); + attributeMetadataList.forEach( + attributeMetadata -> + scopeAndKeyLookup.put( + attributeMetadata.getId(), + new AttributeScopeAndKey( + attributeMetadata.getScopeString(), attributeMetadata.getKey()))); + return attributeMetadataList.stream() + .collect( + ImmutableTable.toImmutableTable( + AttributeMetadata::getScopeString, AttributeMetadata::getKey, Function.identity())); } private ThreadFactory buildThreadFactory() { return new ThreadFactoryBuilder() - .setDaemon(true) - .setNameFormat("attribute-service-cache-%d") - .build(); + .setDaemon(true) + .setNameFormat("attribute-service-cache-%d") + .build(); } private static final class AttributeScopeAndKey { diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/enrichers/space/SpaceRuleEvaluator.java b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/enrichers/space/SpaceRuleEvaluator.java index 0d3e11f87..215cf46ea 100644 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/enrichers/space/SpaceRuleEvaluator.java +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/enrichers/space/SpaceRuleEvaluator.java @@ -38,6 +38,7 @@ private List calculateSpacesForAttribute( attributeValueRuleData.getAttributeKey()) .flatMap(ValueCoercer::convertToString) .filter(string -> !string.isEmpty()) - .map(List::of).orElse(Collections.emptyList()); + .map(List::of) + .orElse(Collections.emptyList()); } } diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/test/java/org/hypertrace/traceenricher/enrichment/enrichers/space/SpaceRuleEvaluatorTest.java b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/test/java/org/hypertrace/traceenricher/enrichment/enrichers/space/SpaceRuleEvaluatorTest.java index 2f650b9fe..73072b5b4 100644 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/test/java/org/hypertrace/traceenricher/enrichment/enrichers/space/SpaceRuleEvaluatorTest.java +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/test/java/org/hypertrace/traceenricher/enrichment/enrichers/space/SpaceRuleEvaluatorTest.java @@ -3,11 +3,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.when; -import io.reactivex.rxjava3.core.Single; import java.util.List; -import java.util.NoSuchElementException; import java.util.Optional; - import org.hypertrace.core.attribute.service.v1.LiteralValue; import org.hypertrace.core.datamodel.Event; import org.hypertrace.core.datamodel.StructuredTrace; diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/AttributeValueConverter.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/AttributeValueConverter.java index 440ac3e40..e20200824 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/AttributeValueConverter.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/AttributeValueConverter.java @@ -1,14 +1,12 @@ package org.hypertrace.trace.accessor.entities; -import io.reactivex.rxjava3.core.Maybe; +import java.util.Optional; import org.hypertrace.core.attribute.service.v1.LiteralValue; import org.hypertrace.entity.data.service.v1.AttributeValue; import org.hypertrace.entity.data.service.v1.Value; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Optional; - interface AttributeValueConverter { Logger LOG = LoggerFactory.getLogger(AttributeValueConverter.class); @@ -17,7 +15,8 @@ static Optional convertToAttributeValue(LiteralValue literalValu case STRING_VALUE: return attributeValueOptional(Value.newBuilder().setString(literalValue.getStringValue())); case BOOLEAN_VALUE: - return attributeValueOptional(Value.newBuilder().setBoolean(literalValue.getBooleanValue())); + return attributeValueOptional( + Value.newBuilder().setBoolean(literalValue.getBooleanValue())); case FLOAT_VALUE: return attributeValueOptional(Value.newBuilder().setDouble(literalValue.getFloatValue())); case INT_VALUE: diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java index 27fb420ff..e72a7f688 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java @@ -1,11 +1,8 @@ package org.hypertrace.trace.accessor.entities; -import static io.reactivex.rxjava3.core.Maybe.zip; import static java.util.function.Predicate.not; -import io.reactivex.rxjava3.core.Maybe; import java.time.Duration; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -13,7 +10,6 @@ import java.util.Set; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; -import org.hypertrace.core.attribute.service.cachingclient.CachingAttributeClient; import org.hypertrace.core.attribute.service.v1.AttributeMetadata; import org.hypertrace.core.attribute.service.v1.AttributeSource; import org.hypertrace.core.attribute.service.v1.LiteralValue; @@ -78,13 +74,16 @@ private void writeEntityIfExists(EntityType entityType, StructuredTrace trace, E if (entityOptional.isEmpty()) { return; } - Optional upsertConditionOptional = this.buildUpsertCondition(entityType, trace, span); + Optional upsertConditionOptional = + this.buildUpsertCondition(entityType, trace, span); // TODO: in follow up PR batch eventual entity writes for single tenant - upsertConditionOptional.ifPresent(upsertCondition -> this.entityDataClient.createOrUpdateEntityEventually( - RequestContext.forTenantId(this.traceAttributeReader.getTenantId(span)), - entityOptional.get(), - upsertCondition, - this.writeThrottleDuration)); + upsertConditionOptional.ifPresent( + upsertCondition -> + this.entityDataClient.createOrUpdateEntityEventually( + RequestContext.forTenantId(this.traceAttributeReader.getTenantId(span)), + entityOptional.get(), + upsertCondition, + this.writeThrottleDuration)); } private Optional buildUpsertCondition( @@ -92,7 +91,11 @@ private Optional buildUpsertCondition( if (entityType.getTimestampAttributeKey().isEmpty()) { return Optional.empty(); } - return this.attributeProvider.get(this.traceAttributeReader.getTenantId(span), entityType.getAttributeScope(), entityType.getTimestampAttributeKey()) + return this.attributeProvider + .get( + this.traceAttributeReader.getTenantId(span), + entityType.getAttributeScope(), + entityType.getTimestampAttributeKey()) .filter(this::isEntitySourced) .flatMap( attribute -> @@ -125,20 +128,21 @@ private Optional buildEntity(EntityType entityType, StructuredTrace trac Optional> attributes = this.resolveAllAttributes(entityType.getAttributeScope(), trace, span); Optional id = - attributes.flatMap( - map -> this.extractNonEmptyString(map, entityType.getIdAttributeKey())); + attributes.flatMap(map -> this.extractNonEmptyString(map, entityType.getIdAttributeKey())); Optional name = attributes.flatMap( map -> this.extractNonEmptyString(map, entityType.getNameAttributeKey())); if (id.isEmpty() || name.isEmpty()) { return Optional.empty(); } - return Optional.of(Entity.newBuilder() - .setEntityId(id.get()) - .setEntityType(entityType.getName()) - .setEntityName(name.get()) - .putAllAttributes(attributes.get()) - .build()).filter(entity -> this.canCreateEntity(entityType, entity)); + return Optional.of( + Entity.newBuilder() + .setEntityId(id.get()) + .setEntityType(entityType.getName()) + .setEntityName(name.get()) + .putAllAttributes(attributes.get()) + .build()) + .filter(entity -> this.canCreateEntity(entityType, entity)); } private boolean canCreateEntity(EntityType entityType, Entity entity) { @@ -160,11 +164,18 @@ private boolean passesFormationCondition(Entity entity, EntityFormationCondition private Optional> resolveAllAttributes( String scope, StructuredTrace trace, Event span) { - Optional> mayBeAttributeMetadataList = this.attributeProvider.getAllInScope(this.traceAttributeReader.getTenantId(span), scope); + Optional> mayBeAttributeMetadataList = + this.attributeProvider.getAllInScope(this.traceAttributeReader.getTenantId(span), scope); if (mayBeAttributeMetadataList.isEmpty()) { return Optional.empty(); } - Map resolvedAttributes = mayBeAttributeMetadataList.get().stream().filter(this::isEntitySourced).map(attributeMetadata -> this.resolveAttribute(attributeMetadata, trace, span)).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toMap(Entry::getKey, Entry::getValue)); + Map resolvedAttributes = + mayBeAttributeMetadataList.get().stream() + .filter(this::isEntitySourced) + .map(attributeMetadata -> this.resolveAttribute(attributeMetadata, trace, span)) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); if (resolvedAttributes.isEmpty()) { return Optional.empty(); } diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/TraceEntityAccessorBuilder.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/TraceEntityAccessorBuilder.java index 0f4b6f781..75a6f315d 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/TraceEntityAccessorBuilder.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/TraceEntityAccessorBuilder.java @@ -4,7 +4,6 @@ import java.time.Duration; import java.util.Set; - import org.hypertrace.entity.data.service.rxclient.EntityDataClient; import org.hypertrace.entity.type.service.rxclient.EntityTypeClient; import org.hypertrace.trace.provider.AttributeProvider; diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/provider/AttributeProvider.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/provider/AttributeProvider.java index 233deae31..0b6f349a2 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/provider/AttributeProvider.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/provider/AttributeProvider.java @@ -1,9 +1,8 @@ package org.hypertrace.trace.provider; -import org.hypertrace.core.attribute.service.v1.AttributeMetadata; - import java.util.List; import java.util.Optional; +import org.hypertrace.core.attribute.service.v1.AttributeMetadata; public interface AttributeProvider { Optional get(String tenantId, String attributeScope, String attributeKey); diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReader.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReader.java index 5f026ebd6..b85e87cc5 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReader.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReader.java @@ -2,15 +2,13 @@ import static org.hypertrace.trace.reader.attributes.ValueSource.TRACE_SCOPE; -import io.reactivex.rxjava3.core.Single; +import java.util.Optional; import org.hypertrace.core.attribute.service.v1.AttributeMetadata; import org.hypertrace.core.attribute.service.v1.LiteralValue; import org.hypertrace.core.datamodel.Event; import org.hypertrace.core.datamodel.StructuredTrace; import org.hypertrace.trace.provider.AttributeProvider; -import java.util.Optional; - class DefaultTraceAttributeReader implements TraceAttributeReader { private final AttributeProvider attributeProvider; diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java index 59e7435c9..19ece32dc 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java @@ -1,20 +1,13 @@ package org.hypertrace.trace.reader.attributes; -import static io.reactivex.rxjava3.core.Single.zip; - import com.google.common.util.concurrent.RateLimiter; -import io.reactivex.rxjava3.core.Maybe; -import io.reactivex.rxjava3.core.Observable; -import io.reactivex.rxjava3.core.Single; import java.util.List; import java.util.NoSuchElementException; import java.util.Optional; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; - import lombok.extern.slf4j.Slf4j; -import org.hypertrace.core.attribute.service.cachingclient.CachingAttributeClient; import org.hypertrace.core.attribute.service.projection.AttributeProjection; import org.hypertrace.core.attribute.service.projection.AttributeProjectionRegistry; import org.hypertrace.core.attribute.service.v1.AttributeDefinition; @@ -24,7 +17,6 @@ import org.hypertrace.core.attribute.service.v1.AttributeMetadata; import org.hypertrace.core.attribute.service.v1.AttributeType; import org.hypertrace.core.attribute.service.v1.LiteralValue; -import org.hypertrace.core.attribute.service.v1.LiteralValue.ValueCase; import org.hypertrace.core.attribute.service.v1.Projection; import org.hypertrace.core.attribute.service.v1.ProjectionExpression; import org.hypertrace.trace.provider.AttributeProvider; @@ -37,8 +29,7 @@ class DefaultValueResolver implements ValueResolver { private final AttributeProjectionRegistry attributeProjectionRegistry; DefaultValueResolver( - AttributeProvider attributeClient, - AttributeProjectionRegistry attributeProjectionRegistry) { + AttributeProvider attributeClient, AttributeProjectionRegistry attributeProjectionRegistry) { this.attributeClient = attributeClient; this.attributeProjectionRegistry = attributeProjectionRegistry; } @@ -47,8 +38,8 @@ class DefaultValueResolver implements ValueResolver { public Optional resolve( ValueSource valueSource, AttributeMetadata attributeMetadata) { if (!attributeMetadata.hasDefinition()) { - if(LOGGING_LIMITER.tryAcquire()) { - log.error("Attribute definition not set for attribute: " + attributeMetadata.getId()); + if (LOGGING_LIMITER.tryAcquire()) { + log.error("Attribute definition not set for attribute: " + attributeMetadata.getId()); } return Optional.empty(); } @@ -91,19 +82,19 @@ private Optional resolveValue( AttributeKind attributeKind, String path) { Optional matchingValueSource = contextValueSource.sourceForScope(attributeScope); - if(matchingValueSource.isEmpty()) { - if(LOGGING_LIMITER.tryAcquire()) { + if (matchingValueSource.isEmpty()) { + if (LOGGING_LIMITER.tryAcquire()) { log.error("No value source available supporting scope %s", attributeScope); } return Optional.empty(); } switch (attributeType) { case ATTRIBUTE: - return matchingValueSource - .flatMap(valueSource -> valueSource.getAttribute(path, attributeKind)); + return matchingValueSource.flatMap( + valueSource -> valueSource.getAttribute(path, attributeKind)); case METRIC: - return matchingValueSource - .flatMap(valueSource -> valueSource.getMetric(path, attributeKind)); + return matchingValueSource.flatMap( + valueSource -> valueSource.getMetric(path, attributeKind)); case UNRECOGNIZED: case TYPE_UNDEFINED: default: @@ -114,7 +105,8 @@ private Optional resolveValue( private Optional resolveProjection(ValueSource valueSource, Projection projection) { switch (projection.getValueCase()) { case ATTRIBUTE_ID: - return this.attributeClient.getById(valueSource.tenantId(), projection.getAttributeId()) + return this.attributeClient + .getById(valueSource.tenantId(), projection.getAttributeId()) .flatMap(attributeMetadata -> this.resolve(valueSource, attributeMetadata)); case LITERAL: return Optional.of(projection.getLiteral()); @@ -122,7 +114,7 @@ private Optional resolveProjection(ValueSource valueSource, Projec return this.resolveExpression(valueSource, projection.getExpression()); case VALUE_NOT_SET: default: - if(LOGGING_LIMITER.tryAcquire()) { + if (LOGGING_LIMITER.tryAcquire()) { log.error("Unrecognized projection type"); } return Optional.empty(); @@ -140,18 +132,18 @@ private Optional resolveFirstValuePresent( AttributeDefinitions definitions) { return definitions.getDefinitionsList().stream() - .map( - definition -> this.resolveDefinition(valueSource, attributeMetadata, definition)) - .filter(Optional::isPresent) - .findFirst() - .flatMap(Function.identity()); + .map(definition -> this.resolveDefinition(valueSource, attributeMetadata, definition)) + .filter(Optional::isPresent) + .findFirst() + .flatMap(Function.identity()); } private Optional resolveExpression( ValueSource valueSource, ProjectionExpression expression) { - Optional maybeProjection = this.attributeProjectionRegistry.getProjection(expression.getOperator()); - if(maybeProjection.isEmpty()) { - if(LOGGING_LIMITER.tryAcquire()) { + Optional maybeProjection = + this.attributeProjectionRegistry.getProjection(expression.getOperator()); + if (maybeProjection.isEmpty()) { + if (LOGGING_LIMITER.tryAcquire()) { log.error("Unregistered projection operator: {}", expression.getOperator()); } return Optional.empty(); @@ -164,12 +156,14 @@ private Optional resolveExpression( private Optional> resolveArgumentList( ValueSource valueSource, List arguments) { - Stream> resolvedArguments = arguments.stream().map(argument -> this.resolveProjection(valueSource, argument)); - try { - return Optional.of(resolvedArguments.map(Optional::orElseThrow).collect(Collectors.toUnmodifiableList())); - } catch (NoSuchElementException elementException) { - // if any of the arguments don't resolve, fail argument resolution - return Optional.empty(); - } + Stream> resolvedArguments = + arguments.stream().map(argument -> this.resolveProjection(valueSource, argument)); + try { + return Optional.of( + resolvedArguments.map(Optional::orElseThrow).collect(Collectors.toUnmodifiableList())); + } catch (NoSuchElementException elementException) { + // if any of the arguments don't resolve, fail argument resolution + return Optional.empty(); + } } } diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceAttributeReader.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceAttributeReader.java index 4760707cb..5d3e43d46 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceAttributeReader.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceAttributeReader.java @@ -1,11 +1,9 @@ package org.hypertrace.trace.reader.attributes; -import io.reactivex.rxjava3.core.Single; +import java.util.Optional; import org.apache.avro.generic.GenericRecord; import org.hypertrace.core.attribute.service.v1.LiteralValue; -import java.util.Optional; - public interface TraceAttributeReader { Optional getSpanValue(T trace, S span, String attributeScope, String attributeKey); diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceAttributeReaderFactory.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceAttributeReaderFactory.java index 0c1928c96..58f7fddf4 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceAttributeReaderFactory.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceAttributeReaderFactory.java @@ -1,13 +1,11 @@ package org.hypertrace.trace.reader.attributes; -import org.hypertrace.core.attribute.service.cachingclient.CachingAttributeClient; import org.hypertrace.core.datamodel.Event; import org.hypertrace.core.datamodel.StructuredTrace; import org.hypertrace.trace.provider.AttributeProvider; public interface TraceAttributeReaderFactory { - static TraceAttributeReader build( - AttributeProvider attributeProvider) { + static TraceAttributeReader build(AttributeProvider attributeProvider) { return new DefaultTraceAttributeReader(attributeProvider); } } diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/ValueResolver.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/ValueResolver.java index 9a9d3b723..f42ca4caf 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/ValueResolver.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/ValueResolver.java @@ -1,14 +1,11 @@ package org.hypertrace.trace.reader.attributes; -import io.reactivex.rxjava3.core.Single; -import org.hypertrace.core.attribute.service.cachingclient.CachingAttributeClient; +import java.util.Optional; import org.hypertrace.core.attribute.service.projection.AttributeProjectionRegistry; import org.hypertrace.core.attribute.service.v1.AttributeMetadata; import org.hypertrace.core.attribute.service.v1.LiteralValue; import org.hypertrace.trace.provider.AttributeProvider; -import java.util.Optional; - public interface ValueResolver { Optional resolve(ValueSource valueSource, AttributeMetadata attributeMetadata); diff --git a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/AttributeValueConverterTest.java b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/AttributeValueConverterTest.java index 4b5a678d9..15a9315a0 100644 --- a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/AttributeValueConverterTest.java +++ b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/AttributeValueConverterTest.java @@ -19,16 +19,13 @@ class AttributeValueConverterTest { @Test void convertsStringValue() { - assertEquals( - stringAttributeValue("foo"), convertToAttributeValue(stringLiteral("foo")).get()); - assertEquals( - stringAttributeValue(""), convertToAttributeValue(stringLiteral("")).get()); + assertEquals(stringAttributeValue("foo"), convertToAttributeValue(stringLiteral("foo")).get()); + assertEquals(stringAttributeValue(""), convertToAttributeValue(stringLiteral("")).get()); } @Test void convertsBooleanValue() { - assertEquals( - booleanAttributeValue(true), convertToAttributeValue(booleanLiteral(true)).get()); + assertEquals(booleanAttributeValue(true), convertToAttributeValue(booleanLiteral(true)).get()); assertEquals( booleanAttributeValue(false), convertToAttributeValue(booleanLiteral(false)).get()); } @@ -41,10 +38,8 @@ void convertsIntValue() { @Test void convertsFloatValue() { - assertEquals( - doubleAttributeValue(10.4), convertToAttributeValue(doubleLiteral(10.4)).get()); - assertEquals( - doubleAttributeValue(-3.5), convertToAttributeValue(doubleLiteral(-3.5)).get()); + assertEquals(doubleAttributeValue(10.4), convertToAttributeValue(doubleLiteral(10.4)).get()); + assertEquals(doubleAttributeValue(-3.5), convertToAttributeValue(doubleLiteral(-3.5)).get()); } @Test diff --git a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessorTest.java b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessorTest.java index c135f50ff..6fa55fb19 100644 --- a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessorTest.java +++ b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessorTest.java @@ -16,15 +16,12 @@ import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Scheduler; -import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.schedulers.Schedulers; import java.time.Duration; import java.util.Arrays; import java.util.Map; -import java.util.NoSuchElementException; import java.util.Optional; import java.util.Set; - import org.hypertrace.core.attribute.service.v1.AttributeMetadata; import org.hypertrace.core.attribute.service.v1.AttributeSource; import org.hypertrace.core.attribute.service.v1.AttributeType; @@ -121,8 +118,7 @@ class DefaultTraceEntityAccessorTest { @Mock EntityTypeClient mockTypeClient; @Mock EntityDataClient mockDataClient; - @Mock - AttributeProvider mockAttributeProvider; + @Mock AttributeProvider mockAttributeProvider; @Mock TraceAttributeReader mockAttributeReader; MockedStatic mockSchedulers; diff --git a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReaderTest.java b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReaderTest.java index d14b1ea44..15fc0dc3a 100644 --- a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReaderTest.java +++ b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReaderTest.java @@ -8,7 +8,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import io.reactivex.rxjava3.core.Single; +import java.util.Optional; import org.hypertrace.core.attribute.service.v1.AttributeDefinition; import org.hypertrace.core.attribute.service.v1.AttributeKind; import org.hypertrace.core.attribute.service.v1.AttributeMetadata; @@ -22,13 +22,10 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import java.util.Optional; - @ExtendWith(MockitoExtension.class) class DefaultTraceAttributeReaderTest { - @Mock - AttributeProvider mockAttributeProvider; + @Mock AttributeProvider mockAttributeProvider; private TraceAttributeReader traceAttributeReader; @BeforeEach @@ -45,7 +42,8 @@ void canReadSpanValues() { .setValueKind(AttributeKind.TYPE_STRING) .setDefinition(AttributeDefinition.newBuilder().setSourcePath("attrPath").build()) .build(); - when(this.mockAttributeProvider.get("defaultCustomerId","TEST_SCOPE", "key")).thenReturn(Optional.of(metadata)); + when(this.mockAttributeProvider.get("defaultCustomerId", "TEST_SCOPE", "key")) + .thenReturn(Optional.of(metadata)); Event span = defaultedEventBuilder() @@ -68,7 +66,8 @@ void canReadTraceValues() { .setValueKind(AttributeKind.TYPE_STRING) .setDefinition(AttributeDefinition.newBuilder().setSourcePath("attrPath").build()) .build(); - when(this.mockAttributeProvider.get("defaultCustomerId","TRACE", "key")).thenReturn(Optional.of(metadata)); + when(this.mockAttributeProvider.get("defaultCustomerId", "TRACE", "key")) + .thenReturn(Optional.of(metadata)); StructuredTrace trace = defaultedStructuredTraceBuilder() @@ -76,7 +75,6 @@ void canReadTraceValues() { .build(); assertEquals( - stringLiteral("attrValue"), - this.traceAttributeReader.getTraceValue(trace, "key").get()); + stringLiteral("attrValue"), this.traceAttributeReader.getTraceValue(trace, "key").get()); } } diff --git a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultValueResolverTest.java b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultValueResolverTest.java index da233bf47..ef34cf448 100644 --- a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultValueResolverTest.java +++ b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultValueResolverTest.java @@ -11,10 +11,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import io.reactivex.rxjava3.core.Single; import java.util.Map; import java.util.Optional; - import org.hypertrace.core.attribute.service.projection.AttributeProjectionRegistry; import org.hypertrace.core.attribute.service.v1.AttributeDefinition; import org.hypertrace.core.attribute.service.v1.AttributeDefinition.AttributeDefinitions; @@ -38,8 +36,7 @@ @ExtendWith(MockitoExtension.class) class DefaultValueResolverTest { - @Mock - AttributeProvider mockAttributeProvider; + @Mock AttributeProvider mockAttributeProvider; @Mock StructuredTrace mockStructuredTrace; private DefaultValueResolver resolver; @@ -125,7 +122,8 @@ void resolvesAttributeProjections() { .setValueKind(AttributeKind.TYPE_INT64) .setDefinition(AttributeDefinition.newBuilder().setSourcePath("metricPath").build()) .build(); - when(this.mockAttributeProvider.getById("defaultCustomerId", "TEST_SCOPE.other")).thenReturn(Optional.of((otherMetadata))); + when(this.mockAttributeProvider.getById("defaultCustomerId", "TEST_SCOPE.other")) + .thenReturn(Optional.of((otherMetadata))); Event span = defaultedEventBuilder().setMetrics(buildMetricsWithKeyValue("metricPath", 42)).build(); @@ -169,8 +167,10 @@ void resolvesExpressionProjections() { .setValueKind(AttributeKind.TYPE_STRING) .setDefinition(AttributeDefinition.newBuilder().setSourcePath("attrPath").build()) .build(); - when(this.mockAttributeProvider.getById("defaultCustomerId", "TEST_SCOPE.first")).thenReturn(Optional.of(firstMetadata)); - when(this.mockAttributeProvider.getById("defaultCustomerId", "TEST_SCOPE.second")).thenReturn(Optional.of(secondMetadata)); + when(this.mockAttributeProvider.getById("defaultCustomerId", "TEST_SCOPE.first")) + .thenReturn(Optional.of(firstMetadata)); + when(this.mockAttributeProvider.getById("defaultCustomerId", "TEST_SCOPE.second")) + .thenReturn(Optional.of(secondMetadata)); Event span = defaultedEventBuilder() @@ -201,7 +201,8 @@ void resolvesProjectionsAcrossScopes() { .setValueKind(AttributeKind.TYPE_INT64) .setDefinition(AttributeDefinition.newBuilder().setSourcePath("metricPath").build()) .build(); - when(this.mockAttributeProvider.getById("defaultCustomerId", "TRACE.other")).thenReturn(Optional.of(otherMetadata)); + when(this.mockAttributeProvider.getById("defaultCustomerId", "TRACE.other")) + .thenReturn(Optional.of(otherMetadata)); StructuredTrace trace = defaultedStructuredTraceBuilder() From 928e3c9eaf914e98c15a98914c1dfde27b37a297 Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Thu, 16 Nov 2023 13:28:26 +0530 Subject: [PATCH 05/29] wrap up --- .../clients/AttributeServiceCachedClient.java | 43 +++++++++++++------ .../enrichment/clients/ClientRegistry.java | 5 --- .../clients/DefaultClientRegistry.java | 33 ++++---------- .../resources/configs/common/application.conf | 7 +++ .../entities/DefaultTraceEntityAccessor.java | 15 +++---- .../attributes/DefaultValueResolverTest.java | 13 +++--- 6 files changed, 60 insertions(+), 56 deletions(-) diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java index 619d42a53..10d49870e 100644 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java @@ -8,6 +8,8 @@ import com.google.common.collect.Table; import com.google.common.util.concurrent.RateLimiter; import com.google.common.util.concurrent.ThreadFactoryBuilder; +import com.typesafe.config.Config; +import io.grpc.Channel; import java.time.Duration; import java.util.List; import java.util.Optional; @@ -16,6 +18,7 @@ import java.util.concurrent.TimeUnit; import java.util.function.Function; import org.hypertrace.core.attribute.service.v1.AttributeMetadata; +import org.hypertrace.core.attribute.service.v1.AttributeServiceGrpc; import org.hypertrace.core.attribute.service.v1.AttributeServiceGrpc.AttributeServiceBlockingStub; import org.hypertrace.core.attribute.service.v1.GetAttributesRequest; import org.hypertrace.core.grpcutils.context.RequestContext; @@ -24,6 +27,11 @@ import org.slf4j.LoggerFactory; public class AttributeServiceCachedClient implements AttributeProvider { + private static final String DEADLINE_CONFIG_KEY = "deadline"; + private static final String CACHE_MAX_SIZE_CONFIG_KEY = "maxSize"; + private static final String CACHE_REFRESH_AFTER_WRITE_CONFIG_KEY = "refreshAfterWriteDuration"; + private static final String CACHE_EXPIRE_AFTER_WRITE_CONFIG_KEY = "expireAfterWriteDuration"; + private static final String CACHE_EXECUTOR_THREADS_CONFIG_KEY = "executorThreads"; private static final RateLimiter LOG_LIMITER = RateLimiter.create(1 / 60d); private static final Logger LOGGER = LoggerFactory.getLogger(AttributeServiceCachedClient.class); private final LoadingCache> cache; @@ -31,24 +39,35 @@ public class AttributeServiceCachedClient implements AttributeProvider { private final AttributeServiceBlockingStub attributeServiceBlockingStub; private final long deadlineMs; - AttributeServiceCachedClient( - AttributeServiceBlockingStub attributeServiceBlockingStub, - int cacheMaxSize, - Duration expireAfterWriteDuration, - Duration refreshAfterWriteDuration, - Duration deadline, - int executorThreads) { - deadlineMs = deadline.toMillis(); - this.attributeServiceBlockingStub = attributeServiceBlockingStub; + AttributeServiceCachedClient(Channel channel, Config attributeServiceConfig) { + deadlineMs = + attributeServiceConfig.hasPath(DEADLINE_CONFIG_KEY) + ? attributeServiceConfig.getDuration(DEADLINE_CONFIG_KEY).toMillis() + : Duration.ofMinutes(1).toMillis(); + this.attributeServiceBlockingStub = AttributeServiceGrpc.newBlockingStub(channel); + Duration expireAfterWriteDuration = + attributeServiceConfig.hasPath(CACHE_EXPIRE_AFTER_WRITE_CONFIG_KEY) + ? attributeServiceConfig.getDuration(CACHE_EXPIRE_AFTER_WRITE_CONFIG_KEY) + : Duration.ofHours(1); cache = CacheBuilder.newBuilder() - .maximumSize(cacheMaxSize) + .maximumSize( + attributeServiceConfig.hasPath(CACHE_MAX_SIZE_CONFIG_KEY) + ? attributeServiceConfig.getLong(CACHE_MAX_SIZE_CONFIG_KEY) + : 100) .expireAfterWrite(expireAfterWriteDuration) - .refreshAfterWrite(refreshAfterWriteDuration) + .refreshAfterWrite( + attributeServiceConfig.hasPath(CACHE_REFRESH_AFTER_WRITE_CONFIG_KEY) + ? attributeServiceConfig.getDuration(CACHE_REFRESH_AFTER_WRITE_CONFIG_KEY) + : Duration.ofMinutes(15)) .build( CacheLoader.asyncReloading( CacheLoader.from(this::loadTable), - Executors.newFixedThreadPool(executorThreads, this.buildThreadFactory()))); + Executors.newFixedThreadPool( + attributeServiceConfig.hasPath(CACHE_EXECUTOR_THREADS_CONFIG_KEY) + ? attributeServiceConfig.getInt(CACHE_EXECUTOR_THREADS_CONFIG_KEY) + : 4, + this.buildThreadFactory()))); scopeAndKeyLookup = CacheBuilder.newBuilder().expireAfterWrite(expireAfterWriteDuration).build(); } diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/ClientRegistry.java b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/ClientRegistry.java index c05deb472..c4e4ba6ff 100644 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/ClientRegistry.java +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/ClientRegistry.java @@ -1,7 +1,6 @@ package org.hypertrace.traceenricher.enrichment.clients; import io.grpc.Channel; -import org.hypertrace.core.attribute.service.cachingclient.CachingAttributeClient; import org.hypertrace.core.datamodel.Event; import org.hypertrace.core.datamodel.StructuredTrace; import org.hypertrace.core.grpcutils.client.GrpcChannelRegistry; @@ -17,8 +16,6 @@ public interface ClientRegistry { GrpcChannelRegistry getChannelRegistry(); - Channel getAttributeServiceChannel(); - Channel getEntityServiceChannel(); Channel getConfigServiceChannel(); @@ -35,7 +32,5 @@ public interface ClientRegistry { EntityCache getEntityCache(); - CachingAttributeClient getCachingAttributeClient(); - UserAgentParser getUserAgentParser(); } diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/DefaultClientRegistry.java b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/DefaultClientRegistry.java index 7b6f91e46..8681a89ab 100644 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/DefaultClientRegistry.java +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/DefaultClientRegistry.java @@ -5,10 +5,8 @@ import com.typesafe.config.Config; import io.grpc.Channel; import java.time.Duration; -import java.time.temporal.ChronoUnit; import java.util.Set; import java.util.concurrent.Executor; -import org.hypertrace.core.attribute.service.cachingclient.CachingAttributeClient; import org.hypertrace.core.datamodel.Event; import org.hypertrace.core.datamodel.StructuredTrace; import org.hypertrace.core.grpcutils.client.GrpcChannelConfig; @@ -23,14 +21,16 @@ import org.hypertrace.entity.type.service.rxclient.EntityTypeClient; import org.hypertrace.trace.accessor.entities.TraceEntityAccessor; import org.hypertrace.trace.accessor.entities.TraceEntityAccessorBuilder; +import org.hypertrace.trace.provider.AttributeProvider; import org.hypertrace.trace.reader.attributes.TraceAttributeReader; import org.hypertrace.trace.reader.attributes.TraceAttributeReaderFactory; import org.hypertrace.traceenricher.enrichment.enrichers.cache.EntityCache; import org.hypertrace.traceenricher.util.UserAgentParser; public class DefaultClientRegistry implements ClientRegistry { - private static final String ATTRIBUTE_SERVICE_HOST_KEY = "attribute.service.config.host"; - private static final String ATTRIBUTE_SERVICE_PORT_KEY = "attribute.service.config.port"; + private static final String ATTRIBUTE_SERVICE_CONFIG_KEY = "attribute.service.config"; + private static final String ATTRIBUTE_SERVICE_HOST_KEY = ATTRIBUTE_SERVICE_CONFIG_KEY + ".host"; + private static final String ATTRIBUTE_SERVICE_PORT_KEY = ATTRIBUTE_SERVICE_CONFIG_KEY + ".port"; private static final String CONFIG_SERVICE_HOST_KEY = "config.service.config.host"; private static final String CONFIG_SERVICE_PORT_KEY = "config.service.config.port"; private static final String ENTITY_SERVICE_HOST_KEY = "entity.service.config.host"; @@ -39,14 +39,12 @@ public class DefaultClientRegistry implements ClientRegistry { "trace.entity.write.throttle.duration"; private static final String TRACE_ENTITY_WRITE_EXCLUDED_ENTITY_TYPES = "trace.entity.write.excluded.entity.types"; - private static final String USER_AGENT_PARSER_CONFIG_KEY = "useragent.parser"; private final Channel attributeServiceChannel; private final Channel configServiceChannel; private final Channel entityServiceChannel; private final EdsCacheClient edsCacheClient; private final EntityDataClient entityDataClient; - private final CachingAttributeClient cachingAttributeClient; private final EntityCache entityCache; private final TraceEntityAccessor entityAccessor; private final TraceAttributeReader attributeReader; @@ -68,13 +66,10 @@ public DefaultClientRegistry( this.buildChannel( config.getString(ENTITY_SERVICE_HOST_KEY), config.getInt(ENTITY_SERVICE_PORT_KEY)); - this.cachingAttributeClient = - CachingAttributeClient.builder(this.attributeServiceChannel) - .withMaximumCacheContexts(100) // 100 Tenants - .withCacheExpiration(Duration.of(15, ChronoUnit.MINUTES)) - .build(); - - this.attributeReader = TraceAttributeReaderFactory.build(this.cachingAttributeClient); + AttributeProvider attributeProvider = + new AttributeServiceCachedClient( + attributeServiceChannel, config.getConfig(ATTRIBUTE_SERVICE_CONFIG_KEY)); + this.attributeReader = TraceAttributeReaderFactory.build(attributeProvider); this.edsCacheClient = new EdsCacheClient( new EntityDataServiceClient(this.entityServiceChannel), @@ -86,7 +81,7 @@ public DefaultClientRegistry( new TraceEntityAccessorBuilder( EntityTypeClient.builder(this.entityServiceChannel).build(), this.entityDataClient, - this.cachingAttributeClient) + attributeProvider) .withEntityWriteThrottleDuration( config.hasPath(TRACE_ENTITY_WRITE_THROTTLE_DURATION) ? config.getDuration(TRACE_ENTITY_WRITE_THROTTLE_DURATION) @@ -104,11 +99,6 @@ public GrpcChannelRegistry getChannelRegistry() { return grpcChannelRegistry; } - @Override - public Channel getAttributeServiceChannel() { - return this.attributeServiceChannel; - } - @Override public Channel getEntityServiceChannel() { return this.entityServiceChannel; @@ -151,11 +141,6 @@ public EntityCache getEntityCache() { return this.entityCache; } - @Override - public CachingAttributeClient getCachingAttributeClient() { - return this.cachingAttributeClient; - } - @Override public UserAgentParser getUserAgentParser() { return this.userAgentParser; diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher/src/main/resources/configs/common/application.conf b/hypertrace-trace-enricher/hypertrace-trace-enricher/src/main/resources/configs/common/application.conf index 2be70f962..df70cdc08 100644 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher/src/main/resources/configs/common/application.conf +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher/src/main/resources/configs/common/application.conf @@ -38,6 +38,13 @@ enricher { host = ${?ATTRIBUTE_SERVICE_HOST_CONFIG} port = 9012 port = ${?ATTRIBUTE_SERVICE_PORT_CONFIG} + deadline = 30s + cache = { + maxSize = 100 + refreshAfterWriteDuration = 15m + expireAfterWriteDuration = 1h + executorThreads = 4 + } } config.service.config = { host = localhost diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java index e72a7f688..fcfef7256 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java @@ -74,16 +74,13 @@ private void writeEntityIfExists(EntityType entityType, StructuredTrace trace, E if (entityOptional.isEmpty()) { return; } - Optional upsertConditionOptional = - this.buildUpsertCondition(entityType, trace, span); // TODO: in follow up PR batch eventual entity writes for single tenant - upsertConditionOptional.ifPresent( - upsertCondition -> - this.entityDataClient.createOrUpdateEntityEventually( - RequestContext.forTenantId(this.traceAttributeReader.getTenantId(span)), - entityOptional.get(), - upsertCondition, - this.writeThrottleDuration)); + this.entityDataClient.createOrUpdateEntityEventually( + RequestContext.forTenantId(this.traceAttributeReader.getTenantId(span)), + entityOptional.get(), + this.buildUpsertCondition(entityType, trace, span) + .orElse(UpsertCondition.getDefaultInstance()), + this.writeThrottleDuration); } private Optional buildUpsertCondition( diff --git a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultValueResolverTest.java b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultValueResolverTest.java index ef34cf448..ec00499c0 100644 --- a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultValueResolverTest.java +++ b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultValueResolverTest.java @@ -8,6 +8,7 @@ import static org.hypertrace.trace.reader.attributes.LiteralValueUtil.longLiteral; import static org.hypertrace.trace.reader.attributes.LiteralValueUtil.stringLiteral; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -209,11 +210,12 @@ void resolvesProjectionsAcrossScopes() { .setMetrics(buildMetricsWithKeyValue("metricPath", 42)) .build(); + Event span = mock(Event.class); + when(span.getCustomerId()).thenReturn(trace.getCustomerId()); + assertEquals( longLiteral(42), - this.resolver - .resolve(ValueSourceFactory.forSpan(trace, mock(Event.class)), projectionMetadata) - .get()); + this.resolver.resolve(ValueSourceFactory.forSpan(trace, span), projectionMetadata).get()); } @Test @@ -309,11 +311,10 @@ void resolvesEmptyIfNoDefinitionAvailable() { buildAttributesWithKeyValues(Map.of("path.to.string", "foo", "path.to.int", "14"))) .build(); - assertEquals( - LiteralValue.getDefaultInstance(), + assertTrue( this.resolver .resolve(ValueSourceFactory.forSpan(this.mockStructuredTrace, span), metadata) - .get()); + .isEmpty()); } @Test From 1d2bd83852fffebc2a9452786c0002655560b435 Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Thu, 16 Nov 2023 13:46:08 +0530 Subject: [PATCH 06/29] remove dep --- .../trace-reader/build.gradle.kts | 1 - .../entities/TraceEntityClientContext.java | 65 ------------------- 2 files changed, 66 deletions(-) delete mode 100644 hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/TraceEntityClientContext.java diff --git a/hypertrace-trace-enricher/trace-reader/build.gradle.kts b/hypertrace-trace-enricher/trace-reader/build.gradle.kts index 9c3c7486f..0e9519d2f 100644 --- a/hypertrace-trace-enricher/trace-reader/build.gradle.kts +++ b/hypertrace-trace-enricher/trace-reader/build.gradle.kts @@ -7,7 +7,6 @@ plugins { dependencies { api("org.hypertrace.core.attribute.service:attribute-service-api:0.14.26") - api("org.hypertrace.core.attribute.service:caching-attribute-service-client:0.14.26") api("org.hypertrace.entity.service:entity-type-service-rx-client:0.8.75") api("org.hypertrace.entity.service:entity-data-service-rx-client:0.8.75") api(libs.hypertrace.data.model) diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/TraceEntityClientContext.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/TraceEntityClientContext.java deleted file mode 100644 index 31e9a26eb..000000000 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/TraceEntityClientContext.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.hypertrace.trace.accessor.entities; - -import io.grpc.Channel; -import org.hypertrace.core.attribute.service.cachingclient.CachingAttributeClient; -import org.hypertrace.entity.data.service.rxclient.EntityDataClient; -import org.hypertrace.entity.type.service.rxclient.EntityTypeClient; - -public class TraceEntityClientContext { - /** - * Instantiates a new builder which reuses existing clients - * - * @param entityTypeClient - * @param entityDataClient - * @param attributeClient - * @return {@link TraceEntityClientContext} - */ - public static TraceEntityClientContext usingClients( - EntityTypeClient entityTypeClient, - EntityDataClient entityDataClient, - CachingAttributeClient attributeClient) { - return new TraceEntityClientContext(entityTypeClient, entityDataClient, attributeClient); - } - - /** - * Instantiates a new builder which creates default configured caches for connections to each - * service. - * - * @param entityDataChannel - * @param entityTypeChannel - * @param attributeChannel - * @return {@link TraceEntityClientContext} - */ - public static TraceEntityClientContext usingChannels( - Channel entityDataChannel, Channel entityTypeChannel, Channel attributeChannel) { - return new TraceEntityClientContext( - EntityTypeClient.builder(entityTypeChannel).build(), - EntityDataClient.builder(entityDataChannel).build(), - CachingAttributeClient.builder(attributeChannel).build()); - } - - private final EntityTypeClient entityTypeClient; - private final EntityDataClient entityDataClient; - private final CachingAttributeClient attributeClient; - - private TraceEntityClientContext( - EntityTypeClient entityTypeClient, - EntityDataClient entityDataClient, - CachingAttributeClient attributeClient) { - this.entityTypeClient = entityTypeClient; - this.entityDataClient = entityDataClient; - this.attributeClient = attributeClient; - } - - public EntityTypeClient getEntityTypeClient() { - return entityTypeClient; - } - - public EntityDataClient getEntityDataClient() { - return entityDataClient; - } - - public CachingAttributeClient getAttributeClient() { - return attributeClient; - } -} From 31859dd2ca04eb9cc0072cba0661dd435af04acc Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Thu, 16 Nov 2023 15:51:47 +0530 Subject: [PATCH 07/29] todo finish test --- .../clients/AttributeServiceCachedClientTest.java | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/test/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClientTest.java diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/test/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClientTest.java b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/test/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClientTest.java new file mode 100644 index 000000000..c28cebb67 --- /dev/null +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/test/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClientTest.java @@ -0,0 +1,6 @@ +package org.hypertrace.traceenricher.enrichment.clients; + + +class AttributeServiceCachedClientTest { + // TODO: finish +} \ No newline at end of file From 202a47230be91f1ec80e1070752c0ec1227b291b Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Fri, 17 Nov 2023 15:39:29 +0530 Subject: [PATCH 08/29] update --- .../traceenricher/enrichment/clients/ClientRegistry.java | 3 +++ .../enrichment/clients/DefaultClientRegistry.java | 8 +++++++- .../clients/AttributeServiceCachedClientTest.java | 3 +-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/ClientRegistry.java b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/ClientRegistry.java index c4e4ba6ff..964d690f6 100644 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/ClientRegistry.java +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/ClientRegistry.java @@ -8,6 +8,7 @@ import org.hypertrace.entity.data.service.rxclient.EntityDataClient; import org.hypertrace.entity.query.service.v1.EntityQueryServiceGrpc.EntityQueryServiceBlockingStub; import org.hypertrace.trace.accessor.entities.TraceEntityAccessor; +import org.hypertrace.trace.provider.AttributeProvider; import org.hypertrace.trace.reader.attributes.TraceAttributeReader; import org.hypertrace.traceenricher.enrichment.enrichers.cache.EntityCache; import org.hypertrace.traceenricher.util.UserAgentParser; @@ -33,4 +34,6 @@ public interface ClientRegistry { EntityCache getEntityCache(); UserAgentParser getUserAgentParser(); + + AttributeProvider getAttributeProvider(); } diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/DefaultClientRegistry.java b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/DefaultClientRegistry.java index 8681a89ab..ca20a0612 100644 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/DefaultClientRegistry.java +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/DefaultClientRegistry.java @@ -50,6 +50,7 @@ public class DefaultClientRegistry implements ClientRegistry { private final TraceAttributeReader attributeReader; private final GrpcChannelRegistry grpcChannelRegistry; private final UserAgentParser userAgentParser; + private final AttributeProvider attributeProvider; public DefaultClientRegistry( Config config, GrpcChannelRegistry grpcChannelRegistry, Executor cacheLoaderExecutor) { @@ -66,7 +67,7 @@ public DefaultClientRegistry( this.buildChannel( config.getString(ENTITY_SERVICE_HOST_KEY), config.getInt(ENTITY_SERVICE_PORT_KEY)); - AttributeProvider attributeProvider = + this.attributeProvider = new AttributeServiceCachedClient( attributeServiceChannel, config.getConfig(ATTRIBUTE_SERVICE_CONFIG_KEY)); this.attributeReader = TraceAttributeReaderFactory.build(attributeProvider); @@ -146,6 +147,11 @@ public UserAgentParser getUserAgentParser() { return this.userAgentParser; } + @Override + public AttributeProvider getAttributeProvider() { + return attributeProvider; + } + public void shutdown() { this.grpcChannelRegistry.shutdown(); } diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/test/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClientTest.java b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/test/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClientTest.java index c28cebb67..e24298d88 100644 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/test/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClientTest.java +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/test/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClientTest.java @@ -1,6 +1,5 @@ package org.hypertrace.traceenricher.enrichment.clients; - class AttributeServiceCachedClientTest { // TODO: finish -} \ No newline at end of file +} From cdebeb3f68157d4fe3966438b30cc1810ee8a701 Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Fri, 17 Nov 2023 17:55:13 +0530 Subject: [PATCH 09/29] add cache metrics --- .../enrichment/clients/AttributeServiceCachedClient.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java index 10d49870e..0ae3c1daa 100644 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java @@ -11,6 +11,7 @@ import com.typesafe.config.Config; import io.grpc.Channel; import java.time.Duration; +import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.concurrent.Executors; @@ -22,6 +23,7 @@ import org.hypertrace.core.attribute.service.v1.AttributeServiceGrpc.AttributeServiceBlockingStub; import org.hypertrace.core.attribute.service.v1.GetAttributesRequest; import org.hypertrace.core.grpcutils.context.RequestContext; +import org.hypertrace.core.serviceframework.metrics.PlatformMetricsRegistry; import org.hypertrace.trace.provider.AttributeProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -68,6 +70,8 @@ public class AttributeServiceCachedClient implements AttributeProvider { ? attributeServiceConfig.getInt(CACHE_EXECUTOR_THREADS_CONFIG_KEY) : 4, this.buildThreadFactory()))); + PlatformMetricsRegistry.registerCache( + "attribute-service-client-cache", cache, Collections.emptyMap()); scopeAndKeyLookup = CacheBuilder.newBuilder().expireAfterWrite(expireAfterWriteDuration).build(); } From 9645b320e578580e0dcef7a53b078f355577286c Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Fri, 17 Nov 2023 18:03:31 +0530 Subject: [PATCH 10/29] nit --- .../enrichment/clients/AttributeServiceCachedClient.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java index 0ae3c1daa..d45f1f750 100644 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java @@ -70,8 +70,7 @@ public class AttributeServiceCachedClient implements AttributeProvider { ? attributeServiceConfig.getInt(CACHE_EXECUTOR_THREADS_CONFIG_KEY) : 4, this.buildThreadFactory()))); - PlatformMetricsRegistry.registerCache( - "attribute-service-client-cache", cache, Collections.emptyMap()); + PlatformMetricsRegistry.registerCache(AttributeServiceCachedClient.class.getName(), cache, Collections.emptyMap()); scopeAndKeyLookup = CacheBuilder.newBuilder().expireAfterWrite(expireAfterWriteDuration).build(); } @@ -141,7 +140,7 @@ private Table loadTable(String tenantId) { private ThreadFactory buildThreadFactory() { return new ThreadFactoryBuilder() .setDaemon(true) - .setNameFormat("attribute-service-cache-%d") + .setNameFormat("attribute-service-cached-client-%d") .build(); } From 0015a60139f9efe9fac3ce120068f3adc4f50cac Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Fri, 17 Nov 2023 18:23:05 +0530 Subject: [PATCH 11/29] call credential fix --- .../enrichment/clients/AttributeServiceCachedClient.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java index d45f1f750..ed0e54358 100644 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java @@ -22,6 +22,7 @@ import org.hypertrace.core.attribute.service.v1.AttributeServiceGrpc; import org.hypertrace.core.attribute.service.v1.AttributeServiceGrpc.AttributeServiceBlockingStub; import org.hypertrace.core.attribute.service.v1.GetAttributesRequest; +import org.hypertrace.core.grpcutils.client.RequestContextClientCallCredsProviderFactory; import org.hypertrace.core.grpcutils.context.RequestContext; import org.hypertrace.core.serviceframework.metrics.PlatformMetricsRegistry; import org.hypertrace.trace.provider.AttributeProvider; @@ -123,6 +124,7 @@ private Table loadTable(String tenantId) { () -> attributeServiceBlockingStub .withDeadlineAfter(deadlineMs, TimeUnit.MILLISECONDS) + .withCallCredentials(RequestContextClientCallCredsProviderFactory.getClientCallCredsProvider().get()) .getAttributes(GetAttributesRequest.getDefaultInstance())) .getAttributesList(); attributeMetadataList.forEach( From 079e133728562dd3be633ccb683c2c971bd8bac6 Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Wed, 22 Nov 2023 01:25:16 +0530 Subject: [PATCH 12/29] wip --- gradle/libs.versions.toml | 6 + .../build.gradle.kts | 1 + .../clients/AttributeServiceCachedClient.java | 158 ------------------ .../enrichment/clients/ClientRegistry.java | 4 +- .../clients/DefaultClientRegistry.java | 19 ++- .../resources/configs/common/application.conf | 4 +- .../trace-reader/build.gradle.kts | 10 +- .../entities/DefaultTraceEntityAccessor.java | 25 ++- .../entities/TraceEntityAccessorBuilder.java | 12 +- .../trace/provider/AttributeProvider.java | 13 -- .../DefaultTraceAttributeReader.java | 18 +- .../attributes/DefaultValueResolver.java | 9 +- .../reader/attributes/SpanValueSource.java | 11 +- .../attributes/TraceAttributeReader.java | 3 + .../TraceAttributeReaderFactory.java | 7 +- .../reader/attributes/TraceValueSource.java | 11 +- .../reader/attributes/ValueResolver.java | 6 +- .../trace/reader/attributes/ValueSource.java | 6 +- .../DefaultTraceEntityAccessorTest.java | 14 +- .../attributes/DefaultValueResolverTest.java | 17 +- 20 files changed, 98 insertions(+), 256 deletions(-) delete mode 100644 hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java delete mode 100644 hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/provider/AttributeProvider.java diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ea5c94dc0..ef7f33ec0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,6 @@ [versions] hypertrace-entity-service = "0.8.78" +hypertrace-attribute-service = "0.14.38" hypertrace-config-service = "0.1.54" hypertrace-grpc-utils = "0.12.4" hypertrace-serviceFramework = "0.1.62" @@ -10,7 +11,12 @@ grpc = "1.57.2" [libraries] hypertrace-entityService-client = { module = "org.hypertrace.entity.service:entity-service-client", version.ref = "hypertrace-entity-service" } +hypertrace-entityTypeService-rxClient = { module = "org.hypertrace.entity.service:entity-type-service-rx-client", version.ref = "hypertrace-entity-service" } +hypertrace-entityDataService-rxClient = { module = "org.hypertrace.entity.service:entity-data-service-rx-client", version.ref = "hypertrace-entity-service" } hypertrace-entityService-api = { module = "org.hypertrace.entity.service:entity-service-api", version.ref = "hypertrace-entity-service" } +hypertrace-attributeService-client = { module = "org.hypertrace.core.attribute.service:attribute-service-client", version.ref = "hypertrace-attribute-service" } +hypertrace-attributeService-attributeProjectionRegistry = { module = "org.hypertrace.core.attribute.service:attribute-projection-registry", version.ref = "hypertrace-attribute-service" } +hypertrace-attributeService-api = { module = "org.hypertrace.core.attribute.service:attribute-service-api", version.ref = "hypertrace-attribute-service" } hypertrace-grpc-context-utils = { module = "org.hypertrace.core.grpcutils:grpc-context-utils", version.ref = "hypertrace-grpc-utils" } hypertrace-grpc-client-utils ={ module = "org.hypertrace.core.grpcutils:grpc-client-utils", version.ref = "hypertrace-grpc-utils" } hypertrace-grpc-client-rxUtils = { module = "org.hypertrace.core.grpcutils:grpc-client-rx-utils", version.ref = "hypertrace-grpc-utils"} diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/build.gradle.kts b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/build.gradle.kts index 7616f84e0..11de7c8f6 100644 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/build.gradle.kts +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/build.gradle.kts @@ -18,6 +18,7 @@ dependencies { implementation(libs.hypertrace.data.model) implementation(libs.hypertrace.entityService.client) + implementation(libs.hypertrace.attributeService.client) implementation(libs.hypertrace.serviceFramework.metrics) implementation(libs.hypertrace.grpc.client.utils) implementation(libs.hypertrace.spacesConfigServiceApi) diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java deleted file mode 100644 index ed0e54358..000000000 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClient.java +++ /dev/null @@ -1,158 +0,0 @@ -package org.hypertrace.traceenricher.enrichment.clients; - -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import com.google.common.collect.ImmutableTable; -import com.google.common.collect.Table; -import com.google.common.util.concurrent.RateLimiter; -import com.google.common.util.concurrent.ThreadFactoryBuilder; -import com.typesafe.config.Config; -import io.grpc.Channel; -import java.time.Duration; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; -import org.hypertrace.core.attribute.service.v1.AttributeMetadata; -import org.hypertrace.core.attribute.service.v1.AttributeServiceGrpc; -import org.hypertrace.core.attribute.service.v1.AttributeServiceGrpc.AttributeServiceBlockingStub; -import org.hypertrace.core.attribute.service.v1.GetAttributesRequest; -import org.hypertrace.core.grpcutils.client.RequestContextClientCallCredsProviderFactory; -import org.hypertrace.core.grpcutils.context.RequestContext; -import org.hypertrace.core.serviceframework.metrics.PlatformMetricsRegistry; -import org.hypertrace.trace.provider.AttributeProvider; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class AttributeServiceCachedClient implements AttributeProvider { - private static final String DEADLINE_CONFIG_KEY = "deadline"; - private static final String CACHE_MAX_SIZE_CONFIG_KEY = "maxSize"; - private static final String CACHE_REFRESH_AFTER_WRITE_CONFIG_KEY = "refreshAfterWriteDuration"; - private static final String CACHE_EXPIRE_AFTER_WRITE_CONFIG_KEY = "expireAfterWriteDuration"; - private static final String CACHE_EXECUTOR_THREADS_CONFIG_KEY = "executorThreads"; - private static final RateLimiter LOG_LIMITER = RateLimiter.create(1 / 60d); - private static final Logger LOGGER = LoggerFactory.getLogger(AttributeServiceCachedClient.class); - private final LoadingCache> cache; - private final Cache scopeAndKeyLookup; - private final AttributeServiceBlockingStub attributeServiceBlockingStub; - private final long deadlineMs; - - AttributeServiceCachedClient(Channel channel, Config attributeServiceConfig) { - deadlineMs = - attributeServiceConfig.hasPath(DEADLINE_CONFIG_KEY) - ? attributeServiceConfig.getDuration(DEADLINE_CONFIG_KEY).toMillis() - : Duration.ofMinutes(1).toMillis(); - this.attributeServiceBlockingStub = AttributeServiceGrpc.newBlockingStub(channel); - Duration expireAfterWriteDuration = - attributeServiceConfig.hasPath(CACHE_EXPIRE_AFTER_WRITE_CONFIG_KEY) - ? attributeServiceConfig.getDuration(CACHE_EXPIRE_AFTER_WRITE_CONFIG_KEY) - : Duration.ofHours(1); - cache = - CacheBuilder.newBuilder() - .maximumSize( - attributeServiceConfig.hasPath(CACHE_MAX_SIZE_CONFIG_KEY) - ? attributeServiceConfig.getLong(CACHE_MAX_SIZE_CONFIG_KEY) - : 100) - .expireAfterWrite(expireAfterWriteDuration) - .refreshAfterWrite( - attributeServiceConfig.hasPath(CACHE_REFRESH_AFTER_WRITE_CONFIG_KEY) - ? attributeServiceConfig.getDuration(CACHE_REFRESH_AFTER_WRITE_CONFIG_KEY) - : Duration.ofMinutes(15)) - .build( - CacheLoader.asyncReloading( - CacheLoader.from(this::loadTable), - Executors.newFixedThreadPool( - attributeServiceConfig.hasPath(CACHE_EXECUTOR_THREADS_CONFIG_KEY) - ? attributeServiceConfig.getInt(CACHE_EXECUTOR_THREADS_CONFIG_KEY) - : 4, - this.buildThreadFactory()))); - PlatformMetricsRegistry.registerCache(AttributeServiceCachedClient.class.getName(), cache, Collections.emptyMap()); - scopeAndKeyLookup = - CacheBuilder.newBuilder().expireAfterWrite(expireAfterWriteDuration).build(); - } - - public Optional get( - String tenantId, String attributeScope, String attributeKey) { - try { - return Optional.ofNullable(cache.getUnchecked(tenantId)) - .map(table -> table.get(attributeScope, attributeKey)); - } catch (Exception e) { - if (LOG_LIMITER.tryAcquire()) { - LOGGER.error( - "No attribute available for scope {} and key {}", attributeScope, attributeKey); - } - return Optional.empty(); - } - } - - public Optional getById(String tenantId, String attributeId) { - try { - return Optional.ofNullable(cache.getUnchecked(tenantId)) - .flatMap( - table -> - Optional.ofNullable(scopeAndKeyLookup.getIfPresent(attributeId)) - .map(scopeAndKey -> table.get(scopeAndKey.scope, scopeAndKey.key))); - } catch (Exception e) { - if (LOG_LIMITER.tryAcquire()) { - LOGGER.error("No attribute available for id {}", attributeId); - } - return Optional.empty(); - } - } - - public Optional> getAllInScope(String tenantId, String attributeScope) { - try { - return Optional.ofNullable(cache.getUnchecked(tenantId)) - .map(table -> List.copyOf(table.row(attributeScope).values())); - } catch (Exception e) { - if (LOG_LIMITER.tryAcquire()) { - LOGGER.error("No attributes available for scope {}", attributeScope); - } - return Optional.empty(); - } - } - - private Table loadTable(String tenantId) { - List attributeMetadataList = - RequestContext.forTenantId(tenantId) - .call( - () -> - attributeServiceBlockingStub - .withDeadlineAfter(deadlineMs, TimeUnit.MILLISECONDS) - .withCallCredentials(RequestContextClientCallCredsProviderFactory.getClientCallCredsProvider().get()) - .getAttributes(GetAttributesRequest.getDefaultInstance())) - .getAttributesList(); - attributeMetadataList.forEach( - attributeMetadata -> - scopeAndKeyLookup.put( - attributeMetadata.getId(), - new AttributeScopeAndKey( - attributeMetadata.getScopeString(), attributeMetadata.getKey()))); - return attributeMetadataList.stream() - .collect( - ImmutableTable.toImmutableTable( - AttributeMetadata::getScopeString, AttributeMetadata::getKey, Function.identity())); - } - - private ThreadFactory buildThreadFactory() { - return new ThreadFactoryBuilder() - .setDaemon(true) - .setNameFormat("attribute-service-cached-client-%d") - .build(); - } - - private static final class AttributeScopeAndKey { - private final String scope; - private final String key; - - private AttributeScopeAndKey(String scope, String key) { - this.scope = scope; - this.key = key; - } - } -} diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/ClientRegistry.java b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/ClientRegistry.java index 964d690f6..5754633c3 100644 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/ClientRegistry.java +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/ClientRegistry.java @@ -1,6 +1,7 @@ package org.hypertrace.traceenricher.enrichment.clients; import io.grpc.Channel; +import org.hypertrace.core.attribute.service.client.AttributeServiceCachedClient; import org.hypertrace.core.datamodel.Event; import org.hypertrace.core.datamodel.StructuredTrace; import org.hypertrace.core.grpcutils.client.GrpcChannelRegistry; @@ -8,7 +9,6 @@ import org.hypertrace.entity.data.service.rxclient.EntityDataClient; import org.hypertrace.entity.query.service.v1.EntityQueryServiceGrpc.EntityQueryServiceBlockingStub; import org.hypertrace.trace.accessor.entities.TraceEntityAccessor; -import org.hypertrace.trace.provider.AttributeProvider; import org.hypertrace.trace.reader.attributes.TraceAttributeReader; import org.hypertrace.traceenricher.enrichment.enrichers.cache.EntityCache; import org.hypertrace.traceenricher.util.UserAgentParser; @@ -35,5 +35,5 @@ public interface ClientRegistry { UserAgentParser getUserAgentParser(); - AttributeProvider getAttributeProvider(); + AttributeServiceCachedClient getAttributeClient(); } diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/DefaultClientRegistry.java b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/DefaultClientRegistry.java index ca20a0612..92bb2c93c 100644 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/DefaultClientRegistry.java +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/DefaultClientRegistry.java @@ -7,6 +7,8 @@ import java.time.Duration; import java.util.Set; import java.util.concurrent.Executor; +import org.hypertrace.core.attribute.service.client.AttributeServiceCachedClient; +import org.hypertrace.core.attribute.service.client.config.AttributeServiceCachedClientConfig; import org.hypertrace.core.datamodel.Event; import org.hypertrace.core.datamodel.StructuredTrace; import org.hypertrace.core.grpcutils.client.GrpcChannelConfig; @@ -21,7 +23,6 @@ import org.hypertrace.entity.type.service.rxclient.EntityTypeClient; import org.hypertrace.trace.accessor.entities.TraceEntityAccessor; import org.hypertrace.trace.accessor.entities.TraceEntityAccessorBuilder; -import org.hypertrace.trace.provider.AttributeProvider; import org.hypertrace.trace.reader.attributes.TraceAttributeReader; import org.hypertrace.trace.reader.attributes.TraceAttributeReaderFactory; import org.hypertrace.traceenricher.enrichment.enrichers.cache.EntityCache; @@ -50,7 +51,7 @@ public class DefaultClientRegistry implements ClientRegistry { private final TraceAttributeReader attributeReader; private final GrpcChannelRegistry grpcChannelRegistry; private final UserAgentParser userAgentParser; - private final AttributeProvider attributeProvider; + private final AttributeServiceCachedClient attributeClient; public DefaultClientRegistry( Config config, GrpcChannelRegistry grpcChannelRegistry, Executor cacheLoaderExecutor) { @@ -67,10 +68,12 @@ public DefaultClientRegistry( this.buildChannel( config.getString(ENTITY_SERVICE_HOST_KEY), config.getInt(ENTITY_SERVICE_PORT_KEY)); - this.attributeProvider = + this.attributeClient = new AttributeServiceCachedClient( - attributeServiceChannel, config.getConfig(ATTRIBUTE_SERVICE_CONFIG_KEY)); - this.attributeReader = TraceAttributeReaderFactory.build(attributeProvider); + attributeServiceChannel, + AttributeServiceCachedClientConfig.from( + config.getConfig(ATTRIBUTE_SERVICE_CONFIG_KEY))); + this.attributeReader = TraceAttributeReaderFactory.build(attributeClient); this.edsCacheClient = new EdsCacheClient( new EntityDataServiceClient(this.entityServiceChannel), @@ -82,7 +85,7 @@ public DefaultClientRegistry( new TraceEntityAccessorBuilder( EntityTypeClient.builder(this.entityServiceChannel).build(), this.entityDataClient, - attributeProvider) + attributeClient) .withEntityWriteThrottleDuration( config.hasPath(TRACE_ENTITY_WRITE_THROTTLE_DURATION) ? config.getDuration(TRACE_ENTITY_WRITE_THROTTLE_DURATION) @@ -148,8 +151,8 @@ public UserAgentParser getUserAgentParser() { } @Override - public AttributeProvider getAttributeProvider() { - return attributeProvider; + public AttributeServiceCachedClient getAttributeClient() { + return attributeClient; } public void shutdown() { diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher/src/main/resources/configs/common/application.conf b/hypertrace-trace-enricher/hypertrace-trace-enricher/src/main/resources/configs/common/application.conf index df70cdc08..b01fc49ad 100644 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher/src/main/resources/configs/common/application.conf +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher/src/main/resources/configs/common/application.conf @@ -38,11 +38,11 @@ enricher { host = ${?ATTRIBUTE_SERVICE_HOST_CONFIG} port = 9012 port = ${?ATTRIBUTE_SERVICE_PORT_CONFIG} - deadline = 30s cache = { + deadline = 30s maxSize = 100 refreshAfterWriteDuration = 15m - expireAfterWriteDuration = 1h + expireAfterAccessDuration = 1h executorThreads = 4 } } diff --git a/hypertrace-trace-enricher/trace-reader/build.gradle.kts b/hypertrace-trace-enricher/trace-reader/build.gradle.kts index 0e9519d2f..13b8d2b31 100644 --- a/hypertrace-trace-enricher/trace-reader/build.gradle.kts +++ b/hypertrace-trace-enricher/trace-reader/build.gradle.kts @@ -6,11 +6,13 @@ plugins { } dependencies { - api("org.hypertrace.core.attribute.service:attribute-service-api:0.14.26") - api("org.hypertrace.entity.service:entity-type-service-rx-client:0.8.75") - api("org.hypertrace.entity.service:entity-data-service-rx-client:0.8.75") + api(libs.hypertrace.attributeService.api) + api(libs.hypertrace.entityTypeService.rxClient) + api(libs.hypertrace.entityDataService.rxClient) api(libs.hypertrace.data.model) - implementation("org.hypertrace.core.attribute.service:attribute-projection-registry:0.14.26") + + implementation(libs.hypertrace.attributeService.client) + implementation(libs.hypertrace.attributeService.attributeProjectionRegistry) implementation(libs.hypertrace.grpc.client.rxUtils) implementation(libs.hypertrace.grpc.context.utils) implementation(libs.hypertrace.grpc.client.utils) diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java index fcfef7256..996085d8e 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java @@ -10,13 +10,13 @@ import java.util.Set; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; +import org.hypertrace.core.attribute.service.client.AttributeServiceCachedClient; import org.hypertrace.core.attribute.service.v1.AttributeMetadata; import org.hypertrace.core.attribute.service.v1.AttributeSource; import org.hypertrace.core.attribute.service.v1.LiteralValue; import org.hypertrace.core.datamodel.Event; import org.hypertrace.core.datamodel.StructuredTrace; import org.hypertrace.core.grpcutils.client.rx.GrpcRxExecutionContext; -import org.hypertrace.core.grpcutils.context.RequestContext; import org.hypertrace.entity.data.service.rxclient.EntityDataClient; import org.hypertrace.entity.data.service.v1.AttributeValue; import org.hypertrace.entity.data.service.v1.AttributeValue.TypeCase; @@ -28,14 +28,13 @@ import org.hypertrace.entity.type.service.rxclient.EntityTypeClient; import org.hypertrace.entity.type.service.v2.EntityType; import org.hypertrace.entity.type.service.v2.EntityType.EntityFormationCondition; -import org.hypertrace.trace.provider.AttributeProvider; import org.hypertrace.trace.reader.attributes.TraceAttributeReader; @Slf4j class DefaultTraceEntityAccessor implements TraceEntityAccessor { private final EntityTypeClient entityTypeClient; private final EntityDataClient entityDataClient; - private final AttributeProvider attributeProvider; + private final AttributeServiceCachedClient attributeClient; private final TraceAttributeReader traceAttributeReader; private final Duration writeThrottleDuration; private final Set excludedEntityTypes; @@ -43,13 +42,13 @@ class DefaultTraceEntityAccessor implements TraceEntityAccessor { DefaultTraceEntityAccessor( EntityTypeClient entityTypeClient, EntityDataClient entityDataClient, - AttributeProvider attributeProvider, + AttributeServiceCachedClient attributeClient, TraceAttributeReader traceAttributeReader, Duration writeThrottleDuration, Set excludedEntityTypes) { this.entityTypeClient = entityTypeClient; this.entityDataClient = entityDataClient; - this.attributeProvider = attributeProvider; + this.attributeClient = attributeClient; this.traceAttributeReader = traceAttributeReader; this.writeThrottleDuration = writeThrottleDuration; this.excludedEntityTypes = excludedEntityTypes; @@ -74,9 +73,8 @@ private void writeEntityIfExists(EntityType entityType, StructuredTrace trace, E if (entityOptional.isEmpty()) { return; } - // TODO: in follow up PR batch eventual entity writes for single tenant this.entityDataClient.createOrUpdateEntityEventually( - RequestContext.forTenantId(this.traceAttributeReader.getTenantId(span)), + this.traceAttributeReader.getRequestContext(span), entityOptional.get(), this.buildUpsertCondition(entityType, trace, span) .orElse(UpsertCondition.getDefaultInstance()), @@ -88,9 +86,9 @@ private Optional buildUpsertCondition( if (entityType.getTimestampAttributeKey().isEmpty()) { return Optional.empty(); } - return this.attributeProvider + return this.attributeClient .get( - this.traceAttributeReader.getTenantId(span), + traceAttributeReader.getRequestContext(span), entityType.getAttributeScope(), entityType.getTimestampAttributeKey()) .filter(this::isEntitySourced) @@ -161,13 +159,14 @@ private boolean passesFormationCondition(Entity entity, EntityFormationCondition private Optional> resolveAllAttributes( String scope, StructuredTrace trace, Event span) { - Optional> mayBeAttributeMetadataList = - this.attributeProvider.getAllInScope(this.traceAttributeReader.getTenantId(span), scope); - if (mayBeAttributeMetadataList.isEmpty()) { + List attributeMetadataList = + this.attributeClient.getAllInScope( + this.traceAttributeReader.getRequestContext(span), scope); + if (attributeMetadataList.isEmpty()) { return Optional.empty(); } Map resolvedAttributes = - mayBeAttributeMetadataList.get().stream() + attributeMetadataList.stream() .filter(this::isEntitySourced) .map(attributeMetadata -> this.resolveAttribute(attributeMetadata, trace, span)) .filter(Optional::isPresent) diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/TraceEntityAccessorBuilder.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/TraceEntityAccessorBuilder.java index 75a6f315d..97ea1a062 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/TraceEntityAccessorBuilder.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/TraceEntityAccessorBuilder.java @@ -4,25 +4,25 @@ import java.time.Duration; import java.util.Set; +import org.hypertrace.core.attribute.service.client.AttributeServiceCachedClient; import org.hypertrace.entity.data.service.rxclient.EntityDataClient; import org.hypertrace.entity.type.service.rxclient.EntityTypeClient; -import org.hypertrace.trace.provider.AttributeProvider; import org.hypertrace.trace.reader.attributes.TraceAttributeReaderFactory; public class TraceEntityAccessorBuilder { private final EntityTypeClient entityTypeClient; private final EntityDataClient entityDataClient; - private final AttributeProvider attributeProvider; + private final AttributeServiceCachedClient attributeClient; private Duration entityWriteThrottleDuration = Duration.ofSeconds(15); private Set excludedEntityTypes = emptySet(); public TraceEntityAccessorBuilder( EntityTypeClient entityTypeClient, EntityDataClient entityDataClient, - AttributeProvider attributeProvider) { + AttributeServiceCachedClient attributeClient) { this.entityTypeClient = entityTypeClient; this.entityDataClient = entityDataClient; - this.attributeProvider = attributeProvider; + this.attributeClient = attributeClient; } public TraceEntityAccessorBuilder withEntityWriteThrottleDuration(Duration duration) { @@ -39,8 +39,8 @@ public TraceEntityAccessor build() { return new DefaultTraceEntityAccessor( this.entityTypeClient, this.entityDataClient, - this.attributeProvider, - TraceAttributeReaderFactory.build(this.attributeProvider), + this.attributeClient, + TraceAttributeReaderFactory.build(this.attributeClient), entityWriteThrottleDuration, excludedEntityTypes); } diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/provider/AttributeProvider.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/provider/AttributeProvider.java deleted file mode 100644 index 0b6f349a2..000000000 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/provider/AttributeProvider.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.hypertrace.trace.provider; - -import java.util.List; -import java.util.Optional; -import org.hypertrace.core.attribute.service.v1.AttributeMetadata; - -public interface AttributeProvider { - Optional get(String tenantId, String attributeScope, String attributeKey); - - Optional getById(String tenantId, String attributeId); - - Optional> getAllInScope(String tenantId, String scope); -} diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReader.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReader.java index b85e87cc5..3b4c60cd7 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReader.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReader.java @@ -3,20 +3,21 @@ import static org.hypertrace.trace.reader.attributes.ValueSource.TRACE_SCOPE; import java.util.Optional; +import org.hypertrace.core.attribute.service.client.AttributeServiceCachedClient; import org.hypertrace.core.attribute.service.v1.AttributeMetadata; import org.hypertrace.core.attribute.service.v1.LiteralValue; import org.hypertrace.core.datamodel.Event; import org.hypertrace.core.datamodel.StructuredTrace; -import org.hypertrace.trace.provider.AttributeProvider; +import org.hypertrace.core.grpcutils.context.RequestContext; class DefaultTraceAttributeReader implements TraceAttributeReader { - private final AttributeProvider attributeProvider; + private final AttributeServiceCachedClient attributeClient; private final ValueResolver valueResolver; - DefaultTraceAttributeReader(AttributeProvider attributeProvider) { - this.attributeProvider = attributeProvider; - this.valueResolver = ValueResolver.build(this.attributeProvider); + DefaultTraceAttributeReader(AttributeServiceCachedClient attributeClient) { + this.attributeClient = attributeClient; + this.valueResolver = ValueResolver.build(this.attributeClient); } @Override @@ -39,8 +40,13 @@ public String getTenantId(Event span) { return span.getCustomerId(); } + @Override + public RequestContext getRequestContext(Event span) { + return RequestContext.forTenantId(span.getCustomerId()); + } + private Optional getAttribute( ValueSource valueSource, String attributeScope, String attributeKey) { - return this.attributeProvider.get(valueSource.tenantId(), attributeScope, attributeKey); + return this.attributeClient.get(valueSource.requestContext(), attributeScope, attributeKey); } } diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java index 19ece32dc..a1260efbb 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java @@ -8,6 +8,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import lombok.extern.slf4j.Slf4j; +import org.hypertrace.core.attribute.service.client.AttributeServiceCachedClient; import org.hypertrace.core.attribute.service.projection.AttributeProjection; import org.hypertrace.core.attribute.service.projection.AttributeProjectionRegistry; import org.hypertrace.core.attribute.service.v1.AttributeDefinition; @@ -19,17 +20,17 @@ import org.hypertrace.core.attribute.service.v1.LiteralValue; import org.hypertrace.core.attribute.service.v1.Projection; import org.hypertrace.core.attribute.service.v1.ProjectionExpression; -import org.hypertrace.trace.provider.AttributeProvider; @Slf4j class DefaultValueResolver implements ValueResolver { // One log a minute private static final RateLimiter LOGGING_LIMITER = RateLimiter.create(1 / 60d); - private final AttributeProvider attributeClient; + private final AttributeServiceCachedClient attributeClient; private final AttributeProjectionRegistry attributeProjectionRegistry; DefaultValueResolver( - AttributeProvider attributeClient, AttributeProjectionRegistry attributeProjectionRegistry) { + AttributeServiceCachedClient attributeClient, + AttributeProjectionRegistry attributeProjectionRegistry) { this.attributeClient = attributeClient; this.attributeProjectionRegistry = attributeProjectionRegistry; } @@ -106,7 +107,7 @@ private Optional resolveProjection(ValueSource valueSource, Projec switch (projection.getValueCase()) { case ATTRIBUTE_ID: return this.attributeClient - .getById(valueSource.tenantId(), projection.getAttributeId()) + .getById(valueSource.requestContext(), projection.getAttributeId()) .flatMap(attributeMetadata -> this.resolve(valueSource, attributeMetadata)); case LITERAL: return Optional.of(projection.getLiteral()); diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/SpanValueSource.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/SpanValueSource.java index 95227e552..22e947b61 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/SpanValueSource.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/SpanValueSource.java @@ -8,7 +8,7 @@ import org.hypertrace.core.datamodel.Event; import org.hypertrace.core.datamodel.Resource; import org.hypertrace.core.datamodel.StructuredTrace; -import org.hypertrace.core.grpcutils.client.rx.GrpcRxExecutionContext; +import org.hypertrace.core.grpcutils.context.RequestContext; class SpanValueSource extends AvroBackedValueSource { @@ -57,13 +57,8 @@ public Optional sourceForScope(String scope) { } @Override - public GrpcRxExecutionContext executionContext() { - return GrpcRxExecutionContext.forTenantContext(this.span.getCustomerId()); - } - - @Override - public String tenantId() { - return this.span.getCustomerId(); + public RequestContext requestContext() { + return RequestContext.forTenantId(span.getCustomerId()); } @Override diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceAttributeReader.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceAttributeReader.java index 5d3e43d46..909026e87 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceAttributeReader.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceAttributeReader.java @@ -3,6 +3,7 @@ import java.util.Optional; import org.apache.avro.generic.GenericRecord; import org.hypertrace.core.attribute.service.v1.LiteralValue; +import org.hypertrace.core.grpcutils.context.RequestContext; public interface TraceAttributeReader { Optional getSpanValue(T trace, S span, String attributeScope, String attributeKey); @@ -10,4 +11,6 @@ public interface TraceAttributeReader getTraceValue(T trace, String attributeKey); String getTenantId(S span); + + RequestContext getRequestContext(S span); } diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceAttributeReaderFactory.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceAttributeReaderFactory.java index 58f7fddf4..1c1640e41 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceAttributeReaderFactory.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceAttributeReaderFactory.java @@ -1,11 +1,12 @@ package org.hypertrace.trace.reader.attributes; +import org.hypertrace.core.attribute.service.client.AttributeServiceCachedClient; import org.hypertrace.core.datamodel.Event; import org.hypertrace.core.datamodel.StructuredTrace; -import org.hypertrace.trace.provider.AttributeProvider; public interface TraceAttributeReaderFactory { - static TraceAttributeReader build(AttributeProvider attributeProvider) { - return new DefaultTraceAttributeReader(attributeProvider); + static TraceAttributeReader build( + AttributeServiceCachedClient attributeClient) { + return new DefaultTraceAttributeReader(attributeClient); } } diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceValueSource.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceValueSource.java index ad670ce68..54f8df4b2 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceValueSource.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/TraceValueSource.java @@ -6,7 +6,7 @@ import org.hypertrace.core.attribute.service.v1.AttributeKind; import org.hypertrace.core.attribute.service.v1.LiteralValue; import org.hypertrace.core.datamodel.StructuredTrace; -import org.hypertrace.core.grpcutils.client.rx.GrpcRxExecutionContext; +import org.hypertrace.core.grpcutils.context.RequestContext; class TraceValueSource extends AvroBackedValueSource { @@ -49,13 +49,8 @@ public Optional sourceForScope(String scope) { } @Override - public GrpcRxExecutionContext executionContext() { - return GrpcRxExecutionContext.forTenantContext(this.trace.getCustomerId()); - } - - @Override - public String tenantId() { - return this.trace.getCustomerId(); + public RequestContext requestContext() { + return RequestContext.forTenantId(trace.getCustomerId()); } @Override diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/ValueResolver.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/ValueResolver.java index f42ca4caf..f17de434e 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/ValueResolver.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/ValueResolver.java @@ -1,16 +1,16 @@ package org.hypertrace.trace.reader.attributes; import java.util.Optional; +import org.hypertrace.core.attribute.service.client.AttributeServiceCachedClient; import org.hypertrace.core.attribute.service.projection.AttributeProjectionRegistry; import org.hypertrace.core.attribute.service.v1.AttributeMetadata; import org.hypertrace.core.attribute.service.v1.LiteralValue; -import org.hypertrace.trace.provider.AttributeProvider; public interface ValueResolver { Optional resolve(ValueSource valueSource, AttributeMetadata attributeMetadata); - static ValueResolver build(AttributeProvider attributeProvider) { - return new DefaultValueResolver(attributeProvider, new AttributeProjectionRegistry()); + static ValueResolver build(AttributeServiceCachedClient attributeClient) { + return new DefaultValueResolver(attributeClient, new AttributeProjectionRegistry()); } } diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/ValueSource.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/ValueSource.java index ee3677d55..89f32bd20 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/ValueSource.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/ValueSource.java @@ -4,7 +4,7 @@ import org.hypertrace.core.attribute.service.v1.AttributeDefinition.SourceField; import org.hypertrace.core.attribute.service.v1.AttributeKind; import org.hypertrace.core.attribute.service.v1.LiteralValue; -import org.hypertrace.core.grpcutils.client.rx.GrpcRxExecutionContext; +import org.hypertrace.core.grpcutils.context.RequestContext; public interface ValueSource { Optional getAttribute(String key, AttributeKind attributeKind); @@ -15,9 +15,7 @@ public interface ValueSource { Optional sourceForScope(String scope); - GrpcRxExecutionContext executionContext(); - - String tenantId(); + RequestContext requestContext(); String TRACE_SCOPE = "TRACE"; } diff --git a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessorTest.java b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessorTest.java index 6fa55fb19..788148810 100644 --- a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessorTest.java +++ b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessorTest.java @@ -22,6 +22,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import org.hypertrace.core.attribute.service.client.AttributeServiceCachedClient; import org.hypertrace.core.attribute.service.v1.AttributeMetadata; import org.hypertrace.core.attribute.service.v1.AttributeSource; import org.hypertrace.core.attribute.service.v1.AttributeType; @@ -37,7 +38,6 @@ import org.hypertrace.entity.type.service.rxclient.EntityTypeClient; import org.hypertrace.entity.type.service.v2.EntityType; import org.hypertrace.entity.type.service.v2.EntityType.EntityFormationCondition; -import org.hypertrace.trace.provider.AttributeProvider; import org.hypertrace.trace.reader.attributes.TraceAttributeReader; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -118,7 +118,7 @@ class DefaultTraceEntityAccessorTest { @Mock EntityTypeClient mockTypeClient; @Mock EntityDataClient mockDataClient; - @Mock AttributeProvider mockAttributeProvider; + @Mock AttributeServiceCachedClient mockAttributeClient; @Mock TraceAttributeReader mockAttributeReader; MockedStatic mockSchedulers; @@ -131,7 +131,7 @@ void beforeEach() { new DefaultTraceEntityAccessor( this.mockTypeClient, this.mockDataClient, - this.mockAttributeProvider, + this.mockAttributeClient, this.mockAttributeReader, DEFAULT_DURATION, EXCLUDE_ENTITY_TYPES); @@ -297,13 +297,13 @@ private void mockAttributeReadError(AttributeMetadata attributeMetadata) { } private void mockGetAllAttributes(AttributeMetadata... attributeMetadata) { - when(this.mockAttributeProvider.getAllInScope(TENANT_ID, TEST_ENTITY_TYPE_NAME)) - .thenReturn(Optional.of(Arrays.asList(attributeMetadata))); + when(this.mockAttributeClient.getAllInScope(any(), eq(TEST_ENTITY_TYPE_NAME))) + .thenReturn(Arrays.asList(attributeMetadata)); } private void mockGetSingleAttribute(AttributeMetadata attributeMetadata) { - when(this.mockAttributeProvider.get( - TENANT_ID, attributeMetadata.getScopeString(), attributeMetadata.getKey())) + when(this.mockAttributeClient.get( + any(), eq(attributeMetadata.getScopeString()), eq(attributeMetadata.getKey()))) .thenReturn(Optional.of(attributeMetadata)); } diff --git a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultValueResolverTest.java b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultValueResolverTest.java index ec00499c0..17babbeed 100644 --- a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultValueResolverTest.java +++ b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultValueResolverTest.java @@ -9,11 +9,14 @@ import static org.hypertrace.trace.reader.attributes.LiteralValueUtil.stringLiteral; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.Map; import java.util.Optional; +import org.hypertrace.core.attribute.service.client.AttributeServiceCachedClient; import org.hypertrace.core.attribute.service.projection.AttributeProjectionRegistry; import org.hypertrace.core.attribute.service.v1.AttributeDefinition; import org.hypertrace.core.attribute.service.v1.AttributeDefinition.AttributeDefinitions; @@ -27,7 +30,6 @@ import org.hypertrace.core.attribute.service.v1.ProjectionOperator; import org.hypertrace.core.datamodel.Event; import org.hypertrace.core.datamodel.StructuredTrace; -import org.hypertrace.trace.provider.AttributeProvider; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -37,7 +39,7 @@ @ExtendWith(MockitoExtension.class) class DefaultValueResolverTest { - @Mock AttributeProvider mockAttributeProvider; + @Mock AttributeServiceCachedClient mockAttributeServiceCachedClient; @Mock StructuredTrace mockStructuredTrace; private DefaultValueResolver resolver; @@ -45,7 +47,8 @@ class DefaultValueResolverTest { @BeforeEach void beforeEach() { this.resolver = - new DefaultValueResolver(this.mockAttributeProvider, new AttributeProjectionRegistry()); + new DefaultValueResolver( + this.mockAttributeServiceCachedClient, new AttributeProjectionRegistry()); } @Test @@ -123,7 +126,7 @@ void resolvesAttributeProjections() { .setValueKind(AttributeKind.TYPE_INT64) .setDefinition(AttributeDefinition.newBuilder().setSourcePath("metricPath").build()) .build(); - when(this.mockAttributeProvider.getById("defaultCustomerId", "TEST_SCOPE.other")) + when(this.mockAttributeServiceCachedClient.getById(any(), eq("TEST_SCOPE.other"))) .thenReturn(Optional.of((otherMetadata))); Event span = @@ -168,9 +171,9 @@ void resolvesExpressionProjections() { .setValueKind(AttributeKind.TYPE_STRING) .setDefinition(AttributeDefinition.newBuilder().setSourcePath("attrPath").build()) .build(); - when(this.mockAttributeProvider.getById("defaultCustomerId", "TEST_SCOPE.first")) + when(this.mockAttributeServiceCachedClient.getById(any(), eq("TEST_SCOPE.first"))) .thenReturn(Optional.of(firstMetadata)); - when(this.mockAttributeProvider.getById("defaultCustomerId", "TEST_SCOPE.second")) + when(this.mockAttributeServiceCachedClient.getById(any(), eq("TEST_SCOPE.second"))) .thenReturn(Optional.of(secondMetadata)); Event span = @@ -202,7 +205,7 @@ void resolvesProjectionsAcrossScopes() { .setValueKind(AttributeKind.TYPE_INT64) .setDefinition(AttributeDefinition.newBuilder().setSourcePath("metricPath").build()) .build(); - when(this.mockAttributeProvider.getById("defaultCustomerId", "TRACE.other")) + when(this.mockAttributeServiceCachedClient.getById(any(), eq("TRACE.other"))) .thenReturn(Optional.of(otherMetadata)); StructuredTrace trace = From 1ef33cb6f225e3d9362c6a69d9f145de22317ba9 Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Wed, 22 Nov 2023 01:26:09 +0530 Subject: [PATCH 13/29] remove --- .../enrichment/clients/AttributeServiceCachedClientTest.java | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/test/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClientTest.java diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/test/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClientTest.java b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/test/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClientTest.java deleted file mode 100644 index e24298d88..000000000 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/test/java/org/hypertrace/traceenricher/enrichment/clients/AttributeServiceCachedClientTest.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.hypertrace.traceenricher.enrichment.clients; - -class AttributeServiceCachedClientTest { - // TODO: finish -} From 012b274b9ad54fea3952ef774abfc30f75f15a76 Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Wed, 22 Nov 2023 18:18:15 +0530 Subject: [PATCH 14/29] update gradle to 8 --- gradle/wrapper/gradle-wrapper.jar | Bin 59536 -> 63721 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- gradlew | 31 ++++++++---- gradlew.bat | 15 +++--- .../enriched-span-constants/build.gradle.kts | 45 +++++------------- settings.gradle.kts | 2 - .../raw-span-constants/build.gradle.kts | 41 +++++----------- .../span-normalizer-api/build.gradle.kts | 44 +++++------------ 8 files changed, 71 insertions(+), 111 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f2ae8848c63b8b4dea2cb829da983f2fa..7f93135c49b765f8051ef9d0a6055ff8e46073d8 100644 GIT binary patch delta 44391 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%0D+ym=_w*d`k$@iM%aZm>(FB zFT}u)TP%ySs`RyJx#=oxe&Idagc(F$Sv(5*pWd?HYll`LXi*p`s9;8=FNKjA$zuDap z1iJ4_{GzB&FY7I^Z=V2WPV}duAN#+$7*g>}M-8M^ZIIB>-^X-D90KS2pYi;cJpI2t z9+!d$Z~~_e2!65t2V*tx>VN>IOBPZ~3v6t}2DR0RaAz+Y0&>nb^fj?~75%>lATlV_DagaSI48TP5e#m z`NUsBkeiDL|BMZ$&97r4JO2|SVnYrr+W2*{e!H;xn_8U}Ah!z{@Mwehm%07(!p~pV geSU+(twG={1R5&vkYyYSN)YnO$AzrH2Y-C~e^Sofwg3PC delta 40281 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 /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,22 +131,29 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -193,6 +198,10 @@ if "$cygwin" || "$msys" ; then done fi + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + # Collect all arguments for the java command; # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # shell script including quotes and variable substitutions, so put them in @@ -205,6 +214,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index ac1b06f93..6689b85be 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/hypertrace-trace-enricher/enriched-span-constants/build.gradle.kts b/hypertrace-trace-enricher/enriched-span-constants/build.gradle.kts index c5881d443..dd2e05323 100644 --- a/hypertrace-trace-enricher/enriched-span-constants/build.gradle.kts +++ b/hypertrace-trace-enricher/enriched-span-constants/build.gradle.kts @@ -1,67 +1,46 @@ -import com.google.protobuf.gradle.generateProtoTasks import com.google.protobuf.gradle.id -import com.google.protobuf.gradle.ofSourceSet -import com.google.protobuf.gradle.plugins -import com.google.protobuf.gradle.protobuf -import com.google.protobuf.gradle.protoc plugins { `java-library` jacoco id("org.hypertrace.jacoco-report-plugin") - id("com.google.protobuf") version "0.8.15" + id("com.google.protobuf") version "0.9.2" id("org.hypertrace.publish-plugin") } -val generateLocalGoGrpcFiles = false protobuf { protoc { - artifact = "com.google.protobuf:protoc:3.21.1" + artifact = "com.google.protobuf:protoc:3.21.12" } plugins { - id("grpc_java") { + id("grpc") { artifact = "io.grpc:protoc-gen-grpc-java:1.57.2" } - - if (generateLocalGoGrpcFiles) { - id("grpc_go") { - path = "/bin/protoc-gen-go" - } - } } generateProtoTasks { - ofSourceSet("main").forEach { - it.plugins { + ofSourceSet("main").configureEach { + plugins { // Apply the "grpc" plugin whose spec is defined above, without options. - id("grpc_java") - - if (generateLocalGoGrpcFiles) { - id("grpc_go") - } - } - it.builtins { - java - if (generateLocalGoGrpcFiles) { - id("go") - } + id("grpc") } } } } -tasks.test { - useJUnitPlatform() -} - sourceSets { main { java { - srcDirs("src/main/java", "build/generated/source/proto/main/java", "build/generated/source/proto/main/grpc_java") + srcDirs("build/generated/source/proto/main/java", "build/generated/source/proto/main/grpc_java") } } } +tasks.test { + useJUnitPlatform() +} + + dependencies { api(libs.google.protobuf.java) diff --git a/settings.gradle.kts b/settings.gradle.kts index 24a2d5115..ef4a050be 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,8 +12,6 @@ plugins { rootProject.name = "hypertrace-ingester-root" -enableFeaturePreview("VERSION_CATALOGS") - // trace-enricher include("hypertrace-trace-enricher:enriched-span-constants") include("hypertrace-trace-enricher:hypertrace-trace-visualizer") diff --git a/span-normalizer/raw-span-constants/build.gradle.kts b/span-normalizer/raw-span-constants/build.gradle.kts index a97046f3f..af8bfa8ea 100644 --- a/span-normalizer/raw-span-constants/build.gradle.kts +++ b/span-normalizer/raw-span-constants/build.gradle.kts @@ -1,48 +1,25 @@ -import com.google.protobuf.gradle.generateProtoTasks import com.google.protobuf.gradle.id -import com.google.protobuf.gradle.ofSourceSet -import com.google.protobuf.gradle.plugins -import com.google.protobuf.gradle.protobuf -import com.google.protobuf.gradle.protoc plugins { `java-library` - id("com.google.protobuf") version "0.8.15" + id("com.google.protobuf") version "0.9.2" id("org.hypertrace.publish-plugin") } -val generateLocalGoGrpcFiles = false - protobuf { protoc { - artifact = "com.google.protobuf:protoc:3.21.1" + artifact = "com.google.protobuf:protoc:3.21.12" } plugins { - id("grpc_java") { + id("grpc") { artifact = "io.grpc:protoc-gen-grpc-java:1.57.2" } - - if (generateLocalGoGrpcFiles) { - id("grpc_go") { - path = "/bin/protoc-gen-go" - } - } } generateProtoTasks { - ofSourceSet("main").forEach { - it.plugins { + ofSourceSet("main").configureEach { + plugins { // Apply the "grpc" plugin whose spec is defined above, without options. - id("grpc_java") - - if (generateLocalGoGrpcFiles) { - id("grpc_go") - } - } - it.builtins { - java - if (generateLocalGoGrpcFiles) { - id("go") - } + id("grpc") } } } @@ -51,11 +28,15 @@ protobuf { sourceSets { main { java { - srcDirs("src/main/java", "build/generated/source/proto/main/java", "build/generated/source/proto/main/grpc_java") + srcDirs("build/generated/source/proto/main/java", "build/generated/source/proto/main/grpc_java") } } } +tasks.test { + useJUnitPlatform() +} + dependencies { api("com.google.protobuf:protobuf-java-util:3.23.3") constraints { diff --git a/span-normalizer/span-normalizer-api/build.gradle.kts b/span-normalizer/span-normalizer-api/build.gradle.kts index 0c22f1c99..722c17aeb 100644 --- a/span-normalizer/span-normalizer-api/build.gradle.kts +++ b/span-normalizer/span-normalizer-api/build.gradle.kts @@ -1,60 +1,42 @@ -import com.google.protobuf.gradle.generateProtoTasks import com.google.protobuf.gradle.id -import com.google.protobuf.gradle.ofSourceSet -import com.google.protobuf.gradle.plugins -import com.google.protobuf.gradle.protobuf -import com.google.protobuf.gradle.protoc plugins { `java-library` - id("com.google.protobuf") version "0.8.15" + id("com.google.protobuf") version "0.9.2" id("org.hypertrace.publish-plugin") - id("org.hypertrace.avro-plugin") } -val generateLocalGoGrpcFiles = false - protobuf { protoc { - artifact = "com.google.protobuf:protoc:3.21.1" + artifact = "com.google.protobuf:protoc:3.21.12" } plugins { - id("grpc_java") { + id("grpc") { artifact = "io.grpc:protoc-gen-grpc-java:1.57.2" } - - if (generateLocalGoGrpcFiles) { - id("grpc_go") { - path = "/bin/protoc-gen-go" - } - } } generateProtoTasks { - ofSourceSet("main").forEach { - it.plugins { + ofSourceSet("main").configureEach { + plugins { // Apply the "grpc" plugin whose spec is defined above, without options. - id("grpc_java") - - if (generateLocalGoGrpcFiles) { - id("grpc_go") - } - } - it.builtins { - java - if (generateLocalGoGrpcFiles) { - id("go") - } + id("grpc") } } } } + sourceSets { main { java { - srcDirs("src/main/java", "build/generated/source/proto/main/java", "build/generated/source/proto/main/grpc_java") + srcDirs("build/generated/source/proto/main/java", "build/generated/source/proto/main/grpc_java") } } } + +tasks.test { + useJUnitPlatform() +} + dependencies { api("com.google.api.grpc:proto-google-common-protos:2.14.1") api("org.apache.avro:avro:1.11.3") From 9a06144e0faa10db6c2dc19c7eb4b39c8767f3a2 Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Wed, 22 Nov 2023 18:19:31 +0530 Subject: [PATCH 15/29] update --- .../enriched-span-constants/build.gradle.kts | 2 -- 1 file changed, 2 deletions(-) diff --git a/hypertrace-trace-enricher/enriched-span-constants/build.gradle.kts b/hypertrace-trace-enricher/enriched-span-constants/build.gradle.kts index dd2e05323..e03c9e8db 100644 --- a/hypertrace-trace-enricher/enriched-span-constants/build.gradle.kts +++ b/hypertrace-trace-enricher/enriched-span-constants/build.gradle.kts @@ -8,7 +8,6 @@ plugins { id("org.hypertrace.publish-plugin") } - protobuf { protoc { artifact = "com.google.protobuf:protoc:3.21.12" @@ -40,7 +39,6 @@ tasks.test { useJUnitPlatform() } - dependencies { api(libs.google.protobuf.java) From de46d2dfd5ff23e77c875f6ded65cec3c5e4c242 Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Wed, 22 Nov 2023 18:30:57 +0530 Subject: [PATCH 16/29] update --- span-normalizer/span-normalizer-api/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/span-normalizer/span-normalizer-api/build.gradle.kts b/span-normalizer/span-normalizer-api/build.gradle.kts index 722c17aeb..9c1f1617a 100644 --- a/span-normalizer/span-normalizer-api/build.gradle.kts +++ b/span-normalizer/span-normalizer-api/build.gradle.kts @@ -4,6 +4,7 @@ plugins { `java-library` id("com.google.protobuf") version "0.9.2" id("org.hypertrace.publish-plugin") + id("org.hypertrace.avro-plugin") } protobuf { From b4c662c551d193c776f18a70288fcc8804394ea1 Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Wed, 22 Nov 2023 18:50:28 +0530 Subject: [PATCH 17/29] nit --- .../entities/DefaultTraceEntityAccessorTest.java | 6 ++++++ .../attributes/DefaultTraceAttributeReaderTest.java | 12 +++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessorTest.java b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessorTest.java index 788148810..bf3b7434c 100644 --- a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessorTest.java +++ b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessorTest.java @@ -135,6 +135,12 @@ void beforeEach() { this.mockAttributeReader, DEFAULT_DURATION, EXCLUDE_ENTITY_TYPES); + when(mockAttributeReader.getRequestContext(any())) + .thenAnswer( + inv -> { + Event event = inv.getArgument(0); + return RequestContext.forTenantId(event.getCustomerId()); + }); mockSchedulers = Mockito.mockStatic(Schedulers.class); mockSchedulers.when(Schedulers::io).thenReturn(trampoline); } diff --git a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReaderTest.java b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReaderTest.java index 15fc0dc3a..86e2ef9a7 100644 --- a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReaderTest.java +++ b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReaderTest.java @@ -5,17 +5,19 @@ import static org.hypertrace.trace.reader.attributes.AvroUtil.defaultedStructuredTraceBuilder; import static org.hypertrace.trace.reader.attributes.LiteralValueUtil.stringLiteral; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.Optional; +import org.hypertrace.core.attribute.service.client.AttributeServiceCachedClient; import org.hypertrace.core.attribute.service.v1.AttributeDefinition; import org.hypertrace.core.attribute.service.v1.AttributeKind; import org.hypertrace.core.attribute.service.v1.AttributeMetadata; import org.hypertrace.core.attribute.service.v1.AttributeType; import org.hypertrace.core.datamodel.Event; import org.hypertrace.core.datamodel.StructuredTrace; -import org.hypertrace.trace.provider.AttributeProvider; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -25,12 +27,12 @@ @ExtendWith(MockitoExtension.class) class DefaultTraceAttributeReaderTest { - @Mock AttributeProvider mockAttributeProvider; + @Mock AttributeServiceCachedClient mockAttributeClient; private TraceAttributeReader traceAttributeReader; @BeforeEach void beforeEach() { - this.traceAttributeReader = TraceAttributeReaderFactory.build(this.mockAttributeProvider); + this.traceAttributeReader = TraceAttributeReaderFactory.build(this.mockAttributeClient); } @Test @@ -42,7 +44,7 @@ void canReadSpanValues() { .setValueKind(AttributeKind.TYPE_STRING) .setDefinition(AttributeDefinition.newBuilder().setSourcePath("attrPath").build()) .build(); - when(this.mockAttributeProvider.get("defaultCustomerId", "TEST_SCOPE", "key")) + when(this.mockAttributeClient.get(any(), eq("TEST_SCOPE"), eq("key"))) .thenReturn(Optional.of(metadata)); Event span = @@ -66,7 +68,7 @@ void canReadTraceValues() { .setValueKind(AttributeKind.TYPE_STRING) .setDefinition(AttributeDefinition.newBuilder().setSourcePath("attrPath").build()) .build(); - when(this.mockAttributeProvider.get("defaultCustomerId", "TRACE", "key")) + when(this.mockAttributeClient.get(any(), eq("TRACE"), eq("key"))) .thenReturn(Optional.of(metadata)); StructuredTrace trace = From 86cc2c200f12a08dfcea87973cce0f7ea9e70e16 Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Wed, 22 Nov 2023 19:45:13 +0530 Subject: [PATCH 18/29] ok --- .../entities/DefaultTraceEntityAccessorTest.java | 7 +++++-- .../DefaultTraceAttributeReaderTest.java | 14 +++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessorTest.java b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessorTest.java index bf3b7434c..117bb43cc 100644 --- a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessorTest.java +++ b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessorTest.java @@ -303,13 +303,16 @@ private void mockAttributeReadError(AttributeMetadata attributeMetadata) { } private void mockGetAllAttributes(AttributeMetadata... attributeMetadata) { - when(this.mockAttributeClient.getAllInScope(any(), eq(TEST_ENTITY_TYPE_NAME))) + when(this.mockAttributeClient.getAllInScope( + argThat(MATCHING_TENANT_REQUEST_CONTEXT), eq(TEST_ENTITY_TYPE_NAME))) .thenReturn(Arrays.asList(attributeMetadata)); } private void mockGetSingleAttribute(AttributeMetadata attributeMetadata) { when(this.mockAttributeClient.get( - any(), eq(attributeMetadata.getScopeString()), eq(attributeMetadata.getKey()))) + argThat(MATCHING_TENANT_REQUEST_CONTEXT), + eq(attributeMetadata.getScopeString()), + eq(attributeMetadata.getKey()))) .thenReturn(Optional.of(attributeMetadata)); } diff --git a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReaderTest.java b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReaderTest.java index 86e2ef9a7..653c8ed8f 100644 --- a/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReaderTest.java +++ b/hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultTraceAttributeReaderTest.java @@ -5,7 +5,7 @@ import static org.hypertrace.trace.reader.attributes.AvroUtil.defaultedStructuredTraceBuilder; import static org.hypertrace.trace.reader.attributes.LiteralValueUtil.stringLiteral; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -18,9 +18,11 @@ import org.hypertrace.core.attribute.service.v1.AttributeType; import org.hypertrace.core.datamodel.Event; import org.hypertrace.core.datamodel.StructuredTrace; +import org.hypertrace.core.grpcutils.context.RequestContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -29,6 +31,10 @@ class DefaultTraceAttributeReaderTest { @Mock AttributeServiceCachedClient mockAttributeClient; private TraceAttributeReader traceAttributeReader; + private static final ArgumentMatcher MATCHING_TENANT_REQUEST_CONTEXT = + arg -> + arg.buildContextualKey() + .equals(RequestContext.forTenantId("defaultCustomerId").buildContextualKey()); @BeforeEach void beforeEach() { @@ -44,7 +50,8 @@ void canReadSpanValues() { .setValueKind(AttributeKind.TYPE_STRING) .setDefinition(AttributeDefinition.newBuilder().setSourcePath("attrPath").build()) .build(); - when(this.mockAttributeClient.get(any(), eq("TEST_SCOPE"), eq("key"))) + when(this.mockAttributeClient.get( + argThat(MATCHING_TENANT_REQUEST_CONTEXT), eq("TEST_SCOPE"), eq("key"))) .thenReturn(Optional.of(metadata)); Event span = @@ -68,7 +75,8 @@ void canReadTraceValues() { .setValueKind(AttributeKind.TYPE_STRING) .setDefinition(AttributeDefinition.newBuilder().setSourcePath("attrPath").build()) .build(); - when(this.mockAttributeClient.get(any(), eq("TRACE"), eq("key"))) + when(this.mockAttributeClient.get( + argThat(MATCHING_TENANT_REQUEST_CONTEXT), eq("TRACE"), eq("key"))) .thenReturn(Optional.of(metadata)); StructuredTrace trace = From 8eb9bf10a1f506ec76fda68d93fda92074c617e8 Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Wed, 22 Nov 2023 19:48:40 +0530 Subject: [PATCH 19/29] remove grpc --- .../enriched-span-constants/build.gradle.kts | 15 +-------------- .../raw-span-constants/build.gradle.kts | 15 +-------------- .../span-normalizer-api/build.gradle.kts | 15 +-------------- 3 files changed, 3 insertions(+), 42 deletions(-) diff --git a/hypertrace-trace-enricher/enriched-span-constants/build.gradle.kts b/hypertrace-trace-enricher/enriched-span-constants/build.gradle.kts index e03c9e8db..430b0fd50 100644 --- a/hypertrace-trace-enricher/enriched-span-constants/build.gradle.kts +++ b/hypertrace-trace-enricher/enriched-span-constants/build.gradle.kts @@ -12,25 +12,12 @@ protobuf { protoc { artifact = "com.google.protobuf:protoc:3.21.12" } - plugins { - id("grpc") { - artifact = "io.grpc:protoc-gen-grpc-java:1.57.2" - } - } - generateProtoTasks { - ofSourceSet("main").configureEach { - plugins { - // Apply the "grpc" plugin whose spec is defined above, without options. - id("grpc") - } - } - } } sourceSets { main { java { - srcDirs("build/generated/source/proto/main/java", "build/generated/source/proto/main/grpc_java") + srcDirs("build/generated/source/proto/main/java") } } } diff --git a/span-normalizer/raw-span-constants/build.gradle.kts b/span-normalizer/raw-span-constants/build.gradle.kts index af8bfa8ea..5019a1a75 100644 --- a/span-normalizer/raw-span-constants/build.gradle.kts +++ b/span-normalizer/raw-span-constants/build.gradle.kts @@ -10,25 +10,12 @@ protobuf { protoc { artifact = "com.google.protobuf:protoc:3.21.12" } - plugins { - id("grpc") { - artifact = "io.grpc:protoc-gen-grpc-java:1.57.2" - } - } - generateProtoTasks { - ofSourceSet("main").configureEach { - plugins { - // Apply the "grpc" plugin whose spec is defined above, without options. - id("grpc") - } - } - } } sourceSets { main { java { - srcDirs("build/generated/source/proto/main/java", "build/generated/source/proto/main/grpc_java") + srcDirs("build/generated/source/proto/main/java") } } } diff --git a/span-normalizer/span-normalizer-api/build.gradle.kts b/span-normalizer/span-normalizer-api/build.gradle.kts index 9c1f1617a..4c8e40743 100644 --- a/span-normalizer/span-normalizer-api/build.gradle.kts +++ b/span-normalizer/span-normalizer-api/build.gradle.kts @@ -11,25 +11,12 @@ protobuf { protoc { artifact = "com.google.protobuf:protoc:3.21.12" } - plugins { - id("grpc") { - artifact = "io.grpc:protoc-gen-grpc-java:1.57.2" - } - } - generateProtoTasks { - ofSourceSet("main").configureEach { - plugins { - // Apply the "grpc" plugin whose spec is defined above, without options. - id("grpc") - } - } - } } sourceSets { main { java { - srcDirs("build/generated/source/proto/main/java", "build/generated/source/proto/main/grpc_java") + srcDirs("build/generated/source/proto/main/java",) } } } From f1f1dd847e47bafc8e71a79d34c5a1065027d2e6 Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Wed, 22 Nov 2023 19:58:14 +0530 Subject: [PATCH 20/29] spotless --- span-normalizer/span-normalizer-api/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/span-normalizer/span-normalizer-api/build.gradle.kts b/span-normalizer/span-normalizer-api/build.gradle.kts index 4c8e40743..e7e3c607b 100644 --- a/span-normalizer/span-normalizer-api/build.gradle.kts +++ b/span-normalizer/span-normalizer-api/build.gradle.kts @@ -16,7 +16,7 @@ protobuf { sourceSets { main { java { - srcDirs("build/generated/source/proto/main/java",) + srcDirs("build/generated/source/proto/main/java") } } } From d096f6555786b611799e867f71cb2a4ee5d8e6cb Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Wed, 22 Nov 2023 20:24:36 +0530 Subject: [PATCH 21/29] trivy ssl --- .trivyignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.trivyignore b/.trivyignore index e69de29bb..30675bb53 100644 --- a/.trivyignore +++ b/.trivyignore @@ -0,0 +1,2 @@ +# libssl3 +CVE-2023-5678 exp:2023-12-31 \ No newline at end of file From f89dd0caa84a0e73080041f53650b41da5136f72 Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Wed, 22 Nov 2023 20:42:20 +0530 Subject: [PATCH 22/29] update --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ea5c94dc0..ab859f541 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,10 +1,10 @@ [versions] hypertrace-entity-service = "0.8.78" hypertrace-config-service = "0.1.54" -hypertrace-grpc-utils = "0.12.4" +hypertrace-grpc-utils = "0.12.6" hypertrace-serviceFramework = "0.1.62" hypertrace-kafkaStreams = "0.4.3" -hypertrace-view-generator = "0.4.20" +hypertrace-view-generator = "0.4.21" grpc = "1.57.2" [libraries] From 14e674914d99041553d7abbc668843de306dd40e Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Wed, 22 Nov 2023 20:56:01 +0530 Subject: [PATCH 23/29] nit --- .../attributes/DefaultValueResolver.java | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java index a1260efbb..d4f1ba02b 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java @@ -1,6 +1,7 @@ package org.hypertrace.trace.reader.attributes; import com.google.common.util.concurrent.RateLimiter; +import java.util.Collections; import java.util.List; import java.util.NoSuchElementException; import java.util.Optional; @@ -149,22 +150,29 @@ private Optional resolveExpression( } return Optional.empty(); } - Optional> maybeArguments = + List argumentList = this.resolveArgumentList(valueSource, expression.getArgumentsList()); - return maybeArguments.map(arguments -> maybeProjection.get().project(arguments)); + if (argumentList.isEmpty()) { + if (LOGGING_LIMITER.tryAcquire()) { + log.error( + "Failed to resolve argument list for expression with operator: {}", + expression.getOperator()); + } + return Optional.empty(); + } + return Optional.of(maybeProjection.get().project(argumentList)); } - private Optional> resolveArgumentList( + private List resolveArgumentList( ValueSource valueSource, List arguments) { Stream> resolvedArguments = arguments.stream().map(argument -> this.resolveProjection(valueSource, argument)); try { - return Optional.of( - resolvedArguments.map(Optional::orElseThrow).collect(Collectors.toUnmodifiableList())); + return resolvedArguments.map(Optional::orElseThrow).collect(Collectors.toUnmodifiableList()); } catch (NoSuchElementException elementException) { - // if any of the arguments don't resolve, fail argument resolution - return Optional.empty(); + // if any of the arguments don't resolve partial, fail argument resolution + return Collections.emptyList(); } } } From a335c7fc6a107b52d46c2071e8a7ee484a69898e Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Wed, 22 Nov 2023 20:58:31 +0530 Subject: [PATCH 24/29] client utils grouper --- raw-spans-grouper/raw-spans-grouper/build.gradle.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/raw-spans-grouper/raw-spans-grouper/build.gradle.kts b/raw-spans-grouper/raw-spans-grouper/build.gradle.kts index e3f795799..fde7b7d7c 100644 --- a/raw-spans-grouper/raw-spans-grouper/build.gradle.kts +++ b/raw-spans-grouper/raw-spans-grouper/build.gradle.kts @@ -36,6 +36,8 @@ dependencies { implementation(libs.hypertrace.data.model) implementation(libs.hypertrace.serviceFramework.framework) implementation(libs.hypertrace.serviceFramework.metrics) + implementation(libs.hypertrace.grpc.client.utils) + implementation(libs.hypertrace.kafkaStreams.framework) implementation(libs.hypertrace.kafkaStreams.weightedGroupPartitioners) From 8213a79b742e9d194571a8594f708260d7944e83 Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Wed, 22 Nov 2023 22:01:21 +0530 Subject: [PATCH 25/29] nit --- .../src/main/resources/configs/common/application.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher/src/main/resources/configs/common/application.conf b/hypertrace-trace-enricher/hypertrace-trace-enricher/src/main/resources/configs/common/application.conf index b01fc49ad..3772d7f8c 100644 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher/src/main/resources/configs/common/application.conf +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher/src/main/resources/configs/common/application.conf @@ -40,7 +40,7 @@ enricher { port = ${?ATTRIBUTE_SERVICE_PORT_CONFIG} cache = { deadline = 30s - maxSize = 100 + maxSize = 1000 refreshAfterWriteDuration = 15m expireAfterAccessDuration = 1h executorThreads = 4 From 78cb34e27c039292e61875889003d29714da35c3 Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Wed, 22 Nov 2023 22:01:37 +0530 Subject: [PATCH 26/29] nit --- .../src/main/resources/configs/common/application.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher/src/main/resources/configs/common/application.conf b/hypertrace-trace-enricher/hypertrace-trace-enricher/src/main/resources/configs/common/application.conf index 3772d7f8c..4e604c805 100644 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher/src/main/resources/configs/common/application.conf +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher/src/main/resources/configs/common/application.conf @@ -43,7 +43,7 @@ enricher { maxSize = 1000 refreshAfterWriteDuration = 15m expireAfterAccessDuration = 1h - executorThreads = 4 + executorThreads = 1 } } config.service.config = { From b8486b2c1151a2a31892086468d8bba5403ffcba Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Thu, 23 Nov 2023 18:43:15 +0530 Subject: [PATCH 27/29] update --- .../entities/DefaultTraceEntityAccessor.java | 29 +++++----- .../attributes/DefaultValueResolver.java | 53 ++++++++----------- 2 files changed, 36 insertions(+), 46 deletions(-) diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java index 996085d8e..29c4f60be 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java @@ -69,16 +69,17 @@ private boolean isExcludedEntityType(final EntityType entityType) { } private void writeEntityIfExists(EntityType entityType, StructuredTrace trace, Event span) { - Optional entityOptional = this.buildEntity(entityType, trace, span); - if (entityOptional.isEmpty()) { - return; - } - this.entityDataClient.createOrUpdateEntityEventually( - this.traceAttributeReader.getRequestContext(span), - entityOptional.get(), - this.buildUpsertCondition(entityType, trace, span) - .orElse(UpsertCondition.getDefaultInstance()), - this.writeThrottleDuration); + this.buildEntity(entityType, trace, span) + .map( + entity -> { + this.entityDataClient.createOrUpdateEntityEventually( + this.traceAttributeReader.getRequestContext(span), + entity, + this.buildUpsertCondition(entityType, trace, span) + .orElse(UpsertCondition.getDefaultInstance()), + this.writeThrottleDuration); + return null; + }); } private Optional buildUpsertCondition( @@ -162,16 +163,12 @@ private Optional> resolveAllAttributes( List attributeMetadataList = this.attributeClient.getAllInScope( this.traceAttributeReader.getRequestContext(span), scope); - if (attributeMetadataList.isEmpty()) { - return Optional.empty(); - } Map resolvedAttributes = attributeMetadataList.stream() .filter(this::isEntitySourced) .map(attributeMetadata -> this.resolveAttribute(attributeMetadata, trace, span)) - .filter(Optional::isPresent) - .map(Optional::get) - .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); + .flatMap(Optional::stream) + .collect(Collectors.toUnmodifiableMap(Entry::getKey, Entry::getValue)); if (resolvedAttributes.isEmpty()) { return Optional.empty(); } diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java index d4f1ba02b..8dab3f18b 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java @@ -3,11 +3,8 @@ import com.google.common.util.concurrent.RateLimiter; import java.util.Collections; import java.util.List; -import java.util.NoSuchElementException; import java.util.Optional; -import java.util.function.Function; import java.util.stream.Collectors; -import java.util.stream.Stream; import lombok.extern.slf4j.Slf4j; import org.hypertrace.core.attribute.service.client.AttributeServiceCachedClient; import org.hypertrace.core.attribute.service.projection.AttributeProjection; @@ -40,9 +37,7 @@ class DefaultValueResolver implements ValueResolver { public Optional resolve( ValueSource valueSource, AttributeMetadata attributeMetadata) { if (!attributeMetadata.hasDefinition()) { - if (LOGGING_LIMITER.tryAcquire()) { - log.error("Attribute definition not set for attribute: " + attributeMetadata.getId()); - } + logError("Attribute definition not set for attribute: {}", attributeMetadata.getId()); return Optional.empty(); } @@ -85,9 +80,7 @@ private Optional resolveValue( String path) { Optional matchingValueSource = contextValueSource.sourceForScope(attributeScope); if (matchingValueSource.isEmpty()) { - if (LOGGING_LIMITER.tryAcquire()) { - log.error("No value source available supporting scope %s", attributeScope); - } + logError("No value source available supporting scope {}", attributeScope); return Optional.empty(); } switch (attributeType) { @@ -116,9 +109,7 @@ private Optional resolveProjection(ValueSource valueSource, Projec return this.resolveExpression(valueSource, projection.getExpression()); case VALUE_NOT_SET: default: - if (LOGGING_LIMITER.tryAcquire()) { - log.error("Unrecognized projection type"); - } + logError("Unrecognized projection type"); return Optional.empty(); } } @@ -135,9 +126,8 @@ private Optional resolveFirstValuePresent( return definitions.getDefinitionsList().stream() .map(definition -> this.resolveDefinition(valueSource, attributeMetadata, definition)) - .filter(Optional::isPresent) - .findFirst() - .flatMap(Function.identity()); + .flatMap(Optional::stream) + .findFirst(); } private Optional resolveExpression( @@ -145,34 +135,37 @@ private Optional resolveExpression( Optional maybeProjection = this.attributeProjectionRegistry.getProjection(expression.getOperator()); if (maybeProjection.isEmpty()) { - if (LOGGING_LIMITER.tryAcquire()) { - log.error("Unregistered projection operator: {}", expression.getOperator()); - } + logError("Unregistered projection operator: {}", expression.getOperator()); return Optional.empty(); } List argumentList = this.resolveArgumentList(valueSource, expression.getArgumentsList()); if (argumentList.isEmpty()) { - if (LOGGING_LIMITER.tryAcquire()) { - log.error( - "Failed to resolve argument list for expression with operator: {}", - expression.getOperator()); - } + logError("Failed to resolve argument list for expression with operator: {}", expression); return Optional.empty(); } - return Optional.of(maybeProjection.get().project(argumentList)); + return maybeProjection.map(projection -> projection.project(argumentList)); + } + + private void logError(String format, Object... args) { + if (LOGGING_LIMITER.tryAcquire()) { + log.error(format, args); + } } private List resolveArgumentList( ValueSource valueSource, List arguments) { - Stream> resolvedArguments = - arguments.stream().map(argument -> this.resolveProjection(valueSource, argument)); - try { - return resolvedArguments.map(Optional::orElseThrow).collect(Collectors.toUnmodifiableList()); - } catch (NoSuchElementException elementException) { - // if any of the arguments don't resolve partial, fail argument resolution + List resolvedArguments = + arguments.stream() + .map(argument -> this.resolveProjection(valueSource, argument)) + .flatMap(Optional::stream) + .collect(Collectors.toUnmodifiableList()); + if (resolvedArguments.size() != arguments.size()) { + // if any of the arguments don't resolve, return empty list to don't support partial + // resolution return Collections.emptyList(); } + return resolvedArguments; } } From 7566d8f289655e645739aa72e630257c1bf17b36 Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Thu, 23 Nov 2023 18:46:13 +0530 Subject: [PATCH 28/29] update --- .../trace/reader/attributes/DefaultValueResolver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java index 8dab3f18b..06ce49695 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java @@ -109,7 +109,7 @@ private Optional resolveProjection(ValueSource valueSource, Projec return this.resolveExpression(valueSource, projection.getExpression()); case VALUE_NOT_SET: default: - logError("Unrecognized projection type"); + logError("Unrecognized projection type {}", projection.getValueCase()); return Optional.empty(); } } From 9cf9b346fb197dbf1597dcdfc124890d6358c02e Mon Sep 17 00:00:00 2001 From: kishansairam9 Date: Mon, 27 Nov 2023 21:42:15 +0530 Subject: [PATCH 29/29] update --- .../clients/DefaultClientRegistry.java | 3 +-- .../entities/DefaultTraceEntityAccessor.java | 18 ++++++++---------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/DefaultClientRegistry.java b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/DefaultClientRegistry.java index 92bb2c93c..48a61dc8e 100644 --- a/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/DefaultClientRegistry.java +++ b/hypertrace-trace-enricher/hypertrace-trace-enricher-impl/src/main/java/org/hypertrace/traceenricher/enrichment/clients/DefaultClientRegistry.java @@ -41,7 +41,6 @@ public class DefaultClientRegistry implements ClientRegistry { private static final String TRACE_ENTITY_WRITE_EXCLUDED_ENTITY_TYPES = "trace.entity.write.excluded.entity.types"; private static final String USER_AGENT_PARSER_CONFIG_KEY = "useragent.parser"; - private final Channel attributeServiceChannel; private final Channel configServiceChannel; private final Channel entityServiceChannel; private final EdsCacheClient edsCacheClient; @@ -57,7 +56,7 @@ public DefaultClientRegistry( Config config, GrpcChannelRegistry grpcChannelRegistry, Executor cacheLoaderExecutor) { this.grpcChannelRegistry = grpcChannelRegistry; - this.attributeServiceChannel = + Channel attributeServiceChannel = this.buildChannel( config.getString(ATTRIBUTE_SERVICE_HOST_KEY), config.getInt(ATTRIBUTE_SERVICE_PORT_KEY)); diff --git a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java index 29c4f60be..215d3c330 100644 --- a/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java +++ b/hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/accessor/entities/DefaultTraceEntityAccessor.java @@ -70,16 +70,14 @@ private boolean isExcludedEntityType(final EntityType entityType) { private void writeEntityIfExists(EntityType entityType, StructuredTrace trace, Event span) { this.buildEntity(entityType, trace, span) - .map( - entity -> { - this.entityDataClient.createOrUpdateEntityEventually( - this.traceAttributeReader.getRequestContext(span), - entity, - this.buildUpsertCondition(entityType, trace, span) - .orElse(UpsertCondition.getDefaultInstance()), - this.writeThrottleDuration); - return null; - }); + .ifPresent( + entity -> + this.entityDataClient.createOrUpdateEntityEventually( + this.traceAttributeReader.getRequestContext(span), + entity, + this.buildUpsertCondition(entityType, trace, span) + .orElse(UpsertCondition.getDefaultInstance()), + this.writeThrottleDuration)); } private Optional buildUpsertCondition(