Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,11 @@
<artifactId>commons-lang3</artifactId>
<version>3.11</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-observation</artifactId>
<version>1.17.0</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
Expand All @@ -252,19 +257,19 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.0</version>
<version>5.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.7.0</version>
<version>5.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.7.0</version>
<version>5.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
Expand All @@ -279,6 +284,12 @@
<version>2.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-integration-test</artifactId>
<version>1.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/ru/rt/restream/reindexer/ReindexerConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package ru.rt.restream.reindexer;

import io.micrometer.observation.ObservationRegistry;
import ru.rt.restream.reindexer.binding.Binding;
import ru.rt.restream.reindexer.binding.builtin.Builtin;
import ru.rt.restream.reindexer.binding.builtin.server.BuiltinServer;
Expand Down Expand Up @@ -58,6 +59,8 @@ public final class ReindexerConfiguration {

private SSLSocketFactory sslSocketFactory;

private ObservationRegistry observationRegistry = ObservationRegistry.NOOP;

private ReindexerConfiguration() {

}
Expand Down Expand Up @@ -176,6 +179,18 @@ public ReindexerConfiguration sslSocketFactory(SSLSocketFactory sslSocketFactory
return this;
}

/**
* Configure an {@link ObservationRegistry} to record connector's metrics and traces.
* Defaults to {@link ObservationRegistry#NOOP}.
*
* @param observationRegistry the {@link ObservationRegistry} to use
* @return the {@link ReindexerConfiguration} for further customizations
*/
public ReindexerConfiguration observationRegistry(ObservationRegistry observationRegistry) {
this.observationRegistry = Objects.requireNonNull(observationRegistry, "observationRegistry cannot be null");
return this;
}

/**
* Build and return reindexer connector instance.
*
Expand Down Expand Up @@ -210,6 +225,7 @@ private Binding getBinding(String protocol, List<URI> uris) {
.urls(urls)
.allowUnlistedDataSource(allowUnlistedDataSource)
.sslSocketFactory(sslSocketFactory)
.observationRegistry(observationRegistry)
.build();
return new Cproto(dataSourceFactory, dataSourceConfig, connectionPoolSize, requestTimeout);
case "builtin":
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/ru/rt/restream/reindexer/binding/Binding.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,16 @@ public interface Binding {

int RESULTS_NEED_OUTPUT_RANK = 0x400;

int ADD_TX_ITEM = 26;

int UPDATE_QUERY_TX = 31;

int DELETE_QUERY_TX = 30;

int COMMIT_TX = 27;

int ROLLBACK_TX = 28;

/**
* Open or create a new namespace and indexes based on passed definition.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2020-present Restream
*
* 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 ru.rt.restream.reindexer.binding.cproto;

import io.micrometer.observation.transport.Kind;
import io.micrometer.observation.transport.RequestReplySenderContext;
import lombok.Getter;
import ru.rt.restream.reindexer.ReindexerResponse;

/**
* A context for command observation.
*/
@Getter
final class CommandObservationContext extends RequestReplySenderContext<Object, ReindexerResponse> {

private final int command;

private final Object[] arguments;

CommandObservationContext(int command, Object[] arguments) {
super((carrier, key, value) -> {}, Kind.CLIENT);
this.command = command;
this.arguments = arguments;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
/*
* Copyright 2020-present Restream
*
* 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 ru.rt.restream.reindexer.binding.cproto;

import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import io.micrometer.common.KeyValues;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationConvention;
import org.apache.commons.lang3.ArrayUtils;
import ru.rt.restream.reindexer.binding.Binding;
import ru.rt.restream.reindexer.binding.definition.NamespaceDefinition;

import java.net.URI;

/**
* An {@link ObservationConvention} to handle {@link CommandObservationContext} observations.
*/
final class CommandObservationConvention implements ObservationConvention<CommandObservationContext> {

private static final String OBSERVATION_NAME = "reindexer.rpc";

private final Gson gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create();

@Override
public String getName() {
return OBSERVATION_NAME;
}

@Override
public String getContextualName(CommandObservationContext context) {
return OBSERVATION_NAME + "." + getCommandName(context.getCommand());
}

@Override
public KeyValues getLowCardinalityKeyValues(CommandObservationContext context) {
String command = getCommandName(context.getCommand());
String collection = getCollectionName(context);
String responseStatusCode = context.getResponse() != null ? String.valueOf(context.getResponse().getCode()) : "";
String networkTransport = "";
String namespace = "";
String serverAddress = "";
String serverPort = "";
if (context.getRemoteServiceAddress() != null) {
URI uri = URI.create(context.getRemoteServiceAddress());
networkTransport = uri.getScheme();
namespace = uri.getPath().substring(1);
serverAddress = uri.getHost();
serverPort = String.valueOf(uri.getPort());
}
return KeyValues.of(
"db.system.name", "reindexer",
"db.command.name", command,
"db.namespace", namespace,
"db.collection.name", collection,
"network.transport", networkTransport,
"server.address", serverAddress,
"server.port", serverPort,
"db.response.status_code", responseStatusCode
);
}

@Override
public KeyValues getHighCardinalityKeyValues(CommandObservationContext context) {
String transactionId = getTransactionId(context);
String requestId = getRequestId(context);
return KeyValues.of(
"db.reindexer.tx_id", transactionId,
"db.reindexer.rq_id", requestId
);
}

@Override
public boolean supportsContext(Observation.Context context) {
return context instanceof CommandObservationContext;
}

private String getCommandName(int command) {
switch (command) {
case Binding.OPEN_NAMESPACE:
return "openNamespace";
case Binding.CLOSE_NAMESPACE:
return "closeNamespace";
case Binding.DROP_NAMESPACE:
return "dropNamespace";
case Binding.ADD_INDEX:
return "addIndex";
case Binding.UPDATE_INDEX:
return "updateIndex";
case Binding.DROP_INDEX:
return "dropIndex";
case Binding.MODIFY_ITEM:
return "modifyItem";
case Binding.SELECT:
return "selectQuery";
case Binding.UPDATE_QUERY:
return "updateQuery";
case Binding.UPDATE_QUERY_TX:
return "updateQueryTx";
case Binding.DELETE_QUERY:
return "deleteQuery";
case Binding.DELETE_QUERY_TX:
return "deleteQueryTx";
case Binding.SELECT_SQL:
return "selectSql";
case Binding.FETCH_RESULTS:
return "fetchResults";
case Binding.CLOSE_RESULTS:
return "closeResults";
case Binding.START_TRANSACTION:
return "startTransaction";
case Binding.ADD_TX_ITEM:
return "addTxItem";
case Binding.COMMIT_TX:
return "commitTx";
case Binding.ROLLBACK_TX:
return "rollbackTx";
case Binding.PING:
return "ping";
case Binding.GET_META:
return "getMeta";
case Binding.PUT_META:
return "putMeta";
default:
// Fallback to command code.
return String.valueOf(command);
}
}

private String getCollectionName(CommandObservationContext context) {
switch (context.getCommand()) {
case Binding.OPEN_NAMESPACE:
case Binding.DROP_NAMESPACE:
case Binding.CLOSE_NAMESPACE:
case Binding.ADD_INDEX:
case Binding.UPDATE_INDEX:
case Binding.DROP_INDEX:
case Binding.MODIFY_ITEM:
case Binding.PUT_META:
case Binding.GET_META:
case Binding.START_TRANSACTION:
// Command arguments[0] is the namespace.
String value = ArrayUtils.get(context.getArguments(), 0, "").toString();
if (context.getCommand() == Binding.OPEN_NAMESPACE) {
// For openNamespace command, the [0] argument is a JSON string representing the namespace definition.
try {
NamespaceDefinition namespace = gson.fromJson(value, NamespaceDefinition.class);
return namespace.getName() != null ? namespace.getName() : "";
} catch (JsonSyntaxException ignored) {
// Return an empty string if the JSON string is invalid.
return "";
}
}
return value;
default:
return "";
}
}

private String getTransactionId(CommandObservationContext context) {
switch (context.getCommand()) {
case Binding.ADD_TX_ITEM:
// Command arguments[5] is the transaction id.
return ArrayUtils.get(context.getArguments(), 5, "").toString();
case Binding.UPDATE_QUERY_TX:
case Binding.DELETE_QUERY_TX:
// Command arguments[1] is the transaction id.
return ArrayUtils.get(context.getArguments(), 1, "").toString();
case Binding.START_TRANSACTION:
// Response arguments[0] is the transaction id.
return context.getResponse() != null
? ArrayUtils.get(context.getResponse().getArguments(), 0, "").toString()
: "";
case Binding.COMMIT_TX:
case Binding.ROLLBACK_TX:
// Command arguments[0] is the transaction id.
return ArrayUtils.get(context.getArguments(), 0, "").toString();
default:
return "";
}
}

private String getRequestId(CommandObservationContext context) {
switch (context.getCommand()) {
case Binding.FETCH_RESULTS:
case Binding.CLOSE_RESULTS:
// Command arguments[0] is the request id.
return ArrayUtils.get(context.getArguments(), 0, "").toString();
case Binding.SELECT:
case Binding.SELECT_SQL:
// Response arguments[1] is the request id.
return context.getResponse() != null
? ArrayUtils.get(context.getResponse().getArguments(), 1, "").toString()
: "";
default:
return "";
}
}

}
Loading
Loading