Skip to content

Commit

Permalink
Issue helidon-io#6991 - Blocking DB Client: Tracing support
Browse files Browse the repository at this point in the history
Signed-off-by: Tomas Kraus <[email protected]>
  • Loading branch information
Tomas-Kraus committed Jul 7, 2023
1 parent 407869a commit 760ce55
Show file tree
Hide file tree
Showing 15 changed files with 736 additions and 40 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
/*
* Copyright (c) 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.helidon.dbclient.common;

import java.util.EnumSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import io.helidon.common.Builder;
import io.helidon.config.Config;
import io.helidon.dbclient.DbClientService;
import io.helidon.dbclient.DbClientServiceContext;
import io.helidon.dbclient.DbStatementType;

/**
* A base implementation of a client service that supports configuration
* of execution based on a statement name pattern and statement types.
*/
public abstract class CommonService implements DbClientService {

private final Predicate<DbClientServiceContext> predicate;

/**
* Create a new instance based on the builder base each implementation must extend.
*
* @param builder builder to configure predicate to use
*/
protected CommonService(CommonServiceBuilder<?> builder) {
this.predicate = builder.predicate();
}

@Override
public final DbClientServiceContext statement(DbClientServiceContext context) {
if (predicate.test(context)) {
return apply(context);
}
return context;
}

/**
* This method is only invoked if the predicate for this service
* was passed.
*
* @param context db client invocation context
* @return single with the new context (or the same one if not modified)
* @see #statement(io.helidon.dbclient.DbClientServiceContext)
*/
protected abstract DbClientServiceContext apply(DbClientServiceContext context);

/**
* A base class for builders of {@link CommonService}.
*
* @param <B> type of the builder extending this class
*/
public abstract static class CommonServiceBuilder<B extends CommonServiceBuilder<B>> implements Builder<B, CommonService> {
private static final Predicate<DbClientServiceContext> YES = it -> true;
private static final Predicate<DbClientServiceContext> NO = it -> false;

// we can filter by statement name
private Set<String> statementNames = new LinkedHashSet<>();
// and statement type
private Set<DbStatementType> statementTypes = EnumSet.noneOf(DbStatementType.class);
private Predicate<DbClientServiceContext> predicate;
private boolean enabled = true;

/**
* Default constructor.
*/
protected CommonServiceBuilder() {
}

/**
* Configure this client service from config.
* <p>
* Supported keys:
* <table class="config">
* <caption>DB Client Service configuration options</caption>
* <tr>
* <th>key</th>
* <th>default value</th>
* <th>description</th>
* </tr>
* <tr>
* <td>statement-names</td>
* <td>&nbsp;</td>
* <td>An array of statement name patterns to apply this service for. If undefined, service
* would be executed for all statements.
* See {@link #statementNames(String...)} and {@link java.util.regex.Pattern}</td>
* </tr>
* <tr>
* <td>statement-types</td>
* <td>&nbsp;</td>
* <td>An array of statement types to apply this service for. If undefined, service
* would be executed for all statements.
* See {@link #statementTypes(io.helidon.dbclient.DbStatementType...)}.</td>
* </tr>
* <tr>
* <td>enabled</td>
* <td>{@code true}</td>
* <td>Whether this client service is enabled. See {@link #enabled(boolean)}</td>
* </tr>
* </table>
*
* @param config configuration on the node of this service
* @return updated builder instance
*/
public B config(Config config) {
config.get("statement-names").asList(String.class).ifPresent(this::statementNames);
config.get("statement-types").asList(cfg -> cfg.asString().map(DbStatementType::valueOf).get())
.ifPresent(this::statementTypes);
config.get("enabled").asBoolean().ifPresent(this::enabled);
return identity();
}

/**
* Configure a predicate whose result will be used to decide whether to
* trigger this service or not.
* <p>
* When a predicate is explicitly configured, {@link #statementNames(String...)}
* and {@link #statementTypes(io.helidon.dbclient.DbStatementType...)} is ignored.
*
* @param predicate predicate that should return {@code true} to enable this
* service, or {@code false} to disable it
* @return updated builder instance
*/
public B statementPredicate(Predicate<DbClientServiceContext> predicate) {
this.predicate = predicate;
return identity();
}

/**
* Configure statement types this service will be triggered for.
* If an explicit {@link #statementPredicate(java.util.function.Predicate)} is configured,
* this method is ignored.
*
* @param types types that trigger this service
* @return updated builder instance
*/
public B statementTypes(DbStatementType... types) {
return statementTypes(List.of(types));
}

/**
* Configure statement name patterns this service will be triggered for.
* If an explicit {@link #statementPredicate(java.util.function.Predicate)} is configured,
* this method is ignored.
*
* @param names name patterns (as in {@link java.util.regex.Pattern}) that trigger this service
* @return updated builder instance
*/
public B statementNames(String... names) {
return statementNames(List.of(names));
}

/**
* Configure whether this service is enabled or not.
*
* @param enabled whether to enable this service or disable it, {@code true} by default
*/
public void enabled(boolean enabled) {
this.enabled = enabled;
}

/**
* Configures statement types from configuration.
*
* @param types types to add for this service
* @return updated builder instance
*/
protected B statementTypes(List<DbStatementType> types) {
this.statementTypes.addAll(types);
return identity();
}

/**
* Configures statement name patterns from configuration.
*
* @param names names to add for this service
* @return updated builder instance
*/
protected B statementNames(List<String> names) {
this.statementNames.addAll(names);
return identity();
}

/**
* Set of statement name patterns.
*
* @return configured statement names
*/
protected Set<String> statementNames() {
return statementNames;
}

/**
* Set of statement types.
*
* @return configured statement types
*/
protected Set<DbStatementType> statementTypes() {
return statementTypes;
}

/**
* Predicate used to build a client service.
* <p>
* The predicate always returns {@code false} if service is disabled.
* <p>
* The predicate is obtained from the configured predicate using
* {@link #statementPredicate(java.util.function.Predicate)},
* if none is configured, it is created from configured statement types and statement names.
* If none are configured, the predicate just returns {@code true}.
*
* @return predicate to check whether this service should be invoked for current statement context
*/
protected Predicate<DbClientServiceContext> predicate() {
if (!enabled) {
return NO;
}

if (null != predicate) {
return predicate;
}

List<Pattern> namePatterns = statementNames.stream()
.map(Pattern::compile)
.collect(Collectors.toList());

Set<DbStatementType> types = EnumSet.copyOf(statementTypes);

Predicate<DbClientServiceContext> namePredicate;
Predicate<DbClientServiceContext> typePredicate;
if (namePatterns.isEmpty()) {
namePredicate = YES;
} else {
namePredicate = it -> {
String statementName = it.statementName();
for (Pattern namePattern : namePatterns) {
if (namePattern.matcher(statementName).matches()) {
return true;
}
}
return false;
};
}
if (types.isEmpty()) {
typePredicate = YES;
} else {
typePredicate = it -> types.contains(it.statementType());
}

return context -> namePredicate.test(context) && typePredicate.test(context);
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletionStage;

import io.helidon.common.context.Context;

Expand Down Expand Up @@ -58,20 +59,20 @@ public interface DbClientServiceContext {
*/
String statement();

// /**
// * A stage that is completed once the statement finishes execution.
// *
// * @return statement future
// */
// CompletionStage<Void> statementFuture();
//
// /**
// * A stage that is completed once the results were fully read. The number returns either the number of modified
// * records or the number of records actually read.
// *
// * @return stage that completes once all query results were processed.
// */
// CompletionStage<Long> resultFuture();
/**
* A stage that is completed once the statement finishes execution.
*
* @return statement future
*/
CompletionStage<Void> statementFuture();

/**
* A stage that is completed once the results were fully read. The number returns either the number of modified
* records or the number of records actually read.
*
* @return stage that completes once all query results were processed.
*/
CompletionStage<Long> resultFuture();

/**
* Indexed parameters (if used).
Expand Down Expand Up @@ -123,21 +124,21 @@ public interface DbClientServiceContext {
*/
DbClientServiceContext statementName(String newName);

// /**
// * Set a new future to mark completion of the statement.
// *
// * @param statementFuture future
// * @return updated interceptor context
// */
// DbClientServiceContext statementFuture(CompletionStage<Void> statementFuture);
//
// /**
// * Set a new future to mark completion of the result (e.g. query or number of modified records).
// *
// * @param queryFuture future
// * @return updated interceptor context
// */
// DbClientServiceContext resultFuture(CompletionStage<Long> queryFuture);
/**
* Set a new future to mark completion of the statement.
*
* @param statementFuture future
* @return updated interceptor context
*/
DbClientServiceContext statementFuture(CompletionStage<Void> statementFuture);

/**
* Set a new future to mark completion of the result (e.g. query or number of modified records).
*
* @param queryFuture future
* @return updated interceptor context
*/
DbClientServiceContext resultFuture(CompletionStage<Long> queryFuture);

/**
* Set a new statement with indexed parameters to be used.
Expand Down
2 changes: 1 addition & 1 deletion dbclient/health/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,4 @@
</plugins>
</build>

</project>
</project>
Loading

0 comments on commit 760ce55

Please sign in to comment.