From 32a29773ad9b319d4e0de87bc40590bb7f9c0ed6 Mon Sep 17 00:00:00 2001 From: Kishan Sairam Adapa Date: Wed, 22 Nov 2023 00:25:19 +0530 Subject: [PATCH] Add non rx based caching attribute client (#200) * Update gradle locks * trivy * Add non rx based caching attribute client * remove * update * update * fix --------- Co-authored-by: kishansairam9 --- .trivyignore | 2 + attribute-projection-registry/gradle.lockfile | 2 +- attribute-service-api/gradle.lockfile | 2 +- attribute-service-client/build.gradle.kts | 19 +- attribute-service-client/gradle.lockfile | 40 +++- .../client/AttributeServiceCachedClient.java | 128 ++++++++++++ .../AttributeServiceCachedClientConfig.java | 52 +++++ .../AttributeServiceCachedClientTest.java | 193 ++++++++++++++++++ attribute-service-factory/gradle.lockfile | 2 +- attribute-service-impl/gradle.lockfile | 2 +- attribute-service-tenant-api/gradle.lockfile | 2 +- attribute-service/gradle.lockfile | 2 +- .../gradle.lockfile | 2 +- settings.gradle.kts | 2 +- 14 files changed, 437 insertions(+), 13 deletions(-) create mode 100644 attribute-service-client/src/main/java/org/hypertrace/core/attribute/service/client/AttributeServiceCachedClient.java create mode 100644 attribute-service-client/src/main/java/org/hypertrace/core/attribute/service/client/config/AttributeServiceCachedClientConfig.java create mode 100644 attribute-service-client/src/test/java/org/hypertrace/core/attribute/service/client/AttributeServiceCachedClientTest.java diff --git a/.trivyignore b/.trivyignore index e69de29b..30675bb5 100644 --- a/.trivyignore +++ b/.trivyignore @@ -0,0 +1,2 @@ +# libssl3 +CVE-2023-5678 exp:2023-12-31 \ No newline at end of file diff --git a/attribute-projection-registry/gradle.lockfile b/attribute-projection-registry/gradle.lockfile index e52d006a..64362614 100644 --- a/attribute-projection-registry/gradle.lockfile +++ b/attribute-projection-registry/gradle.lockfile @@ -19,6 +19,6 @@ io.grpc:grpc-protobuf:1.57.2=compileClasspath,runtimeClasspath io.grpc:grpc-stub:1.57.2=compileClasspath,runtimeClasspath javax.annotation:javax.annotation-api:1.3.2=runtimeClasspath org.checkerframework:checker-qual:3.33.0=compileClasspath,runtimeClasspath -org.hypertrace.bom:hypertrace-bom:0.2.11=compileClasspath,runtimeClasspath +org.hypertrace.bom:hypertrace-bom:0.3.0=compileClasspath,runtimeClasspath org.hypertrace.core.kafkastreams.framework:kafka-bom:0.3.9=compileClasspath,runtimeClasspath empty=annotationProcessor diff --git a/attribute-service-api/gradle.lockfile b/attribute-service-api/gradle.lockfile index 75c16c0d..817d8fe4 100644 --- a/attribute-service-api/gradle.lockfile +++ b/attribute-service-api/gradle.lockfile @@ -18,6 +18,6 @@ io.grpc:grpc-protobuf:1.57.2=compileClasspath,runtimeClasspath io.grpc:grpc-stub:1.57.2=compileClasspath,runtimeClasspath javax.annotation:javax.annotation-api:1.3.2=compileClasspath,runtimeClasspath org.checkerframework:checker-qual:3.33.0=compileClasspath,runtimeClasspath -org.hypertrace.bom:hypertrace-bom:0.2.11=compileClasspath,runtimeClasspath +org.hypertrace.bom:hypertrace-bom:0.3.0=compileClasspath,runtimeClasspath org.hypertrace.core.kafkastreams.framework:kafka-bom:0.3.9=compileClasspath,runtimeClasspath empty=annotationProcessor diff --git a/attribute-service-client/build.gradle.kts b/attribute-service-client/build.gradle.kts index 4729a9de..8c6e5ec6 100644 --- a/attribute-service-client/build.gradle.kts +++ b/attribute-service-client/build.gradle.kts @@ -5,7 +5,24 @@ plugins { dependencies { api(projects.attributeServiceApi) - api(commonLibs.typesafe.config) implementation(commonLibs.hypertrace.grpcutils.client) + implementation(commonLibs.hypertrace.grpcutils.context) + implementation(commonLibs.hypertrace.framework.metrics) + implementation(commonLibs.slf4j2.api) + implementation(commonLibs.typesafe.config) + implementation(commonLibs.guava) + + annotationProcessor(commonLibs.lombok) + compileOnly(commonLibs.lombok) + + testImplementation(commonLibs.junit.jupiter) + testImplementation(commonLibs.mockito.core) + testImplementation(commonLibs.mockito.junit) + testImplementation(commonLibs.grpc.core) + testImplementation(commonLibs.log4j.slf4j2.impl) +} + +tasks.test { + useJUnitPlatform() } diff --git a/attribute-service-client/gradle.lockfile b/attribute-service-client/gradle.lockfile index 434ba101..cf6d7516 100644 --- a/attribute-service-client/gradle.lockfile +++ b/attribute-service-client/gradle.lockfile @@ -19,6 +19,9 @@ com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=comp com.google.j2objc:j2objc-annotations:2.8=compileClasspath com.google.protobuf:protobuf-java:3.24.1=compileClasspath,runtimeClasspath com.typesafe:config:1.4.2=compileClasspath,runtimeClasspath +io.dropwizard.metrics:metrics-core:4.2.16=compileClasspath,runtimeClasspath +io.dropwizard.metrics:metrics-jvm:4.2.16=runtimeClasspath +io.github.mweirauch:micrometer-jvm-extras:0.2.2=runtimeClasspath io.grpc:grpc-api:1.57.2=compileClasspath,runtimeClasspath io.grpc:grpc-bom:1.57.2=compileClasspath,runtimeClasspath io.grpc:grpc-context:1.57.2=compileClasspath,runtimeClasspath @@ -26,14 +29,43 @@ io.grpc:grpc-core:1.57.2=runtimeClasspath io.grpc:grpc-protobuf-lite:1.57.2=compileClasspath,runtimeClasspath io.grpc:grpc-protobuf:1.57.2=compileClasspath,runtimeClasspath io.grpc:grpc-stub:1.57.2=compileClasspath,runtimeClasspath +io.micrometer:micrometer-commons:1.10.2=compileClasspath,runtimeClasspath +io.micrometer:micrometer-core:1.10.2=compileClasspath,runtimeClasspath +io.micrometer:micrometer-observation:1.10.2=compileClasspath,runtimeClasspath +io.micrometer:micrometer-registry-prometheus:1.10.2=runtimeClasspath io.netty:netty-bom:4.1.100.Final=compileClasspath,runtimeClasspath io.perfmark:perfmark-api:0.26.0=runtimeClasspath +io.prometheus:simpleclient:0.16.0=runtimeClasspath +io.prometheus:simpleclient_common:0.16.0=runtimeClasspath +io.prometheus:simpleclient_dropwizard:0.12.0=runtimeClasspath +io.prometheus:simpleclient_pushgateway:0.12.0=runtimeClasspath +io.prometheus:simpleclient_servlet:0.12.0=runtimeClasspath +io.prometheus:simpleclient_servlet_common:0.12.0=runtimeClasspath +io.prometheus:simpleclient_tracer_common:0.16.0=runtimeClasspath +io.prometheus:simpleclient_tracer_otel:0.16.0=runtimeClasspath +io.prometheus:simpleclient_tracer_otel_agent:0.16.0=runtimeClasspath javax.annotation:javax.annotation-api:1.3.2=runtimeClasspath +javax.servlet:javax.servlet-api:3.1.0=compileClasspath,runtimeClasspath +javax.xml.bind:jaxb-api:2.3.0=runtimeClasspath +org.apache.logging.log4j:log4j-api:2.19.0=runtimeClasspath +org.apache.logging.log4j:log4j-core:2.19.0=runtimeClasspath +org.apache.logging.log4j:log4j-slf4j-impl:2.19.0=runtimeClasspath org.checkerframework:checker-qual:3.33.0=compileClasspath,runtimeClasspath org.codehaus.mojo:animal-sniffer-annotations:1.23=runtimeClasspath -org.hypertrace.bom:hypertrace-bom:0.2.11=compileClasspath,runtimeClasspath +org.eclipse.jetty:jetty-http:9.4.53.v20231009=runtimeClasspath +org.eclipse.jetty:jetty-io:9.4.53.v20231009=runtimeClasspath +org.eclipse.jetty:jetty-security:9.4.53.v20231009=runtimeClasspath +org.eclipse.jetty:jetty-server:9.4.53.v20231009=runtimeClasspath +org.eclipse.jetty:jetty-servlet:9.4.53.v20231009=runtimeClasspath +org.eclipse.jetty:jetty-util-ajax:9.4.53.v20231009=runtimeClasspath +org.eclipse.jetty:jetty-util:9.4.53.v20231009=runtimeClasspath +org.hdrhistogram:HdrHistogram:2.1.12=runtimeClasspath +org.hypertrace.bom:hypertrace-bom:0.3.0=compileClasspath,runtimeClasspath org.hypertrace.core.grpcutils:grpc-client-utils:0.12.6=compileClasspath,runtimeClasspath -org.hypertrace.core.grpcutils:grpc-context-utils:0.12.6=runtimeClasspath +org.hypertrace.core.grpcutils:grpc-context-utils:0.12.6=compileClasspath,runtimeClasspath org.hypertrace.core.kafkastreams.framework:kafka-bom:0.3.9=compileClasspath,runtimeClasspath -org.slf4j:slf4j-api:2.0.7=runtimeClasspath -empty=annotationProcessor +org.hypertrace.core.serviceframework:platform-metrics:0.1.62=compileClasspath,runtimeClasspath +org.latencyutils:LatencyUtils:2.0.3=runtimeClasspath +org.projectlombok:lombok:1.18.28=annotationProcessor,compileClasspath +org.slf4j:slf4j-api:2.0.7=compileClasspath,runtimeClasspath +empty= diff --git a/attribute-service-client/src/main/java/org/hypertrace/core/attribute/service/client/AttributeServiceCachedClient.java b/attribute-service-client/src/main/java/org/hypertrace/core/attribute/service/client/AttributeServiceCachedClient.java new file mode 100644 index 00000000..d1350f59 --- /dev/null +++ b/attribute-service-client/src/main/java/org/hypertrace/core/attribute/service/client/AttributeServiceCachedClient.java @@ -0,0 +1,128 @@ +package org.hypertrace.core.attribute.service.client; + +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.ThreadFactoryBuilder; +import io.grpc.Channel; +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 javax.annotation.Nonnull; +import lombok.Value; +import lombok.extern.slf4j.Slf4j; +import org.hypertrace.core.attribute.service.client.config.AttributeServiceCachedClientConfig; +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.ClientCallCredentialsProvider; +import org.hypertrace.core.grpcutils.client.RequestContextClientCallCredsProviderFactory; +import org.hypertrace.core.grpcutils.context.ContextualKey; +import org.hypertrace.core.grpcutils.context.RequestContext; +import org.hypertrace.core.serviceframework.metrics.PlatformMetricsRegistry; + +@Slf4j +public class AttributeServiceCachedClient { + private final LoadingCache, Table> cache; + private final Cache scopeAndKeyLookup; + private final AttributeServiceBlockingStub attributeServiceBlockingStub; + private final long deadlineMs; + private final ClientCallCredentialsProvider callCredentialsProvider; + + AttributeServiceCachedClient(Channel channel, AttributeServiceCachedClientConfig clientConfig) { + this( + channel, + clientConfig, + RequestContextClientCallCredsProviderFactory.getClientCallCredsProvider()); + } + + AttributeServiceCachedClient( + Channel channel, + AttributeServiceCachedClientConfig clientConfig, + ClientCallCredentialsProvider callCredentialsProvider) { + this.callCredentialsProvider = callCredentialsProvider; + this.attributeServiceBlockingStub = AttributeServiceGrpc.newBlockingStub(channel); + deadlineMs = clientConfig.getDeadline().toMillis(); + cache = + CacheBuilder.newBuilder() + .maximumSize(clientConfig.getMaxSize()) + .refreshAfterWrite(clientConfig.getRefreshAfterWrite()) + .expireAfterAccess(clientConfig.getExpireAfterAccess()) + .build( + CacheLoader.asyncReloading( + CacheLoader.from(this::loadTable), + Executors.newFixedThreadPool( + clientConfig.getExecutorThreads(), this.buildThreadFactory()))); + PlatformMetricsRegistry.registerCache( + clientConfig.getCacheMetricsName(), cache, Collections.emptyMap()); + scopeAndKeyLookup = + CacheBuilder.newBuilder().expireAfterWrite(clientConfig.getExpireAfterAccess()).build(); + } + + public Optional get( + @Nonnull RequestContext requestContext, + @Nonnull String attributeScope, + @Nonnull String attributeKey) { + return Optional.ofNullable(getTable(requestContext).get(attributeScope, attributeKey)); + } + + public Optional getById( + @Nonnull RequestContext requestContext, @Nonnull String attributeId) { + Table table = getTable(requestContext); + return Optional.ofNullable(scopeAndKeyLookup.getIfPresent(attributeId)) + .map(scopeAndKey -> table.get(scopeAndKey.getScope(), scopeAndKey.getKey())); + } + + public List getAllInScope( + @Nonnull RequestContext requestContext, @Nonnull String attributeScope) { + return List.copyOf(getTable(requestContext).row(attributeScope).values()); + } + + private Table getTable(RequestContext requestContext) { + return cache.getUnchecked(requestContext.buildInternalContextualKey()); + } + + private Table loadTable(ContextualKey contextualKey) { + List attributeMetadataList = + contextualKey + .getContext() + .call( + () -> + attributeServiceBlockingStub + .withDeadlineAfter(deadlineMs, TimeUnit.MILLISECONDS) + .withCallCredentials(callCredentialsProvider.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(); + } + + @Value + private static class AttributeScopeAndKey { + String scope; + String key; + } +} diff --git a/attribute-service-client/src/main/java/org/hypertrace/core/attribute/service/client/config/AttributeServiceCachedClientConfig.java b/attribute-service-client/src/main/java/org/hypertrace/core/attribute/service/client/config/AttributeServiceCachedClientConfig.java new file mode 100644 index 00000000..9b399ed1 --- /dev/null +++ b/attribute-service-client/src/main/java/org/hypertrace/core/attribute/service/client/config/AttributeServiceCachedClientConfig.java @@ -0,0 +1,52 @@ +package org.hypertrace.core.attribute.service.client.config; + +import com.typesafe.config.Config; +import java.time.Duration; +import lombok.Value; +import org.hypertrace.core.attribute.service.client.AttributeServiceCachedClient; + +@Value +public class AttributeServiceCachedClientConfig { + 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_ACCESS_CONFIG_KEY = "expireAfterAccessDuration"; + private static final String CACHE_EXECUTOR_THREADS_CONFIG_KEY = "executorThreads"; + + Duration deadline; + long maxSize; + Duration refreshAfterWrite; + Duration expireAfterAccess; + int executorThreads; + String cacheMetricsName; + + public static AttributeServiceCachedClientConfig from(Config attributeServiceConfig) { + return from(attributeServiceConfig, AttributeServiceCachedClient.class.getName()); + } + + public static AttributeServiceCachedClientConfig from( + Config attributeServiceConfig, String cacheMetricsName) { + Duration deadline = + attributeServiceConfig.hasPath(DEADLINE_CONFIG_KEY) + ? attributeServiceConfig.getDuration(DEADLINE_CONFIG_KEY) + : Duration.ofSeconds(30); + long maxSize = + attributeServiceConfig.hasPath(CACHE_MAX_SIZE_CONFIG_KEY) + ? attributeServiceConfig.getLong(CACHE_MAX_SIZE_CONFIG_KEY) + : 1000; + Duration refreshAfterWrite = + attributeServiceConfig.hasPath(CACHE_REFRESH_AFTER_WRITE_CONFIG_KEY) + ? attributeServiceConfig.getDuration(CACHE_REFRESH_AFTER_WRITE_CONFIG_KEY) + : Duration.ofMinutes(15); + Duration expireAfterWrite = + attributeServiceConfig.hasPath(CACHE_EXPIRE_AFTER_ACCESS_CONFIG_KEY) + ? attributeServiceConfig.getDuration(CACHE_EXPIRE_AFTER_ACCESS_CONFIG_KEY) + : Duration.ofHours(1); + int executorThreads = + attributeServiceConfig.hasPath(CACHE_EXECUTOR_THREADS_CONFIG_KEY) + ? attributeServiceConfig.getInt(CACHE_EXECUTOR_THREADS_CONFIG_KEY) + : 1; + return new AttributeServiceCachedClientConfig( + deadline, maxSize, refreshAfterWrite, expireAfterWrite, executorThreads, cacheMetricsName); + } +} diff --git a/attribute-service-client/src/test/java/org/hypertrace/core/attribute/service/client/AttributeServiceCachedClientTest.java b/attribute-service-client/src/test/java/org/hypertrace/core/attribute/service/client/AttributeServiceCachedClientTest.java new file mode 100644 index 00000000..5dc59e8f --- /dev/null +++ b/attribute-service-client/src/test/java/org/hypertrace/core/attribute/service/client/AttributeServiceCachedClientTest.java @@ -0,0 +1,193 @@ +package org.hypertrace.core.attribute.service.client; + +import static java.util.Collections.emptyList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import com.typesafe.config.ConfigFactory; +import io.grpc.ManagedChannel; +import io.grpc.Server; +import io.grpc.inprocess.InProcessChannelBuilder; +import io.grpc.inprocess.InProcessServerBuilder; +import io.grpc.stub.StreamObserver; +import java.io.IOException; +import java.util.List; +import java.util.Optional; +import org.hypertrace.core.attribute.service.client.config.AttributeServiceCachedClientConfig; +import org.hypertrace.core.attribute.service.v1.AttributeMetadata; +import org.hypertrace.core.attribute.service.v1.AttributeMetadataFilter; +import org.hypertrace.core.attribute.service.v1.AttributeScope; +import org.hypertrace.core.attribute.service.v1.AttributeServiceGrpc; +import org.hypertrace.core.attribute.service.v1.GetAttributesResponse; +import org.hypertrace.core.grpcutils.context.RequestContext; +import org.junit.jupiter.api.AfterEach; +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; + +@ExtendWith(MockitoExtension.class) +class AttributeServiceCachedClientTest { + AttributeMetadata metadata1 = + AttributeMetadata.newBuilder() + .setScopeString(AttributeScope.EVENT.name()) + .setKey("first") + .setId("first-id") + .build(); + AttributeMetadata metadata2 = + AttributeMetadata.newBuilder() + .setScopeString(AttributeScope.EVENT.name()) + .setKey("second") + .setId("second-id") + .build(); + + RequestContext requestContext; + + @Mock AttributeServiceGrpc.AttributeServiceImplBase mockAttributeService; + Server grpcServer; + ManagedChannel grpcChannel; + List responseMetadata; + Optional responseError; + AttributeMetadataFilter metadataFilter; + AttributeServiceCachedClient attributeServiceCachedClient; + + @BeforeEach + void beforeEach() throws IOException { + when(mockAttributeService.bindService()) + .thenReturn(AttributeServiceGrpc.bindService(mockAttributeService)); + String uniqueName = InProcessServerBuilder.generateName(); + this.grpcServer = + InProcessServerBuilder.forName(uniqueName) + .directExecutor() // directExecutor is fine for unit tests + .addService(mockAttributeService) + .build() + .start(); + this.grpcChannel = InProcessChannelBuilder.forName(uniqueName).directExecutor().build(); + this.attributeServiceCachedClient = + new AttributeServiceCachedClient( + grpcChannel, AttributeServiceCachedClientConfig.from(ConfigFactory.empty())); + this.requestContext = RequestContext.forTenantId("default tenant"); + this.responseMetadata = List.of(this.metadata1, this.metadata2); + this.responseError = Optional.empty(); + this.metadataFilter = AttributeMetadataFilter.getDefaultInstance(); + + lenient() + .doAnswer( + invocation -> { + StreamObserver observer = + invocation.getArgument(1, StreamObserver.class); + responseError.ifPresentOrElse( + observer::onError, + () -> { + observer.onNext( + GetAttributesResponse.newBuilder() + .addAllAttributes(responseMetadata) + .build()); + observer.onCompleted(); + }); + return null; + }) + .when(this.mockAttributeService) + .getAttributes(any(), any()); + } + + @AfterEach + void afterEach() { + this.grpcServer.shutdownNow(); + this.grpcChannel.shutdownNow(); + } + + @Test + void cachesConsecutiveGetAllCallsInSameContext() { + assertSame( + this.metadata1, + this.attributeServiceCachedClient.get(requestContext, "EVENT", "first").get()); + verify(this.mockAttributeService, times(1)).getAttributes(any(), any()); + verifyNoMoreInteractions(this.mockAttributeService); + assertSame( + this.metadata2, + this.attributeServiceCachedClient.get(requestContext, "EVENT", "second").get()); + } + + @Test + void returnEmptyIfNoMatch() { + assertTrue(this.attributeServiceCachedClient.get(requestContext, "EVENT", "fake").isEmpty()); + } + + @Test + void supportsMultipleConcurrentCacheKeys() { + AttributeMetadata defaultRetrieved = + this.attributeServiceCachedClient.get(requestContext, "EVENT", "first").get(); + assertSame(this.metadata1, defaultRetrieved); + verify(this.mockAttributeService, times(1)).getAttributes(any(), any()); + + RequestContext otherRequestContext = RequestContext.forTenantId("other tenant"); + AttributeMetadata otherContextMetadata = AttributeMetadata.newBuilder(this.metadata1).build(); + + this.responseMetadata = List.of(otherContextMetadata); + + AttributeMetadata otherRetrieved = + this.attributeServiceCachedClient.get(otherRequestContext, "EVENT", "first").get(); + assertSame(otherContextMetadata, otherRetrieved); + assertNotSame(defaultRetrieved, otherRetrieved); + verify(this.mockAttributeService, times(2)).getAttributes(any(), any()); + verifyNoMoreInteractions(this.mockAttributeService); + + assertSame( + defaultRetrieved, + this.attributeServiceCachedClient.get(requestContext, "EVENT", "first").get()); + + assertSame( + otherRetrieved, + this.attributeServiceCachedClient.get(otherRequestContext, "EVENT", "first").get()); + } + + @Test + void supportsCachedLookupById() { + assertSame( + this.metadata1, + this.attributeServiceCachedClient.getById(requestContext, "first-id").get()); + verify(this.mockAttributeService, times(1)).getAttributes(any(), any()); + verifyNoMoreInteractions(this.mockAttributeService); + assertSame( + this.metadata2, + this.attributeServiceCachedClient.getById(requestContext, "second-id").get()); + } + + @Test + void sharesIdAndKeyCache() { + assertSame( + this.metadata1, + this.attributeServiceCachedClient.getById(requestContext, "first-id").get()); + verify(this.mockAttributeService, times(1)).getAttributes(any(), any()); + verifyNoMoreInteractions(this.mockAttributeService); + assertSame( + this.metadata1, + this.attributeServiceCachedClient.get(requestContext, "EVENT", "first").get()); + } + + @Test + void emptyIfNoIdMatch() { + assertTrue(this.attributeServiceCachedClient.getById(requestContext, "fakeId").isEmpty()); + } + + @Test + void getsAllAttributesInScope() { + assertEquals( + this.responseMetadata, + this.attributeServiceCachedClient.getAllInScope(requestContext, "EVENT")); + + assertEquals( + emptyList(), + this.attributeServiceCachedClient.getAllInScope(requestContext, "DOESNT_EXIST")); + } +} diff --git a/attribute-service-factory/gradle.lockfile b/attribute-service-factory/gradle.lockfile index 0fe410e6..f52dedc0 100644 --- a/attribute-service-factory/gradle.lockfile +++ b/attribute-service-factory/gradle.lockfile @@ -75,7 +75,7 @@ org.eclipse.jetty:jetty-servlet:9.4.53.v20231009=runtimeClasspath org.eclipse.jetty:jetty-util-ajax:9.4.53.v20231009=runtimeClasspath org.eclipse.jetty:jetty-util:9.4.53.v20231009=runtimeClasspath org.hdrhistogram:HdrHistogram:2.1.12=runtimeClasspath -org.hypertrace.bom:hypertrace-bom:0.2.11=compileClasspath,runtimeClasspath +org.hypertrace.bom:hypertrace-bom:0.3.0=compileClasspath,runtimeClasspath org.hypertrace.core.documentstore:document-store:0.7.49=compileClasspath,runtimeClasspath org.hypertrace.core.grpcutils:grpc-client-utils:0.12.6=compileClasspath,runtimeClasspath org.hypertrace.core.grpcutils:grpc-context-utils:0.12.6=runtimeClasspath diff --git a/attribute-service-impl/gradle.lockfile b/attribute-service-impl/gradle.lockfile index 00977942..cddf93ad 100644 --- a/attribute-service-impl/gradle.lockfile +++ b/attribute-service-impl/gradle.lockfile @@ -67,7 +67,7 @@ org.eclipse.jetty:jetty-servlet:9.4.53.v20231009=runtimeClasspath org.eclipse.jetty:jetty-util-ajax:9.4.53.v20231009=runtimeClasspath org.eclipse.jetty:jetty-util:9.4.53.v20231009=runtimeClasspath org.hdrhistogram:HdrHistogram:2.1.12=runtimeClasspath -org.hypertrace.bom:hypertrace-bom:0.2.11=compileClasspath,runtimeClasspath +org.hypertrace.bom:hypertrace-bom:0.3.0=compileClasspath,runtimeClasspath org.hypertrace.core.documentstore:document-store:0.7.49=compileClasspath,runtimeClasspath org.hypertrace.core.grpcutils:grpc-context-utils:0.12.6=compileClasspath,runtimeClasspath org.hypertrace.core.kafkastreams.framework:kafka-bom:0.3.9=compileClasspath,runtimeClasspath diff --git a/attribute-service-tenant-api/gradle.lockfile b/attribute-service-tenant-api/gradle.lockfile index c65ff409..1f79c771 100644 --- a/attribute-service-tenant-api/gradle.lockfile +++ b/attribute-service-tenant-api/gradle.lockfile @@ -3,6 +3,6 @@ # This file is expected to be part of source control. com.fasterxml.jackson:jackson-bom:2.15.2=compileClasspath,runtimeClasspath io.grpc:grpc-bom:1.57.2=compileClasspath,runtimeClasspath -org.hypertrace.bom:hypertrace-bom:0.2.11=compileClasspath,runtimeClasspath +org.hypertrace.bom:hypertrace-bom:0.3.0=compileClasspath,runtimeClasspath org.hypertrace.core.kafkastreams.framework:kafka-bom:0.3.9=compileClasspath,runtimeClasspath empty=annotationProcessor diff --git a/attribute-service/gradle.lockfile b/attribute-service/gradle.lockfile index 91273b09..89b0ea38 100644 --- a/attribute-service/gradle.lockfile +++ b/attribute-service/gradle.lockfile @@ -88,7 +88,7 @@ org.eclipse.jetty:jetty-servlet:9.4.53.v20231009=runtimeClasspath org.eclipse.jetty:jetty-util-ajax:9.4.53.v20231009=runtimeClasspath org.eclipse.jetty:jetty-util:9.4.53.v20231009=runtimeClasspath org.hdrhistogram:HdrHistogram:2.1.12=runtimeClasspath -org.hypertrace.bom:hypertrace-bom:0.2.11=compileClasspath,runtimeClasspath +org.hypertrace.bom:hypertrace-bom:0.3.0=compileClasspath,runtimeClasspath org.hypertrace.core.documentstore:document-store:0.7.49=runtimeClasspath org.hypertrace.core.grpcutils:grpc-client-utils:0.12.6=compileClasspath,runtimeClasspath org.hypertrace.core.grpcutils:grpc-context-utils:0.12.6=runtimeClasspath diff --git a/caching-attribute-service-client/gradle.lockfile b/caching-attribute-service-client/gradle.lockfile index de345bf4..e928cd90 100644 --- a/caching-attribute-service-client/gradle.lockfile +++ b/caching-attribute-service-client/gradle.lockfile @@ -31,7 +31,7 @@ io.reactivex.rxjava3:rxjava:3.1.7=compileClasspath,runtimeClasspath javax.annotation:javax.annotation-api:1.3.2=runtimeClasspath org.checkerframework:checker-qual:3.33.0=compileClasspath,runtimeClasspath org.codehaus.mojo:animal-sniffer-annotations:1.23=runtimeClasspath -org.hypertrace.bom:hypertrace-bom:0.2.11=compileClasspath,runtimeClasspath +org.hypertrace.bom:hypertrace-bom:0.3.0=compileClasspath,runtimeClasspath org.hypertrace.core.grpcutils:grpc-client-rx-utils:0.12.6=compileClasspath,runtimeClasspath org.hypertrace.core.grpcutils:grpc-client-utils:0.12.6=compileClasspath,runtimeClasspath org.hypertrace.core.grpcutils:grpc-context-utils:0.12.6=compileClasspath,runtimeClasspath diff --git a/settings.gradle.kts b/settings.gradle.kts index 0e484b34..6d5133ab 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -18,7 +18,7 @@ plugins { } configure { - catalogVersion.set("0.2.9") + catalogVersion.set("0.3.0") } include(":attribute-service-api")