Query migration to db done - Query persistence in cache is also done
This commit is contained in:
1
.vscode/launch.json
vendored
1
.vscode/launch.json
vendored
@@ -59,6 +59,7 @@
|
|||||||
"REDIS_HOST": "103.125.129.116",
|
"REDIS_HOST": "103.125.129.116",
|
||||||
"REDIS_PORT": "7901",
|
"REDIS_PORT": "7901",
|
||||||
"REDIS_PASSWORD": "M@triXR3d1s@6202",
|
"REDIS_PASSWORD": "M@triXR3d1s@6202",
|
||||||
|
"REDIS_DATABASE": "1",
|
||||||
"REDIS_SSL": "false",
|
"REDIS_SSL": "false",
|
||||||
"CYGNUS_CLOUD_BASE_URL": "http://localhost:8090",
|
"CYGNUS_CLOUD_BASE_URL": "http://localhost:8090",
|
||||||
"CYGNUS_TOKEN_URL": "http://localhost:8090/oauth2/token",
|
"CYGNUS_TOKEN_URL": "http://localhost:8090/oauth2/token",
|
||||||
|
|||||||
@@ -52,10 +52,10 @@ public class CloudIdentityClient {
|
|||||||
.timeout(properties.requestTimeout());
|
.timeout(properties.requestTimeout());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Mono<CloudQueryResponse> fetchQuery(String queryId) {
|
public Mono<CloudQueryResponse> fetchQuery(int queryId) {
|
||||||
return tokenProvider.accessToken()
|
return tokenProvider.accessToken()
|
||||||
.flatMap(token -> webClient.get()
|
.flatMap(token -> webClient.get()
|
||||||
.uri(properties.baseUri().resolve("/api/v1/queries/").resolve(queryId))
|
.uri(properties.baseUri().resolve("/api/v1/queries/" + queryId))
|
||||||
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
|
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
|
||||||
.accept(MediaType.APPLICATION_JSON)
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
.retrieve()
|
.retrieve()
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
package com.cygnus.client.model;
|
package com.cygnus.client.model;
|
||||||
|
|
||||||
public record CloudQueryResponse(String queryId, String query) {
|
public record CloudQueryResponse(int queryId, String query) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
package com.cygnus.cloud.query;
|
package com.cygnus.cloud.query;
|
||||||
|
|
||||||
public record CloudQuery(String queryId, String query) {
|
public record CloudQuery(int queryId, String query) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.cygnus.cloud.query;
|
package com.cygnus.cloud.query;
|
||||||
|
|
||||||
import jakarta.validation.constraints.Pattern;
|
import jakarta.validation.constraints.Min;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
@@ -21,10 +21,8 @@ public class CloudQueryController {
|
|||||||
|
|
||||||
@GetMapping("/{queryId}")
|
@GetMapping("/{queryId}")
|
||||||
public Mono<CloudQuery> query(
|
public Mono<CloudQuery> query(
|
||||||
@PathVariable
|
@PathVariable @Min(1) int queryId) {
|
||||||
@Pattern(regexp = "^[A-Za-z0-9._-]+$") String queryId) {
|
|
||||||
return repository.findEnabled(queryId)
|
return repository.findEnabled(queryId)
|
||||||
.switchIfEmpty(Mono.error(new QueryNotFoundException(queryId)));
|
.switchIfEmpty(Mono.error(new QueryNotFoundException(queryId)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
package com.cygnus.cloud.query;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import org.springframework.boot.ApplicationArguments;
|
|
||||||
import org.springframework.boot.ApplicationRunner;
|
|
||||||
import org.springframework.core.io.ClassPathResource;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import reactor.core.publisher.Flux;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
public class QueryCatalogInitializer implements ApplicationRunner {
|
|
||||||
|
|
||||||
static final String DELIMITER = "!C0L!";
|
|
||||||
private final QueryCatalogRepository repository;
|
|
||||||
|
|
||||||
public QueryCatalogInitializer(QueryCatalogRepository repository) {
|
|
||||||
this.repository = repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run(ApplicationArguments arguments) {
|
|
||||||
List<CloudQuery> queries = readCatalog();
|
|
||||||
repository.initialize()
|
|
||||||
.thenMany(Flux.fromIterable(queries)
|
|
||||||
.concatMap(repository::insertIfAbsent))
|
|
||||||
.then()
|
|
||||||
.block(Duration.ofMinutes(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
List<CloudQuery> readCatalog() {
|
|
||||||
ClassPathResource resource = new ClassPathResource("queries/nimble.qry");
|
|
||||||
List<CloudQuery> queries = new ArrayList<>();
|
|
||||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(
|
|
||||||
resource.getInputStream(), StandardCharsets.UTF_8))) {
|
|
||||||
String line;
|
|
||||||
while ((line = reader.readLine()) != null) {
|
|
||||||
int delimiter = line.indexOf(DELIMITER);
|
|
||||||
if (delimiter <= 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
String queryId = line.substring(0, delimiter).trim();
|
|
||||||
String query = line.substring(delimiter + DELIMITER.length());
|
|
||||||
if (queryId.matches("^Query\\d+$") && !query.isBlank()) {
|
|
||||||
queries.add(new CloudQuery(queryId, query));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return List.copyOf(queries);
|
|
||||||
} catch (Exception exception) {
|
|
||||||
throw new IllegalStateException("Unable to load cloud query catalog", exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,13 +11,13 @@ public class QueryCatalogRepository {
|
|||||||
private static final String INITIALIZE = """
|
private static final String INITIALIZE = """
|
||||||
CREATE SCHEMA IF NOT EXISTS platform;
|
CREATE SCHEMA IF NOT EXISTS platform;
|
||||||
CREATE TABLE IF NOT EXISTS platform.application_query (
|
CREATE TABLE IF NOT EXISTS platform.application_query (
|
||||||
query_id varchar(100) PRIMARY KEY,
|
query_id integer PRIMARY KEY,
|
||||||
query_text text NOT NULL,
|
query_text text NOT NULL,
|
||||||
enabled boolean NOT NULL DEFAULT true,
|
enabled boolean NOT NULL DEFAULT true,
|
||||||
created_at timestamptz NOT NULL DEFAULT current_timestamp,
|
created_at timestamptz NOT NULL DEFAULT current_timestamp,
|
||||||
updated_at timestamptz NOT NULL DEFAULT current_timestamp,
|
updated_at timestamptz NOT NULL DEFAULT current_timestamp,
|
||||||
CONSTRAINT ck_platform_application_query_id
|
CONSTRAINT ck_platform_application_query_id
|
||||||
CHECK (query_id ~ '^[A-Za-z0-9._-]+$'),
|
CHECK (query_id > 0),
|
||||||
CONSTRAINT ck_platform_application_query_text
|
CONSTRAINT ck_platform_application_query_text
|
||||||
CHECK (length(btrim(query_text)) > 0)
|
CHECK (length(btrim(query_text)) > 0)
|
||||||
)
|
)
|
||||||
@@ -28,12 +28,6 @@ public class QueryCatalogRepository {
|
|||||||
WHERE query_id = $1
|
WHERE query_id = $1
|
||||||
AND enabled = true
|
AND enabled = true
|
||||||
""";
|
""";
|
||||||
private static final String INSERT_IF_ABSENT = """
|
|
||||||
INSERT INTO platform.application_query (query_id, query_text)
|
|
||||||
VALUES ($1, $2)
|
|
||||||
ON CONFLICT (query_id) DO NOTHING
|
|
||||||
""";
|
|
||||||
|
|
||||||
private final ReactiveDatabaseClient database;
|
private final ReactiveDatabaseClient database;
|
||||||
|
|
||||||
public QueryCatalogRepository(ReactiveDatabaseClient database) {
|
public QueryCatalogRepository(ReactiveDatabaseClient database) {
|
||||||
@@ -44,18 +38,11 @@ public class QueryCatalogRepository {
|
|||||||
return database.query(INITIALIZE).then();
|
return database.query(INITIALIZE).then();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Mono<CloudQuery> findEnabled(String queryId) {
|
public Mono<CloudQuery> findEnabled(int queryId) {
|
||||||
return database.preparedQuery(FIND, Tuple.of(queryId))
|
return database.preparedQuery(FIND, Tuple.of(queryId))
|
||||||
.flatMapMany(rows -> reactor.core.publisher.Flux.fromIterable(rows))
|
.flatMapMany(rows -> reactor.core.publisher.Flux.fromIterable(rows))
|
||||||
.next()
|
.next()
|
||||||
.map(row -> new CloudQuery(
|
.map(row -> new CloudQuery(
|
||||||
row.getString("query_id"), row.getString("query_text")));
|
row.getInteger("query_id"), row.getString("query_text")));
|
||||||
}
|
|
||||||
|
|
||||||
public Mono<Void> insertIfAbsent(CloudQuery query) {
|
|
||||||
return database.preparedUpdate(
|
|
||||||
INSERT_IF_ABSENT,
|
|
||||||
Tuple.of(query.queryId(), query.query()))
|
|
||||||
.then();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.cygnus.cloud.query;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import org.springframework.boot.ApplicationArguments;
|
||||||
|
import org.springframework.boot.ApplicationRunner;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class QueryCatalogSchemaInitializer implements ApplicationRunner {
|
||||||
|
|
||||||
|
private final QueryCatalogRepository repository;
|
||||||
|
|
||||||
|
public QueryCatalogSchemaInitializer(QueryCatalogRepository repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(ApplicationArguments arguments) {
|
||||||
|
repository.initialize()
|
||||||
|
.block(Duration.ofMinutes(2));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,8 +6,7 @@ import org.springframework.web.bind.annotation.ResponseStatus;
|
|||||||
@ResponseStatus(HttpStatus.NOT_FOUND)
|
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||||
public class QueryNotFoundException extends RuntimeException {
|
public class QueryNotFoundException extends RuntimeException {
|
||||||
|
|
||||||
public QueryNotFoundException(String queryId) {
|
public QueryNotFoundException(int queryId) {
|
||||||
super("Query was not found: " + queryId);
|
super("Query was not found: " + queryId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
CREATE SCHEMA IF NOT EXISTS platform;
|
CREATE SCHEMA IF NOT EXISTS platform;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS platform.application_query (
|
CREATE TABLE IF NOT EXISTS platform.application_query (
|
||||||
query_id varchar(100) PRIMARY KEY,
|
query_id integer PRIMARY KEY,
|
||||||
query_text text NOT NULL,
|
query_text text NOT NULL,
|
||||||
enabled boolean NOT NULL DEFAULT true,
|
enabled boolean NOT NULL DEFAULT true,
|
||||||
created_at timestamptz NOT NULL DEFAULT current_timestamp,
|
created_at timestamptz NOT NULL DEFAULT current_timestamp,
|
||||||
updated_at timestamptz NOT NULL DEFAULT current_timestamp,
|
updated_at timestamptz NOT NULL DEFAULT current_timestamp,
|
||||||
CONSTRAINT ck_platform_application_query_id
|
CONSTRAINT ck_platform_application_query_id
|
||||||
CHECK (query_id ~ '^[A-Za-z0-9._-]+$'),
|
CHECK (query_id > 0),
|
||||||
CONSTRAINT ck_platform_application_query_text
|
CONSTRAINT ck_platform_application_query_text
|
||||||
CHECK (length(btrim(query_text)) > 0)
|
CHECK (length(btrim(query_text)) > 0)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
DO $$
|
||||||
|
DECLARE
|
||||||
|
current_type text;
|
||||||
|
BEGIN
|
||||||
|
SELECT data_type
|
||||||
|
INTO current_type
|
||||||
|
FROM information_schema.columns
|
||||||
|
WHERE table_schema = 'platform'
|
||||||
|
AND table_name = 'application_query'
|
||||||
|
AND column_name = 'query_id';
|
||||||
|
|
||||||
|
IF current_type IN ('character varying', 'text') THEN
|
||||||
|
ALTER TABLE platform.application_query
|
||||||
|
DROP CONSTRAINT IF EXISTS ck_platform_application_query_id;
|
||||||
|
|
||||||
|
ALTER TABLE platform.application_query
|
||||||
|
ALTER COLUMN query_id TYPE integer
|
||||||
|
USING regexp_replace(query_id, '^Query', '')::integer;
|
||||||
|
|
||||||
|
ALTER TABLE platform.application_query
|
||||||
|
ADD CONSTRAINT ck_platform_application_query_id
|
||||||
|
CHECK (query_id > 0);
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,20 +0,0 @@
|
|||||||
package com.cygnus.cloud.query;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
class QueryCatalogInitializerTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void readsUniqueNumericQueryIdsFromCloudCatalog() {
|
|
||||||
var queries = new QueryCatalogInitializer(null).readCatalog();
|
|
||||||
|
|
||||||
assertThat(queries).hasSize(455);
|
|
||||||
assertThat(queries).extracting(CloudQuery::queryId).doesNotHaveDuplicates();
|
|
||||||
assertThat(queries).anySatisfy(query -> {
|
|
||||||
assertThat(query.queryId()).isEqualTo("Query3");
|
|
||||||
assertThat(query.query()).startsWith("select!C0L!select portfolio_id");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -192,6 +192,7 @@ public class DeploymentWriter {
|
|||||||
appendEnvironment(env, "REDIS_HOST", runtime.redisHost());
|
appendEnvironment(env, "REDIS_HOST", runtime.redisHost());
|
||||||
appendEnvironment(env, "REDIS_PORT", runtime.redisPort());
|
appendEnvironment(env, "REDIS_PORT", runtime.redisPort());
|
||||||
appendEnvironment(env, "REDIS_PASSWORD", runtime.redisPassword());
|
appendEnvironment(env, "REDIS_PASSWORD", runtime.redisPassword());
|
||||||
|
appendEnvironment(env, "REDIS_DATABASE", "1");
|
||||||
appendEnvironment(env, "REDIS_SSL", Boolean.toString(runtime.redisSsl()));
|
appendEnvironment(env, "REDIS_SSL", Boolean.toString(runtime.redisSsl()));
|
||||||
appendEnvironment(
|
appendEnvironment(
|
||||||
env, "CYGNUS_CLOUD_BASE_URL", normalizedCloudServiceUrl(runtime));
|
env, "CYGNUS_CLOUD_BASE_URL", normalizedCloudServiceUrl(runtime));
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ class DeploymentWriterTest {
|
|||||||
.contains("MATRIX_IMAGE=\"registry.example.com/matrix:1.0.0\"")
|
.contains("MATRIX_IMAGE=\"registry.example.com/matrix:1.0.0\"")
|
||||||
.contains("MATRIX_DB_URL=\"jdbc:postgresql://db:5432/matrix\"")
|
.contains("MATRIX_DB_URL=\"jdbc:postgresql://db:5432/matrix\"")
|
||||||
.contains("REDIS_HOST=\"redis\"")
|
.contains("REDIS_HOST=\"redis\"")
|
||||||
|
.contains("REDIS_DATABASE=\"1\"")
|
||||||
.contains("CYGNUS_CLOUD_BASE_URL=\"http://host.docker.internal:8090\"")
|
.contains("CYGNUS_CLOUD_BASE_URL=\"http://host.docker.internal:8090\"")
|
||||||
.contains("CYGNUS_TOKEN_URL=\"http://host.docker.internal:8090/oauth2/token\"")
|
.contains("CYGNUS_TOKEN_URL=\"http://host.docker.internal:8090/oauth2/token\"")
|
||||||
.contains("CYGNUS_CLIENT_ID=\"matrix-client\"")
|
.contains("CYGNUS_CLIENT_ID=\"matrix-client\"")
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -173,16 +173,6 @@
|
|||||||
<build>
|
<build>
|
||||||
<finalName>matrix</finalName>
|
<finalName>matrix</finalName>
|
||||||
|
|
||||||
<resources>
|
|
||||||
<resource>
|
|
||||||
<directory>src/main/resources</directory>
|
|
||||||
<excludes>
|
|
||||||
<!-- Queries are supplied by the cloud query catalog at runtime. -->
|
|
||||||
<exclude>matrix/nimble/conf/nimble.qry</exclude>
|
|
||||||
</excludes>
|
|
||||||
</resource>
|
|
||||||
</resources>
|
|
||||||
|
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
|||||||
@@ -16,11 +16,16 @@ public class OnPremRedisConfiguration {
|
|||||||
@Value("${REDIS_HOST:192.168.0.111}") String host,
|
@Value("${REDIS_HOST:192.168.0.111}") String host,
|
||||||
@Value("${REDIS_PORT:7901}") int port,
|
@Value("${REDIS_PORT:7901}") int port,
|
||||||
@Value("${REDIS_PASSWORD:}") String password,
|
@Value("${REDIS_PASSWORD:}") String password,
|
||||||
|
@Value("${REDIS_DATABASE:1}") int database,
|
||||||
@Value("${REDIS_SSL:false}") boolean ssl,
|
@Value("${REDIS_SSL:false}") boolean ssl,
|
||||||
@Value("${REDIS_CONNECT_TIMEOUT_SECONDS:3}") long connectTimeoutSeconds) {
|
@Value("${REDIS_CONNECT_TIMEOUT_SECONDS:3}") long connectTimeoutSeconds) {
|
||||||
|
if (database < 0) {
|
||||||
|
throw new IllegalArgumentException("REDIS_DATABASE must be zero or greater");
|
||||||
|
}
|
||||||
RedisURI.Builder uri = RedisURI.builder()
|
RedisURI.Builder uri = RedisURI.builder()
|
||||||
.withHost(host)
|
.withHost(host)
|
||||||
.withPort(port)
|
.withPort(port)
|
||||||
|
.withDatabase(database)
|
||||||
.withSsl(ssl)
|
.withSsl(ssl)
|
||||||
.withTimeout(Duration.ofSeconds(connectTimeoutSeconds));
|
.withTimeout(Duration.ofSeconds(connectTimeoutSeconds));
|
||||||
if (password != null && !password.isBlank()) {
|
if (password != null && !password.isBlank()) {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public final class CloudQuerySource implements QuerySource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String fetch(String queryId) {
|
public String fetch(int queryId) {
|
||||||
return client.fetchQuery(queryId)
|
return client.fetchQuery(queryId)
|
||||||
.map(response -> response.query())
|
.map(response -> response.query())
|
||||||
.blockOptional()
|
.blockOptional()
|
||||||
@@ -20,4 +20,3 @@ public final class CloudQuerySource implements QuerySource {
|
|||||||
"Cloud query was empty: " + queryId));
|
"Cloud query was empty: " + queryId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,5 @@ package matrix.nimble.query;
|
|||||||
|
|
||||||
public interface QueryProvider {
|
public interface QueryProvider {
|
||||||
|
|
||||||
String getQuery(String queryId);
|
String getQuery(int queryId);
|
||||||
|
|
||||||
default String getQuery(int queryId) {
|
|
||||||
return getQuery("Query" + queryId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,5 @@ package matrix.nimble.query;
|
|||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface QuerySource {
|
public interface QuerySource {
|
||||||
|
|
||||||
String fetch(String queryId);
|
String fetch(int queryId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,27 +30,27 @@ public final class RedisCachingQueryProvider implements QueryProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getQuery(String queryId) {
|
public String getQuery(int queryId) {
|
||||||
String normalized = normalize(queryId);
|
String cacheKey = cacheKey(queryId);
|
||||||
Optional<String> cached = cached(normalized);
|
Optional<String> cached = cached(cacheKey);
|
||||||
if (cached.isPresent()) {
|
if (cached.isPresent()) {
|
||||||
return cached.get();
|
return cached.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (lock(normalized)) {
|
synchronized (lock(cacheKey)) {
|
||||||
cached = cached(normalized);
|
cached = cached(cacheKey);
|
||||||
if (cached.isPresent()) {
|
if (cached.isPresent()) {
|
||||||
return cached.get();
|
return cached.get();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
String query = cloudSource.fetch(normalized);
|
String query = cloudSource.fetch(queryId);
|
||||||
cache.put(normalized, cipher.encrypt(normalized, query), QUERY_TTL);
|
cache.put(cacheKey, cipher.encrypt(cacheKey, query), QUERY_TTL);
|
||||||
return query;
|
return query;
|
||||||
} catch (QueryProviderException exception) {
|
} catch (QueryProviderException exception) {
|
||||||
throw exception;
|
throw exception;
|
||||||
} catch (RuntimeException exception) {
|
} catch (RuntimeException exception) {
|
||||||
throw new QueryProviderException(
|
throw new QueryProviderException(
|
||||||
"Unable to retrieve query: " + normalized, exception);
|
"Unable to retrieve query: " + queryId, exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,10 +72,10 @@ public final class RedisCachingQueryProvider implements QueryProvider {
|
|||||||
return locks[(queryId.hashCode() & Integer.MAX_VALUE) % locks.length];
|
return locks[(queryId.hashCode() & Integer.MAX_VALUE) % locks.length];
|
||||||
}
|
}
|
||||||
|
|
||||||
private String normalize(String queryId) {
|
private String cacheKey(int queryId) {
|
||||||
if (queryId == null || !queryId.matches("^[A-Za-z0-9._-]+$")) {
|
if (queryId <= 0) {
|
||||||
throw new IllegalArgumentException("Invalid query ID");
|
throw new IllegalArgumentException("Invalid query ID");
|
||||||
}
|
}
|
||||||
return queryId;
|
return Integer.toString(queryId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,6 @@ class FileFunctionsQueryCacheTest {
|
|||||||
void delegatesLegacyQueryLookupToQueryProvider() throws Exception {
|
void delegatesLegacyQueryLookupToQueryProvider() throws Exception {
|
||||||
QueryProviders.install(queryId -> "provided:" + queryId);
|
QueryProviders.install(queryId -> "provided:" + queryId);
|
||||||
FileFunctions files = new FileFunctions("TEST");
|
FileFunctions files = new FileFunctions("TEST");
|
||||||
assertEquals("provided:Query3", files.GetQuery(3));
|
assertEquals("provided:3", files.GetQuery(3));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,13 +15,13 @@ class AesGcmQueryCipherTest {
|
|||||||
Arrays.fill(key, (byte) 7);
|
Arrays.fill(key, (byte) 7);
|
||||||
AesGcmQueryCipher cipher = new AesGcmQueryCipher(key);
|
AesGcmQueryCipher cipher = new AesGcmQueryCipher(key);
|
||||||
|
|
||||||
String encrypted = cipher.encrypt("Query3", "select * from portfolio");
|
String encrypted = cipher.encrypt("3", "select * from portfolio");
|
||||||
|
|
||||||
assertNotEquals("select * from portfolio", encrypted);
|
assertNotEquals("select * from portfolio", encrypted);
|
||||||
assertTrue(encrypted.startsWith("v1."));
|
assertTrue(encrypted.startsWith("v1."));
|
||||||
org.junit.jupiter.api.Assertions.assertEquals(
|
org.junit.jupiter.api.Assertions.assertEquals(
|
||||||
"select * from portfolio", cipher.decrypt("Query3", encrypted));
|
"select * from portfolio", cipher.decrypt("3", encrypted));
|
||||||
assertThrows(IllegalStateException.class,
|
assertThrows(IllegalStateException.class,
|
||||||
() -> cipher.decrypt("Query4", encrypted));
|
() -> cipher.decrypt("4", encrypted));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,13 +27,13 @@ class RedisCachingQueryProviderTest {
|
|||||||
},
|
},
|
||||||
cipher);
|
cipher);
|
||||||
|
|
||||||
assertEquals("select!C0L!select 1", provider.getQuery("Query10"));
|
assertEquals("select!C0L!select 1", provider.getQuery(10));
|
||||||
assertEquals("select!C0L!select 1", provider.getQuery("Query10"));
|
assertEquals("select!C0L!select 1", provider.getQuery(10));
|
||||||
|
|
||||||
assertEquals(1, cloudCalls.get());
|
assertEquals(1, cloudCalls.get());
|
||||||
assertEquals(Duration.ofHours(3), cache.ttl.get());
|
assertEquals(Duration.ofHours(3), cache.ttl.get());
|
||||||
org.junit.jupiter.api.Assertions.assertNotEquals(
|
org.junit.jupiter.api.Assertions.assertNotEquals(
|
||||||
"select!C0L!select 1", cache.values.get("Query10"));
|
"select!C0L!select 1", cache.values.get("10"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -55,7 +55,7 @@ class RedisCachingQueryProviderTest {
|
|||||||
var executor = Executors.newFixedThreadPool(8);
|
var executor = Executors.newFixedThreadPool(8);
|
||||||
try {
|
try {
|
||||||
for (int index = 0; index < 20; index++) {
|
for (int index = 0; index < 20; index++) {
|
||||||
executor.submit(() -> provider.getQuery("Query20"));
|
executor.submit(() -> provider.getQuery(20));
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
executor.shutdown();
|
executor.shutdown();
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user